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

Stream Laravel Package

hoa/stream

Hoa\Stream provides an object-oriented abstraction layer for PHP streams, wrapping resources with consistent APIs for reading, writing, filtering, and context handling. It simplifies working with files, sockets, and custom stream wrappers while preserving native stream behavior.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install (the package is archived and pins old Hoa siblings):

    composer require hoa/stream:1.17.02.21
    

    You pull in hoa/consistency, hoa/event, hoa/exception, hoa/protocol, and hoa/zformat. Require vendor/autoload.php and confirm Hoa\Stream\Stream is autoloadable before doing anything else — if Hoa's own Hoa\Consistency\Autoloader also registers itself you will get double-registration warnings.

  2. Read the right files first. Almost everything you need is in four places:

    • README.md — since 1.17.02.21 this actually exists and includes a "Quick usage" section, a listener section, and a dedicated stream context walkthrough. Start here; it is the only onboarding doc the package has ever had.
    • Hoa\Stream\Stream — the abstract base (_open() / _close() template, event firing, getStream(), deferred-open handling).
    • Hoa\Stream\IStream\* — the capability interfaces (In, Out, Pathable, Pointable, Lockable, Bufferable, Touchable, Statable, Structural). These tell you which methods a concrete stream guarantees.
    • Hoa\Stream\Wrapper\Wrapper and Hoa\Stream\Filter\Filter — the two static "manager" classes you will actually call directly.
    • Test/Unit/… — as of this release the suite is essentially complete (every interface, Wrapper, Filter, Filter\Basic, Filter\LateComputed, Context, Composite, Bucket). It is now the best source of runnable examples for anything the README glosses over.
  3. First use case: stop instantiating Stream yourself. Day-to-day you use the concrete subclasses in sibling packages (Hoa\File\Read, Hoa\File\Write, Hoa\Socket\Client, Hoa\Database\Dal). The realistic first direct use of this package is registering a stream wrapper:

    use Hoa\Stream\Wrapper\Wrapper;
    
    if (false === Wrapper::get('store')) {
        Wrapper::register('store', App\Streams\StoreWrapper::class);
    }
    $fp = fopen('store://bucket/key.txt', 'rb');
    // ...
    Wrapper::restore('store'); // put the original wrapper back
    
  4. First custom class. If you need a domain resource that behaves like a stream, extend Hoa\Stream\Stream, implement the IStream interfaces you support, and implement:

    protected function &_open($streamName, $streamPort = null, $context = null, $bucket = null)
    {
        $resource = fopen($this->resolve($streamName), 'rb');
        if (false === $resource) {
            throw new \Hoa\Stream\Exception('Cannot open '.$streamName.'.');
        }
        return $resource; // by reference
    }
    
    protected function _close()
    {
        return fclose($this->getStream());
    }
    

    Never hand a falsy or already-closed resource to _setStream() — since 1.17.02.21 that is rejected outright (see Gotchas). From there $stream->on('open', …) and $stream->on('close', …) give you observers for free, and getStream() hands the raw resource to native f*() functions when you need an escape hatch.

Implementation Patterns

  • Wrapper registration as the integration seam. Wrapper::register($scheme, $className) is the clean way to expose S3, Redis, encrypted files, or an in-memory store to the rest of a Hoa-based stack. The wrapper class must be autoloadable before the first fopen() — you cannot pass a closure. Pair every register() with Wrapper::unregister($scheme) or Wrapper::restore($scheme); both are now quiet on failure, so branch on the boolean.

  • Subclass Stream for domain objects, not for file I/O. This is the pattern hoa/database and hoa/socket use: a Dal/Client class extends Stream, opens a socket or PDO handle inside _open(), and exposes typed methods on top. You get lifecycle events and context handling without reimplementing them.

  • Use the deferred-open escape hatch. If _open() throws during construction, Stream can defer and retry the open on first real use instead of blowing up in the constructor. Probe it with the new hasBeenDeferred() method before assuming the resource exists — this is the correct guard in service providers and container bindings where you want lazy connection semantics.

  • Compose, don't inherit, for mixed capabilities. If a resource is read/write but only sometimes seekable, implement In, Out, Pathable on your class and use Hoa\Stream\Composite when you need to graft two streams together (for example a read stream and a write stream behind one API). Keep _open() doing exactly one thing so composites stay tractable.

  • Filter pipelines. Filters are attached by stream name, not by object:

    use Hoa\Stream\Filter\Filter;
    
    Filter::register('upper', App\Filter\Uppercase::class);
    Filter::append('store://bucket/key.txt', 'upper', [], Filter::MODE_READ);
    

    The filter class implements filter($in, $out, &$consumed, $closing) and static onCreate() / onClose() hooks. Hoa\Stream\Filter\Basic and LateComputed cover the common "transform each chunk" and "buffer until complete" cases. Always Filter::remove($streamName, 'upper') in a finally — the registration is process-global.

  • Event wiring. $stream->on('open', function (\Hoa\Event\Bucket $bucket) { … }) is the idiomatic hook for logging, metrics, and audit trails. Stream events are namespaced under hoa://Event/Stream/…, so global listeners registered through Hoa\Event\Event::getEvent() will see them too. Use the bucket's getData() for the payload and getSource() to reach the stream. The README now documents this listener flow end to end.

  • Contexts for shared options. Hoa\Stream\Context::getInstance($id) wraps stream_context_create() and lets multiple streams share TLS, HTTP headers, or proxy settings. Set options once via setOptions() rather than duplicating arrays at each call site. The new README section (with the php.net/context.http link) is the fastest way to get the option-array shape right for HTTP/SSL.

  • Protocol-relative names. Hoa\Stream\Stream resolves names beginning with hoa:// through Hoa\Protocol\Protocol, so hoa://Data/cache/x works across dev and prod. Prefer this over hardcoded paths if you are already inside the Hoa ecosystem; use plain paths otherwise.

  • Testing pattern. Extend Stream and return fopen('php://memory', 'w+b') from _open(). That gives you a real resource with real semantics, so readLine(), seek(), and eof() behave as in production without touching the filesystem. Mirror the upstream suite's structure — it is now complete enough to copy from directly.

Gotchas and Tips

  • Archived since 2017 and effectively unmaintained. 1.17.02.21 is a docs/tests/hardening release, not a revival. Do not build new features on it. If you inherit a hoa/* codebase, isolate Hoa\Stream behind your own interfaces so the migration to native streams, Flysystem, or Symfony components is a one-file change.

  • PHP 8 compatibility is not guaranteed. The Hoa family predates PHP 7.4 typed properties and 8.0 union types; expect deprecations and possibly hard failures. Pin to a PHP 7.x runtime, or vendor/fork the file and patch signatures before upgrading PHP.

  • Composer versioning is date-based and the scheme shifted. Tags in this era look like 1.17.02.21 (older lines used 3.17.01.10), and the sibling constraints are unusual (~3.0, not ^3). Do not assume semver: a composer update can move all five Hoa packages at once. Lock the versions you tested.

  • _setStream() now rejects invalid resources. Since this release, passing false, null, or a closed resource throws instead of silently producing a broken stream object. Funnel every fopen() through an explicit if (false === $resource) { throw … } before returning from _open(), and re-check is_resource() after any operation that can close the handle.

  • Buffer size is reset on re-open. setStreamBuffer($size) no longer survives a close() / open() cycle — the stream falls back to the default. Re-apply your buffer size in an open listener (or right after the reopen call) if you depend on it, and flush() before closing.

  • Wrapper::unregister() and Wrapper::restore() are now silent. This release explicitly suppresses error reporting from both. That means a failed restore no longer prints a PHP warning — it just returns false. Check the return value in every finally block, otherwise a leaked wrapper cascades into unrelated test failures.

    try {
        Wrapper::register('store', App\Streams\StoreWrapper::class);
        // ...
    } finally {
        Wrapper::restore('store'); // returns bool — check it in debugging
    }
    
  • Wrapper::register() and Wrapper::restore() are process-global. Tests leak wrappers between cases. Re-registering an existing scheme fails — call Wrapper::restore($scheme) first, or check Wrapper::get($scheme) and skip registration. There is no "unregister just this one" API beyond unregister()/restore() per scheme.

  • Wrapper exception messages now carry more context. Registration/restore failures name the scheme and offending wrapper class. When you see one, Wrapper::get($scheme) returns the currently active class — usually the fastest way to spot a sibling package that grabbed your scheme first.

  • Filters have a dedicated exception type. Filter creation/processing errors now throw Hoa\Stream\Filter\Exception rather than bubbling raw \Exceptions or warnings. Catch that specific class around Filter::append() / first read, and still Filter::remove() in finally — a leaked filter corrupts every subsequent read on that exact stream name.

  • Filters attach by stream name, not instance. A filter appended for file:///a.txt will fire for every stream opened with that exact name. Prefer unique, absolute stream names in filter code paths.

  • hoa:// resolution needs Hoa\Protocol. If the protocol layer is not bootstrapped, a hoa:// name fails inside _open() with a somewhat opaque exception. Debug by resolving the name manually before opening, or switch to a plain path.

  • Reading after close usually throws Hoa\Stream\Exception rather than returning false. Do not carry a stream object past its close() and rely on defensive fread()-style checks — you will get exceptions where native code returns falsy values.

  • readInteger() and readBoolean() are opinionated parsers (base handling, 'true'/'false'/'1'/'0'). If you need byte-exact behaviour, cast getStream() and use fread()/fgets() directly.

  • Locking is advisory and wrapper-dependent. Lockable::lock() returns false for wrappers that do not support flock. Always check the return value and the by-reference $wouldBlock argument; do not treat a true return as an OS-level guarantee.

  • Debugger toolbox:

    stream_get_wrappers();                                  // confirm registration
    stream_get_filters();                                   // confirm filter registration
    stream_get_meta_data($stream->getStream());            // wrapper, mode, seekable, timed_out
    stream_context_get_options($stream->getStreamContext());
    $stream->hasBeenDeferred();                             // did _open() ever actually run?
    Wrapper::get($scheme);                                  // which class owns this scheme right now
    

    For deeper issues, Wrapper::get($scheme) and hasBeenDeferred() are usually the first two things to inspect when fopen() returns false with no warning or a stream looks empty on first read.

  • Extension points worth knowing. Hoa\Stream\Exception gives you a single catch type from _open(); Hoa\Stream\Filter\Exception does the same for filter failures; overriding _close() is the right place for flush-then-close logic; and adding domain methods (e.g. fetchAll(), sendCommand()) directly on your subclass is the intended way to grow the API without touching the base.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package