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