Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Binary Stream Laravel Package

wapmorgan/binary-stream

A PHP library for reading and parsing binary data from streams and files, with convenient typed readers for integers, strings, and custom structures, plus seeking and position tracking for binary file formats.

View on GitHub
Deep Wiki
Context7

Getting Started

wapmorgan/binary-stream is a standalone PHP library — there is no service provider, facade, or config file to publish. Install it with Composer and use the reader/writer classes directly anywhere in your app (controllers, console commands, jobs, service classes):

composer require wapmorgan/binary-stream

Two entry points matter:

  • wapmorgan\BinaryStream\BinaryStreamReader — opens a file and reads typed fields sequentially.
  • wapmorgan\BinaryStream\BinaryStreamWriter — builds a binary payload field by field.

The README's type table (int8/uint8, int16/uint16, int32/uint32, int64/uint64, float, double, byte, char, string:N, cstring, …) is the source of truth for what you can pass to read()/write(). Everything else is a thin wrapper around those type strings.

First use case — parsing the header of a binary file an admin has uploaded, inside a queued job or artisan command:

use wapmorgan\BinaryStream\BinaryStream;
use wapmorgan\BinaryStream\BinaryStreamReader;
use wapmorgan\BinaryStream\BinaryStreamException;

try {
    $reader = new BinaryStreamReader(storage_path('app/imports/firmware.bin'));
    $reader->setEndian(BinaryStream::LITTLE_ENDIAN);

    $signature = $reader->read('string:4');   // "FW01"
    $version   = $reader->read('uint16');
    $recordCnt = $reader->read('uint32');

    // ... loop over records, then:
    $reader->goToEnd();
} catch (BinaryStreamException $e) {
    report($e); // truncated / malformed file
}

Start by opening a small fixture file, reading one field at a time, and dumping values with dd() to confirm endianness and type sizes before writing the real parser.

Implementation Patterns

Keep the layout in one place. Define your packet/file layout once as a class constant array of offset => [type, name] or as a "group" the library can save and reuse (saveGroup() / group() in the reader). Your writer should replay the same map, so read and write can never drift apart. This is the single biggest win over scattering pack('v', ...) / unpack('V', ...) calls through the codebase.

Wrap parsing in a dedicated service + DTO. Don't let BinaryStreamReader leak into controllers. Create e.g. App\Binary\FirmwareHeaderParser::parse(string $path): FirmwareHeader where FirmwareHeader is a readonly/typed value object. Throw a domain-specific InvalidFirmwareException that wraps BinaryStreamException. This keeps the third-party API (which is old and unmaintained) at one replaceable seam.

Bridge Laravel storage to the reader. The reader wants a local, seekable file. For S3 or uploaded files:

$tmp = tempnam(sys_get_temp_dir(), 'bs_');
file_put_contents($tmp, Storage::disk('s3')->get($key));
try {
    $reader = new BinaryStreamReader($tmp);
    // ...
} finally {
    @unlink($tmp);
}

For very large files, prefer a chunked job: read a fixed-size record, dispatch a child job with the decoded record, then goTo($recordStart + $recordSize) and continue — rather than materialising the whole file into an array.

Generate binary outbound payloads with the writer, not with string concatenation. This is where the library pays for itself on things like WebSocket frames, custom TCP/UDP payloads, .ico/font/archive building, or hardware protocol simulators:

$writer = new BinaryStreamWriter($path);
$writer->setEndian(BinaryStream::BIG_ENDIAN);
$writer->write('string:4', 'FW01');
$writer->write('uint16', $version);
$writer->write('uint32', count($records));
$writer->save();

Use it in tests as a fixture generator. Round-trip tests are the cheapest correctness check you'll get: write a payload with BinaryStreamWriter, parse it with BinaryStreamReader, assert the DTO equals the original. Keep small .bin fixtures in tests/fixtures/ under version control rather than generating everything at runtime.

Custom types are the extension point. Built-in types won't cover things like BCD, packed bitfields, or length-prefixed arrays of structs. Look at the Types source directory and mirror an existing type's read/write contract rather than trying to shoehorn string/int combos into your layout — the latter quickly becomes unreadable.

Gotchas and Tips

  • Unmaintained since 2017. There's a real chance of PHP 8.1+/8.2+ deprecation noise (implicit nullable params, dynamic properties) and no upstream fixes. Run your test suite on the target PHP version before adopting; pin the version and be prepared to vendor/fork the few classes you actually use.
  • Set endianness explicitly, every time. Don't rely on the default — different formats disagree (PNG is big-endian, RIFF/BMP is little-endian, most network protocols are big-endian). Put setEndian() on the same line as the constructor so it's impossible to miss during review.
  • string:N is bytes, not characters. Multi-byte encodings will bite you; convert with mb_convert_encoding($raw, 'UTF-8', 'Windows-1252') after reading if the source is legacy. cstring vs string:N differ in null-termination and padding — off-by-one errors here usually surface as a shifted record count, not an obvious failure.
  • Seekability matters. goTo(), goToEnd(), and getPosition() need a seekable stream. php://input and some HTTP/S3 streams are not — spool to php://temp or a temp file first.
  • 64-bit integers on 32-bit PHP silently overflow. If you're reading uint64/int64 on a 32-bit runtime, cast to string and use bcmath/gmp for arithmetic.
  • Guard against truncation. Wrap every parse loop in a while ($reader->getPosition() < $reader->getLength()) check (or track expected record count from the header) so a malformed upload fails fast with your domain exception instead of reading zeroed/garbage bytes.
  • Bits are on you. Bit-level flags (a byte of booleans) aren't a first-class type in this library on most versions; do the masking ($flags & 0x01, ($flags >> 1) & 0x03) in a small helper and unit-test it separately.
  • float is 32-bit, double is 64-bit. Pick deliberately for sensor/telemetry formats; a mismatch produces plausible-looking but wrong numbers rather than errors.
  • Untrusted input = DoS surface. A hostile header claiming ten billion records will happily loop the CPU. Cap record counts and file sizes before you start parsing, and run it in a queued job with a timeout.
  • No Laravel-isms. There's no config to tweak, no facade, no auto-discovery. That's arguably a feature — treat it as a pure library and keep it behind your own App\Binary\ namespace, which also makes it trivial to swap for pack()/unpack() later if the package goes stale.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings