Search by

nolikein / stream-manager

Nolikein

A stream library to manage files and URIs for SAPI and non-SAPI systems. It follows PSR-7 and PSR-17

Package info

gitlab.com/Come-Wasik/php-stream-manager

Issues

pkg:composer/nolikein/stream-manager

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

2.0.0 2026-11-08 16:23 UTC

This package is auto-updated.

Last update: 2026-11-08 23:06:52 UTC


README

PHP Packagist version Downloads License Pipeline Coverage

Overview

A PHP library to manage streams (files, URIs, data in memory) for SAPI and non-SAPI systems. It implements PSR-7 (StreamInterface) and PSR-17 (StreamFactoryInterface).

Requirements

  • PHP 8.4 or later
  • psr/http-message 2.x and psr/http-factory 1.1 or later (installed by Composer)

Installation

composer require nolikein/stream-manager ^2.0

Usage

A stream is created by the factory or by hand. Use the factory in most cases; create the stream by hand when you want to choose whether the resource is closed when the stream is destroyed (by default it is).

Creation

By factory

use Nolikein\Stream\StreamFactory;

$factory = new StreamFactory();
$stream = $factory->createStream('my stream content');
$stream = $factory->createStreamFromFile(__DIR__ . '/myfile.json', 'r+');
$stream = $factory->createStreamFromResource(fopen(__DIR__ . '/myfile.json', 'r+'));

Manually

The library provides Stream and its child FileStream.

Stream
use Nolikein\Stream\Stream;

$stream = new Stream();

// attach() throws InvalidStreamResource when the value is not an open stream resource
$stream->attach(fopen(__DIR__ . '/myfile.json', 'r+'));
$stream->attach(fopen('php://temp', 'r+'), false);
$stream->attach(STDOUT, false); // Do not close STDOUT

Attaching a resource closes the one which was attached before, unless it was attached as not closable. Attaching the same resource again does not close it and only updates its closable flag.

FileStream
use Nolikein\Stream\FileStream;

$stream = new FileStream(__DIR__ . '/myfile.json', 'r+');

// Open another file in the same stream: the previous file is closed
$stream->attachFile(__DIR__ . '/other.json', 'r+');

A relative path is relative to the working directory. The path can also be the URL of a PHP stream wrapper such as php://temp.

Using a stream

if ($stream->isWritable()) {
    $stream->write('message');

    if ($stream->isSeekable()) {
        // Go back to the beginning of the stream
        $stream->rewind();
    }
}

if ($stream->isReadable()) {
    // Read 3 bytes
    $part = $stream->read(3);

    if ($stream->tell() === 3) {
        // The position is after the third byte
    }

    // Read what is left, up to the end of the stream
    $rest = $stream->getContents();

    if ($stream->eof()) {
        // The position is at the end of the stream
    }

    // The whole content, from the beginning when the stream is seekable
    echo $stream;
}

// The size in bytes, or null when it is unknown (not seekable stream, no resource)
$size = $stream->getSize();

// All the metadata, or one entry (null when the key is unknown)
$metadata = $stream->getMetadata();
$mode = $stream->getMetadata('mode');

// Get the resource and leave the stream without resource
$resource = $stream->detach();

// Attach a resource again
$stream->attach($resource);

// Close the resource (when it is closable) and leave the stream without resource
$stream->close();

On a closed or detached stream getSize() returns null, eof() returns true, isSeekable(), isReadable() and isWritable() return false, getMetadata() returns an empty array (null for a key), detach() returns null and close() does nothing. Any other method throws StreamAlreadyClosed.

Exceptions

Every exception thrown by the library implements Nolikein\Stream\Exception\StreamExceptionInterface, so one catch handles all of them. They extend the matching PHP class: StreamInvalidArgumentException (\InvalidArgumentException) for a refused value, StreamRuntimeException (\RuntimeException) for a failure at runtime. The default code is 500.

ExceptionFamilyThrown when
InvalidStreamResourceinvalid argumentattach() or createStreamFromResource() receives something other than an open stream resource
InvalidFileModeinvalid argumentthe file mode is not supported (FileStream, createStreamFromFile())
InvalidFilePathinvalid argumentthe path of a file contains a NUL byte
InvalidReadLengthinvalid argumentread() receives a negative length
StreamAlreadyClosedruntimean operation needs a resource and the stream is closed or detached
UnreadableStreamruntimeread() or getContents() on a stream which is not readable
UnwritableStreamruntimewrite() on a stream which is not writable
UnseekableStreamruntimeseek() or rewind() on a stream which is not seekable
DirectoryNotFoundruntimethe directory of the file does not exist
CannotOpenFileruntimethe file cannot be opened
StreamOperationFailedruntimetell(), seek(), read(), write() or getContents() fails on a valid stream

Development

Every PHP command runs in a Docker container: you only need Docker and Docker Compose.

docker compose build
docker compose run --rm php composer install
docker compose run --rm php composer qa              # code style, static analysis (PHPStan level max), tests
docker compose run --rm php composer test:coverage   # tests with the coverage report (Xdebug)
PHP_VERSION=8.5 docker compose run --rm php composer qa   # another PHP version (default: 8.4)

The container runs as 1000:1000 by default. If your user ids differ, write them in a .env file at the root of the project (UID=... and GID=...).

Links