diff --git a/src/Stream.php b/src/Stream.php index 73d24f43..d9e7409c 100644 --- a/src/Stream.php +++ b/src/Stream.php @@ -10,6 +10,17 @@ */ class Stream implements StreamInterface { + /** + * Resource modes. + * + * @var string + * + * @see http://php.net/manual/function.fopen.php + * @see http://php.net/manual/en/function.gzopen.php + */ + const READABLE_MODES = '/r|a\+|ab\+|w\+|wb\+|x\+|xb\+|c\+|cb\+/'; + const WRITABLE_MODES = '/a|w|r\+|rb\+|rw|x|c/'; + private $stream; private $size; private $seekable; @@ -18,22 +29,6 @@ class Stream implements StreamInterface private $uri; private $customMetadata; - /** @var array Hash of readable and writable stream types */ - private static $readWriteHash = [ - 'read' => [ - 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true, - 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, - 'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true, - 'x+t' => true, 'c+t' => true, 'a+' => true, 'rb+' => true, - ], - 'write' => [ - 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, - 'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true, 'rb+' => true, - 'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true, - 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true - ] - ]; - /** * This constructor accepts an associative array of options. * @@ -65,8 +60,8 @@ public function __construct($stream, $options = []) $this->stream = $stream; $meta = stream_get_meta_data($this->stream); $this->seekable = $meta['seekable']; - $this->readable = isset(self::$readWriteHash['read'][$meta['mode']]); - $this->writable = isset(self::$readWriteHash['write'][$meta['mode']]); + $this->readable = (bool)preg_match(self::READABLE_MODES, $meta['mode']); + $this->writable = (bool)preg_match(self::WRITABLE_MODES, $meta['mode']); $this->uri = $this->getMetadata('uri'); } diff --git a/tests/StreamTest.php b/tests/StreamTest.php index bf4653c0..63421ce5 100644 --- a/tests/StreamTest.php +++ b/tests/StreamTest.php @@ -231,6 +231,133 @@ public function testStreamReadingFreadError() 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')) { + $this->markTestSkipped('This does not work on HHVM.'); + } + + $r = gzopen('php://temp', $mode); + $stream = new Stream($r); + + $this->assertSame($readable, $stream->isReadable()); + $this->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); + + $this->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); + + $this->assertFalse($stream->isReadable()); + + $stream->close(); + } + + /** + * @dataProvider writableModeProvider + * + * @param string $mode + */ + public function testWritableStream($mode) + { + $r = fopen('php://temp', $mode); + $stream = new Stream($r); + + $this->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); + + $this->assertFalse($stream->isWritable()); + + $stream->close(); + } } namespace GuzzleHttp\Psr7;