setStream($stream, $mode); } /** * {@inheritdoc} */ public function __toString() { if (! $this->isReadable()) { return ''; } try { if ($this->isSeekable()) { $this->rewind(); } return $this->getContents(); } catch (RuntimeException $e) { return ''; } } /** * {@inheritdoc} */ public function close() { if (! $this->resource) { return; } $resource = $this->detach(); fclose($resource); } /** * {@inheritdoc} */ public function detach() { $resource = $this->resource; $this->resource = null; return $resource; } /** * Attach a new stream/resource to the instance. * * @param string|resource $resource * @param string $mode * @throws InvalidArgumentException for stream identifier that cannot be * cast to a resource * @throws InvalidArgumentException for non-resource stream */ public function attach($resource, $mode = 'r') { $this->setStream($resource, $mode); } /** * {@inheritdoc} */ public function getSize() { if (null === $this->resource) { return null; } $stats = fstat($this->resource); if ($stats !== false) { return $stats['size']; } return null; } /** * {@inheritdoc} */ public function tell() { if (! $this->resource) { throw new RuntimeException('No resource available; cannot tell position'); } $result = ftell($this->resource); if (! is_int($result)) { throw new RuntimeException('Error occurred during tell operation'); } return $result; } /** * {@inheritdoc} */ public function eof() { if (! $this->resource) { return true; } return feof($this->resource); } /** * {@inheritdoc} */ public function isSeekable() { if (! $this->resource) { return false; } $meta = stream_get_meta_data($this->resource); return $meta['seekable']; } /** * {@inheritdoc} */ public function seek($offset, $whence = SEEK_SET) { if (! $this->resource) { throw new RuntimeException('No resource available; cannot seek position'); } if (! $this->isSeekable()) { throw new RuntimeException('Stream is not seekable'); } $result = fseek($this->resource, $offset, $whence); if (0 !== $result) { throw new RuntimeException('Error seeking within stream'); } return true; } /** * {@inheritdoc} */ public function rewind() { return $this->seek(0); } /** * {@inheritdoc} */ public function isWritable() { if (! $this->resource) { return false; } $meta = stream_get_meta_data($this->resource); $mode = $meta['mode']; return ( strstr($mode, 'x') || strstr($mode, 'w') || strstr($mode, 'c') || strstr($mode, 'a') || strstr($mode, '+') ); } /** * {@inheritdoc} */ public function write($string) { if (! $this->resource) { throw new RuntimeException('No resource available; cannot write'); } if (! $this->isWritable()) { throw new RuntimeException('Stream is not writable'); } $result = fwrite($this->resource, $string); if (false === $result) { throw new RuntimeException('Error writing to stream'); } return $result; } /** * {@inheritdoc} */ public function isReadable() { if (! $this->resource) { return false; } $meta = stream_get_meta_data($this->resource); $mode = $meta['mode']; return (strstr($mode, 'r') || strstr($mode, '+')); } /** * {@inheritdoc} */ public function read($length) { if (! $this->resource) { throw new RuntimeException('No resource available; cannot read'); } if (! $this->isReadable()) { throw new RuntimeException('Stream is not readable'); } $result = fread($this->resource, $length); if (false === $result) { throw new RuntimeException('Error reading stream'); } return $result; } /** * {@inheritdoc} */ public function getContents() { if (! $this->isReadable()) { throw new RuntimeException('Stream is not readable'); } $result = stream_get_contents($this->resource); if (false === $result) { throw new RuntimeException('Error reading from stream'); } return $result; } /** * {@inheritdoc} */ public function getMetadata($key = null) { if (null === $key) { return stream_get_meta_data($this->resource); } $metadata = stream_get_meta_data($this->resource); if (! array_key_exists($key, $metadata)) { return null; } return $metadata[$key]; } /** * Set the internal stream resource. * * @param string|resource $stream String stream target or stream resource. * @param string $mode Resource mode for stream target. * @throws InvalidArgumentException for invalid streams or resources. */ private function setStream($stream, $mode = 'r') { $error = null; $resource = $stream; if (is_string($stream)) { set_error_handler(function ($e) use (&$error) { $error = $e; }, E_WARNING); $resource = fopen($stream, $mode); restore_error_handler(); } if ($error) { throw new InvalidArgumentException('Invalid stream reference provided'); } if (! is_resource($resource) || 'stream' !== get_resource_type($resource)) { throw new InvalidArgumentException( 'Invalid stream provided; must be a string stream identifier or stream resource' ); } if ($stream !== $resource) { $this->stream = $stream; } $this->resource = $resource; } }