diff --git a/VERSION b/VERSION index 77e563d..7ff7cf6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2410.20209 +0.2410.21617 diff --git a/src/Bencode/Bencode.php b/src/Bencode/Bencode.php index bbd716c..de689b8 100644 --- a/src/Bencode/Bencode.php +++ b/src/Bencode/Bencode.php @@ -7,7 +7,6 @@ namespace Index\Bencode; use InvalidArgumentException; use RuntimeException; -use Index\Io\{GenericStream,Stream,TempFileStream}; /** * Provides a Bencode serialiser. @@ -80,7 +79,7 @@ final class Bencode { /** * Decodes a bencoded string. * - * @param mixed $bencoded The bencoded string being decoded. Can be an Index Stream, readable resource or a string. + * @param mixed $bencoded The bencoded string being decoded. Can be a readable resource or a string. * @param int $depth Maximum nesting depth of the structure being decoded. The value must be greater than 0. * @param bool $associative When true, Bencoded dictionaries will be returned as associative arrays; when false, Bencoded dictionaries will be returned as objects. * @throws InvalidArgumentException If $bencoded string is not set to a valid type. @@ -90,18 +89,22 @@ final class Bencode { */ public static function decode(mixed $bencoded, int $depth = self::DEFAULT_DEPTH, bool $associative = true): mixed { if(is_string($bencoded)) { - $bencoded = TempFileStream::fromString($bencoded); - $bencoded->seek(0); - } elseif(is_resource($bencoded)) - $bencoded = new GenericStream($bencoded); - elseif(!($bencoded instanceof Stream)) - throw new InvalidArgumentException('$bencoded must be a string, an Index Stream or a file resource.'); + $handle = fopen('php://memory', 'r+b'); + if($handle === false) + throw new RuntimeException('failed to fopen a memory stream'); + + fwrite($handle, $bencoded); + fseek($handle, 0); + $bencoded = $handle; + $handle = null; + } elseif(!is_resource($bencoded)) + throw new InvalidArgumentException('$bencoded must be a string or a stream resource.'); if($depth < 1) throw new InvalidArgumentException('Maximum depth reached, structure is too dense.'); - $char = $bencoded->readChar(); - if($char === null) + $char = fgetc($bencoded); + if($char === false) throw new RuntimeException('Unexpected end of stream in $bencoded.'); switch($char) { @@ -109,8 +112,8 @@ final class Bencode { $number = ''; for(;;) { - $char = $bencoded->readChar(); - if($char === null) + $char = fgetc($bencoded); + if($char === false) throw new RuntimeException('Unexpected end of stream while parsing integer.'); if($char === 'e') break; @@ -131,12 +134,12 @@ final class Bencode { $list = []; for(;;) { - $char = $bencoded->readChar(); - if($char === null) + $char = fgetc($bencoded); + if($char === false) throw new RuntimeException('Unexpected end of stream while parsing list.'); if($char === 'e') break; - $bencoded->seek(-1, Stream::CURRENT); + fseek($bencoded, -1, SEEK_CUR); $list[] = self::decode($bencoded, $depth - 1, $associative); } @@ -146,14 +149,14 @@ final class Bencode { $dict = []; for(;;) { - $char = $bencoded->readChar(); - if($char === null) + $char = fgetc($bencoded); + if($char === false) throw new RuntimeException('Unexpected end of stream while parsing dictionary'); if($char === 'e') break; if(!ctype_digit($char)) throw new RuntimeException('Unexpected dictionary key type, expected a string.'); - $bencoded->seek(-1, Stream::CURRENT); + fseek($bencoded, -1, SEEK_CUR); $dict[self::decode($bencoded, $depth - 1, $associative)] = self::decode($bencoded, $depth - 1, $associative); } @@ -169,8 +172,8 @@ final class Bencode { $length = $char; for(;;) { - $char = $bencoded->readChar(); - if($char === null) + $char = fgetc($bencoded); + if($char === false) throw new RuntimeException('Unexpected end of character while parsing string length.'); if($char === ':') break; @@ -182,7 +185,13 @@ final class Bencode { $length .= $char; } - return $bencoded->read((int)$length); + $length = (int)$length; + if($length < 0) + throw new RuntimeException('Length is Bencoded stream is less than 0.'); + if($length < 1) + return ''; + + return fread($bencoded, $length); } } } diff --git a/src/Data/DbResult.php b/src/Data/DbResult.php index ed05b4b..44be9de 100644 --- a/src/Data/DbResult.php +++ b/src/Data/DbResult.php @@ -6,7 +6,6 @@ namespace Index\Data; use Index\Closeable; -use Index\Io\Stream; /** * Represents a database result set. @@ -99,14 +98,6 @@ interface DbResult extends Closeable { */ function getBooleanOrNull(int|string $index): ?bool; - /** - * Gets the value from the target index as a Stream. - * - * @param int|string $index Target index. - * @return ?Stream A Stream if data is available, null if not. - */ - function getStream(int|string $index): ?Stream; - /** * Creates an iterator for this result. * diff --git a/src/Data/DbTools.php b/src/Data/DbTools.php index 992e532..572dda6 100644 --- a/src/Data/DbTools.php +++ b/src/Data/DbTools.php @@ -154,11 +154,9 @@ final class DbTools { return DbType::FLOAT; if(is_int($value)) return DbType::INTEGER; - // ┌ should probably also check for Stringable, length should also be taken into consideration - // ↓ though maybe with that it's better to assume that when an object is passed it'll always be Massive - if(is_string($value)) - return DbType::STRING; - return DbType::BLOB; + if(is_resource($value)) + return DbType::BLOB; + return DbType::STRING; } /** diff --git a/src/Data/MariaDb/MariaDbResult.php b/src/Data/MariaDb/MariaDbResult.php index 169ef86..89099e8 100644 --- a/src/Data/MariaDb/MariaDbResult.php +++ b/src/Data/MariaDb/MariaDbResult.php @@ -9,7 +9,6 @@ use mysqli_result; use mysqli_stmt; use InvalidArgumentException; use Index\Data\{DbResult,DbResultTrait}; -use Index\Io\{Stream,TempFileStream}; /** * Represents a MariaDB/MySQL database result. @@ -68,17 +67,6 @@ abstract class MariaDbResult implements DbResult { return $this->currentRow[$index] ?? null; } - public function getStream(int|string $index): ?Stream { - if($this->isNull($index)) - return null; - - $value = $this->getValue($index); - if(!is_scalar($value)) - return null; - - return new TempFileStream((string)$value); - } - abstract function close(): void; public function __destruct() { diff --git a/src/Data/MariaDb/MariaDbStatement.php b/src/Data/MariaDb/MariaDbStatement.php index 58e0879..f7ba462 100644 --- a/src/Data/MariaDb/MariaDbStatement.php +++ b/src/Data/MariaDb/MariaDbStatement.php @@ -8,8 +8,8 @@ namespace Index\Data\MariaDb; use mysqli_stmt; use InvalidArgumentException; use RuntimeException; +use Stringable; use Index\Data\{DbType,DbStatement}; -use Index\Io\Stream; /** * Represents a MariaDB/MySQL prepared statement. @@ -132,13 +132,10 @@ class MariaDbStatement implements DbStatement { if($type === DbType::NULL) $value = null; elseif($type === DbType::BLOB) { - if($value instanceof Stream) { - while(($data = $value->read(8192)) !== null) - $this->statement->send_long_data($key, $data); - } elseif(is_resource($value)) { + if(is_resource($value)) { while($data = fread($value, 8192)) $this->statement->send_long_data($key, $data); - } elseif(is_scalar($value)) + } elseif(is_scalar($value) || $value instanceof Stringable) $this->statement->send_long_data($key, (string)$value); $value = null; diff --git a/src/Data/NullDb/NullDbResult.php b/src/Data/NullDb/NullDbResult.php index 622790d..5dbcd54 100644 --- a/src/Data/NullDb/NullDbResult.php +++ b/src/Data/NullDb/NullDbResult.php @@ -6,7 +6,6 @@ namespace Index\Data\NullDb; use Index\Data\{DbResult,DbResultIterator}; -use Index\Io\Stream; /** * Represents a dummy database result. @@ -56,10 +55,6 @@ class NullDbResult implements DbResult { return null; } - public function getStream(int|string $index): ?Stream { - return null; - } - public function getIterator(callable $construct): DbResultIterator { return new DbResultIterator($this, $construct); } diff --git a/src/Data/Sqlite/SqliteConnection.php b/src/Data/Sqlite/SqliteConnection.php index d07fb2b..2214d83 100644 --- a/src/Data/Sqlite/SqliteConnection.php +++ b/src/Data/Sqlite/SqliteConnection.php @@ -8,7 +8,6 @@ namespace Index\Data\Sqlite; use RuntimeException; use SQLite3; use Index\Data\{DbConnection,DbTransactions,DbStatement,DbResult}; -use Index\Io\{GenericStream,Stream}; /** * Represents a client for an SQLite database. @@ -458,22 +457,20 @@ class SqliteConnection implements DbConnection, DbTransactions { } /** - * Opens a BLOB field as a Stream. - * - * To be used instead of getStream on SqliteResult. + * Opens a BLOB field as a resource. * * @param string $table Name of the source table. * @param string $column Name of the source column. * @param int $rowId ID of the source row. * @throws RuntimeException If opening the BLOB failed. - * @return Stream BLOB field as a Stream. + * @return resource BLOB field as a file resource. */ - public function getBlobStream(string $table, string $column, int $rowId): Stream { + public function getBlobStream(string $table, string $column, int $rowId): mixed { $handle = $this->connection->openBlob($table, $column, $rowId); if(!is_resource($handle)) throw new RuntimeException((string)$this->getLastErrorString(), $this->getLastErrorCode()); - return new GenericStream($handle); + return $handle; } public function getLastInsertId(): int|string { diff --git a/src/Data/Sqlite/SqliteResult.php b/src/Data/Sqlite/SqliteResult.php index 916b788..c51cdb8 100644 --- a/src/Data/Sqlite/SqliteResult.php +++ b/src/Data/Sqlite/SqliteResult.php @@ -8,7 +8,6 @@ namespace Index\Data\Sqlite; use SQLite3Result; use InvalidArgumentException; use Index\Data\{DbResult,DbResultTrait}; -use Index\Io\{Stream,TempFileStream}; /** * Represents an SQLite result set. @@ -54,20 +53,5 @@ class SqliteResult implements DbResult { return $this->currentRow[$index] ?? null; } - /** - * Gets the value from the target index as a Stream. - * If you're aware that you're using SQLite it may make more sense to use SqliteConnection::getBlobStream instead. - */ - public function getStream(int|string $index): ?Stream { - if($this->isNull($index)) - return null; - - $value = $this->getValue($index); - if(!is_scalar($value)) - return null; - - return new TempFileStream((string)$value); - } - public function close(): void {} } diff --git a/src/Data/Sqlite/SqliteStatement.php b/src/Data/Sqlite/SqliteStatement.php index 8eac99b..0eb94c6 100644 --- a/src/Data/Sqlite/SqliteStatement.php +++ b/src/Data/Sqlite/SqliteStatement.php @@ -10,7 +10,6 @@ use SQLite3Stmt; use InvalidArgumentException; use RuntimeException; use Index\Data\{DbTools,DbType,DbStatement,DbResult}; -use Index\Io\Stream; /** * Represents a prepared SQLite SQL statement. diff --git a/src/Http/Content/BencodedContent.php b/src/Http/Content/BencodedContent.php index eb07ae4..2a7a0b4 100644 --- a/src/Http/Content/BencodedContent.php +++ b/src/Http/Content/BencodedContent.php @@ -5,8 +5,8 @@ namespace Index\Http\Content; +use RuntimeException; use Index\Bencode\{Bencode,BencodeSerializable}; -use Index\Io\{Stream,FileStream}; /** * Represents Bencoded body content for a HTTP message. @@ -62,7 +62,15 @@ class BencodedContent implements BencodeSerializable, HttpContent { * @return BencodedContent Instance representing the provided path. */ public static function fromFile(string $path): BencodedContent { - return self::fromEncoded(FileStream::openRead($path)); + $handle = fopen($path, 'rb'); + if(!is_resource($handle)) + throw new RuntimeException('$path could not be opened'); + + try { + return self::fromEncoded($handle); + } finally { + fclose($handle); + } } /** diff --git a/src/Http/Content/JsonContent.php b/src/Http/Content/JsonContent.php index e125523..8a46054 100644 --- a/src/Http/Content/JsonContent.php +++ b/src/Http/Content/JsonContent.php @@ -6,7 +6,7 @@ namespace Index\Http\Content; use JsonSerializable; -use Index\Io\{Stream,FileStream}; +use RuntimeException; /** * Represents JSON body content for a HTTP message. @@ -66,7 +66,11 @@ class JsonContent implements HttpContent, JsonSerializable { * @return JsonContent Instance representing the provided path. */ public static function fromFile(string $path): JsonContent { - return self::fromEncoded(FileStream::openRead($path)); + $contents = file_get_contents($path); + if($contents === false) + throw new RuntimeException('was unable to read file at $path'); + + return self::fromEncoded($contents); } /** diff --git a/src/Http/Content/StreamContent.php b/src/Http/Content/StreamContent.php deleted file mode 100644 index 249ae69..0000000 --- a/src/Http/Content/StreamContent.php +++ /dev/null @@ -1,54 +0,0 @@ -stream; - } - - public function __toString(): string { - return (string)$this->stream; - } - - /** - * Creates an instance from a file. - * - * @param string $path Path to the file. - * @return StreamContent Instance representing the provided path. - */ - public static function fromFile(string $path): StreamContent { - return new StreamContent( - FileStream::openRead($path) - ); - } - - /** - * Creates an instance from the raw request body. - * - * @return StreamContent Instance representing the request body. - */ - public static function fromRequest(): StreamContent { - return self::fromFile('php://input'); - } -} diff --git a/src/Http/ContentHandling/StreamContentHandler.php b/src/Http/ContentHandling/StreamContentHandler.php deleted file mode 100644 index df4f043..0000000 --- a/src/Http/ContentHandling/StreamContentHandler.php +++ /dev/null @@ -1,27 +0,0 @@ -hasContentType()) - $response->setTypeStream(); - - $response->setContent(new StreamContent($content)); - } -} diff --git a/src/Http/HttpMessage.php b/src/Http/HttpMessage.php index 147f046..8a9362c 100644 --- a/src/Http/HttpMessage.php +++ b/src/Http/HttpMessage.php @@ -6,8 +6,7 @@ namespace Index\Http; use RuntimeException; -use Index\Io\Stream; -use Index\Http\Content\{BencodedContent,FormContent,HttpContent,JsonContent,StreamContent,StringContent}; +use Index\Http\Content\{BencodedContent,FormContent,HttpContent,JsonContent,StringContent}; /** * Represents a base HTTP message. @@ -129,15 +128,6 @@ abstract class HttpMessage { return $this->content instanceof FormContent; } - /** - * Checks if the body content is StreamContent. - * - * @return bool true if it is StreamContent. - */ - public function isStreamContent(): bool { - return $this->content instanceof StreamContent; - } - /** * Checks if the body content is StringContent. * diff --git a/src/Http/HttpMessageBuilder.php b/src/Http/HttpMessageBuilder.php index c012e04..6aa89ca 100644 --- a/src/Http/HttpMessageBuilder.php +++ b/src/Http/HttpMessageBuilder.php @@ -5,8 +5,10 @@ namespace Index\Http; -use Index\Io\Stream; -use Index\Http\Content\{HttpContent,StreamContent,StringContent}; +use InvalidArgumentException; +use RuntimeException; +use Stringable; +use Index\Http\Content\{HttpContent,StringContent}; /** * Represents a base HTTP message builder. @@ -119,13 +121,28 @@ class HttpMessageBuilder { /** * Sets HTTP message body contents. * - * @param HttpContent|Stream|string|null $content Body contents + * @param HttpContent|Stringable|string|int|float|resource|null $content Body contents */ - public function setContent(HttpContent|Stream|string|null $content): void { - if($content instanceof Stream) - $content = new StreamContent($content); - elseif(is_string($content)) - $content = new StringContent($content); - $this->content = $content; + public function setContent(mixed $content): void { + if($content instanceof HttpContent || $content === null) { + $this->content = $content; + return; + } + + if(is_scalar($content) || $content instanceof Stringable) { + $this->content = new StringContent((string)$content); + return; + } + + if(is_resource($content)) { + $content = stream_get_contents($content); + if($content === false) + throw new RuntimeException('was unable to read the stream resource in $content'); + + $this->content = new StringContent($content); + return; + } + + throw new InvalidArgumentException('$content not a supported type'); } } diff --git a/src/Http/HttpRequest.php b/src/Http/HttpRequest.php index 0e58989..99e5769 100644 --- a/src/Http/HttpRequest.php +++ b/src/Http/HttpRequest.php @@ -8,7 +8,7 @@ namespace Index\Http; use InvalidArgumentException; use RuntimeException; use Index\MediaType; -use Index\Http\Content\{HttpContent,JsonContent,StreamContent,FormContent}; +use Index\Http\Content\{HttpContent,FormContent,JsonContent,StringContent}; /** * Represents a HTTP request message. @@ -187,7 +187,7 @@ class HttpRequest extends HttpMessage { && ($contentType->equals('application/x-www-form-urlencoded') || $contentType->equals('multipart/form-data'))) $build->setContent(FormContent::fromRequest()); elseif($contentLength > 0) - $build->setContent(StreamContent::fromRequest()); + $build->setContent(StringContent::fromRequest()); return $build->toRequest(); } diff --git a/src/Http/HttpUploadedFile.php b/src/Http/HttpUploadedFile.php index b1427e8..f977d1b 100644 --- a/src/Http/HttpUploadedFile.php +++ b/src/Http/HttpUploadedFile.php @@ -7,16 +7,13 @@ namespace Index\Http; use InvalidArgumentException; use RuntimeException; -use Index\{MediaType,Closeable}; -use Index\Io\{Stream,FileStream}; +use Index\MediaType; /** * Represents an uploaded file in a multipart/form-data request. */ -class HttpUploadedFile implements Closeable { +class HttpUploadedFile { private bool $hasMoved = false; - private ?Stream $stream = null; - private MediaType $suggestedMediaType; /** @@ -101,25 +98,6 @@ class HttpUploadedFile implements Closeable { return $this->hasMoved; } - /** - * Gets a read-only stream to the uploaded file. - * - * @throws RuntimeException If the file cannot be opened. - * @return Stream Read-only stream of the upload file. - */ - public function getStream(): Stream { - if($this->stream === null) { - if($this->errorCode !== UPLOAD_ERR_OK) - throw new RuntimeException('Can\'t open stream because of an upload error.'); - if($this->hasMoved) - throw new RuntimeException('Can\'t open stream because file has already been moved.'); - - $this->stream = FileStream::openRead($this->localFileName); - } - - return $this->stream; - } - /** * Moves the uploaded file to its final destination. * @@ -138,9 +116,6 @@ class HttpUploadedFile implements Closeable { if(empty($path)) throw new InvalidArgumentException('$path is not a valid path.'); - if($this->stream !== null) - $this->stream->close(); - $this->hasMoved = PHP_SAPI === 'CLI' ? rename($this->localFileName, $path) : move_uploaded_file($this->localFileName, $path); @@ -151,15 +126,6 @@ class HttpUploadedFile implements Closeable { $this->localFileName = $path; } - public function close(): void { - if($this->stream !== null) - $this->stream->close(); - } - - public function __destruct() { - $this->close(); - } - /** * Creates a HttpUploadedFile instance from an entry in the $_FILES superglobal. * diff --git a/src/Http/Routing/HttpRouter.php b/src/Http/Routing/HttpRouter.php index d3e419f..ca28306 100644 --- a/src/Http/Routing/HttpRouter.php +++ b/src/Http/Routing/HttpRouter.php @@ -9,7 +9,7 @@ use stdClass; use InvalidArgumentException; use Index\Http\{HttpResponse,HttpResponseBuilder,HttpRequest}; use Index\Http\Content\StringContent; -use Index\Http\ContentHandling\{BencodeContentHandler,ContentHandler,JsonContentHandler,StreamContentHandler}; +use Index\Http\ContentHandling\{BencodeContentHandler,ContentHandler,JsonContentHandler}; use Index\Http\ErrorHandling\{HtmlErrorHandler,ErrorHandler,PlainErrorHandler}; class HttpRouter implements Router { @@ -113,7 +113,6 @@ class HttpRouter implements Router { * Register the default content handlers. */ public function registerDefaultContentHandlers(): void { - $this->registerContentHandler(new StreamContentHandler); $this->registerContentHandler(new JsonContentHandler); $this->registerContentHandler(new BencodeContentHandler); } diff --git a/src/Io/FileStream.php b/src/Io/FileStream.php deleted file mode 100644 index 472bf23..0000000 --- a/src/Io/FileStream.php +++ /dev/null @@ -1,256 +0,0 @@ -getCode(), $ex); - } - - if($stream === false) - throw new RuntimeException('An unhandled error occurred while trying to open a file.'); - - parent::__construct($stream); - - if($this->lock & self::LOCK_WRITE) - flock($this->stream, LOCK_EX); - elseif($this->lock & self::LOCK_READ) - flock($this->stream, LOCK_SH); - } - - public function close(): void { - if($this->lock !== self::LOCK_NONE) - flock($this->stream, LOCK_UN); - parent::close(); - } - - /** - * Open file for reading, throw if not exists. - * - * @param string $path Path to the file. - * @param int $lock Locking method to use. - * @return FileStream Stream to file. - */ - public static function openRead(string $path, int $lock = self::LOCK_NONE): FileStream { - return new FileStream($path, self::OPEN_READ, $lock); - } - - /** - * Open file for reading and writing, throw if not exist. - * - * @param string $path Path to the file. - * @param int $lock Locking method to use. - * @return FileStream Stream to file. - */ - public static function openReadWrite(string $path, int $lock = self::LOCK_NONE): FileStream { - return new FileStream($path, self::OPEN_READ_WRITE, $lock); - } - - /** - * Create file for writing, truncate if exist. - * - * @param string $path Path to the file. - * @param int $lock Locking method to use. - * @return FileStream Stream to file. - */ - public static function newWrite(string $path, int $lock = self::LOCK_NONE): FileStream { - return new FileStream($path, self::NEW_WRITE, $lock); - } - - /** - * Create file for reading and writing, truncate if exist. - * - * @param string $path Path to the file. - * @param int $lock Locking method to use. - * @return FileStream Stream to file. - */ - public static function newReadWrite(string $path, int $lock = self::LOCK_NONE): FileStream { - return new FileStream($path, self::NEW_READ_WRITE, $lock); - } - - /** - * Open file for appending, create if not exist. - * - * @param string $path Path to the file. - * @param int $lock Locking method to use. - * @return FileStream Stream to file. - */ - public static function appendWrite(string $path, int $lock = self::LOCK_NONE): FileStream { - return new FileStream($path, self::APPEND_WRITE, $lock); - } - - /** - * Open file for reading and appending, create if not exist. - * - * @param string $path Path to the file. - * @param int $lock Locking method to use. - * @return FileStream Stream to file. - */ - public static function appendReadWrite(string $path, int $lock = self::LOCK_NONE): FileStream { - return new FileStream($path, self::APPEND_READ_WRITE, $lock); - } - - /** - * Create file for writing, throw if exist. - * - * @param string $path Path to the file. - * @param int $lock Locking method to use. - * @return FileStream Stream to file. - */ - public static function createWrite(string $path, int $lock = self::LOCK_NONE): FileStream { - return new FileStream($path, self::CREATE_WRITE, $lock); - } - - /** - * Create file for reading and writing, throw if exist. - * - * @param string $path Path to the file. - * @param int $lock Locking method to use. - * @return FileStream Stream to file. - */ - public static function createReadWrite(string $path, int $lock = self::LOCK_NONE): FileStream { - return new FileStream($path, self::CREATE_READ_WRITE, $lock); - } - - /** - * Opens or creates a file for writing. - * - * @param string $path Path to the file. - * @param int $lock Locking method to use. - * @return FileStream Stream to file. - */ - public static function openOrCreateWrite(string $path, int $lock = self::LOCK_NONE): FileStream { - return new FileStream($path, self::OPEN_OR_CREATE_WRITE, $lock); - } - - /** - * Opens or creates a file for reading and writing. - * - * @param string $path Path to the file. - * @param int $lock Locking method to use. - * @return FileStream Stream to file. - */ - public static function openOrCreateReadWrite(string $path, int $lock = self::LOCK_NONE): FileStream { - return new FileStream($path, self::OPEN_OR_CREATE_READ_WRITE, $lock); - } -} diff --git a/src/Io/GenericStream.php b/src/Io/GenericStream.php deleted file mode 100644 index 3f311f3..0000000 --- a/src/Io/GenericStream.php +++ /dev/null @@ -1,195 +0,0 @@ -stream = $stream; - - $metaData = $this->metaData(); - $this->canRead = in_array($metaData['mode'], self::READABLE); - $this->canWrite = in_array($metaData['mode'], self::WRITEABLE); - $this->canSeek = is_bool($metaData['seekable']) ? $metaData['seekable'] : false; - } - - /** - * Returns the underlying resource handle. - * - * @return resource Underlying resource handle. - */ - public function getResource() { - return $this->stream; - } - - /** @return array */ - private function stat(): array { - $stat = fstat($this->stream); - if($stat === false) - throw new RuntimeException('fstat returned false'); - - return $stat; - } - - public function getPosition(): int { - $tell = ftell($this->stream); - if($tell === false) - return -1; - - return $tell; - } - - public function getLength(): int { - if(!$this->canSeek()) - return -1; - - $size = $this->stat()['size']; - if(!is_int($size)) - return -1; - - return $size; - } - public function setLength(int $length): void { - if($length < 0) - throw new InvalidArgumentException('$length must be a positive integer'); - - ftruncate($this->stream, $length); - } - - /** @return array */ - private function metaData(): array { - return stream_get_meta_data($this->stream); - } - public function canRead(): bool { - return $this->canRead; - } - public function canWrite(): bool { - return $this->canWrite; - } - public function canSeek(): bool { - return $this->canSeek; - } - public function hasTimedOut(): bool { - $timedOut = $this->metaData()['timed_out']; - if(!is_bool($timedOut)) - return false; - - return $timedOut; - } - public function isBlocking(): bool { - $blocked = $this->metaData()['blocked']; - if(!is_bool($blocked)) - return false; - - return $blocked; - } - - public function isEnded(): bool { - return feof($this->stream); - } - - public function readChar(): ?string { - $char = fgetc($this->stream); - if($char === false) - return null; - return $char; - } - - public function readLine(): ?string { - return ($line = fgets($this->stream)) === false - ? null : $line; - } - - public function read(int $length): ?string { - if($length < 1) - throw new InvalidArgumentException('$length must be greater than 0'); - - $buffer = fread($this->stream, $length); - if($buffer === false) - return null; - - return $buffer; - } - - public function seek(int $offset, int $origin = Stream::START): bool { - return fseek($this->stream, $offset, $origin) === 0; - } - - public function write(string $buffer, int $length = -1): void { - if($length >= 0) - fwrite($this->stream, $buffer, $length); - else - fwrite($this->stream, $buffer); - } - - public function writeChar(string $char): void { - fwrite($this->stream, $char, 1); - } - - public function flush(): void { - fflush($this->stream); - } - - public function close(): void { - try { - fclose($this->stream); - } catch(\Error $ex) {} - } - - #[\Override] - public function copyTo(Stream $other): void { - if($other instanceof GenericStream) { - stream_copy_to_stream($this->stream, $other->stream); - } else parent::copyTo($other); - } - - public function __toString(): string { - $contents = stream_get_contents($this->stream); - if($contents === false) - return ''; - - return $contents; - } -} diff --git a/src/Io/MemoryStream.php b/src/Io/MemoryStream.php deleted file mode 100644 index f472573..0000000 --- a/src/Io/MemoryStream.php +++ /dev/null @@ -1,33 +0,0 @@ -write($string); - return $stream; - } -} diff --git a/src/Io/NetworkStream.php b/src/Io/NetworkStream.php deleted file mode 100644 index dde059f..0000000 --- a/src/Io/NetworkStream.php +++ /dev/null @@ -1,149 +0,0 @@ -getCode(), $ex); - } - - if($stream === false) - throw new RuntimeException('An unhandled error occurred while trying to connect.'); - - parent::__construct($stream); - } - - /** - * Sets whether the network stream should have blocking reads and writes. - * - * @param bool $blocking true to block, false to async. - */ - public function setBlocking(bool $blocking): void { - stream_set_blocking($this->stream, $blocking); - } - - /** - * Opens a network socket stream to an endpoint represented by an Index EndPoint instance. - * - * @param EndPoint $endPoint Host to connect to. - * @param float|null $timeout Amount of seconds until timeout, null for php.ini default. - */ - public static function openEndPoint(EndPoint $endPoint, float|null $timeout = null): NetworkStream { - return new NetworkStream((string)$endPoint, -1, $timeout); - } - - /** - * Opens an SSL network socket stream to an endpoint represented by an Index EndPoint instance. - * - * @param EndPoint $endPoint Host to connect to. - * @param float|null $timeout Amount of seconds until timeout, null for php.ini default. - */ - public static function openEndPointSSL(EndPoint $endPoint, float|null $timeout = null): NetworkStream { - return new NetworkStream('ssl://' . ((string)$endPoint), -1, $timeout); - } - - /** - * Opens a TLS network socket stream to an endpoint represented by an Index EndPoint instance. - * - * @param EndPoint $endPoint Host to connect to. - * @param float|null $timeout Amount of seconds until timeout, null for php.ini default. - */ - public static function openEndPointTLS(EndPoint $endPoint, float|null $timeout = null): NetworkStream { - return new NetworkStream('tls://' . ((string)$endPoint), -1, $timeout); - } - - /** - * Opens a network socket stream to an endpoint represented by an hostname and port. - * - * @param string $hostname Hostname to connect to. - * @param int $port Port to connect at. - * @param float|null $timeout Amount of seconds until timeout, null for php.ini default. - */ - public static function openHost(string $hostname, int $port, float|null $timeout = null): NetworkStream { - return new NetworkStream($hostname, $port, $timeout); - } - - /** - * Opens an SSL network socket stream to an endpoint represented by an hostname and port. - * - * @param string $hostname Hostname to connect to. - * @param int $port Port to connect at. - * @param float|null $timeout Amount of seconds until timeout, null for php.ini default. - */ - public static function openHostSSL(string $hostname, int $port, float|null $timeout = null): NetworkStream { - return new NetworkStream('ssl://' . ((string)$hostname), $port, $timeout); - } - - /** - * Opens a TLS network socket stream to an endpoint represented by an hostname and port. - * - * @param string $hostname Hostname to connect to. - * @param int $port Port to connect at. - * @param float|null $timeout Amount of seconds until timeout, null for php.ini default. - */ - public static function openHostTLS(string $hostname, int $port, float|null $timeout = null): NetworkStream { - return new NetworkStream('tls://' . ((string)$hostname), $port, $timeout); - } - - /** - * Opens a network socket stream to an endpoint represented by an Index IpAddress instance and port. - * - * @param IpAddress $address Address to connect to. - * @param int $port Port to connect at. - * @param float|null $timeout Amount of seconds until timeout, null for php.ini default. - */ - public static function openAddress(IpAddress $address, int $port, float|null $timeout = null): NetworkStream { - return new NetworkStream($address->getAddress(), $port, $timeout); - } - - /** - * Opens an SSL network socket stream to an endpoint represented by an Index IpAddress instance and port. - * - * @param IpAddress $address Address to connect to. - * @param int $port Port to connect at. - * @param float|null $timeout Amount of seconds until timeout, null for php.ini default. - */ - public static function openAddressSSL(IpAddress $address, int $port, float|null $timeout = null): NetworkStream { - return new NetworkStream('ssl://' . $address->getAddress(), $port, $timeout); - } - - /** - * Opens a TLS network socket stream to an endpoint represented by an Index IpAddress instance and port. - * - * @param IpAddress $address Address to connect to. - * @param int $port Port to connect at. - * @param float|null $timeout Amount of seconds until timeout, null for php.ini default. - */ - public static function openAddressTLS(IpAddress $address, int $port, float|null $timeout = null): NetworkStream { - return new NetworkStream('tls://' . $address->getAddress(), $port, $timeout); - } - - /** - * Opens a network socket stream to an endpoint represented by a UNIX socket path. - * - * @param string $path Path to connect to. - * @param float|null $timeout Amount of seconds until timeout, null for php.ini default. - */ - public static function openUnix(string $path, float|null $timeout = null): NetworkStream { - return new NetworkStream('unix://' . $path, -1, $timeout); - } -} diff --git a/src/Io/ProcessStream.php b/src/Io/ProcessStream.php deleted file mode 100644 index 4da2560..0000000 --- a/src/Io/ProcessStream.php +++ /dev/null @@ -1,116 +0,0 @@ -handle = $handle; - $this->canRead = strpos($mode, 'r') !== false; - $this->canWrite = strpos($mode, 'w') !== false; - - if(!is_resource($this->handle)) - throw new RuntimeException('Failed to create process.'); - } - - public function getPosition(): int { - return -1; - } - - public function getLength(): int { - return -1; - } - public function setLength(int $length): void {} - - public function canRead(): bool { - return $this->canRead; - } - - public function canWrite(): bool { - return $this->canWrite; - } - - public function canSeek(): bool { - return false; - } - - public function hasTimedOut(): bool { - return false; - } - - public function isBlocking(): bool { - return true; - } - - public function isEnded(): bool { - return feof($this->handle); - } - - public function readChar(): ?string { - $char = fgetc($this->handle); - if($char === false) - return null; - return $char; - } - - public function readLine(): ?string { - return ($line = fgets($this->handle)) === false - ? null : $line; - } - - public function read(int $length): ?string { - if($length < 1) - throw new InvalidArgumentException('$length must be greater than 0'); - - $buffer = fread($this->handle, $length); - if($buffer === false) - return null; - - return $buffer; - } - - public function seek(int $offset, int $origin = self::START): bool { - throw new RuntimeException('Cannot seek ProcessStream.'); - } - - public function write(string $buffer, int $length = -1): void { - if($length >= 0) - fwrite($this->handle, $buffer, $length); - else - fwrite($this->handle, $buffer); - } - - public function writeChar(string $char): void { - fwrite($this->handle, $char, 1); - } - - public function flush(): void {} - - public function close(): void { - if(is_resource($this->handle)) - pclose($this->handle); - } -} diff --git a/src/Io/Stream.php b/src/Io/Stream.php deleted file mode 100644 index e030684..0000000 --- a/src/Io/Stream.php +++ /dev/null @@ -1,219 +0,0 @@ -readChar(); - if($char === null) - return -1; - return ord($char); - } - - /** - * Read a line of text from the stream. - * - * @return ?string One line of text or null if we're at the end of the stream. - */ - abstract public function readLine(): ?string; - - /** - * Read an amount of data from the stream. - * - * @param int $length Maximum amount of data to read. - * @return ?string At most a $length sized string or null if we're at the end of the stream. - */ - abstract public function read(int $length): ?string; - - /** - * Move the cursor to a different place in the stream. - * - * @param int $offset Offset to apply to the cursor position. - * @param int $origin Point from which to apply the offset. - * @throws RuntimeException If the stream is not seekable. - * @return bool true if the cursor offset was applied successfully. - */ - abstract public function seek(int $offset, int $origin = self::START): bool; - - /** - * Write an amount of data to the stream. - * - * @param string $buffer Buffer to write from. - * @param int $length Amount of data to write from the buffer, or -1 to write the entire buffer. - */ - abstract public function write(string $buffer, int $length = -1): void; - - /** - * Writes a line of text to the stream. - * - * @param string $line Line of text to write. - */ - public function writeLine(string $line): void { - $this->write($line); - $this->write(PHP_EOL); - } - - /** - * Writes a single character to the stream. - * - * @param string $char Character to write to the stream. - */ - abstract public function writeChar(string $char): void; - - /** - * Writes a single byte to the stream. - * - * @param int $byte Byte to write to the stream. - */ - public function writeByte(int $byte): void { - $this->writeChar(chr($byte & 0xFF)); - } - - /** - * Copy the entire contents of this stream to another. - * - * Will seek to the beginning of the stream if the stream is seekable. - * - * @param Stream $other Target stream. - */ - public function copyTo(Stream $other): void { - if($this->canSeek()) - $this->seek(0); - - while(!$this->isEnded()) { - $buffer = $this->read(8192); - if($buffer === null) - break; - - $other->write($buffer); - } - } - - /** - * Force write of all buffered output to the stream. - */ - abstract public function flush(): void; - - abstract public function close(): void; - - public function __toString(): string { - if($this->canSeek()) - $this->seek(0); - - $string = ''; - while(!$this->isEnded()) - $string .= $this->read(8192); - - return $string; - } - - public function __destruct() { - $this->close(); - } -} diff --git a/src/Io/TempFileStream.php b/src/Io/TempFileStream.php deleted file mode 100644 index 26381fc..0000000 --- a/src/Io/TempFileStream.php +++ /dev/null @@ -1,47 +0,0 @@ -write($body); - $this->flush(); - } - } - - /** - * Creates a TempFileStream from a string. - * - * @param string $string Data to write to the temporary file. - * @return TempFileStream Stream representing the given text. - */ - public static function fromString(string $string): TempFileStream { - return new TempFileStream($string); - } -} diff --git a/tests/DbToolsTest.php b/tests/DbToolsTest.php index 5a04e77..00e0165 100644 --- a/tests/DbToolsTest.php +++ b/tests/DbToolsTest.php @@ -11,21 +11,27 @@ use Index\Data\{DbTools,DbType}; use Index\Data\MariaDb\MariaDbBackend; use Index\Data\NullDb\NullDbConnection; use Index\Data\Sqlite\SqliteBackend; -use Index\Io\MemoryStream; #[CoversClass(DbTools::class)] #[CoversClass(DbType::class)] #[CoversClass(NullDbConnection::class)] #[CoversClass(MariaDbBackend::class)] #[CoversClass(SqliteBackend::class)] -#[UsesClass(MemoryStream::class)] final class DbToolsTest extends TestCase { public function testDetectType(): void { $this->assertEquals(DbType::NULL, DbTools::detectType(null)); $this->assertEquals(DbType::INTEGER, DbTools::detectType(12345)); $this->assertEquals(DbType::FLOAT, DbTools::detectType(123.45)); $this->assertEquals(DbType::STRING, DbTools::detectType('This is a string.')); - $this->assertEquals(DbType::BLOB, DbTools::detectType(MemoryStream::fromString('This is a string inside a memory stream.'))); + + $blob = fopen('php://memory', 'r+b'); + if($blob === false) + throw new RuntimeException('failed to fopen a memory stream'); + + fwrite($blob, 'This is a string inside a memory stream.'); + fseek($blob, 0); + + $this->assertEquals(DbType::BLOB, DbTools::detectType($blob)); } public function testDSN(): void {