getContents(); } public static function fromTemp(string $content = "", string $mode = 'wb+') : StreamInterface { return static::fromMemory($content, $mode, "php://temp"); } public static function fromMemory(string $content = "", string $mode = 'wb+', string $destination = "php://memory") : StreamInterface { $obj = new static(); $obj->create($destination, $mode); $obj->write($content); return $obj; } public function create(mixed $stream, string $mode) : void { if (is_string($stream)) { try { $stream = fopen($stream, $mode); } catch (\Throwable $e) { throw new \RuntimeException(sprintf('Invalid stream reference provided: %s', $error->getMessage()), 0, $e); } } if ( is_resource($stream) || $stream instanceof StreamInterface ) { $this->attach($stream); } else { throw new \RuntimeException("Given stream must be a resource, a StreamInterface or a string pointing to a stream destination for fopen."); } } public function attach(mixed $stream) : void { $this->stream = $stream; } public function detach() : mixed { $stream = $this->stream; unset($this->stream); return $stream; } public function getSize(): null|int { if (false !== $result = fstat($this->stream)){ return $result['size']; } return null; } public function tell() : int { if ( false === $result = ftell($this->stream) ) { throw new \RuntimeException("An error occured while trying to fetch current position pointer in a stream."); } return $result; } public function eof() : bool { return ! $this->stream || feof($this->stream); } public function isSeekable() : bool { return stream_get_meta_data($this->stream)['seekable'] ?? false; } public function seek($offset, $whence = SEEK_SET) : void { if (! $this->isSeekable()) { throw new \InvalidArgumentException("An error occured while trying to seek an unseekable stream."); } if ( 0 !== fseek($this->stream, $offset, $whence) ) { throw new \RuntimeException("An error occured while trying to seek a position in a stream."); } } public function rewind() : void { $this->seek(0); } public function isWritable() : bool { return $this->hasMode('acwx+'); } public function write($string) : false|int { if (! $this->isWritable()) { throw new \UnexpectedValueException("An error occured while trying to seek an unseekable stream."); } $result = fwrite($this->stream, $string); if ( $result === false ) { throw new \UnexpectedValueException("fwrite() failed to write into stream."); } return $result; } public function close(): void { if (! $this->stream) { return; } $stream = $this->detach(); fclose($stream); } public function isReadable(): bool { return $this->hasMode('r+'); } public function read($length): string { if (! $this->isReadable()) { throw new \UnexpectedValueException("An error occured while trying to read a stream which was not readable."); } elseif ( false === $result = fread($this->stream, $length) ) throw new \RuntimeException("Unreadable stream given; please check your stream's mode");{ } return $result; } public function getContents(): string { if (! $this->isReadable()) { throw new \UnexpectedValueException("Unreadable stream given; please check your stream's mode"); } if ( $this->isSeekable() ) { $this->rewind(); } if (false === $result = stream_get_contents($this->stream)) { throw new \RuntimeException("Failure happened on stream_get_contents() from stream. "); } return $result; } public function getMetadata(string $key = null) : mixed { $metadata = stream_get_meta_data($this->stream); return is_null($key) ? $metadata : $metadata[$key] ?? null; } protected function hasMode(string $search) : bool { $meta = stream_get_meta_data($this->stream); $mode = $meta['mode']; return strpbrk($mode, $search) !== false; } }