expectExceptionGuzzle('InvalidArgumentException'); new Stream(true); } public function testConstructorInitializesProperties() { $handle = fopen('php://temp', 'r+'); fwrite($handle, 'data'); $stream = new Stream($handle); self::assertTrue($stream->isReadable()); self::assertTrue($stream->isWritable()); self::assertTrue($stream->isSeekable()); self::assertSame('php://temp', $stream->getMetadata('uri')); $this->assertInternalTypeGuzzle('array', $stream->getMetadata()); self::assertSame(4, $stream->getSize()); self::assertFalse($stream->eof()); $stream->close(); } public function testConstructorInitializesPropertiesWithRbPlus() { $handle = fopen('php://temp', 'rb+'); fwrite($handle, 'data'); $stream = new Stream($handle); self::assertTrue($stream->isReadable()); self::assertTrue($stream->isWritable()); self::assertTrue($stream->isSeekable()); self::assertSame('php://temp', $stream->getMetadata('uri')); $this->assertInternalTypeGuzzle('array', $stream->getMetadata()); self::assertSame(4, $stream->getSize()); self::assertFalse($stream->eof()); $stream->close(); } public function testStreamClosesHandleOnDestruct() { $handle = fopen('php://temp', 'r'); $stream = new Stream($handle); unset($stream); self::assertFalse(is_resource($handle)); } public function testConvertsToString() { $handle = fopen('php://temp', 'w+'); fwrite($handle, 'data'); $stream = new Stream($handle); self::assertSame('data', (string) $stream); self::assertSame('data', (string) $stream); $stream->close(); } public function testConvertsToStringNonSeekableStream() { if (defined('HHVM_VERSION')) { self::markTestSkipped('This does not work on HHVM.'); } $handle = popen('echo foo', 'r'); $stream = new Stream($handle); self::assertFalse($stream->isSeekable()); self::assertSame('foo', trim((string) $stream)); } public function testConvertsToStringNonSeekablePartiallyReadStream() { if (defined('HHVM_VERSION')) { self::markTestSkipped('This does not work on HHVM.'); } $handle = popen('echo bar', 'r'); $stream = new Stream($handle); $firstLetter = $stream->read(1); self::assertFalse($stream->isSeekable()); self::assertSame('b', $firstLetter); self::assertSame('ar', trim((string) $stream)); } public function testGetsContents() { $handle = fopen('php://temp', 'w+'); fwrite($handle, 'data'); $stream = new Stream($handle); self::assertSame('', $stream->getContents()); $stream->seek(0); self::assertSame('data', $stream->getContents()); self::assertSame('', $stream->getContents()); $stream->close(); } public function testChecksEof() { $handle = fopen('php://temp', 'w+'); fwrite($handle, 'data'); $stream = new Stream($handle); self::assertSame(4, $stream->tell(), 'Stream cursor already at the end'); self::assertFalse($stream->eof(), 'Stream still not eof'); self::assertSame('', $stream->read(1), 'Need to read one more byte to reach eof'); self::assertTrue($stream->eof()); $stream->close(); } public function testGetSize() { $size = filesize(__FILE__); $handle = fopen(__FILE__, 'r'); $stream = new Stream($handle); self::assertSame($size, $stream->getSize()); // Load from cache self::assertSame($size, $stream->getSize()); $stream->close(); } public function testEnsuresSizeIsConsistent() { $h = fopen('php://temp', 'w+'); self::assertSame(3, fwrite($h, 'foo')); $stream = new Stream($h); self::assertSame(3, $stream->getSize()); self::assertSame(4, $stream->write('test')); self::assertSame(7, $stream->getSize()); self::assertSame(7, $stream->getSize()); $stream->close(); } public function testProvidesStreamPosition() { $handle = fopen('php://temp', 'w+'); $stream = new Stream($handle); self::assertSame(0, $stream->tell()); $stream->write('foo'); self::assertSame(3, $stream->tell()); $stream->seek(1); self::assertSame(1, $stream->tell()); self::assertSame(ftell($handle), $stream->tell()); $stream->close(); } public function testDetachStreamAndClearProperties() { $handle = fopen('php://temp', 'r'); $stream = new Stream($handle); self::assertSame($handle, $stream->detach()); $this->assertInternalTypeGuzzle('resource', $handle, 'Stream is not closed'); self::assertNull($stream->detach()); $this->assertStreamStateAfterClosedOrDetached($stream); $stream->close(); } public function testCloseResourceAndClearProperties() { $handle = fopen('php://temp', 'r'); $stream = new Stream($handle); $stream->close(); self::assertFalse(is_resource($handle)); $this->assertStreamStateAfterClosedOrDetached($stream); } private function assertStreamStateAfterClosedOrDetached(Stream $stream) { self::assertFalse($stream->isReadable()); self::assertFalse($stream->isWritable()); self::assertFalse($stream->isSeekable()); self::assertNull($stream->getSize()); self::assertSame([], $stream->getMetadata()); self::assertNull($stream->getMetadata('foo')); $throws = function (callable $fn) { try { $fn(); } catch (\Exception $e) { $this->assertStringContainsStringGuzzle('Stream is detached', $e->getMessage()); return; } $this->fail('Exception should be thrown after the stream is detached.'); }; $throws(function () use ($stream) { $stream->read(10); }); $throws(function () use ($stream) { $stream->write('bar'); }); $throws(function () use ($stream) { $stream->seek(10); }); $throws(function () use ($stream) { $stream->tell(); }); $throws(function () use ($stream) { $stream->eof(); }); $throws(function () use ($stream) { $stream->getContents(); }); self::assertSame('', (string) $stream); } public function testStreamReadingWithZeroLength() { $r = fopen('php://temp', 'r'); $stream = new Stream($r); self::assertSame('', $stream->read(0)); $stream->close(); } public function testStreamReadingWithNegativeLength() { $r = fopen('php://temp', 'r'); $stream = new Stream($r); $this->expectExceptionGuzzle('RuntimeException', 'Length parameter cannot be negative'); try { $stream->read(-1); } catch (\Exception $e) { $stream->close(); throw $e; } $stream->close(); } public function testStreamReadingFreadError() { self::$isFReadError = true; $r = fopen('php://temp', 'r'); $stream = new Stream($r); $this->expectExceptionGuzzle('RuntimeException', 'Unable to read from stream'); try { $stream->read(1); } catch (\Exception $e) { self::$isFReadError = false; $stream->close(); throw $e; } self::$isFReadError = false; $stream->close(); } /** * @dataProvider gzipModeProvider * * @param string $mode * @param bool $readable * @param bool $writable */ public function testGzipStreamModes($mode, $readable, $writable) { if (defined('HHVM_VERSION')) { self::markTestSkipped('This does not work on HHVM.'); } $r = gzopen('php://temp', $mode); $stream = new Stream($r); self::assertSame($readable, $stream->isReadable()); self::assertSame($writable, $stream->isWritable()); $stream->close(); } public function gzipModeProvider() { return [ ['mode' => 'rb9', 'readable' => true, 'writable' => false], ['mode' => 'wb2', 'readable' => false, 'writable' => true], ]; } /** * @dataProvider readableModeProvider * * @param string $mode */ public function testReadableStream($mode) { $r = fopen('php://temp', $mode); $stream = new Stream($r); self::assertTrue($stream->isReadable()); $stream->close(); } public function readableModeProvider() { return [ ['r'], ['w+'], ['r+'], ['x+'], ['c+'], ['rb'], ['w+b'], ['r+b'], ['x+b'], ['c+b'], ['rt'], ['w+t'], ['r+t'], ['x+t'], ['c+t'], ['a+'], ['rb+'], ]; } public function testWriteOnlyStreamIsNotReadable() { $r = fopen('php://output', 'w'); $stream = new Stream($r); self::assertFalse($stream->isReadable()); $stream->close(); } /** * @dataProvider writableModeProvider * * @param string $mode */ public function testWritableStream($mode) { $r = fopen('php://temp', $mode); $stream = new Stream($r); self::assertTrue($stream->isWritable()); $stream->close(); } public function writableModeProvider() { return [ ['w'], ['w+'], ['rw'], ['r+'], ['x+'], ['c+'], ['wb'], ['w+b'], ['r+b'], ['rb+'], ['x+b'], ['c+b'], ['w+t'], ['r+t'], ['x+t'], ['c+t'], ['a'], ['a+'], ]; } public function testReadOnlyStreamIsNotWritable() { $r = fopen('php://input', 'r'); $stream = new Stream($r); self::assertFalse($stream->isWritable()); $stream->close(); } } namespace GuzzleHttp\Psr7; use GuzzleHttp\Tests\Psr7\StreamTest; function fread($handle, $length) { return StreamTest::$isFReadError ? false : \fread($handle, $length); }