diff --git a/.travis.yml b/.travis.yml index 7c466c1..2abd083 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,9 @@ php: - 5.5 - 5.6 - 7.0 - - hhvm + - 7.1 + - 7.2 + - 7.3 env: global: diff --git a/CHANGELOG.md b/CHANGELOG.md index 233e4d0..e68d269 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Change Log +## 1.2.0 - 2019-06-05 + +Maintenance release with some cleanups in case anyone is still using this. + +## 1.1.0 - 2016-05-05 + +### Deprecated + +- Core plugins and plugin client, moved to [client-common](https://github.com/php-http/client-common) +- Logger plugin, moved to [logger-plugin](https://github.com/php-http/logger-plugin) +- Cache plugin, moved to [cache-plugin](https://github.com/php-http/cache-plugin) +- Stopwatch plugin, moved to [stopwatch-plugin](https://github.com/php-http/stopwatch-plugin) + + ## 1.0.1 - 2016-01-29 ### Changed diff --git a/README.md b/README.md index ec184d8..72d8b27 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ +> DEPRECATED +> +> As of version 1.1 this package is deprecated. Most of the plugins are merged into `php-http/client-common`. +> +> The following plugins are in separate packages: logger, cache, stopwatch + # Plugins for Httplug [![Latest Version](https://img.shields.io/github/release/php-http/plugins.svg?style=flat-square)](https://github.com/php-http/plugins/releases) diff --git a/composer.json b/composer.json index 5b2179d..2c063c1 100644 --- a/composer.json +++ b/composer.json @@ -11,19 +11,23 @@ } ], "require": { - "php": ">=5.4", + "php": "^5.4 | ^7.0", "php-http/httplug": "^1.0", - "php-http/message-factory": "^1.0", - "psr/log": "^1.0", - "psr/cache": "^1.0", - "php-http/client-common": "^1.0", + "php-http/message-factory": "^1.0.2", + "php-http/client-common": "^1.1", "php-http/message": "^1.0", "symfony/options-resolver": "^2.6|^3.0" }, "require-dev": { "symfony/stopwatch": "^2.3", "phpspec/phpspec": "^2.4", - "henrikbjorn/phpspec-code-coverage" : "^1.0" + "henrikbjorn/phpspec-code-coverage" : "^1.0", + "psr/log": "^1.0", + "psr/cache": "^1.0" + }, + "conflict": { + "psr/log": ">=2.0.0", + "psr/cache": ">=2.0.0" }, "autoload": { "psr-4": { @@ -46,7 +50,7 @@ }, "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "1.2-dev" } } } diff --git a/spec/CachePluginSpec.php b/spec/CachePluginSpec.php index 6824f4f..178154f 100644 --- a/spec/CachePluginSpec.php +++ b/spec/CachePluginSpec.php @@ -39,8 +39,8 @@ function it_caches_responses(CacheItemPoolInterface $pool, CacheItemInterface $i $request->getUri()->willReturn('/'); $response->getStatusCode()->willReturn(200); $response->getBody()->willReturn($stream); - $response->getHeader('Cache-Control')->willReturn(array()); - $response->getHeader('Expires')->willReturn(array()); + $response->getHeader('Cache-Control')->willReturn([]); + $response->getHeader('Expires')->willReturn([]); $pool->getItem('e3b717d5883a45ef9493d009741f7c64')->shouldBeCalled()->willReturn($item); $item->isHit()->willReturn(false); @@ -60,8 +60,8 @@ function it_doesnt_store_failed_responses(CacheItemPoolInterface $pool, CacheIte $request->getMethod()->willReturn('GET'); $request->getUri()->willReturn('/'); $response->getStatusCode()->willReturn(400); - $response->getHeader('Cache-Control')->willReturn(array()); - $response->getHeader('Expires')->willReturn(array()); + $response->getHeader('Cache-Control')->willReturn([]); + $response->getHeader('Expires')->willReturn([]); $pool->getItem('e3b717d5883a45ef9493d009741f7c64')->shouldBeCalled()->willReturn($item); $item->isHit()->willReturn(false); @@ -97,9 +97,9 @@ function it_calculate_age_from_response(CacheItemPoolInterface $pool, CacheItemI $request->getUri()->willReturn('/'); $response->getStatusCode()->willReturn(200); $response->getBody()->willReturn($stream); - $response->getHeader('Cache-Control')->willReturn(array('max-age=40')); - $response->getHeader('Age')->willReturn(array('15')); - $response->getHeader('Expires')->willReturn(array()); + $response->getHeader('Cache-Control')->willReturn(['max-age=40']); + $response->getHeader('Age')->willReturn(['15']); + $response->getHeader('Expires')->willReturn([]); $pool->getItem('e3b717d5883a45ef9493d009741f7c64')->shouldBeCalled()->willReturn($item); $item->isHit()->willReturn(false); diff --git a/spec/ContentLengthPluginSpec.php b/spec/ContentLengthPluginSpec.php index 0fa463f..d0dedf0 100644 --- a/spec/ContentLengthPluginSpec.php +++ b/spec/ContentLengthPluginSpec.php @@ -28,10 +28,6 @@ function it_adds_content_length_header(RequestInterface $request, StreamInterfac function it_streams_chunked_if_no_size(RequestInterface $request, StreamInterface $stream) { - if(defined('HHVM_VERSION')) { - throw new SkippingException('Skipping test on hhvm, as there is no chunk encoding on hhvm'); - } - $request->hasHeader('Content-Length')->shouldBeCalled()->willReturn(false); $request->getBody()->shouldBeCalled()->willReturn($stream); diff --git a/spec/CookiePluginSpec.php b/spec/CookiePluginSpec.php index 15c3102..d0c40e2 100644 --- a/spec/CookiePluginSpec.php +++ b/spec/CookiePluginSpec.php @@ -2,15 +2,15 @@ namespace spec\Http\Client\Plugin; -use Http\Promise\FulfilledPromise; use Http\Message\Cookie; use Http\Message\CookieJar; +use Http\Promise\FulfilledPromise; use Http\Promise\Promise; +use PhpSpec\ObjectBehavior; +use Prophecy\Argument; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\UriInterface; -use PhpSpec\ObjectBehavior; -use Prophecy\Argument; class CookiePluginSpec extends ObjectBehavior { @@ -146,7 +146,7 @@ function it_saves_cookie(RequestInterface $request, ResponseInterface $response, $response->hasHeader('Set-Cookie')->willReturn(true); $response->getHeader('Set-Cookie')->willReturn([ - 'cookie=value', + 'cookie=value; expires=Tuesday, 31-Mar-99 07:42:12 GMT; Max-Age=60; path=/; domain=test.com; secure; HttpOnly' ]); $request->getUri()->willReturn($uri); @@ -157,4 +157,24 @@ function it_saves_cookie(RequestInterface $request, ResponseInterface $response, $promise->shouldHaveType('Http\Promise\Promise'); $promise->wait()->shouldReturnAnInstanceOf('Psr\Http\Message\ResponseInterface'); } + + function it_throws_exception_on_invalid_expires_date(RequestInterface $request, ResponseInterface $response, UriInterface $uri) + { + $next = function () use ($response) { + return new FulfilledPromise($response->getWrappedObject()); + }; + + $response->hasHeader('Set-Cookie')->willReturn(true); + $response->getHeader('Set-Cookie')->willReturn([ + 'cookie=value; expires=i-am-an-invalid-date;' + ]); + + $request->getUri()->willReturn($uri); + $uri->getHost()->willReturn('test.com'); + $uri->getPath()->willReturn('/'); + + $promise = $this->handleRequest($request, $next, function () {}); + $promise->shouldReturnAnInstanceOf('Http\Promise\RejectedPromise'); + $promise->shouldThrow('Http\Client\Exception\TransferException')->duringWait(); + } } diff --git a/spec/DecoderPluginSpec.php b/spec/DecoderPluginSpec.php index 4408898..2b846ad 100644 --- a/spec/DecoderPluginSpec.php +++ b/spec/DecoderPluginSpec.php @@ -24,10 +24,6 @@ function it_is_a_plugin() function it_decodes(RequestInterface $request, ResponseInterface $response, StreamInterface $stream) { - if(defined('HHVM_VERSION')) { - throw new SkippingException('Skipping test on hhvm, as there is no chunk encoding on hhvm'); - } - $request->withHeader('TE', ['gzip', 'deflate', 'compress', 'chunked'])->shouldBeCalled()->willReturn($request); $request->withHeader('Accept-Encoding', ['gzip', 'deflate', 'compress'])->shouldBeCalled()->willReturn($request); $next = function () use($response) { diff --git a/spec/StopwatchPluginSpec.php b/spec/StopwatchPluginSpec.php index 2cc2141..c4b74db 100644 --- a/spec/StopwatchPluginSpec.php +++ b/spec/StopwatchPluginSpec.php @@ -33,7 +33,7 @@ function it_records_event(Stopwatch $stopwatch, RequestInterface $request, Respo $request->getRequestTarget()->willReturn('/'); $stopwatch->start('GET /', 'php_http.request')->shouldBeCalled(); - $stopwatch->stop('GET /', 'php_http.request')->shouldBeCalled(); + $stopwatch->stop('GET /')->shouldBeCalled(); $next = function (RequestInterface $request) use ($response) { return new FulfilledPromise($response->getWrappedObject()); @@ -48,7 +48,7 @@ function it_records_event_on_error(Stopwatch $stopwatch, RequestInterface $reque $request->getRequestTarget()->willReturn('/'); $stopwatch->start('GET /', 'php_http.request')->shouldBeCalled(); - $stopwatch->stop('GET /', 'php_http.request')->shouldBeCalled(); + $stopwatch->stop('GET /')->shouldBeCalled(); $next = function (RequestInterface $request) { return new RejectedPromise(new NetworkException('', $request)); diff --git a/src/AddHostPlugin.php b/src/AddHostPlugin.php index 025ef80..69b8c16 100644 --- a/src/AddHostPlugin.php +++ b/src/AddHostPlugin.php @@ -2,6 +2,8 @@ namespace Http\Client\Plugin; +@trigger_error('The '.__NAMESPACE__.'\AddHostPlugin class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Plugin\AddHostPlugin instead.', E_USER_DEPRECATED); + use Psr\Http\Message\RequestInterface; use Psr\Http\Message\UriInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -10,6 +12,8 @@ * Add schema and host to a request. Can be set to overwrite the schema and host if desired. * * @author Tobias Nyholm + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Plugin\AddHostPlugin} instead. */ class AddHostPlugin implements Plugin { @@ -32,7 +36,7 @@ class AddHostPlugin implements Plugin */ public function __construct(UriInterface $host, array $config = []) { - if ($host->getHost() === '') { + if ('' === $host->getHost()) { throw new \LogicException('Host can not be empty'); } @@ -50,7 +54,7 @@ public function __construct(UriInterface $host, array $config = []) */ public function handleRequest(RequestInterface $request, callable $next, callable $first) { - if ($this->replace || $request->getUri()->getHost() === '') { + if ($this->replace || '' === $request->getUri()->getHost()) { $uri = $request->getUri()->withHost($this->host->getHost()); $uri = $uri->withScheme($this->host->getScheme()); diff --git a/src/AuthenticationPlugin.php b/src/AuthenticationPlugin.php index 29e368b..c47a42d 100644 --- a/src/AuthenticationPlugin.php +++ b/src/AuthenticationPlugin.php @@ -2,6 +2,8 @@ namespace Http\Client\Plugin; +@trigger_error('The '.__NAMESPACE__.'\AuthenticationPlugin class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Plugin\AuthenticationPlugin instead.', E_USER_DEPRECATED); + use Http\Message\Authentication; use Psr\Http\Message\RequestInterface; @@ -9,6 +11,8 @@ * Send an authenticated request. * * @author Joel Wurtz + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Plugin\AuthenticationPlugin} instead. */ class AuthenticationPlugin implements Plugin { diff --git a/src/CachePlugin.php b/src/CachePlugin.php index 62c9715..ca33adf 100644 --- a/src/CachePlugin.php +++ b/src/CachePlugin.php @@ -2,6 +2,8 @@ namespace Http\Client\Plugin; +@trigger_error('The '.__NAMESPACE__.'\CachePlugin class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Plugin\CachePlugin instead.', E_USER_DEPRECATED); + use Http\Message\StreamFactory; use Http\Promise\FulfilledPromise; use Psr\Cache\CacheItemPoolInterface; @@ -10,9 +12,13 @@ use Symfony\Component\OptionsResolver\OptionsResolver; /** - * Allow for caching a response. + * Allow for caching a response with a PSR-6 compatible caching engine. + * + * It can follow the RFC-7234 caching specification or use a fixed cache lifetime. * * @author Tobias Nyholm + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Plugin\CachePlugin} instead. */ class CachePlugin implements Plugin { @@ -36,8 +42,8 @@ class CachePlugin implements Plugin * @param StreamFactory $streamFactory * @param array $config { * - * @var bool $respect_cache_headers Whether to look at the cache directives or ignore them. - * @var int $default_ttl If we do not respect cache headers or can't calculate a good ttl, use this value. + * @var bool $respect_cache_headers Whether to look at the cache directives or ignore them + * @var int $default_ttl If we do not respect cache headers or the headers specify cache control, use this value * } */ public function __construct(CacheItemPoolInterface $pool, StreamFactory $streamFactory, array $config = []) @@ -57,18 +63,17 @@ public function handleRequest(RequestInterface $request, callable $next, callabl { $method = strtoupper($request->getMethod()); - // if the request not is cachable, move to $next - if ($method !== 'GET' && $method !== 'HEAD') { + // if the request is not cacheable, move to $next + if ('GET' !== $method && 'HEAD' !== $method) { return $next($request); } - // If we can cache the request $key = $this->createCacheKey($request); $cacheItem = $this->pool->getItem($key); if ($cacheItem->isHit()) { - // return cached response $data = $cacheItem->get(); + /** @var ResponseInterface $response */ $response = $data['response']; $response = $response->withBody($this->streamFactory->createStream($data['body'])); @@ -78,7 +83,7 @@ public function handleRequest(RequestInterface $request, callable $next, callabl return $next($request)->then(function (ResponseInterface $response) use ($cacheItem) { if ($this->isCacheable($response)) { $bodyStream = $response->getBody(); - $body = $bodyStream->__toString(); + $body = (string) $bodyStream; if ($bodyStream->isSeekable()) { $bodyStream->rewind(); } else { @@ -122,14 +127,13 @@ protected function isCacheable(ResponseInterface $response) * @param ResponseInterface $response * @param string $name The field of Cache-Control to fetch * - * @return bool|string The value of the directive, true if directive without value, false if directive not present. + * @return bool|string the value of the directive, true if directive without value, false if directive not present */ private function getCacheControlDirective(ResponseInterface $response, $name) { $headers = $response->getHeader('Cache-Control'); foreach ($headers as $header) { if (preg_match(sprintf('|%s=?([0-9]+)?|i', $name), $header, $matches)) { - // return the value for $name if it exists if (isset($matches[1])) { return $matches[1]; diff --git a/src/ContentLengthPlugin.php b/src/ContentLengthPlugin.php index 068d65e..b8b9c7a 100644 --- a/src/ContentLengthPlugin.php +++ b/src/ContentLengthPlugin.php @@ -2,6 +2,8 @@ namespace Http\Client\Plugin; +@trigger_error('The '.__NAMESPACE__.'\ContentLengthPlugin class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Plugin\ContentLengthPlugin instead.', E_USER_DEPRECATED); + use Http\Message\Encoding\ChunkStream; use Psr\Http\Message\RequestInterface; @@ -9,6 +11,8 @@ * Allow to set the correct content length header on the request or to transfer it as a chunk if not possible. * * @author Joel Wurtz + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Plugin\ContentLengthPlugin} instead. */ class ContentLengthPlugin implements Plugin { diff --git a/src/CookiePlugin.php b/src/CookiePlugin.php index 71b56b2..7847112 100644 --- a/src/CookiePlugin.php +++ b/src/CookiePlugin.php @@ -2,6 +2,9 @@ namespace Http\Client\Plugin; +@trigger_error('The '.__NAMESPACE__.'\CookiePlugin class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Plugin\CookiePlugin instead.', E_USER_DEPRECATED); + +use Http\Client\Exception\TransferException; use Http\Message\Cookie; use Http\Message\CookieJar; use Psr\Http\Message\RequestInterface; @@ -11,6 +14,8 @@ * Handle request cookies. * * @author Joel Wurtz + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Plugin\CookiePlugin} instead. */ class CookiePlugin implements Plugin { @@ -47,7 +52,7 @@ public function handleRequest(RequestInterface $request, callable $next, callabl continue; } - if ($cookie->isSecure() && ($request->getUri()->getScheme() !== 'https')) { + if ($cookie->isSecure() && ('https' !== $request->getUri()->getScheme())) { continue; } @@ -86,6 +91,8 @@ public function handleRequest(RequestInterface $request, callable $next, callabl * @param $setCookie * * @return Cookie|null + * + * @throws \Http\Client\Exception\TransferException */ private function createCookie(RequestInterface $request, $setCookie) { @@ -97,7 +104,8 @@ private function createCookie(RequestInterface $request, $setCookie) list($name, $cookieValue) = $this->createValueKey(array_shift($parts)); - $expires = 0; + $maxAge = null; + $expires = null; $domain = $request->getUri()->getHost(); $path = $request->getUri()->getPath(); $secure = false; @@ -109,32 +117,48 @@ private function createCookie(RequestInterface $request, $setCookie) switch (strtolower($key)) { case 'expires': - $expires = \DateTime::createFromFormat(DATE_COOKIE, $value); + $expires = \DateTime::createFromFormat(\DateTime::COOKIE, $value); + + if (true !== ($expires instanceof \DateTime)) { + throw new TransferException( + sprintf( + 'Cookie header `%s` expires value `%s` could not be converted to date', + $name, + $value + ) + ); + } + break; case 'max-age': - $expires = (new \DateTime())->add(new \DateInterval('PT'.(int) $value.'S')); + $maxAge = (int) $value; + break; case 'domain': $domain = $value; + break; case 'path': $path = $value; + break; case 'secure': $secure = true; + break; case 'httponly': $httpOnly = true; + break; } } - return new Cookie($name, $cookieValue, $expires, $domain, $path, $secure, $httpOnly); + return new Cookie($name, $cookieValue, $maxAge, $domain, $path, $secure, $httpOnly, $expires); } /** diff --git a/src/DecoderPlugin.php b/src/DecoderPlugin.php index 4b49bfb..a8b3437 100644 --- a/src/DecoderPlugin.php +++ b/src/DecoderPlugin.php @@ -2,6 +2,8 @@ namespace Http\Client\Plugin; +@trigger_error('The '.__NAMESPACE__.'\DecoderPlugin class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Plugin\DecoderPlugin instead.', E_USER_DEPRECATED); + use Http\Message\Encoding\DechunkStream; use Http\Message\Encoding\DecompressStream; use Http\Message\Encoding\GzipDecodeStream; @@ -19,6 +21,8 @@ * If Content-Encoding is not disabled, the plugin will add an Accept-Encoding header for the encoding methods it supports. * * @author Joel Wurtz + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Plugin\DecoderPlugin} instead. */ class DecoderPlugin implements Plugin { @@ -125,19 +129,19 @@ private function decodeOnEncodingHeader($headerName, ResponseInterface $response */ private function decorateStream($encoding, StreamInterface $stream) { - if (strtolower($encoding) == 'chunked') { + if ('chunked' == strtolower($encoding)) { return new DechunkStream($stream); } - if (strtolower($encoding) == 'compress') { + if ('compress' == strtolower($encoding)) { return new DecompressStream($stream); } - if (strtolower($encoding) == 'deflate') { + if ('deflate' == strtolower($encoding)) { return new InflateStream($stream); } - if (strtolower($encoding) == 'gzip') { + if ('gzip' == strtolower($encoding)) { return new GzipDecodeStream($stream); } diff --git a/src/ErrorPlugin.php b/src/ErrorPlugin.php index 36ca121..02b4d19 100644 --- a/src/ErrorPlugin.php +++ b/src/ErrorPlugin.php @@ -2,6 +2,8 @@ namespace Http\Client\Plugin; +@trigger_error('The '.__NAMESPACE__.'\ErrorPlugin class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Plugin\ErrorPlugin instead.', E_USER_DEPRECATED); + use Http\Client\Plugin\Exception\ClientErrorException; use Http\Client\Plugin\Exception\ServerErrorException; use Psr\Http\Message\RequestInterface; @@ -13,6 +15,8 @@ * By default an exception will be thrown for all status codes from 400 to 599. * * @author Joel Wurtz + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Plugin\ErrorPlugin} instead. */ class ErrorPlugin implements Plugin { diff --git a/src/Exception/CircularRedirectionException.php b/src/Exception/CircularRedirectionException.php index bf71b1a..bc4df78 100644 --- a/src/Exception/CircularRedirectionException.php +++ b/src/Exception/CircularRedirectionException.php @@ -2,12 +2,16 @@ namespace Http\Client\Plugin\Exception; +@trigger_error('The '.__NAMESPACE__.'\CircularRedirectionException class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Exception\CircularRedirectionException instead.', E_USER_DEPRECATED); + use Http\Client\Exception\HttpException; /** * Thrown when circular redirection is detected. * * @author Joel Wurtz + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Exception\CircularRedirectionException} instead. */ class CircularRedirectionException extends HttpException { diff --git a/src/Exception/ClientErrorException.php b/src/Exception/ClientErrorException.php index 8624660..28e0b85 100644 --- a/src/Exception/ClientErrorException.php +++ b/src/Exception/ClientErrorException.php @@ -2,12 +2,16 @@ namespace Http\Client\Plugin\Exception; +@trigger_error('The '.__NAMESPACE__.'\ClientErrorException class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Exception\ClientErrorException instead.', E_USER_DEPRECATED); + use Http\Client\Exception\HttpException; /** * Thrown when there is a client error (4xx). * * @author Joel Wurtz + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Exception\ClientErrorException} instead. */ class ClientErrorException extends HttpException { diff --git a/src/Exception/LoopException.php b/src/Exception/LoopException.php index 5194ed7..cb291da 100644 --- a/src/Exception/LoopException.php +++ b/src/Exception/LoopException.php @@ -2,10 +2,14 @@ namespace Http\Client\Plugin\Exception; +@trigger_error('The '.__NAMESPACE__.'\LoopException class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Exception\LoopException instead.', E_USER_DEPRECATED); + use Http\Client\Exception\RequestException; /** * @author Joel Wurtz + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Exception\LoopException} instead. */ class LoopException extends RequestException { diff --git a/src/Exception/MultipleRedirectionException.php b/src/Exception/MultipleRedirectionException.php index b16c026..0ded5da 100644 --- a/src/Exception/MultipleRedirectionException.php +++ b/src/Exception/MultipleRedirectionException.php @@ -2,12 +2,16 @@ namespace Http\Client\Plugin\Exception; +@trigger_error('The '.__NAMESPACE__.'\MultipleRedirectionException class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Exception\MultipleRedirectionException instead.', E_USER_DEPRECATED); + use Http\Client\Exception\HttpException; /** * Redirect location cannot be chosen. * * @author Joel Wurtz + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Exception\MultipleRedirectionException} instead. */ class MultipleRedirectionException extends HttpException { diff --git a/src/Exception/ServerErrorException.php b/src/Exception/ServerErrorException.php index 65117f1..8702d27 100644 --- a/src/Exception/ServerErrorException.php +++ b/src/Exception/ServerErrorException.php @@ -2,12 +2,16 @@ namespace Http\Client\Plugin\Exception; +@trigger_error('The '.__NAMESPACE__.'\ServerErrorException class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Exception\ServerErrorException instead.', E_USER_DEPRECATED); + use Http\Client\Exception\HttpException; /** * Thrown when there is a server error (5xx). * * @author Joel Wurtz + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Exception\ServerErrorException} instead. */ class ServerErrorException extends HttpException { diff --git a/src/HeaderAppendPlugin.php b/src/HeaderAppendPlugin.php index b3fcac2..33cd939 100644 --- a/src/HeaderAppendPlugin.php +++ b/src/HeaderAppendPlugin.php @@ -2,6 +2,8 @@ namespace Http\Client\Plugin; +@trigger_error('The '.__NAMESPACE__.'\HeaderAppendPlugin class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Plugin\HeaderAppendPlugin instead.', E_USER_DEPRECATED); + use Psr\Http\Message\RequestInterface; /** @@ -10,9 +12,11 @@ * * This only makes sense for headers that can have multiple values like 'Forwarded' * - * @link https://en.wikipedia.org/wiki/List_of_HTTP_header_fields + * @see https://en.wikipedia.org/wiki/List_of_HTTP_header_fields * * @author Soufiane Ghzal + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Plugin\HeaderAppendPlugin} instead. */ class HeaderAppendPlugin implements Plugin { diff --git a/src/HeaderDefaultsPlugin.php b/src/HeaderDefaultsPlugin.php index fbee194..c2415e9 100644 --- a/src/HeaderDefaultsPlugin.php +++ b/src/HeaderDefaultsPlugin.php @@ -2,6 +2,8 @@ namespace Http\Client\Plugin; +@trigger_error('The '.__NAMESPACE__.'\HeaderDefaultsPlugin class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Plugin\HeaderDefaultsPlugin instead.', E_USER_DEPRECATED); + use Psr\Http\Message\RequestInterface; /** @@ -9,6 +11,8 @@ * If a given header already exists the value wont be replaced and the request wont be changed. * * @author Soufiane Ghzal + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Plugin\HeaderDefaultsPlugin} instead. */ class HeaderDefaultsPlugin implements Plugin { diff --git a/src/HeaderRemovePlugin.php b/src/HeaderRemovePlugin.php index b49ac26..800e8e6 100644 --- a/src/HeaderRemovePlugin.php +++ b/src/HeaderRemovePlugin.php @@ -2,12 +2,16 @@ namespace Http\Client\Plugin; +@trigger_error('The '.__NAMESPACE__.'\HeaderRemovePlugin class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Plugin\HeaderRemovePlugin instead.', E_USER_DEPRECATED); + use Psr\Http\Message\RequestInterface; /** * Removes headers from the request. * * @author Soufiane Ghzal + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Plugin\HeaderRemovePlugin} instead. */ class HeaderRemovePlugin implements Plugin { diff --git a/src/HeaderSetPlugin.php b/src/HeaderSetPlugin.php index 0e855f9..fe06c08 100644 --- a/src/HeaderSetPlugin.php +++ b/src/HeaderSetPlugin.php @@ -2,6 +2,8 @@ namespace Http\Client\Plugin; +@trigger_error('The '.__NAMESPACE__.'\HeaderSetPlugin class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Plugin\HeaderSetPlugin instead.', E_USER_DEPRECATED); + use Psr\Http\Message\RequestInterface; /** @@ -9,6 +11,8 @@ * If the header does not exist it wil be set, if the header already exists it will be replaced. * * @author Soufiane Ghzal + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Plugin\HeaderSetPlugin} instead. */ class HeaderSetPlugin implements Plugin { diff --git a/src/HistoryPlugin.php b/src/HistoryPlugin.php index 798b32e..0c02679 100644 --- a/src/HistoryPlugin.php +++ b/src/HistoryPlugin.php @@ -2,12 +2,16 @@ namespace Http\Client\Plugin; +@trigger_error('The '.__NAMESPACE__.'\HistoryPlugin class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Plugin\HistoryPlugin instead.', E_USER_DEPRECATED); + use Http\Client\Exception; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; /** * Record http call. + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Plugin\HistoryPlugin} instead. */ class HistoryPlugin implements Plugin { diff --git a/src/Journal.php b/src/Journal.php index 711ed43..a35504c 100644 --- a/src/Journal.php +++ b/src/Journal.php @@ -2,6 +2,8 @@ namespace Http\Client\Plugin; +@trigger_error('The '.__NAMESPACE__.'\Journal class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Plugin\Journal instead.', E_USER_DEPRECATED); + use Http\Client\Exception; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; @@ -10,6 +12,8 @@ * Records history of http calls. * * @author Joel Wurtz + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Plugin\Journal} instead. */ interface Journal { diff --git a/src/LoggerPlugin.php b/src/LoggerPlugin.php index 5feec57..0fe9cbe 100644 --- a/src/LoggerPlugin.php +++ b/src/LoggerPlugin.php @@ -2,6 +2,8 @@ namespace Http\Client\Plugin; +@trigger_error('The '.__NAMESPACE__.'\LoggerPlugin class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Plugin\LoggerPlugin instead.', E_USER_DEPRECATED); + use Http\Client\Exception; use Http\Message\Formatter; use Http\Message\Formatter\SimpleFormatter; @@ -13,6 +15,8 @@ * Log request, response and exception for a HTTP Client. * * @author Joel Wurtz + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Plugin\LoggerPlugin} instead. */ class LoggerPlugin implements Plugin { diff --git a/src/Plugin.php b/src/Plugin.php index 2f09ff1..66c7f3f 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -2,8 +2,7 @@ namespace Http\Client\Plugin; -use Http\Promise\Promise; -use Psr\Http\Message\RequestInterface; +@trigger_error('The '.__NAMESPACE__.'\Plugin class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Plugin instead.', E_USER_DEPRECATED); /** * A plugin is a middleware to transform the request and/or the response. @@ -14,17 +13,9 @@ * - restart the request * * @author Joel Wurtz + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Plugin} instead. */ -interface Plugin +interface Plugin extends \Http\Client\Common\Plugin { - /** - * Handle the request and return the response coming from the next callable. - * - * @param RequestInterface $request - * @param callable $next Next middleware in the chain, the request is passed as the first argument - * @param callable $first First middleware in the chain, used to to restart a request - * - * @return Promise - */ - public function handleRequest(RequestInterface $request, callable $next, callable $first); } diff --git a/src/PluginClient.php b/src/PluginClient.php index 2d276ed..e91f866 100644 --- a/src/PluginClient.php +++ b/src/PluginClient.php @@ -2,66 +2,40 @@ namespace Http\Client\Plugin; -use Http\Client\Common\EmulatedHttpAsyncClient; -use Http\Client\Exception; +@trigger_error('The '.__NAMESPACE__.'\PluginClient class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\PluginClient instead.', E_USER_DEPRECATED); + use Http\Client\HttpAsyncClient; use Http\Client\HttpClient; use Http\Client\Plugin\Exception\LoopException; -use Http\Promise\FulfilledPromise; -use Http\Promise\RejectedPromise; use Psr\Http\Message\RequestInterface; -use Symfony\Component\OptionsResolver\OptionsResolver; /** * The client managing plugins and providing a decorator around HTTP Clients. * * @author Joel Wurtz + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\PluginClient} instead. */ final class PluginClient implements HttpClient, HttpAsyncClient { /** - * An HTTP async client. - * - * @var HttpAsyncClient - */ - private $client; - - /** - * The plugin chain. - * - * @var Plugin[] + * @var \Http\Client\Common\PluginClient */ - private $plugins; - - /** - * A list of options. - * - * @var array - */ - private $options; + private $pluginClient; /** * @param HttpClient|HttpAsyncClient $client * @param Plugin[] $plugins * @param array $options { * - * @var int $max_restarts + * @var int $max_restarts Number of times plugins may initiate a restart of the plugin chain. Defaults to 10. * } * * @throws \RuntimeException if client is not an instance of HttpClient or HttpAsyncClient */ public function __construct($client, array $plugins = [], array $options = []) { - if ($client instanceof HttpAsyncClient) { - $this->client = $client; - } elseif ($client instanceof HttpClient) { - $this->client = new EmulatedHttpAsyncClient($client); - } else { - throw new \RuntimeException('Client must be an instance of Http\\Client\\HttpClient or Http\\Client\\HttpAsyncClient'); - } - - $this->plugins = $plugins; - $this->options = $this->configure($options); + $this->pluginClient = new \Http\Client\Common\PluginClient($client, $plugins, $options); } /** @@ -69,22 +43,11 @@ public function __construct($client, array $plugins = [], array $options = []) */ public function sendRequest(RequestInterface $request) { - // If we don't have an http client, use the async call - if (!($this->client instanceof HttpClient)) { - return $this->sendAsyncRequest($request)->wait(); + try { + return $this->pluginClient->sendRequest($request); + } catch (\Http\Client\Common\Exception\LoopException $e) { + throw new LoopException($e->getMessage(), $e->getRequest(), $e); } - - // Else we want to use the synchronous call of the underlying client, and not the async one in the case - // we have both an async and sync call - $pluginChain = $this->createPluginChain($this->plugins, function (RequestInterface $request) { - try { - return new FulfilledPromise($this->client->sendRequest($request)); - } catch (Exception $exception) { - return new RejectedPromise($exception); - } - }); - - return $pluginChain($request)->wait(); } /** @@ -92,61 +55,10 @@ public function sendRequest(RequestInterface $request) */ public function sendAsyncRequest(RequestInterface $request) { - $pluginChain = $this->createPluginChain($this->plugins, function (RequestInterface $request) { - return $this->client->sendAsyncRequest($request); - }); - - return $pluginChain($request); - } - - /** - * Configure the plugin client. - * - * @param array $options - * - * @return array - */ - private function configure(array $options = []) - { - $resolver = new OptionsResolver(); - $resolver->setDefaults([ - 'max_restarts' => 10, - ]); - - return $resolver->resolve($options); - } - - /** - * Create the plugin chain. - * - * @param Plugin[] $pluginList A list of plugins - * @param callable $clientCallable Callable making the HTTP call - * - * @return callable - */ - private function createPluginChain($pluginList, callable $clientCallable) - { - $firstCallable = $lastCallable = $clientCallable; - - while ($plugin = array_pop($pluginList)) { - $lastCallable = function (RequestInterface $request) use ($plugin, $lastCallable, &$firstCallable) { - return $plugin->handleRequest($request, $lastCallable, $firstCallable); - }; - - $firstCallable = $lastCallable; + try { + return $this->pluginClient->sendAsyncRequest($request); + } catch (\Http\Client\Common\Exception\LoopException $e) { + throw new LoopException($e->getMessage(), $e->getRequest(), $e); } - - $firstCalls = 0; - $firstCallable = function (RequestInterface $request) use ($lastCallable, &$firstCalls) { - if ($firstCalls > $this->options['max_restarts']) { - throw new LoopException('Too many restarts in plugin client', $request); - } - - ++$firstCalls; - - return $lastCallable($request); - }; - - return $firstCallable; } } diff --git a/src/RedirectPlugin.php b/src/RedirectPlugin.php index 7a3f775..a42b34f 100644 --- a/src/RedirectPlugin.php +++ b/src/RedirectPlugin.php @@ -2,6 +2,8 @@ namespace Http\Client\Plugin; +@trigger_error('The '.__NAMESPACE__.'\RedirectPlugin class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Plugin\RedirectPlugin instead.', E_USER_DEPRECATED); + use Http\Client\Exception\HttpException; use Http\Client\Plugin\Exception\CircularRedirectionException; use Http\Client\Plugin\Exception\MultipleRedirectionException; @@ -12,9 +14,11 @@ use Symfony\Component\OptionsResolver\OptionsResolver; /** - * Follow redirections. + * Follow redirect responses from the server. * * @author Joel Wurtz + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Plugin\RedirectPlugin} instead. */ class RedirectPlugin implements Plugin { @@ -101,7 +105,7 @@ class RedirectPlugin implements Plugin /** * @param array $config { * - * @var bool|string[] $preserve_header True keeps all headers, false remove all of them, an array is interpreted as a list of header names to keep. + * @var bool|string[] $preserve_header true keeps all headers, false remove all of them, an array is interpreted as a list of header names to keep * @var bool $use_default_for_multiple Whether the location header must be directly used for a multiple redirection status code (300). * } */ diff --git a/src/RetryPlugin.php b/src/RetryPlugin.php index 80097da..4d81970 100644 --- a/src/RetryPlugin.php +++ b/src/RetryPlugin.php @@ -2,6 +2,8 @@ namespace Http\Client\Plugin; +@trigger_error('The '.__NAMESPACE__.'\RetryPlugin class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Plugin\RetryPlugin instead.', E_USER_DEPRECATED); + use Http\Client\Exception; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; @@ -13,6 +15,8 @@ * By default will retry only one time. * * @author Joel Wurtz + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Plugin\RetryPlugin} instead. */ class RetryPlugin implements Plugin { diff --git a/src/StopwatchPlugin.php b/src/StopwatchPlugin.php index 48ce9ce..0314a65 100644 --- a/src/StopwatchPlugin.php +++ b/src/StopwatchPlugin.php @@ -2,6 +2,8 @@ namespace Http\Client\Plugin; +@trigger_error('The '.__NAMESPACE__.'\StopwatchPlugin class is deprecated since version 1.1 and will be removed in 2.0. Use Http\Client\Common\Plugin\StopwatchPlugin instead.', E_USER_DEPRECATED); + use Http\Client\Exception; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; @@ -11,6 +13,8 @@ * Measure the duration of a request call with the stopwatch component. * * @author Joel Wurtz + * + * @deprecated since since version 1.1, and will be removed in 2.0. Use {@link \Http\Client\Common\Plugin\StopwatchPlugin} instead. */ class StopwatchPlugin implements Plugin { @@ -38,11 +42,11 @@ public function handleRequest(RequestInterface $request, callable $next, callabl $this->stopwatch->start($eventName, self::CATEGORY); return $next($request)->then(function (ResponseInterface $response) use ($eventName) { - $this->stopwatch->stop($eventName, self::CATEGORY); + $this->stopwatch->stop($eventName); return $response; }, function (Exception $exception) use ($eventName) { - $this->stopwatch->stop($eventName, self::CATEGORY); + $this->stopwatch->stop($eventName); throw $exception; });