1
0
Fork 0
mirror of https://github.com/shufflingpixels/php-io.git synced 2026-08-18 21:28:14 +02:00

Adopt Go-style minimal interfaces for streams

Replace PSR-7 StreamInterface with a set of small, composable interfaces
(ReaderInterface, WriterInterface, SeekerInterface and their
combinations).
BinaryReader now depends only on ReaderInterface, BinaryWriter on
WriterInterface, making both easy to satisfy with any byte source or
sink.

Buffer and Resource are simplified — no capability flags, no
detach/close
lifecycle, no PSR-7 metadata methods. LimitedResource is replaced by
LimitedReader, a lightweight reader that enforces a byte count limit.
BinaryReader::read() is renamed to readExact() for clarity.

Tests updated throughout to match the new APIs.
This commit is contained in:
Henrik Hautakoski 2026-06-27 08:40:11 +02:00
parent a27c0a87ea
commit 07ec405512
25 changed files with 451 additions and 1168 deletions

124
README.md
View file

@ -4,10 +4,10 @@ A small, focused PHP I/O toolkit for working with streams and binary data.
`php-io` gives you:
- A consistent `StreamInterface` abstraction
- A minimal, Go-inspired interface hierarchy for byte streams
- In-memory and file-backed stream implementations
- A `BinaryReader` for common integer formats (8/16/32-bit, LE/BE)
- Clear exception types for I/O and end-of-stream conditions
- `BinaryReader` and `BinaryWriter` for common integer formats (8/16/32-bit, LE/BE)
- Clear exception types for I/O failures
## Requirements
@ -21,35 +21,53 @@ composer require shufflingpixels/php-io
## Quick Start
### Read binary values from a string
### Read binary values from an in-memory buffer
```php
<?php
use Shufflingpixels\IO\BinaryReader;
use Shufflingpixels\IO\Buffer;
$reader = BinaryReader::string("\x34\x12\x80\xff");
$reader = new BinaryReader(new Buffer("\x34\x12\x80\xff"));
$a = $reader->readUInt16LE(); // 0x1234 => 4660
$b = $reader->readInt8(); // -128
$c = $reader->readInt8(); // -1
```
### Work with an in-memory buffer
### Write binary values to a buffer
```php
<?php
use Shufflingpixels\IO\BinaryWriter;
use Shufflingpixels\IO\Buffer;
$buffer = new Buffer('');
$writer = new BinaryWriter($buffer);
$writer->writeUInt16LE(0x1234);
$writer->writeInt8(-1);
$buffer->seek(0);
$bytes = $buffer->read($buffer->length()); // "\x34\x12\xff"
```
### Seek and write in-place with a Buffer
```php
<?php
use Shufflingpixels\IO\Buffer;
use Shufflingpixels\IO\SeekMode;
$buffer = new Buffer('abcdef');
$buffer->seek(2); // position = 2
$buffer->write('XY'); // data becomes: abXYef
$buffer->seek(-2, SeekMode::END); // position near end
$buffer->seek(2);
$buffer->write('XY'); // data becomes: abXYef
$tail = $buffer->read(2); // "ef"
$buffer->seek(-2, SEEK_END);
$tail = $buffer->read(2); // "ef"
```
### Open and use a file stream
@ -70,24 +88,86 @@ $bytes = $file->read(3); // "ABC"
$file->close();
```
## Main Types
### Limit reads to a byte window
- `Shufflingpixels\IO\StreamInterface`: common stream contract (`read`, `write`, `seek`, `tell`, `eof`, `length`)
- `Shufflingpixels\IO\Buffer`: in-memory stream implementation
- `Shufflingpixels\IO\File`: file-backed stream implementation
- `Shufflingpixels\IO\BinaryReader`: typed binary reads over any `StreamInterface`
- `Shufflingpixels\IO\SeekMode`: type-safe seek modes (`SET`, `CUR`, `END`)
- `Shufflingpixels\IO\FileMode`: file open modes (`READ`, `WRITE`, `RW`)
```php
<?php
use Shufflingpixels\IO\BinaryReader;
use Shufflingpixels\IO\Buffer;
use Shufflingpixels\IO\LimitedReader;
$buffer = new Buffer("header\x34\x12rest");
$buffer->seek(6); // skip header
$section = new LimitedReader($buffer, 2);
$reader = new BinaryReader($section);
$value = $reader->readUInt16LE(); // 0x1234 — cannot read past the 2-byte window
```
## Interfaces
`php-io` uses a minimal, composable interface hierarchy inspired by Go's `io` package.
Each interface adds exactly one capability.
| Interface | Methods |
|---|---|
| `ReaderInterface` | `read(int $length): string\|false` |
| `WriterInterface` | `write(string $data): int` |
| `SeekerInterface` | `seek()`, `tell()`, `eof()`, `length()` |
| `ReadSeekerInterface` | `ReaderInterface` + `SeekerInterface` |
| `WriteSeekerInterface` | `WriterInterface` + `SeekerInterface` |
| `ReadWriterInterface` | `ReaderInterface` + `WriterInterface` |
| `ReadWriteSeekerInterface` | `ReaderInterface` + `WriterInterface` + `SeekerInterface` |
`read()` returns `false` when the stream is at EOF.
## Implementations
| Class | Implements | Description |
|---|---|---|
| `Buffer` | `ReadWriteSeekerInterface` | In-memory stream backed by a PHP string |
| `Resource` | `ReadWriteSeekerInterface` | Base class wrapping a PHP file resource |
| `File` | `ReadWriteSeekerInterface` | File-backed stream opened via `FileMode` |
| `LimitedReader` | `ReaderInterface` | Limits reads to a fixed byte budget |
| `BinaryReader` | — | Typed binary reads over any `ReaderInterface` |
| `BinaryWriter` | — | Typed binary writes over any `WriterInterface` |
## BinaryReader methods
Integer names follow `read{Signedness}{Bits}{Endianness}`:
| Method | Size | Range |
|---|---|---|
| `readUInt8()` | 1 byte | 0255 |
| `readInt8()` | 1 byte | 128127 |
| `readUInt16LE()` / `readUInt16BE()` | 2 bytes | 065535 |
| `readInt16LE()` / `readInt16BE()` | 2 bytes | 3276832767 |
| `readUInt32LE()` / `readUInt32BE()` | 4 bytes | 04294967295 |
| `readInt32LE()` / `readInt32BE()` | 4 bytes | 21474836482147483647 |
| `readPaddedString(int $length, string $pad_chars)` | `$length` bytes | strips trailing `$pad_chars` |
`readExact(int $length)` reads exactly `$length` bytes and throws `RuntimeException` if fewer are available.
## BinaryWriter methods
Integer names follow `write{Signedness}{Bits}{Endianness}`. All methods return bytes written.
| Method | Size |
|---|---|
| `writeUInt8()` / `writeInt8()` | 1 byte |
| `writeUInt16LE()` / `writeUInt16BE()` / `writeInt16LE()` / `writeInt16BE()` | 2 bytes |
| `writeUInt32LE()` / `writeUInt32BE()` / `writeInt32LE()` / `writeInt32BE()` | 4 bytes |
| `writePaddedString(string $data, int $length, string $pad_char)` | `$length` bytes |
## Exceptions
- `Shufflingpixels\IO\Exception\IOException`: generic stream/file I/O failures
- `Shufflingpixels\IO\Exception\EndOfStreamException`: not enough bytes available when reading
- `Shufflingpixels\IO\Exception\IOException` generic stream/file I/O failures
- `Shufflingpixels\IO\Exception\EndOfStreamException` — subclass of `IOException`
## Running Tests
This package uses Pest.
```bash
composer test
```