diff --git a/.gitignore b/.gitignore index 6cf3a90..5cbf483 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ composer.lock vendor downloads .idea/* +tests/.phpunit.result.cache +/rector.php +/.vs diff --git a/.travis.yml b/.travis.yml index d962e3a..9666b4b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,12 @@ language: php php: - - 5.3 - - 5.4 - - 5.5 - - 5.6 - - 7.0 - - hhvm - + - 7.2 + - 7.3 + - 7.4 + matrix: fast_finish: true - allow_failures: - - php: 7.0 - + script: - phpunit -c ./tests/phpunit.xml diff --git a/README.md b/README.md index 705d8cc..c68ee5a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](https://secure.travis-ci.org/nategood/httpful.png?branch=master)](http://travis-ci.org/nategood/httpful) [![Total Downloads](https://poser.pugx.org/nategood/httpful/downloads.png)](https://packagist.org/packages/nategood/httpful) -[Httpful](http://phphttpclient.com) is a simple Http Client library for PHP 5.3+. There is an emphasis of readability, simplicity, and flexibility – basically provide the features and flexibility to get the job done and make those features really easy to use. +Httpful is a simple Http Client library for PHP 7.2+. There is an emphasis of readability, simplicity, and flexibility – basically provide the features and flexibility to get the job done and make those features really easy to use. Features @@ -34,16 +34,6 @@ echo "{$response->body->name} joined GitHub on " . # Installation -## Phar - -A [PHP Archive](http://php.net/manual/en/book.phar.php) (or .phar) file is available for [downloading](http://phphttpclient.com/httpful.phar). Simply [download](http://phphttpclient.com/httpful.phar) the .phar, drop it into your project, and include it like you would any other php file. _This method is ideal for smaller projects, one off scripts, and quick API hacking_. - -```php -include('httpful.phar'); -$r = \Httpful\Request::get($uri)->sendIt(); -... -``` - ## Composer Httpful is PSR-0 compliant and can be installed using [composer](http://getcomposer.org/). Simply add `nategood/httpful` to your composer.json file. _Composer is the sane alternative to PEAR. It is excellent for managing dependencies in larger projects_. @@ -62,11 +52,7 @@ Because Httpful is PSR-0 compliant, you can also just clone the Httpful reposito If you want the build your own [Phar Archive](http://php.net/manual/en/book.phar.php) you can use the `build` script included. Make sure that your `php.ini` has the *Off* or 0 value for the `phar.readonly` setting. -Also you need to create ad empty `downloads` directory in the project root. - -# Show Me More! - -You can checkout the [Httpful Landing Page](http://phphttpclient.com) for more info including many examples and [documentation](http://phphttpclient.com/docs). +Also you need to create an empty `downloads` directory in the project root. # Contributing @@ -81,6 +67,18 @@ Httpful highly encourages sending in pull requests. When submitting a pull requ # Changelog +## 0.3.2 + + - REFACTOR [PR #276](https://github.com/nategood/httpful/pull/276) Add properly subclassed, more descriptive Exceptions for JSON parse errors + +## 0.3.1 + + - FIX [PR #286](https://github.com/nategood/httpful/pull/286) Fixed header case sensitivity + +## 0.3.0 + + - REFACTOR Dropped support for dead versions of PHP. Updated the PHPUnit tests. + ## 0.2.20 - MINOR Move Response building logic into separate function [PR #193](https://github.com/nategood/httpful/pull/193) diff --git a/composer.json b/composer.json index dd7a549..56e93b8 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "homepage": "http://github.com/nategood/httpful", "license": "MIT", "keywords": ["http", "curl", "rest", "restful", "api", "requests"], - "version": "0.2.20", + "version": "0.3.2", "authors": [ { "name": "Nate Good", @@ -13,7 +13,7 @@ } ], "require": { - "php": ">=5.3", + "php": ">=7.2", "ext-curl": "*" }, "autoload": { diff --git a/downloads/httpful.phar b/downloads/httpful.phar new file mode 100644 index 0000000..964bcc4 Binary files /dev/null and b/downloads/httpful.phar differ diff --git a/src/Httpful/Bootstrap.php b/src/Httpful/Bootstrap.php index 9974bcf..6665fd5 100644 --- a/src/Httpful/Bootstrap.php +++ b/src/Httpful/Bootstrap.php @@ -11,8 +11,8 @@ class Bootstrap { - const DIR_GLUE = DIRECTORY_SEPARATOR; - const NS_GLUE = '\\'; + public const DIR_GLUE = DIRECTORY_SEPARATOR; + public const NS_GLUE = '\\'; public static $registered = false; @@ -21,7 +21,7 @@ class Bootstrap */ public static function init() { - spl_autoload_register(array('\Httpful\Bootstrap', 'autoload')); + spl_autoload_register(['\\' . \Httpful\Bootstrap::class, 'autoload']); self::registerHandlers(); } @@ -32,7 +32,7 @@ public static function init() */ public static function autoload($classname) { - self::_autoload(dirname(dirname(__FILE__)), $classname); + self::_autoload(dirname(__FILE__,2), $classname); } /** @@ -40,7 +40,7 @@ public static function autoload($classname) */ public static function pharInit() { - spl_autoload_register(array('\Httpful\Bootstrap', 'pharAutoload')); + spl_autoload_register(['\\' . \Httpful\Bootstrap::class, 'pharAutoload']); self::registerHandlers(); } @@ -78,12 +78,12 @@ public static function registerHandlers() // @todo check a conf file to load from that instead of // hardcoding into the library? - $handlers = array( + $handlers = [ \Httpful\Mime::JSON => new \Httpful\Handlers\JsonHandler(), \Httpful\Mime::XML => new \Httpful\Handlers\XmlHandler(), \Httpful\Mime::FORM => new \Httpful\Handlers\FormHandler(), \Httpful\Mime::CSV => new \Httpful\Handlers\CsvHandler(), - ); + ]; foreach ($handlers as $mime => $handler) { // Don't overwrite if the handler has already been registered diff --git a/src/Httpful/Exception/ConnectionErrorException.php b/src/Httpful/Exception/ConnectionErrorException.php index bba73a6..b0a3391 100644 --- a/src/Httpful/Exception/ConnectionErrorException.php +++ b/src/Httpful/Exception/ConnectionErrorException.php @@ -1,7 +1,54 @@ -curlErrorNumber; + } + + /** + * @param string $curlErrorNumber + * @return $this + */ + public function setCurlErrorNumber($curlErrorNumber) { + $this->curlErrorNumber = $curlErrorNumber; + + return $this; + } + + /** + * @return string + */ + public function getCurlErrorString() { + return $this->curlErrorString; + } + + /** + * @param string $curlErrorString + * @return $this + */ + public function setCurlErrorString($curlErrorString) { + $this->curlErrorString = $curlErrorString; + + return $this; + } + + } \ No newline at end of file diff --git a/src/Httpful/Handlers/CsvHandler.php b/src/Httpful/Handlers/CsvHandler.php index de7b15b..69c79d0 100644 --- a/src/Httpful/Handlers/CsvHandler.php +++ b/src/Httpful/Handlers/CsvHandler.php @@ -18,13 +18,13 @@ public function parse($body) if (empty($body)) return null; - $parsed = array(); + $parsed = []; $fp = fopen('data://text/plain;base64,' . base64_encode($body), 'r'); while (($r = fgetcsv($fp)) !== FALSE) { $parsed[] = $r; } - if (empty($parsed)) + if ($parsed === []) throw new \Exception("Unable to parse response as CSV"); return $parsed; } @@ -33,7 +33,7 @@ public function parse($body) * @param mixed $payload * @return string */ - public function serialize($payload) + public function serialize($payload): string { $fp = fopen('php://temp/maxmemory:'. (6*1024*1024), 'r+'); $i = 0; diff --git a/src/Httpful/Handlers/FormHandler.php b/src/Httpful/Handlers/FormHandler.php index fea1c37..0ff05a8 100644 --- a/src/Httpful/Handlers/FormHandler.php +++ b/src/Httpful/Handlers/FormHandler.php @@ -14,7 +14,7 @@ class FormHandler extends MimeHandlerAdapter */ public function parse($body) { - $parsed = array(); + $parsed = []; parse_str($body, $parsed); return $parsed; } @@ -23,7 +23,7 @@ public function parse($body) * @param mixed $payload * @return string */ - public function serialize($payload) + public function serialize($payload): string { return http_build_query($payload, null, '&'); } diff --git a/src/Httpful/Handlers/JsonHandler.php b/src/Httpful/Handlers/JsonHandler.php index 6166283..414a728 100644 --- a/src/Httpful/Handlers/JsonHandler.php +++ b/src/Httpful/Handlers/JsonHandler.php @@ -14,7 +14,7 @@ class JsonHandler extends MimeHandlerAdapter public function init(array $args) { - $this->decode_as_array = !!(array_key_exists('decode_as_array', $args) ? $args['decode_as_array'] : false); + $this->decode_as_array = (bool) ($args['decode_as_array'] ?? false); } /** @@ -37,7 +37,7 @@ public function parse($body) * @param mixed $payload * @return string */ - public function serialize($payload) + public function serialize($payload): string { return json_encode($payload); } diff --git a/src/Httpful/Handlers/MimeHandlerAdapter.php b/src/Httpful/Handlers/MimeHandlerAdapter.php index e57ebb0..53e04ad 100644 --- a/src/Httpful/Handlers/MimeHandlerAdapter.php +++ b/src/Httpful/Handlers/MimeHandlerAdapter.php @@ -10,7 +10,7 @@ class MimeHandlerAdapter { - public function __construct(array $args = array()) + public function __construct(array $args = []) { $this->init($args); } @@ -36,18 +36,18 @@ public function parse($body) * @param mixed $payload * @return string */ - function serialize($payload) + function serialize($payload): string { return (string) $payload; } - protected function stripBom($body) + protected function stripBom($body): string { if ( substr($body,0,3) === "\xef\xbb\xbf" ) // UTF-8 $body = substr($body,3); - else if ( substr($body,0,4) === "\xff\xfe\x00\x00" || substr($body,0,4) === "\x00\x00\xfe\xff" ) // UTF-32 + elseif ( substr($body,0,4) === "\xff\xfe\x00\x00" || substr($body,0,4) === "\x00\x00\xfe\xff" ) // UTF-32 $body = substr($body,4); - else if ( substr($body,0,2) === "\xff\xfe" || substr($body,0,2) === "\xfe\xff" ) // UTF-16 + elseif ( substr($body,0,2) === "\xff\xfe" || substr($body,0,2) === "\xfe\xff" ) // UTF-16 $body = substr($body,2); return $body; } diff --git a/src/Httpful/Handlers/XmlHandler.php b/src/Httpful/Handlers/XmlHandler.php index 9298a1f..fb8d2b2 100644 --- a/src/Httpful/Handlers/XmlHandler.php +++ b/src/Httpful/Handlers/XmlHandler.php @@ -23,10 +23,10 @@ class XmlHandler extends MimeHandlerAdapter /** * @param array $conf sets configuration options */ - public function __construct(array $conf = array()) + public function __construct(array $conf = []) { - $this->namespace = isset($conf['namespace']) ? $conf['namespace'] : ''; - $this->libxml_opts = isset($conf['libxml_opts']) ? $conf['libxml_opts'] : 0; + $this->namespace = $conf['namespace'] ?? ''; + $this->libxml_opts = $conf['libxml_opts'] ?? 0; } /** @@ -50,9 +50,9 @@ public function parse($body) * @return string * @throws \Exception if unable to serialize */ - public function serialize($payload) + public function serialize($payload): string { - list($_, $dom) = $this->_future_serializeAsXml($payload); + [$_, $dom] = $this->_future_serializeAsXml($payload); return $dom->saveXml(); } @@ -61,7 +61,7 @@ public function serialize($payload) * @return string * @author Ted Zellers */ - public function serialize_clean($payload) + public function serialize_clean($payload): string { $xml = new \XMLWriter; $xml->openMemory(); @@ -75,7 +75,7 @@ public function serialize_clean($payload) * @param mixed $node to serialize * @author Ted Zellers */ - public function serialize_node(&$xmlw, $node){ + public function serialize_node(&$xmlw, $node) { if (!is_array($node)){ $xmlw->text($node); } else { @@ -90,7 +90,7 @@ public function serialize_node(&$xmlw, $node){ /** * @author Zack Douglas */ - private function _future_serializeAsXml($value, $node = null, $dom = null) + private function _future_serializeAsXml($value, $node = null, $dom = null): array { if (!$dom) { $dom = new \DOMDocument; @@ -107,21 +107,21 @@ private function _future_serializeAsXml($value, $node = null, $dom = null) $objNode = $dom->createElement(get_class($value)); $node->appendChild($objNode); $this->_future_serializeObjectAsXml($value, $objNode, $dom); - } else if (is_array($value)) { + } elseif (is_array($value)) { $arrNode = $dom->createElement('array'); $node->appendChild($arrNode); $this->_future_serializeArrayAsXml($value, $arrNode, $dom); - } else if (is_bool($value)) { + } elseif (is_bool($value)) { $node->appendChild($dom->createTextNode($value?'TRUE':'FALSE')); } else { $node->appendChild($dom->createTextNode($value)); } - return array($node, $dom); + return [$node, $dom]; } /** * @author Zack Douglas */ - private function _future_serializeArrayAsXml($value, &$parent, &$dom) + private function _future_serializeArrayAsXml($value, &$parent, &$dom): array { foreach ($value as $k => &$v) { $n = $k; @@ -132,12 +132,12 @@ private function _future_serializeArrayAsXml($value, &$parent, &$dom) $parent->appendChild($el); $this->_future_serializeAsXml($v, $el, $dom); } - return array($parent, $dom); + return [$parent, $dom]; } /** * @author Zack Douglas */ - private function _future_serializeObjectAsXml($value, &$parent, &$dom) + private function _future_serializeObjectAsXml($value, &$parent, &$dom): array { $refl = new \ReflectionObject($value); foreach ($refl->getProperties() as $pr) { @@ -147,6 +147,6 @@ private function _future_serializeObjectAsXml($value, &$parent, &$dom) $this->_future_serializeAsXml($pr->getValue($value), $el, $dom); } } - return array($parent, $dom); + return [$parent, $dom]; } } \ No newline at end of file diff --git a/src/Httpful/Http.php b/src/Httpful/Http.php index 1c9aa0d..c56ab93 100644 --- a/src/Httpful/Http.php +++ b/src/Httpful/Http.php @@ -7,28 +7,28 @@ */ class Http { - const HEAD = 'HEAD'; - const GET = 'GET'; - const POST = 'POST'; - const PUT = 'PUT'; - const DELETE = 'DELETE'; - const PATCH = 'PATCH'; - const OPTIONS = 'OPTIONS'; - const TRACE = 'TRACE'; + public const HEAD = 'HEAD'; + public const GET = 'GET'; + public const POST = 'POST'; + public const PUT = 'PUT'; + public const DELETE = 'DELETE'; + public const PATCH = 'PATCH'; + public const OPTIONS = 'OPTIONS'; + public const TRACE = 'TRACE'; /** * @return array of HTTP method strings */ - public static function safeMethods() + public static function safeMethods(): array { - return array(self::HEAD, self::GET, self::OPTIONS, self::TRACE); + return [self::HEAD, self::GET, self::OPTIONS, self::TRACE]; } /** * @param string HTTP method * @return bool */ - public static function isSafeMethod($method) + public static function isSafeMethod($method): bool { return in_array($method, self::safeMethods()); } @@ -37,7 +37,7 @@ public static function isSafeMethod($method) * @param string HTTP method * @return bool */ - public static function isUnsafeMethod($method) + public static function isUnsafeMethod($method): bool { return !in_array($method, self::safeMethods()); } @@ -45,19 +45,19 @@ public static function isUnsafeMethod($method) /** * @return array list of (always) idempotent HTTP methods */ - public static function idempotentMethods() + public static function idempotentMethods(): array { // Though it is possible to be idempotent, POST // is not guarunteed to be, and more often than // not, it is not. - return array(self::HEAD, self::GET, self::PUT, self::DELETE, self::OPTIONS, self::TRACE, self::PATCH); + return [self::HEAD, self::GET, self::PUT, self::DELETE, self::OPTIONS, self::TRACE, self::PATCH]; } /** * @param string HTTP method * @return bool */ - public static function isIdempotent($method) + public static function isIdempotent($method): bool { return in_array($method, self::safeidempotentMethodsMethods()); } @@ -66,7 +66,7 @@ public static function isIdempotent($method) * @param string HTTP method * @return bool */ - public static function isNotIdempotent($method) + public static function isNotIdempotent($method): bool { return !in_array($method, self::idempotentMethods()); } @@ -78,9 +78,9 @@ public static function isNotIdempotent($method) * * @return array of HTTP method strings */ - public static function canHaveBody() + public static function canHaveBody(): array { - return array(self::POST, self::PUT, self::PATCH, self::OPTIONS); + return [self::POST, self::PUT, self::PATCH, self::OPTIONS]; } } \ No newline at end of file diff --git a/src/Httpful/Httpful.php b/src/Httpful/Httpful.php index e46053d..98d49b7 100644 --- a/src/Httpful/Httpful.php +++ b/src/Httpful/Httpful.php @@ -3,9 +3,9 @@ namespace Httpful; class Httpful { - const VERSION = '0.2.20'; + public const VERSION = '0.3.0'; - private static $mimeRegistrar = array(); + private static $mimeRegistrar = []; private static $default = null; /** @@ -40,7 +40,7 @@ public static function get($mimeType = null) * @param string $mimeType * @return bool */ - public static function hasParserRegistered($mimeType) + public static function hasParserRegistered($mimeType): bool { return isset(self::$mimeRegistrar[$mimeType]); } diff --git a/src/Httpful/Mime.php b/src/Httpful/Mime.php index 930b6e3..bd72396 100644 --- a/src/Httpful/Mime.php +++ b/src/Httpful/Mime.php @@ -8,35 +8,35 @@ */ class Mime { - const JSON = 'application/json'; - const XML = 'application/xml'; - const XHTML = 'application/html+xml'; - const FORM = 'application/x-www-form-urlencoded'; - const UPLOAD = 'multipart/form-data'; - const PLAIN = 'text/plain'; - const JS = 'text/javascript'; - const HTML = 'text/html'; - const YAML = 'application/x-yaml'; - const CSV = 'text/csv'; + public const JSON = 'application/json'; + public const XML = 'application/xml'; + public const XHTML = 'application/html+xml'; + public const FORM = 'application/x-www-form-urlencoded'; + public const UPLOAD = 'multipart/form-data'; + public const PLAIN = 'text/plain'; + public const JS = 'text/javascript'; + public const HTML = 'text/html'; + public const YAML = 'application/x-yaml'; + public const CSV = 'text/csv'; /** * Map short name for a mime type * to a full proper mime type */ - public static $mimes = array( + public static $mimes = [ 'json' => self::JSON, 'xml' => self::XML, 'form' => self::FORM, 'plain' => self::PLAIN, 'text' => self::PLAIN, - 'upload' => self::UPLOAD, + 'upload' => self::UPLOAD, 'html' => self::HTML, 'xhtml' => self::XHTML, 'js' => self::JS, 'javascript'=> self::JS, 'yaml' => self::YAML, 'csv' => self::CSV, - ); + ]; /** * Get the full Mime Type name from a "short name". @@ -44,16 +44,16 @@ class Mime * @param string $short_name common name for mime type (e.g. json) * @return string full mime type (e.g. application/json) */ - public static function getFullMime($short_name) - { - return array_key_exists($short_name, self::$mimes) ? self::$mimes[$short_name] : $short_name; + public static function getFullMime(string $short_name): string + { + return self::$mimes[$short_name] ?? $short_name; } /** * @param string $short_name * @return bool */ - public static function supportsMimeType($short_name) + public static function supportsMimeType(string $short_name): bool { return array_key_exists($short_name, self::$mimes); } diff --git a/src/Httpful/Proxy.php b/src/Httpful/Proxy.php index 4ab9ea6..611f017 100644 --- a/src/Httpful/Proxy.php +++ b/src/Httpful/Proxy.php @@ -10,7 +10,7 @@ */ class Proxy { - const HTTP = CURLPROXY_HTTP; - const SOCKS4 = CURLPROXY_SOCKS4; - const SOCKS5 = CURLPROXY_SOCKS5; + public const HTTP = CURLPROXY_HTTP; + public const SOCKS4 = CURLPROXY_SOCKS4; + public const SOCKS5 = CURLPROXY_SOCKS5; } diff --git a/src/Httpful/Request.php b/src/Httpful/Request.php index b9b03e6..93cc008 100644 --- a/src/Httpful/Request.php +++ b/src/Httpful/Request.php @@ -1,5 +1,4 @@ + * + * @method self sendsJson() + * @method self sendsXml() + * @method self sendsForm() + * @method self sendsPlain() + * @method self sendsText() + * @method self sendsUpload() + * @method self sendsHtml() + * @method self sendsXhtml() + * @method self sendsJs() + * @method self sendsJavascript() + * @method self sendsYaml() + * @method self sendsCsv() + * @method self expectsJson() + * @method self expectsXml() + * @method self expectsForm() + * @method self expectsPlain() + * @method self expectsText() + * @method self expectsUpload() + * @method self expectsHtml() + * @method self expectsXhtml() + * @method self expectsJs() + * @method self expectsJavascript() + * @method self expectsYaml() + * @method self expectsCsv() */ class Request { // Option constants - const SERIALIZE_PAYLOAD_NEVER = 0; - const SERIALIZE_PAYLOAD_ALWAYS = 1; - const SERIALIZE_PAYLOAD_SMART = 2; + public const SERIALIZE_PAYLOAD_NEVER = 0; + public const SERIALIZE_PAYLOAD_ALWAYS = 1; + public const SERIALIZE_PAYLOAD_SMART = 2; - const MAX_REDIRECTS_DEFAULT = 25; + public const MAX_REDIRECTS_DEFAULT = 25; public $uri, $method = Http::GET, - $headers = array(), + $headers = [], $raw_headers = '', $strict_ssl = false, $content_type, $expected_type, - $additional_curl_opts = array(), + $additional_curl_opts = [], $auto_parse = true, $serialize_payload_method = self::SERIALIZE_PAYLOAD_SMART, $username, @@ -47,7 +71,12 @@ class Request $send_callback, $follow_redirects = false, $max_redirects = self::MAX_REDIRECTS_DEFAULT, - $payload_serializers = array(); + $payload_serializers = [], + $timeout = null, + $client_cert = null, + $client_key = null, + $client_passphrase = null, + $client_encoding = null; // Options // private $_options = array( @@ -63,17 +92,18 @@ class Request private static $_template; /** - * We made the constructor private to force the factory style. This was + * We made the constructor protected to force the factory style. This was * done to keep the syntax cleaner and better the support the idea of * "default templates". Very basic and flexible as it is only intended * for internal use. * @param array $attrs hash of initial attribute values */ - private function __construct($attrs = null) + protected function __construct($attrs = null) { if (!is_array($attrs)) return; foreach ($attrs as $attr => $value) { - $this->$attr = $value; + if (property_exists($this, $attr)) + $this->$attr = $value; } } @@ -121,33 +151,33 @@ public static function d($attr) /** * @return bool does the request have a timeout? */ - public function hasTimeout() + public function hasTimeout(): bool { - return isset($this->timeout); + return $this->timeout !== null; } /** * @return bool has the internal curl request been initialized? */ - public function hasBeenInitialized() + public function hasBeenInitialized(): bool { - return isset($this->_ch); + return $this->_ch !== null; } /** * @return bool Is this request setup for basic auth? */ - public function hasBasicAuth() + public function hasBasicAuth(): bool { - return isset($this->password) && isset($this->username); + return $this->password !== null && $this->username !== null; } /** * @return bool Is this request setup for digest auth? */ - public function hasDigestAuth() + public function hasDigestAuth(): bool { - return isset($this->password) && isset($this->username) && $this->additional_curl_opts[CURLOPT_HTTPAUTH] == CURLAUTH_DIGEST; + return $this->password !== null && $this->username !== null && $this->additional_curl_opts[CURLOPT_HTTPAUTH] == CURLAUTH_DIGEST; } /** @@ -204,6 +234,7 @@ public function send() $response = $this->buildResponse($result); curl_close($this->_ch); + $this->_ch = null; return $response; } @@ -281,9 +312,9 @@ public function authenticateWithDigest($username, $password) /** * @return bool is this request setup for client side cert? */ - public function hasClientSideCert() + public function hasClientSideCert(): bool { - return isset($this->client_cert) && isset($this->client_key); + return $this->client_cert !== null && $this->client_key !==null; } /** @@ -369,14 +400,15 @@ public function method($method) * @param string $mime * @return Request */ - public function expects($mime) + public function expects(?string $mime) { if (empty($mime)) return $this; $this->expected_type = Mime::getFullMime($mime); + return $this; } // @alias of expects - public function expectsType($mime) + public function expectsType(?string $mime) { return $this->expects($mime); } @@ -455,7 +487,7 @@ public function useProxy($proxy_host, $proxy_port = 80, $auth_type = null, $auth { $this->addOnCurlOption(CURLOPT_PROXY, "{$proxy_host}:{$proxy_port}"); $this->addOnCurlOption(CURLOPT_PROXYTYPE, $proxy_type); - if (in_array($auth_type, array(CURLAUTH_BASIC,CURLAUTH_NTLM))) { + if (in_array($auth_type, [CURLAUTH_BASIC,CURLAUTH_NTLM])) { $this->addOnCurlOption(CURLOPT_PROXYAUTH, $auth_type) ->addOnCurlOption(CURLOPT_PROXYUSERPWD, "{$auth_username}:{$auth_password}"); } @@ -485,9 +517,14 @@ public function useSocks5Proxy($proxy_host, $proxy_port = 80, $auth_type = null, /** * @return bool is this request setup for using proxy? */ - public function hasProxy() + public function hasProxy(): bool { - return isset($this->additional_curl_opts[CURLOPT_PROXY]) && is_string($this->additional_curl_opts[CURLOPT_PROXY]); + /* We must be aware that proxy variables could come from environment also. + In curl extension, http proxy can be specified not only via CURLOPT_PROXY option, + but also by environment variable called http_proxy. + */ + return isset($this->additional_curl_opts[CURLOPT_PROXY]) && is_string($this->additional_curl_opts[CURLOPT_PROXY]) || + getenv("http_proxy"); } /** @@ -712,6 +749,7 @@ public function __call($method, $args) // throw new \Exception("Unsupported Content-Type $mime"); // } } + if (substr($method, 0, 7) === 'expects') { $mime = strtolower(substr($method, 7)); if (Mime::supportsMimeType($mime)) { @@ -725,7 +763,7 @@ public function __call($method, $args) // This method also adds the custom header support as described in the // method comments - if (count($args) === 0) + if ($args === []) return; // Strip the sugar. If it leads with "with", strip. @@ -760,7 +798,7 @@ private static function _initializeDefaults() // recusion. Do not use this syntax elsewhere. // It goes against the whole readability // and transparency idea. - self::$_template = new Request(array('method' => Http::GET)); + self::$_template = new Request(['method' => Http::GET]); // This is more like it... self::$_template @@ -787,7 +825,7 @@ private function _error($error) { // TODO add in support for various Loggers that follow // PSR 3 https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md - if (isset($this->error_callback)) { + if ($this->error_callback !== null) { $this->error_callback->__invoke($error); } else { error_log($error); @@ -829,14 +867,14 @@ public static function init($method = null, $mime = null) public function _curlPrep() { // Check for required stuff - if (!isset($this->uri)) + if ($this->uri === null) throw new \Exception('Attempting to send a request before defining a URI endpoint.'); - if (isset($this->payload)) { + if ($this->payload !== null) { $this->serialized_payload = $this->_serializePayload($this->payload); } - if (isset($this->send_callback)) { + if ($this->send_callback !== null) { call_user_func($this->send_callback, $this); } @@ -890,7 +928,7 @@ public function _curlPrep() // https://github.com/nategood/httpful/issues/84 // set Content-Length to the size of the payload if present - if (isset($this->payload)) { + if ($this->payload !== null) { curl_setopt($ch, CURLOPT_POSTFIELDS, $this->serialized_payload); if (!$this->isUpload()) { $this->headers['Content-Length'] = @@ -898,7 +936,7 @@ public function _curlPrep() } } - $headers = array(); + $headers = []; // https://github.com/nategood/httpful/issues/37 // Except header removes any HTTP 1.1 Continue from response headers $headers[] = 'Expect:'; @@ -931,9 +969,9 @@ public function _curlPrep() } $url = \parse_url($this->uri); - $path = (isset($url['path']) ? $url['path'] : '/').(isset($url['query']) ? '?'.$url['query'] : ''); + $path = ($url['path'] ?? '/').(isset($url['query']) ? '?'.$url['query'] : ''); $this->raw_headers = "{$this->method} $path HTTP/1.1\r\n"; - $host = (isset($url['host']) ? $url['host'] : 'localhost').(isset($url['port']) ? ':'.$url['port'] : ''); + $host = ($url['host'] ?? 'localhost').(isset($url['port']) ? ':'.$url['port'] : ''); $this->raw_headers .= "Host: $host\r\n"; $this->raw_headers .= \implode("\r\n", $headers); $this->raw_headers .= "\r\n"; @@ -973,7 +1011,7 @@ public function _determineLength($str) /** * @return bool */ - public function isUpload() + public function isUpload(): bool { return Mime::UPLOAD == $this->content_type; } @@ -981,7 +1019,7 @@ public function isUpload() /** * @return string */ - public function buildUserAgent() + public function buildUserAgent(): string { $user_agent = 'User-Agent: Httpful/' . Httpful::VERSION . ' (cURL/'; $curl = \curl_version(); @@ -1011,9 +1049,7 @@ public function buildUserAgent() $user_agent .= " {$_SERVER['HTTP_USER_AGENT']}"; } - $user_agent .= ')'; - - return $user_agent; + return $user_agent . ')'; } /** @@ -1022,10 +1058,17 @@ public function buildUserAgent() */ public function buildResponse($result) { if ($result === false) { - if ($curlErrorNumber = curl_errno($this->_ch)) { + if (($curlErrorNumber = curl_errno($this->_ch)) !== 0) { $curlErrorString = curl_error($this->_ch); $this->_error($curlErrorString); - throw new ConnectionErrorException('Unable to connect to "'.$this->uri.'": ' . $curlErrorNumber . ' ' . $curlErrorString); + + $exception = new ConnectionErrorException('Unable to connect to "'.$this->uri.'": ' + . $curlErrorNumber . ' ' . $curlErrorString); + + $exception->setCurlErrorNumber($curlErrorNumber) + ->setCurlErrorString($curlErrorString); + + throw $exception; } $this->_error('Unable to connect to "'.$this->uri.'".'); @@ -1044,7 +1087,7 @@ public function buildResponse($result) { $body = array_pop($response); $headers = array_pop($response); - + return new Response($body, $headers, $this, $info); } diff --git a/src/Httpful/Response.php b/src/Httpful/Response.php index 9e8747f..2e1d204 100644 --- a/src/Httpful/Response.php +++ b/src/Httpful/Response.php @@ -9,7 +9,6 @@ */ class Response { - public $body, $raw_body, $headers, @@ -18,7 +17,7 @@ class Response $code = 0, $content_type, $parent_type, - $charset, + $charset = null, $meta_data, $is_mime_vendor_specific = false, $is_mime_personal = false; @@ -31,7 +30,7 @@ class Response * @param Request $request * @param array $meta_data */ - public function __construct($body, $headers, Request $request, array $meta_data = array()) + public function __construct(string $body, string $headers, Request $request, array $meta_data = []) { $this->request = $request; $this->raw_headers = $headers; @@ -59,7 +58,7 @@ public function __construct($body, $headers, Request $request, array $meta_data * * @return bool Did we receive a 4xx or 5xx? */ - public function hasErrors() + public function hasErrors(): bool { return $this->code >= 400; } @@ -67,7 +66,7 @@ public function hasErrors() /** * @return bool */ - public function hasBody() + public function hasBody(): bool { return !empty($this->body); } @@ -77,9 +76,9 @@ public function hasBody() * (most often an associative array) based on the expected * Mime type. * @param string Http response body - * @return array|string|object the response parse accordingly + * @return mixed (array|string|object) the response parse accordingly */ - public function _parse($body) + public function _parse(string $body) { // If the user decided to forgo the automatic // smart parsing, short circuit. @@ -98,13 +97,13 @@ public function _parse($body) // 3. If provided, use the "parent type" of the mime type from the response // 4. Default to the content-type provided in the response $parse_with = $this->request->expected_type; + if (empty($this->request->expected_type)) { $parse_with = Httpful::hasParserRegistered($this->content_type) ? $this->content_type : $this->parent_type; } - - return Httpful::get($parse_with)->parse($body); + return Httpful::get($parse_with)->parse($body); } /** @@ -113,29 +112,12 @@ public function _parse($body) * @param string $headers raw headers * @return array parse headers */ - public function _parseHeaders($headers) + public function _parseHeaders(string $headers): array { - $headers = preg_split("/(\r|\n)+/", $headers, -1, \PREG_SPLIT_NO_EMPTY); - $parse_headers = array(); - for ($i = 1; $i < count($headers); $i++) { - list($key, $raw_value) = explode(':', $headers[$i], 2); - $key = trim($key); - $value = trim($raw_value); - if (array_key_exists($key, $parse_headers)) { - // See HTTP RFC Sec 4.2 Paragraph 5 - // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 - // If a header appears more than once, it must also be able to - // be represented as a single header with a comma-separated - // list of values. We transform accordingly. - $parse_headers[$key] .= ',' . $value; - } else { - $parse_headers[$key] = $value; - } - } - return $parse_headers; + return Response\Headers::fromString($headers)->toArray(); } - public function _parseCode($headers) + public function _parseCode(string $headers): int { $end = strpos($headers, "\r\n"); if ($end === false) $end = strlen($headers); @@ -143,7 +125,7 @@ public function _parseCode($headers) if (count($parts) < 2 || !is_numeric($parts[1])) { throw new \Exception("Unable to parse response code from HTTP response due to malformed response"); } - return intval($parts[1]); + return (int) $parts[1]; } /** @@ -153,12 +135,12 @@ public function _parseCode($headers) public function _interpretHeaders() { // Parse the Content-Type and charset - $content_type = isset($this->headers['Content-Type']) ? $this->headers['Content-Type'] : ''; + $content_type = $this->headers['Content-Type'] ?? ''; $content_type = explode(';', $content_type); $this->content_type = $content_type[0]; if (count($content_type) == 2 && strpos($content_type[1], '=') !== false) { - list($nill, $this->charset) = explode('=', $content_type[1]); + [$nill, $this->charset] = explode('=', $content_type[1]); } // RFC 2616 states "text/*" Content-Types should have a default @@ -166,13 +148,13 @@ public function _interpretHeaders() // are assumed to have UTF-8 unless otherwise specified. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.1 // http://www.w3.org/International/O-HTTP-charset.en.php - if (!isset($this->charset)) { + if ($this->charset === null ) { $this->charset = substr($this->content_type, 5) === 'text/' ? 'iso-8859-1' : 'utf-8'; } // Is vendor type? Is personal type? if (strpos($this->content_type, '/') !== false) { - list($type, $sub_type) = explode('/', $this->content_type); + [$type, $sub_type] = explode('/', $this->content_type); $this->is_mime_vendor_specific = substr($sub_type, 0, 4) === 'vnd.'; $this->is_mime_personal = substr($sub_type, 0, 4) === 'prs.'; } @@ -180,7 +162,7 @@ public function _interpretHeaders() // Parent type (e.g. xml for application/vnd.github.message+xml) $this->parent_type = $this->content_type; if (strpos($this->content_type, '+') !== false) { - list($vendor, $this->parent_type) = explode('+', $this->content_type, 2); + [$vendor, $this->parent_type] = explode('+', $this->content_type, 2); $this->parent_type = Mime::getFullMime($this->parent_type); } } @@ -188,7 +170,7 @@ public function _interpretHeaders() /** * @return string */ - public function __toString() + public function __toString(): string { return $this->raw_body; } diff --git a/src/Httpful/Response/Headers.php b/src/Httpful/Response/Headers.php index 0c922a5..32438b7 100644 --- a/src/Httpful/Response/Headers.php +++ b/src/Httpful/Response/Headers.php @@ -15,46 +15,57 @@ private function __construct($headers) } /** - * @param string $string + * @param string $string * @return Headers */ public static function fromString($string) { - $lines = preg_split("/(\r|\n)+/", $string, -1, PREG_SPLIT_NO_EMPTY); - array_shift($lines); // HTTP HEADER - $headers = array(); - foreach ($lines as $line) { - list($name, $value) = explode(':', $line, 2); - $headers[strtolower(trim($name))] = trim($value); + $headers = preg_split("/(\r|\n)+/", $string, -1, \PREG_SPLIT_NO_EMPTY); + $parse_headers = []; + $headersCount = count($headers); + for ($i = 1; $i < $headersCount; $i++) { + [$key, $raw_value] = explode(':', $headers[$i], 2); + $key = trim($key); + $value = trim($raw_value); + if (array_key_exists($key, $parse_headers)) { + // See HTTP RFC Sec 4.2 Paragraph 5 + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 + // If a header appears more than once, it must also be able to + // be represented as a single header with a comma-separated + // list of values. We transform accordingly. + $parse_headers[$key] .= ',' . $value; + } else { + $parse_headers[$key] = $value; + } } - return new self($headers); + return new self($parse_headers); } /** * @param string $offset - * @return bool */ - public function offsetExists($offset) + public function offsetExists($offset): bool { - return isset($this->headers[strtolower($offset)]); + return $this->getCaseInsensitive($offset) !== null; } /** - * @param string $offset + * @param mixed $offset * @return mixed */ + #[\ReturnTypeWillChange] public function offsetGet($offset) { - if (isset($this->headers[$name = strtolower($offset)])) { - return $this->headers[$name]; - } + return $this->getCaseInsensitive($offset); } /** * @param string $offset * @param string $value * @throws \Exception + * @return never */ + #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { throw new \Exception("Headers are read-only."); @@ -63,7 +74,9 @@ public function offsetSet($offset, $value) /** * @param string $offset * @throws \Exception + * @return never */ + #[\ReturnTypeWillChange] public function offsetUnset($offset) { throw new \Exception("Headers are read-only."); @@ -72,7 +85,7 @@ public function offsetUnset($offset) /** * @return int */ - public function count() + public function count(): int { return count($this->headers); } @@ -85,4 +98,14 @@ public function toArray() return $this->headers; } -} \ No newline at end of file + private function getCaseInsensitive(string $key) + { + foreach ($this->headers as $header => $value) { + if (strtolower($key) === strtolower($header)) { + return $value; + } + } + + return null; + } +} diff --git a/tests/Httpful/HttpfulTest.php b/tests/Httpful/HttpfulTest.php index 91efd88..3b8787f 100644 --- a/tests/Httpful/HttpfulTest.php +++ b/tests/Httpful/HttpfulTest.php @@ -9,7 +9,7 @@ */ namespace Httpful\Test; -require(dirname(dirname(dirname(__FILE__))) . '/bootstrap.php'); +require(dirname(__FILE__),3) . '/bootstrap.php'); \Httpful\Bootstrap::init(); use Httpful\Httpful; @@ -21,40 +21,49 @@ define('TEST_SERVER', WEB_SERVER_HOST . ':' . WEB_SERVER_PORT); -class HttpfulTest extends \PHPUnit_Framework_TestCase +class HttpfulTest extends \PHPUnit\Framework\TestCase { - const TEST_SERVER = TEST_SERVER; - const TEST_URL = 'http://127.0.0.1:8008'; - const TEST_URL_400 = 'http://127.0.0.1:8008/400'; + public const TEST_SERVER = TEST_SERVER; + public const TEST_URL = 'http://127.0.0.1:8008'; + public const TEST_URL_400 = 'http://127.0.0.1:8008/400'; - const SAMPLE_JSON_HEADER = + public const SAMPLE_JSON_HEADER = "HTTP/1.1 200 OK Content-Type: application/json Connection: keep-alive Transfer-Encoding: chunked\r\n"; - const SAMPLE_JSON_RESPONSE = '{"key":"value","object":{"key":"value"},"array":[1,2,3,4]}'; - const SAMPLE_CSV_HEADER = + public const SAMPLE_JSON_HEADER_LOWERCASE = + "HTTP/2 200 +date: Tue, 07 Jan 2020 09:11:21 GMT +content-type: application/json +content-length: 513 +access-control-allow-origin: * +access-control-allow-methods: GET, POST, PUT, PATCH, DELETE +access-control-allow-headers: Authorization, Content-Type, Accept-Encoding, Cache-Control, DNT +cache-control: private, must-revalidate\r\n"; + public const SAMPLE_JSON_RESPONSE = '{"key":"value","object":{"key":"value"},"array":[1,2,3,4]}'; + public const SAMPLE_CSV_HEADER = "HTTP/1.1 200 OK Content-Type: text/csv Connection: keep-alive Transfer-Encoding: chunked\r\n"; - const SAMPLE_CSV_RESPONSE = + public const SAMPLE_CSV_RESPONSE = "Key1,Key2 Value1,Value2 \"40.0\",\"Forty\""; - const SAMPLE_XML_RESPONSE = '2a stringTRUE'; - const SAMPLE_XML_HEADER = + public const SAMPLE_XML_RESPONSE = '2a stringTRUE'; + public const SAMPLE_XML_HEADER = "HTTP/1.1 200 OK Content-Type: application/xml Connection: keep-alive Transfer-Encoding: chunked\r\n"; - const SAMPLE_VENDOR_HEADER = + public const SAMPLE_VENDOR_HEADER = "HTTP/1.1 200 OK Content-Type: application/vnd.nategood.message+xml Connection: keep-alive Transfer-Encoding: chunked\r\n"; - const SAMPLE_VENDOR_TYPE = "application/vnd.nategood.message+xml"; - const SAMPLE_MULTI_HEADER = + public const SAMPLE_VENDOR_TYPE = "application/vnd.nategood.message+xml"; + public const SAMPLE_MULTI_HEADER = "HTTP/1.1 200 OK Content-Type: application/json Connection: keep-alive @@ -65,8 +74,8 @@ class HttpfulTest extends \PHPUnit_Framework_TestCase function testInit() { $r = Request::init(); - // Did we get a 'Request' object? - $this->assertEquals('Httpful\Request', get_class($r)); + // Did we get a 'Request' object? + $this->assertEquals(\Httpful\Request::class, get_class($r)); } function testDetermineLength() @@ -81,11 +90,11 @@ function testDetermineLength() function testMethods() { - $valid_methods = array('get', 'post', 'delete', 'put', 'options', 'head'); + $valid_methods = ['get', 'post', 'delete', 'put', 'options', 'head']; $url = 'http://example.com/'; - foreach ($valid_methods as $method) { - $r = call_user_func(array('Httpful\Request', $method), $url); - $this->assertEquals('Httpful\Request', get_class($r)); + foreach ($valid_methods as $method) { + $r = call_user_func([\Httpful\Request::class, $method], $url); + $this->assertEquals(\Httpful\Request::class, get_class($r)); $this->assertEquals(strtoupper($method), $r->method); } } @@ -198,7 +207,7 @@ function testAccept() $this->assertEquals(Mime::JSON, $r->expected_type); $r->_curlPrep(); - $this->assertContains('application/json', $r->raw_headers); + $this->assertStringContainsString('application/json', $r->raw_headers); } function testCustomAccept() @@ -208,7 +217,7 @@ function testCustomAccept() ->addHeader('Accept', $accept); $r->_curlPrep(); - $this->assertContains($accept, $r->raw_headers); + $this->assertStringContainsString($accept, $r->raw_headers); $this->assertEquals($accept, $r->headers['Accept']); } @@ -219,16 +228,16 @@ function testUserAgent() $this->assertArrayHasKey('User-Agent', $r->headers); $r->_curlPrep(); - $this->assertContains('User-Agent: ACME/1.2.3', $r->raw_headers); - $this->assertNotContains('User-Agent: HttpFul/1.0', $r->raw_headers); + $this->assertStringContainsString('User-Agent: ACME/1.2.3', $r->raw_headers); + $this->assertStringNotContainsString('User-Agent: HttpFul/1.0', $r->raw_headers); $r = Request::get('http://example.com/') ->withUserAgent(''); $this->assertArrayHasKey('User-Agent', $r->headers); $r->_curlPrep(); - $this->assertContains('User-Agent:', $r->raw_headers); - $this->assertNotContains('User-Agent: HttpFul/1.0', $r->raw_headers); + $this->assertStringContainsString('User-Agent:', $r->raw_headers); + $this->assertStringNotContainsString('User-Agent: HttpFul/1.0', $r->raw_headers); } function testAuthSetup() @@ -264,7 +273,18 @@ function testJsonResponseParse() $this->assertEquals("value", $response->body->key); $this->assertEquals("value", $response->body->object->key); - $this->assertInternalType('array', $response->body->array); + $this->assertIsArray( $response->body->array); + $this->assertEquals(1, $response->body->array[0]); + } + + function testJsonResponseParseLowercaseHeaders() + { + $req = Request::init(); + $response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER_LOWERCASE, $req); + + $this->assertEquals("value", $response->body->key); + $this->assertEquals("value", $response->body->object->key); + $this->assertIsArray( $response->body->array); $this->assertEquals(1, $response->body->array[0]); } @@ -276,13 +296,16 @@ function testXMLResponseParse() $this->assertEquals("object", gettype($sxe)); $this->assertEquals("SimpleXMLElement", get_class($sxe)); $bools = $sxe->xpath('/stdClass/boolProp'); - list( , $bool ) = each($bools); + // list( , $bool ) = each($bools); + $bool = array_shift($bools); $this->assertEquals("TRUE", (string) $bool); $ints = $sxe->xpath('/stdClass/arrayProp/array/k1/myClass/intProp'); - list( , $int ) = each($ints); + // list( , $int ) = each($ints); + $int = array_shift($ints); $this->assertEquals("2", (string) $int); $strings = $sxe->xpath('/stdClass/stringProp'); - list( , $string ) = each($strings); + // list( , $string ) = each($strings); + $string = array_shift($strings); $this->assertEquals("a string", (string) $string); } @@ -293,7 +316,7 @@ function testCsvResponseParse() $this->assertEquals("Key1", $response->body[0][0]); $this->assertEquals("Value1", $response->body[1][0]); - $this->assertInternalType('string', $response->body[2][0]); + $this->assertIsString( $response->body[2][0]); $this->assertEquals("40.0", $response->body[2][0]); } @@ -321,10 +344,10 @@ function testParsingContentTypeUpload() } function testAttach() { - $req = Request::init(); - $testsPath = realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..'); - $filename = $testsPath . DIRECTORY_SEPARATOR . 'test_image.jpg'; - $req->attach(array('index' => $filename)); + $req = Request::init(); + $testsPath = realpath(__DIR__ . DIRECTORY_SEPARATOR . '..'); + $filename = $testsPath . DIRECTORY_SEPARATOR . 'test_image.jpg'; + $req->attach(['index' => $filename]); $payload = $req->payload['index']; // PHP 5.5 + will take advantage of CURLFile while previous // versions just use the string syntax @@ -361,10 +384,10 @@ function testNoAutoParse() { $req = Request::init()->sendsAndExpects(Mime::JSON)->withoutAutoParsing(); $response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req); - $this->assertInternalType('string', $response->body); + $this->assertIsString( $response->body); $req = Request::init()->sendsAndExpects(Mime::JSON)->withAutoParsing(); $response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req); - $this->assertInternalType('object', $response->body); + $this->assertIsObject($response->body); } function testParseHeaders() @@ -378,7 +401,7 @@ function testRawHeaders() { $req = Request::init()->sendsAndExpects(Mime::JSON); $response = new Response(self::SAMPLE_JSON_RESPONSE, self::SAMPLE_JSON_HEADER, $req); - $this->assertContains('Content-Type: application/json', $response->raw_headers); + $this->assertStringContainsString('Content-Type: application/json', $response->raw_headers); } function testHasErrors() @@ -535,7 +558,7 @@ public function testOverrideXmlHandler() // Lazy test... $prev = \Httpful\Httpful::get(\Httpful\Mime::XML); $this->assertEquals($prev, new \Httpful\Handlers\XmlHandler()); - $conf = array('namespace' => 'http://example.com'); + $conf = ['namespace' => 'http://example.com']; \Httpful\Httpful::register(\Httpful\Mime::XML, new \Httpful\Handlers\XmlHandler($conf)); $new = \Httpful\Httpful::get(\Httpful\Mime::XML); $this->assertNotEquals($prev, $new); @@ -554,16 +577,24 @@ public function testHasProxyWithProxy() $this->assertTrue($r->hasProxy()); } + public function testHasProxyWithEnvironmentProxy() + { + putenv('http_proxy=http://127.0.0.1:300/'); + $r = Request::get('some_other_url'); + $this->assertTrue($r->hasProxy()); + } + + public function testParseJSON() { $handler = new JsonHandler(); - $bodies = array( + $bodies = [ 'foo', - array(), - array('foo', 'bar'), + [], + ['foo', 'bar'], null - ); + ]; foreach ($bodies as $body) { $this->assertEquals($body, $handler->parse(json_encode($body))); } diff --git a/tests/Httpful/requestTest.php b/tests/Httpful/requestTest.php index 3286765..7566521 100644 --- a/tests/Httpful/requestTest.php +++ b/tests/Httpful/requestTest.php @@ -4,18 +4,25 @@ */ namespace Httpful\Test; -class requestTest extends \PHPUnit_Framework_TestCase +class requestTest extends \PHPUnit\Framework\TestCase { /** * @author Nick Fox - * @expectedException Httpful\Exception\ConnectionErrorException - * @expectedExceptionMessage Unable to connect */ public function testGet_InvalidURL() { // Silence the default logger via whenError override - \Httpful\Request::get('unavailable.url')->whenError(function($error) {})->send(); + $caught = false; + try + { + \Httpful\Request::get('unavailable.url')->whenError(function($error) {})->send(); + } + catch (\Httpful\Exception\ConnectionErrorException $e) + { + $caught = true; + } + $this->assertTrue($caught); } } diff --git a/tests/bootstrap-server.php b/tests/bootstrap-server.php index 1c27bd9..1ac0dad 100644 --- a/tests/bootstrap-server.php +++ b/tests/bootstrap-server.php @@ -1,7 +1,7 @@ ./server.log 2>&1 & echo $!', WEB_SERVER_HOST, WEB_SERVER_PORT, WEB_SERVER_DOCROOT); // Execute the command and store the process ID - $output = array(); + $output = []; exec($command, $output, $exit_code); // sleep for a second to let server come up diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 18ab15a..c2c9d44 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -8,6 +8,7 @@ +