183 lines
4.7 KiB
PHP
183 lines
4.7 KiB
PHP
<?php
|
|
|
|
namespace Ulmus\Api\Stream;
|
|
|
|
use Psr\Http\Message\StreamInterface;
|
|
|
|
class Stream implements StreamInterface
|
|
{
|
|
protected mixed $stream;
|
|
|
|
public function __toString()
|
|
{
|
|
return $this->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;
|
|
}
|
|
} |