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:
parent
a27c0a87ea
commit
07ec405512
25 changed files with 451 additions and 1168 deletions
124
README.md
124
README.md
|
|
@ -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 | 0–255 |
|
||||
| `readInt8()` | 1 byte | −128–127 |
|
||||
| `readUInt16LE()` / `readUInt16BE()` | 2 bytes | 0–65535 |
|
||||
| `readInt16LE()` / `readInt16BE()` | 2 bytes | −32768–32767 |
|
||||
| `readUInt32LE()` / `readUInt32BE()` | 4 bytes | 0–4294967295 |
|
||||
| `readInt32LE()` / `readInt32BE()` | 4 bytes | −2147483648–2147483647 |
|
||||
| `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
|
||||
```
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue