From 1ebab24f5c84593966955d31af7a30055cd370d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Sun, 19 Mar 2017 19:12:20 +0100 Subject: [PATCH] Compatibility with legacy PHP 5.3 --- .travis.yml | 4 + README.md | 2 +- composer.json | 4 +- src/ChunkedStreamDecoder.php | 22 ++-- src/Client.php | 2 +- src/Request.php | 30 +++--- src/RequestData.php | 2 +- src/Response.php | 5 +- tests/DecodeChunkedStreamTest.php | 164 +++++++++++++++--------------- tests/RequestDataTest.php | 2 +- tests/RequestTest.php | 7 +- tests/ResponseTest.php | 16 +-- 12 files changed, 130 insertions(+), 130 deletions(-) diff --git a/.travis.yml b/.travis.yml index 07baf1e..46a0486 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: php php: +# - 5.3 # requires old distro - 5.4 - 5.5 - 5.6 @@ -14,6 +15,9 @@ php: dist: trusty matrix: + include: + - php: 5.3 + dist: precise allow_failures: - php: nightly - php: hhvm diff --git a/README.md b/README.md index 2c98d6c..c8494f6 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ $ composer require react/http-client:^0.5.7 See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. This project aims to run on any platform and thus does not require any PHP -extensions and supports running on legacy PHP 5.4 through current PHP 7+ and +extensions and supports running on legacy PHP 5.3 through current PHP 7+ and HHVM. It's *highly recommended to use PHP 7+* for this project. diff --git a/composer.json b/composer.json index c98729d..ecb19e5 100644 --- a/composer.json +++ b/composer.json @@ -4,8 +4,8 @@ "keywords": ["http"], "license": "MIT", "require": { - "php": ">=5.4.0", - "evenement/evenement": "^3.0 || ^2.0", + "php": ">=5.3.0", + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3", "react/promise": "^2.1 || ^1.2.1", "react/socket": "^1.0 || ^0.8.4", diff --git a/src/ChunkedStreamDecoder.php b/src/ChunkedStreamDecoder.php index 703eee3..a96592e 100644 --- a/src/ChunkedStreamDecoder.php +++ b/src/ChunkedStreamDecoder.php @@ -2,7 +2,7 @@ namespace React\HttpClient; -use Evenement\EventEmitterTrait; +use Evenement\EventEmitter; use Exception; use React\Stream\ReadableStreamInterface; use React\Stream\Util; @@ -11,12 +11,10 @@ /** * @internal */ -class ChunkedStreamDecoder implements ReadableStreamInterface +class ChunkedStreamDecoder extends EventEmitter implements ReadableStreamInterface { const CRLF = "\r\n"; - use EventEmitterTrait; - /** * @var string */ @@ -55,9 +53,9 @@ public function __construct(ReadableStreamInterface $stream) $this->stream = $stream; $this->stream->on('data', array($this, 'handleData')); $this->stream->on('end', array($this, 'handleEnd')); - Util::forwardEvents($this->stream, $this, [ + Util::forwardEvents($this->stream, $this, array( 'error', - ]); + )); } /** @internal */ @@ -89,9 +87,9 @@ protected function iterateBuffer() if ($this->nextChunkIsLength) { $crlfPosition = strpos($this->buffer, static::CRLF); if ($crlfPosition === false && strlen($this->buffer) > 1024) { - $this->emit('error', [ + $this->emit('error', array( new Exception('Chunk length header longer then 1024 bytes'), - ]); + )); $this->close(); return false; } @@ -114,9 +112,9 @@ protected function iterateBuffer() } $this->nextChunkIsLength = false; if (dechex(hexdec($lengthChunk)) !== strtolower($lengthChunk)) { - $this->emit('error', [ + $this->emit('error', array( new Exception('Unable to validate "' . $lengthChunk . '" as chunk length header'), - ]); + )); $this->close(); return false; } @@ -200,9 +198,9 @@ public function handleEnd() $this->emit( 'error', - [ + array( new Exception('Stream ended with incomplete control code') - ] + ) ); $this->close(); } diff --git a/src/Client.php b/src/Client.php index fb8230b..fc14426 100644 --- a/src/Client.php +++ b/src/Client.php @@ -19,7 +19,7 @@ public function __construct(LoopInterface $loop, ConnectorInterface $connector = $this->connector = $connector; } - public function request($method, $url, array $headers = [], $protocolVersion = '1.0') + public function request($method, $url, array $headers = array(), $protocolVersion = '1.0') { $requestData = new RequestData($method, $url, $headers, $protocolVersion); diff --git a/src/Request.php b/src/Request.php index ac42b5f..ea4d50b 100644 --- a/src/Request.php +++ b/src/Request.php @@ -2,7 +2,7 @@ namespace React\HttpClient; -use Evenement\EventEmitterTrait; +use Evenement\EventEmitter; use React\Promise; use React\Socket\ConnectionInterface; use React\Socket\ConnectorInterface; @@ -15,10 +15,8 @@ * @event error * @event end */ -class Request implements WritableStreamInterface +class Request extends EventEmitter implements WritableStreamInterface { - use EventEmitterTrait; - const STATE_INIT = 0; const STATE_WRITING_HEAD = 1; const STATE_HEAD_WRITTEN = 2; @@ -54,17 +52,18 @@ private function writeHead() $streamRef = &$this->stream; $stateRef = &$this->state; $pendingWrites = &$this->pendingWrites; + $that = $this; $promise = $this->connect(); $promise->then( - function (ConnectionInterface $stream) use ($requestData, &$streamRef, &$stateRef, &$pendingWrites) { + function (ConnectionInterface $stream) use ($requestData, &$streamRef, &$stateRef, &$pendingWrites, $that) { $streamRef = $stream; - $stream->on('drain', array($this, 'handleDrain')); - $stream->on('data', array($this, 'handleData')); - $stream->on('end', array($this, 'handleEnd')); - $stream->on('error', array($this, 'handleError')); - $stream->on('close', array($this, 'handleClose')); + $stream->on('drain', array($that, 'handleDrain')); + $stream->on('data', array($that, 'handleData')); + $stream->on('end', array($that, 'handleEnd')); + $stream->on('error', array($that, 'handleError')); + $stream->on('close', array($that, 'handleClose')); $headers = (string) $requestData; @@ -77,7 +76,7 @@ function (ConnectionInterface $stream) use ($requestData, &$streamRef, &$stateRe $pendingWrites = ''; if ($more) { - $this->emit('drain'); + $that->emit('drain'); } } }, @@ -154,11 +153,10 @@ public function handleData($data) return; } - $response->on('close', function () { - $this->close(); - }); - $response->on('error', function (\Exception $error) { - $this->closeError(new \RuntimeException( + $response->on('close', array($this, 'close')); + $that = $this; + $response->on('error', function (\Exception $error) use ($that) { + $that->closeError(new \RuntimeException( "An error occured in the response", 0, $error diff --git a/src/RequestData.php b/src/RequestData.php index 7ec0e2a..1c7d5eb 100644 --- a/src/RequestData.php +++ b/src/RequestData.php @@ -9,7 +9,7 @@ class RequestData private $headers; private $protocolVersion; - public function __construct($method, $url, array $headers = [], $protocolVersion = '1.0') + public function __construct($method, $url, array $headers = array(), $protocolVersion = '1.0') { $this->method = $method; $this->url = $url; diff --git a/src/Response.php b/src/Response.php index 88605dd..5ed271f 100644 --- a/src/Response.php +++ b/src/Response.php @@ -12,9 +12,8 @@ * @event error * @event end */ -class Response extends EventEmitter implements ReadableStreamInterface +class Response extends EventEmitter implements ReadableStreamInterface { - private $stream; private $protocol; private $version; @@ -166,7 +165,7 @@ public function resume() $this->stream->resume(); } - public function pipe(WritableStreamInterface $dest, array $options = []) + public function pipe(WritableStreamInterface $dest, array $options = array()) { Util::pipe($this, $dest, $options); diff --git a/tests/DecodeChunkedStreamTest.php b/tests/DecodeChunkedStreamTest.php index 62aa84f..83e8858 100644 --- a/tests/DecodeChunkedStreamTest.php +++ b/tests/DecodeChunkedStreamTest.php @@ -10,81 +10,81 @@ class DecodeChunkedStreamTest extends TestCase { public function provideChunkedEncoding() { - return [ - 'data-set-1' => [ - ["4\r\nWiki\r\n5\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"], - ], - 'data-set-2' => [ - ["4\r\nWiki\r\n", "5\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"], - ], - 'data-set-3' => [ - ["4\r\nWiki\r\n", "5\r\n", "pedia\r\ne\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"], - ], - 'data-set-4' => [ - ["4\r\nWiki\r\n", "5\r\n", "pedia\r\ne\r\n in\r\n", "\r\nchunks.\r\n0\r\n\r\n"], - ], - 'data-set-5' => [ - ["4\r\n", "Wiki\r\n", "5\r\n", "pedia\r\ne\r\n in\r\n", "\r\nchunks.\r\n0\r\n\r\n"], - ], - 'data-set-6' => [ - ["4\r\n", "Wiki\r\n", "5\r\n", "pedia\r\ne; foo=[bar,beer,pool,cue,win,won]\r\n", " in\r\n", "\r\nchunks.\r\n0\r\n\r\n"], - ], - 'header-fields' => [ - ["4; foo=bar\r\n", "Wiki\r\n", "5\r\n", "pedia\r\ne\r\n", " in\r\n", "\r\nchunks.\r\n", "0\r\n\r\n"], - ], - 'character-for-charactrr' => [ + return array( + 'data-set-1' => array( + array("4\r\nWiki\r\n5\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"), + ), + 'data-set-2' => array( + array("4\r\nWiki\r\n", "5\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"), + ), + 'data-set-3' => array( + array("4\r\nWiki\r\n", "5\r\n", "pedia\r\ne\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"), + ), + 'data-set-4' => array( + array("4\r\nWiki\r\n", "5\r\n", "pedia\r\ne\r\n in\r\n", "\r\nchunks.\r\n0\r\n\r\n"), + ), + 'data-set-5' => array( + array("4\r\n", "Wiki\r\n", "5\r\n", "pedia\r\ne\r\n in\r\n", "\r\nchunks.\r\n0\r\n\r\n"), + ), + 'data-set-6' => array( + array("4\r\n", "Wiki\r\n", "5\r\n", "pedia\r\ne; foo=[bar,beer,pool,cue,win,won]\r\n", " in\r\n", "\r\nchunks.\r\n0\r\n\r\n"), + ), + 'header-fields' => array( + array("4; foo=bar\r\n", "Wiki\r\n", "5\r\n", "pedia\r\ne\r\n", " in\r\n", "\r\nchunks.\r\n", "0\r\n\r\n"), + ), + 'character-for-charactrr' => array( str_split("4\r\nWiki\r\n5\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"), - ], - 'extra-newline-in-wiki-character-for-chatacter' => [ + ), + 'extra-newline-in-wiki-character-for-chatacter' => array( str_split("6\r\nWi\r\nki\r\n5\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"), "Wi\r\nkipedia in\r\n\r\nchunks." - ], - 'extra-newline-in-wiki' => [ - ["6\r\nWi\r\n", "ki\r\n5\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"], + ), + 'extra-newline-in-wiki' => array( + array("6\r\nWi\r\n", "ki\r\n5\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"), "Wi\r\nkipedia in\r\n\r\nchunks." - ], - 'varnish-type-response-1' => [ - ["0017\r\nWikipedia in\r\n\r\nchunks.\r\n0\r\n\r\n"] - ], - 'varnish-type-response-2' => [ - ["000017\r\nWikipedia in\r\n\r\nchunks.\r\n0\r\n\r\n"] - ], - 'varnish-type-response-3' => [ - ["017\r\nWikipedia in\r\n\r\nchunks.\r\n0\r\n\r\n"] - ], - 'varnish-type-response-4' => [ - ["004\r\nWiki\r\n005\r\npedia\r\n00e\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"] - ], - 'varnish-type-response-5' => [ - ["000004\r\nWiki\r\n00005\r\npedia\r\n000e\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"] - ], - 'varnish-type-response-extra-line' => [ - ["006\r\nWi\r\nki\r\n005\r\npedia\r\n00e\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"], + ), + 'varnish-type-response-1' => array( + array("0017\r\nWikipedia in\r\n\r\nchunks.\r\n0\r\n\r\n") + ), + 'varnish-type-response-2' => array( + array("000017\r\nWikipedia in\r\n\r\nchunks.\r\n0\r\n\r\n") + ), + 'varnish-type-response-3' => array( + array("017\r\nWikipedia in\r\n\r\nchunks.\r\n0\r\n\r\n") + ), + 'varnish-type-response-4' => array( + array("004\r\nWiki\r\n005\r\npedia\r\n00e\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n") + ), + 'varnish-type-response-5' => array( + array("000004\r\nWiki\r\n00005\r\npedia\r\n000e\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n") + ), + 'varnish-type-response-extra-line' => array( + array("006\r\nWi\r\nki\r\n005\r\npedia\r\n00e\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"), "Wi\r\nkipedia in\r\n\r\nchunks." - ], - 'varnish-type-response-random' => [ - [str_repeat("0", rand(0, 10)), "4\r\nWiki\r\n", str_repeat("0", rand(0, 10)), "5\r\npedia\r\n", str_repeat("0", rand(0, 10)), "e\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"] - ], - 'end-chunk-zero-check-1' => [ - ["4\r\nWiki\r\n5\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n00\r\n\r\n"] - ], - 'end-chunk-zero-check-2' => [ - ["4\r\nWiki\r\n5\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n000\r\n\r\n"] - ], - 'end-chunk-zero-check-3' => [ - ["00004\r\nWiki\r\n005\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n0000\r\n\r\n"] - ], - 'uppercase-chunk' => [ - ["4\r\nWiki\r\n5\r\npedia\r\nE\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"], - ], - 'extra-space-in-length-chunk' => [ - [" 04 \r\nWiki\r\n5\r\npedia\r\nE\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"], - ], - 'only-whitespace-is-final-chunk' => [ - [" \r\n\r\n"], + ), + 'varnish-type-response-random' => array( + array(str_repeat("0", rand(0, 10)), "4\r\nWiki\r\n", str_repeat("0", rand(0, 10)), "5\r\npedia\r\n", str_repeat("0", rand(0, 10)), "e\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n") + ), + 'end-chunk-zero-check-1' => array( + array("4\r\nWiki\r\n5\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n00\r\n\r\n") + ), + 'end-chunk-zero-check-2' => array( + array("4\r\nWiki\r\n5\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n000\r\n\r\n") + ), + 'end-chunk-zero-check-3' => array( + array("00004\r\nWiki\r\n005\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n0000\r\n\r\n") + ), + 'uppercase-chunk' => array( + array("4\r\nWiki\r\n5\r\npedia\r\nE\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"), + ), + 'extra-space-in-length-chunk' => array( + array(" 04 \r\nWiki\r\n5\r\npedia\r\nE\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"), + ), + 'only-whitespace-is-final-chunk' => array( + array(" \r\n\r\n"), "" - ] - ]; + ) + ); } /** @@ -110,17 +110,17 @@ public function testChunkedEncoding(array $strings, $expected = "Wikipedia in\r\ public function provideInvalidChunkedEncoding() { - return [ - 'chunk-body-longer-than-header-suggests' => [ - ["4\r\nWiwot40n98w3498tw3049nyn039409t34\r\n", "ki\r\n5\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"], - ], - 'invalid-header-charactrrs' => [ + return array( + 'chunk-body-longer-than-header-suggests' => array( + array("4\r\nWiwot40n98w3498tw3049nyn039409t34\r\n", "ki\r\n5\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"), + ), + 'invalid-header-charactrrs' => array( str_split("xyz\r\nWi\r\nki\r\n5\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n") - ], - 'header-chunk-to-long' => [ + ), + 'header-chunk-to-long' => array( str_split(str_repeat('a', 2015) . "\r\nWi\r\nki\r\n5\r\npedia\r\ne\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n") - ] - ]; + ) + ); } /** @@ -142,10 +142,10 @@ public function testInvalidChunkedEncoding(array $strings) public function provideZeroChunk() { - return [ - ['1-zero' => "0\r\n\r\n"], - ['random-zero' => str_repeat("0", rand(2, 10))."\r\n\r\n"] - ]; + return array( + array('1-zero' => "0\r\n\r\n"), + array('random-zero' => str_repeat("0", rand(2, 10))."\r\n\r\n") + ); } /** diff --git a/tests/RequestDataTest.php b/tests/RequestDataTest.php index 48ba9be..4db81cd 100644 --- a/tests/RequestDataTest.php +++ b/tests/RequestDataTest.php @@ -126,7 +126,7 @@ public function toStringReturnsHTTPRequestMessageWithHeadersInCustomCase() /** @test */ public function toStringReturnsHTTPRequestMessageWithProtocolVersionThroughConstructor() { - $requestData = new RequestData('GET', 'http://www.example.com', [], '1.1'); + $requestData = new RequestData('GET', 'http://www.example.com', array(), '1.1'); $expected = "GET / HTTP/1.1\r\n" . "Host: www.example.com\r\n" . diff --git a/tests/RequestTest.php b/tests/RequestTest.php index c55c53b..0ac5d09 100644 --- a/tests/RequestTest.php +++ b/tests/RequestTest.php @@ -634,8 +634,9 @@ private function successfulAsyncConnectionMock() ->with('www.example.com:80') ->will($this->returnValue($deferred->promise())); - return function () use ($deferred) { - $deferred->resolve($this->stream); + $stream = $this->stream; + return function () use ($deferred, $stream) { + $deferred->resolve($stream); }; } @@ -699,7 +700,7 @@ public function chunkedStreamDecoder() $this->stream->expects($this->once()) ->method('emit') - ->with('data', ["1\r\nb\r"]); + ->with('data', array("1\r\nb\r")); $request->handleData("HTTP/1.0 200 OK\r\n"); $request->handleData("Transfer-Encoding: chunked\r\n"); diff --git a/tests/ResponseTest.php b/tests/ResponseTest.php index d7ad6b8..a751ab6 100644 --- a/tests/ResponseTest.php +++ b/tests/ResponseTest.php @@ -64,9 +64,9 @@ public function responseShouldEmitEndEventOnEnd() $response->handleEnd(); $this->assertSame( - [ + array( 'Content-Type' => 'text/plain' - ], + ), $response->getHeaders() ); } @@ -89,9 +89,9 @@ public function closedResponseShouldNotBeResumedOrPaused() $response->pause(); $this->assertSame( - [ + array( 'content-type' => 'text/plain', - ], + ), $response->getHeaders() ); } @@ -106,10 +106,10 @@ public function chunkedEncodingResponse() '1.0', '200', 'ok', - [ + array( 'content-type' => 'text/plain', 'transfer-encoding' => 'chunked', - ] + ) ); $buffer = ''; @@ -123,9 +123,9 @@ public function chunkedEncodingResponse() $this->assertSame('Wiki', $buffer); $this->assertSame( - [ + array( 'content-type' => 'text/plain', - ], + ), $response->getHeaders() ); }