Skip to content

Commit

Permalink
Implement strict_types for PHP8 support. Updated unit tests to PHPUni…
Browse files Browse the repository at this point in the history
…t 9.5. Performance improvements & modernization courtesy of @spider4216. Dependency versions bump. Package version bump.
  • Loading branch information
tcdent committed Apr 9, 2022
1 parent 4522e85 commit ad1b152
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 57 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.phpunit.result.cache
10 changes: 6 additions & 4 deletions README.markdown
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
PHP REST Client
===============
https://github.com/tcdent/php-restclient
(c) 2013-2017 Travis Dent <[email protected]>
(c) 2013-2022 Travis Dent <[email protected]>

Installation
-----------
Requires PHP 8.0 or above. Use [0.1.7](https://github.com/tcdent/php-restclient/releases/tag/0.1.7) for legacy versions of PHP.

``` sh
$ php composer.phar require tcdent/php-restclient
$ composer require tcdent/php-restclient
```


Expand Down Expand Up @@ -240,8 +242,8 @@ Tests
The test package includes a simple server script which returns debug information for verifying functionality. Start the server first, then run tests:

``` sh
$ php -S localhost:8888 test.php
$ phpunit test
$ php -S localhost:8888 RestClientTest.php
$ phpunit RestClientTest
```

* Requires PHP > 5.5.7 in order for `getallheaders` data to populate.
Expand Down
32 changes: 17 additions & 15 deletions test.php → RestClientTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

use PHPUnit\Framework\TestCase;

// Test are comprised of two components: a simple json response for testing
// interaction via the built-in PHP server, and PHPUnit test methods.

Expand All @@ -8,14 +10,14 @@
// JSON debug information for validating behavior.
if(php_sapi_name() == 'cli-server'){
header("Content-Type: application/json");
die(json_encode(array(
die(json_encode([
'SERVER' => $_SERVER,
'REQUEST' => $_REQUEST,
'POST' => $_POST,
'GET' => $_GET,
'body' => file_get_contents('php://input'),
'headers' => getallheaders()
)));
]));
}


Expand All @@ -28,9 +30,9 @@
if(!isset($TEST_SERVER_URL))
$TEST_SERVER_URL = "http://localhost:8888";

class RestClientTest extends PHPUnit_Framework_TestCase {
class RestClientTest extends TestCase {

public function test_get(){
public function test_get() : void {
global $TEST_SERVER_URL;

$api = new RestClient;
Expand All @@ -47,7 +49,7 @@ public function test_get(){
$response_json->body);
}

public function test_post(){
public function test_post() : void {
global $TEST_SERVER_URL;

$api = new RestClient;
Expand All @@ -62,7 +64,7 @@ public function test_post(){
$response_json->body);
}

public function test_put(){
public function test_put() : void {
global $TEST_SERVER_URL;

$api = new RestClient;
Expand All @@ -76,7 +78,7 @@ public function test_put(){
$response_json->body);
}

public function test_delete(){
public function test_delete() : void {
global $TEST_SERVER_URL;

$api = new RestClient;
Expand All @@ -90,7 +92,7 @@ public function test_delete(){
$response_json->body);
}

public function test_user_agent(){
public function test_user_agent() : void {
global $TEST_SERVER_URL;

$api = new RestClient(array(
Expand All @@ -103,7 +105,7 @@ public function test_user_agent(){
$response_json->headers->{"User-Agent"});
}

public function test_json_patch(){
public function test_json_patch() : void {
global $TEST_SERVER_URL;

$api = new RestClient;
Expand All @@ -124,7 +126,7 @@ public function test_json_patch(){
$response_json->body);
}

public function test_json_post(){
public function test_json_post() : void {
global $TEST_SERVER_URL;

$api = new RestClient;
Expand All @@ -140,7 +142,7 @@ public function test_json_post(){
$response_json->body);
}

public function test_multiheader_response(){
public function test_multiheader_response() : void {
$RESPONSE = "HTTP/1.1 200 OK\r\nContent-type: text/json\r\nContent-Type: application/json\r\n\r\nbody";

$api = new RestClient;
Expand All @@ -155,7 +157,7 @@ public function test_multiheader_response(){
$this->assertEquals("body", $api->response);
}

public function test_multistatus_response(){
public function test_multistatus_response() : void {
$RESPONSE = "HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nContent-Type: application/json\r\n\r\nbody";

$api = new RestClient;
Expand All @@ -171,7 +173,7 @@ public function test_multistatus_response(){
$this->assertEquals("body", $api->response);
}

public function test_status_only_response(){
public function test_status_only_response() : void {
$RESPONSE = "HTTP/1.1 100 Continue\r\n\r\n";

$api = new RestClient;
Expand All @@ -184,7 +186,7 @@ public function test_status_only_response(){
$this->assertEquals("", $api->response);
}

public function test_build_indexed_queries(){
public function test_build_indexed_queries() : void {
global $TEST_SERVER_URL;

$api = new RestClient(['build_indexed_queries' => TRUE]);
Expand All @@ -197,7 +199,7 @@ public function test_build_indexed_queries(){
$response_json->SERVER->QUERY_STRING);
}

public function test_build_non_indexed_queries(){
public function test_build_non_indexed_queries() : void {
global $TEST_SERVER_URL;

$api = new RestClient;
Expand Down
10 changes: 5 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tcdent/php-restclient",
"version": "0.1.7",
"version": "0.1.8",
"description": "A generic REST API client for PHP",
"type": "library",
"keywords": ["REST", "api", "client", "curl", "JSON", "XML"],
Expand All @@ -17,15 +17,15 @@
"issues": "http://github.com/tcdent/php-restclient/issues"
},
"require": {
"php": ">=5.4.0",
"php": ">=8.0",
"ext-curl": "*",
"ext-json": "*"
},
"require-dev": {
"php": ">=5.5.7",
"phpunit/phpunit": ">=4.5"
"php": ">=8.0",
"phpunit/phpunit": ">=9.5"
},
"autoload": {
"files": ["restclient.php"]
}
}
}
66 changes: 33 additions & 33 deletions restclient.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?php
<?php declare(strict_types=1);

/**
* PHP REST Client
* https://github.com/tcdent/php-restclient
* (c) 2013-2017 Travis Dent <[email protected]>
* (c) 2013-2022 Travis Dent <[email protected]>
*/

class RestClientException extends Exception {}
Expand All @@ -23,13 +23,13 @@ class RestClient implements Iterator, ArrayAccess {
// Populated as-needed.
public $decoded_response; // Decoded response body.

public function __construct($options=[]){
public function __construct(array $options=[]){
$default_options = [
'headers' => [],
'parameters' => [],
'curl_options' => [],
'build_indexed_queries' => FALSE,
'user_agent' => "PHP RestClient/0.1.7",
'user_agent' => "PHP RestClient/0.1.8",
'base_url' => NULL,
'format' => NULL,
'format_regex' => "/(\w+)\/(\w+)(;[.+])?/",
Expand All @@ -47,47 +47,47 @@ public function __construct($options=[]){
$default_options['decoders'], $options['decoders']);
}

public function set_option($key, $value){
public function set_option($key, $value) : void {
$this->options[$key] = $value;
}

public function register_decoder($format, $method){
public function register_decoder(string $format, callable $method) : void {
// Decoder callbacks must adhere to the following pattern:
// array my_decoder(string $data)
$this->options['decoders'][$format] = $method;
}

// Iterable methods:
public function rewind(){
public function rewind() : void {
$this->decode_response();
return reset($this->decoded_response);
reset($this->decoded_response);
}

public function current(){
public function current() : mixed {
return current($this->decoded_response);
}

public function key(){
public function key() : mixed {
return key($this->decoded_response);
}

public function next(){
return next($this->decoded_response);
public function next() : void {
next($this->decoded_response);
}

public function valid(){
public function valid() : bool {
return is_array($this->decoded_response)
&& (key($this->decoded_response) !== NULL);
}

// ArrayAccess methods:
public function offsetExists($key){
public function offsetExists($key) : bool {
$this->decode_response();
return is_array($this->decoded_response)?
isset($this->decoded_response[$key]) : isset($this->decoded_response->{$key});
}

public function offsetGet($key){
public function offsetGet($key) : mixed {
$this->decode_response();
if(!$this->offsetExists($key))
return NULL;
Expand All @@ -96,40 +96,40 @@ public function offsetGet($key){
$this->decoded_response[$key] : $this->decoded_response->{$key};
}

public function offsetSet($key, $value){
public function offsetSet($key, $value) : void {
throw new RestClientException("Decoded response data is immutable.");
}

public function offsetUnset($key){
public function offsetUnset($key) : void {
throw new RestClientException("Decoded response data is immutable.");
}

// Request methods:
public function get($url, $parameters=[], $headers=[]){
public function get(string $url, array|string $parameters=[], array $headers=[]) : RestClient {
return $this->execute($url, 'GET', $parameters, $headers);
}

public function post($url, $parameters=[], $headers=[]){
public function post(string $url, array|string $parameters=[], array $headers=[]) : RestClient {
return $this->execute($url, 'POST', $parameters, $headers);
}

public function put($url, $parameters=[], $headers=[]){
public function put(string $url, array|string $parameters=[], array $headers=[]) : RestClient {
return $this->execute($url, 'PUT', $parameters, $headers);
}

public function patch($url, $parameters=[], $headers=[]){
public function patch(string $url, array|string $parameters=[], array $headers=[]) : RestClient {
return $this->execute($url, 'PATCH', $parameters, $headers);
}

public function delete($url, $parameters=[], $headers=[]){
public function delete(string $url, array|string $parameters=[], array $headers=[]) : RestClient {
return $this->execute($url, 'DELETE', $parameters, $headers);
}

public function head($url, $parameters=[], $headers=[]){
public function head(string $url, array|string $parameters=[], array $headers=[]) : RestClient {
return $this->execute($url, 'HEAD', $parameters, $headers);
}

public function execute($url, $method='GET', $parameters=[], $headers=[]){
public function execute(string $url, string $method='GET', array|string $parameters=[], array $headers=[]) : RestClient {
$client = clone $this;
$client->url = $url;
$client->handle = curl_init();
Expand Down Expand Up @@ -173,11 +173,11 @@ public function execute($url, $method='GET', $parameters=[], $headers=[]){
else
$parameters_string = (string) $parameters;

if(strtoupper($method) == 'POST'){
if(strtoupper($method) === 'POST'){
$curlopt[CURLOPT_POST] = TRUE;
$curlopt[CURLOPT_POSTFIELDS] = $parameters_string;
}
elseif(strtoupper($method) != 'GET'){
elseif(strtoupper($method) !== 'GET'){
$curlopt[CURLOPT_CUSTOMREQUEST] = strtoupper($method);
$curlopt[CURLOPT_POSTFIELDS] = $parameters_string;
}
Expand All @@ -187,7 +187,7 @@ public function execute($url, $method='GET', $parameters=[], $headers=[]){
}

if($client->options['base_url']){
if($client->url[0] != '/' && substr($client->options['base_url'], -1) != '/')
if($client->url[0] !== '/' && !str_ends_with($client->options['base_url'], '/'))
$client->url = '/' . $client->url;
$client->url = $client->options['base_url'] . $client->url;
}
Expand All @@ -209,7 +209,7 @@ public function execute($url, $method='GET', $parameters=[], $headers=[]){
return $client;
}

public function parse_response($response){
public function parse_response($response) : void {
$headers = [];
$this->response_status_lines = [];
$line = strtok($response, "\n");
Expand All @@ -218,14 +218,14 @@ public function parse_response($response){
// Since we tokenize on \n, use the remaining \r to detect empty lines.
if(count($headers) > 0) break; // Must be the newline after headers, move on to response body
}
elseif(strpos($line, 'HTTP') === 0){
elseif(str_starts_with($line, 'HTTP')){
// One or more HTTP status lines
$this->response_status_lines[] = trim($line);
}
else {
// Has to be a header
list($key, $value) = explode(':', $line, 2);
$key = trim(strtolower(str_replace('-', '_', $key)));
[$key, $value] = explode(':', $line, 2);
$key = strtolower(trim(str_replace('-', '_', $key)));
$value = trim($value);

if(empty($headers[$key]))
Expand All @@ -241,7 +241,7 @@ public function parse_response($response){
$this->response = strtok("");
}

public function get_response_format(){
public function get_response_format() : string {
if(!$this->response)
throw new RestClientException(
"A response must exist before it can be decoded.");
Expand All @@ -259,7 +259,7 @@ public function get_response_format(){
"Response format could not be determined.");
}

public function decode_response(){
public function decode_response() : mixed {
if(empty($this->decoded_response)){
$format = $this->get_response_format();
if(!array_key_exists($format, $this->options['decoders']))
Expand Down

0 comments on commit ad1b152

Please sign in to comment.