ulmus-api/src/Stream/Stream.php
2023-10-27 18:33:33 -04:00

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;
}
}