3 * @see https://github.com/zendframework/zend-diactoros for the canonical source repository
4 * @copyright Copyright (c) 2015-2017 Zend Technologies USA Inc. (http://www.zend.com)
5 * @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
8 namespace Zend\Diactoros;
10 use InvalidArgumentException;
11 use Psr\Http\Message\StreamInterface;
14 use function array_key_exists;
23 use function get_resource_type;
25 use function is_resource;
26 use function is_string;
27 use function restore_error_handler;
28 use function set_error_handler;
29 use function stream_get_contents;
30 use function stream_get_meta_data;
37 * Implementation of PSR HTTP streams
39 class Stream implements StreamInterface
47 * @var string|resource
52 * @param string|resource $stream
53 * @param string $mode Mode with which to open stream
54 * @throws InvalidArgumentException
56 public function __construct($stream, $mode = 'r')
58 $this->setStream($stream, $mode);
64 public function __toString()
66 if (! $this->isReadable()) {
71 if ($this->isSeekable()) {
75 return $this->getContents();
76 } catch (RuntimeException $e) {
84 public function close()
86 if (! $this->resource) {
90 $resource = $this->detach();
97 public function detach()
99 $resource = $this->resource;
100 $this->resource = null;
105 * Attach a new stream/resource to the instance.
107 * @param string|resource $resource
108 * @param string $mode
109 * @throws InvalidArgumentException for stream identifier that cannot be
111 * @throws InvalidArgumentException for non-resource stream
113 public function attach($resource, $mode = 'r')
115 $this->setStream($resource, $mode);
121 public function getSize()
123 if (null === $this->resource) {
127 $stats = fstat($this->resource);
128 if ($stats !== false) {
129 return $stats['size'];
138 public function tell()
140 if (! $this->resource) {
141 throw new RuntimeException('No resource available; cannot tell position');
144 $result = ftell($this->resource);
145 if (! is_int($result)) {
146 throw new RuntimeException('Error occurred during tell operation');
155 public function eof()
157 if (! $this->resource) {
161 return feof($this->resource);
167 public function isSeekable()
169 if (! $this->resource) {
173 $meta = stream_get_meta_data($this->resource);
174 return $meta['seekable'];
180 public function seek($offset, $whence = SEEK_SET)
182 if (! $this->resource) {
183 throw new RuntimeException('No resource available; cannot seek position');
186 if (! $this->isSeekable()) {
187 throw new RuntimeException('Stream is not seekable');
190 $result = fseek($this->resource, $offset, $whence);
193 throw new RuntimeException('Error seeking within stream');
202 public function rewind()
204 return $this->seek(0);
210 public function isWritable()
212 if (! $this->resource) {
216 $meta = stream_get_meta_data($this->resource);
217 $mode = $meta['mode'];
221 || strstr($mode, 'w')
222 || strstr($mode, 'c')
223 || strstr($mode, 'a')
224 || strstr($mode, '+')
231 public function write($string)
233 if (! $this->resource) {
234 throw new RuntimeException('No resource available; cannot write');
237 if (! $this->isWritable()) {
238 throw new RuntimeException('Stream is not writable');
241 $result = fwrite($this->resource, $string);
243 if (false === $result) {
244 throw new RuntimeException('Error writing to stream');
252 public function isReadable()
254 if (! $this->resource) {
258 $meta = stream_get_meta_data($this->resource);
259 $mode = $meta['mode'];
261 return (strstr($mode, 'r') || strstr($mode, '+'));
267 public function read($length)
269 if (! $this->resource) {
270 throw new RuntimeException('No resource available; cannot read');
273 if (! $this->isReadable()) {
274 throw new RuntimeException('Stream is not readable');
277 $result = fread($this->resource, $length);
279 if (false === $result) {
280 throw new RuntimeException('Error reading stream');
289 public function getContents()
291 if (! $this->isReadable()) {
292 throw new RuntimeException('Stream is not readable');
295 $result = stream_get_contents($this->resource);
296 if (false === $result) {
297 throw new RuntimeException('Error reading from stream');
305 public function getMetadata($key = null)
308 return stream_get_meta_data($this->resource);
311 $metadata = stream_get_meta_data($this->resource);
312 if (! array_key_exists($key, $metadata)) {
316 return $metadata[$key];
320 * Set the internal stream resource.
322 * @param string|resource $stream String stream target or stream resource.
323 * @param string $mode Resource mode for stream target.
324 * @throws InvalidArgumentException for invalid streams or resources.
326 private function setStream($stream, $mode = 'r')
331 if (is_string($stream)) {
332 set_error_handler(function ($e) use (&$error) {
333 if ($e !== E_WARNING) {
339 $resource = fopen($stream, $mode);
340 restore_error_handler();
344 throw new InvalidArgumentException('Invalid stream reference provided');
347 if (! is_resource($resource) || 'stream' !== get_resource_type($resource)) {
348 throw new InvalidArgumentException(
349 'Invalid stream provided; must be a string stream identifier or stream resource'
353 if ($stream !== $resource) {
354 $this->stream = $stream;
357 $this->resource = $resource;