PHPackages                             stream-interop/interface - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [PSR &amp; Standards](/categories/psr-standards)
4. /
5. stream-interop/interface

ActiveLibrary[PSR &amp; Standards](/categories/psr-standards)

stream-interop/interface
========================

Interoperable interfaces for streams in PHP 8.4 and later.

1.0.1(2mo ago)1010212MITPHPPHP &gt;=8.4CI passing

Since Jan 19Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/stream-interop/interface)[ Packagist](https://packagist.org/packages/stream-interop/interface)[ Docs](https://github.com/stream-interop/interface)[ RSS](/packages/stream-interop-interface/feed)WikiDiscussions 1.x Synced 1mo ago

READMEChangelog (6)Dependencies (8)Versions (9)Used By (2)

Stream-Interop Standard Interface Package
=========================================

[](#stream-interop-standard-interface-package)

[![PDS Skeleton](https://camo.githubusercontent.com/50d01a5094afcc3a827c3cadaec43d23b2a256cb249f5fdd6e5ffdb53ea7971c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7064732d736b656c65746f6e2d626c75652e7376673f7374796c653d666c61742d737175617265)](https://github.com/php-pds/skeleton)[![PDS Composer Script Names](https://camo.githubusercontent.com/0c17df07fd0a51ad878f1de0d4c17ea8e460f2e96ce796c8cd58e6c96ed9c08d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7064732d636f6d706f7365722d2d7363726970742d2d6e616d65732d626c75653f7374796c653d666c61742d737175617265)](https://github.com/php-pds/composer-script-names)

Stream-Interop provides an interoperable package of standard interfaces for working with stream resources in PHP 8.4 or later. It reflects, refines, and reconciles the common practices identified within [several pre-existing projects](./README-RESEARCH.md).

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [BCP 14](https://datatracker.ietf.org/doc/bcp14/) ([RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119), [RFC 8174](https://datatracker.ietf.org/doc/html/rfc8174)).

This package attempts to adhere to the [Package Development Standards](https://php-pds.com/) approach to [naming and versioning](https://php-pds.com/#naming-and-versioning).

Interfaces
----------

[](#interfaces)

Stream-Interop defines separate interfaces for various affordances around stream resources so that (1) implementations can advertise well-tailored affordances, and (2) consumers can typehint to the specific affordances they require for specific situations.

- [*Stream*](#stream) is a common baseline for streams.
- [*ResourceStream*](#resourcestream) extends [*Stream*](#stream) to afford direct access to the encapsulated resource.
- [*ClosableStream*](#closablestream) extends [*Stream*](#stream) to afford closing the stream.
- [*SizableStream*](#sizablestream) extends [*Stream*](#stream) to afford getting the full length of the stream in bytes.
- [*ReadableStream*](#readablestream) extends [*Stream*](#stream) to afford non-idempotent reading from the stream.
- [*SeekableStream*](#seekablestream) extends [*Stream*](#stream) to afford moving the stream pointer position back and forth.
- [*StringableStream*](#stringablestream) extends [*Stream*](#stream) to afford idempotent reading from the encapsulated resource.
- [*WritableStream*](#writablestream) extends [*Stream*](#stream) to afford writing to the stream at the current pointer position.
- [*ReadonlyStream*](#readonlystream) is a marker interface that extends [*Stream*](#stream) to indicate the implementation attempts to enforce readonly constraints on the encapsulated resource.
- [*ImmutableStream*](#immutablestream) is a marker interface that extends [*ReadonlyStream*](#readonlystream) to indicate the implementation attempts to enforce immutability constraints on the encapsulated resource.
- [*StreamThrowable*](#streamthrowable) is a marker interface that extends [*Throwable*](https://php.net/Throwable) to indicate an [*Exception*](https://php.net/Exception) is stream-related.
- [*StreamTypeAliases*](#streamtypealiases) defines custom PHPStan types to aid static analysis.

### *Stream*

[](#stream)

[*Stream*](#stream) is a common baseline for streams.

- Notes:

    - **The `$metadata` property is expected to change dynamically.** That is, as the encapsulated resource gets read from and written to, the metadata for that resource is likely to change. Thus, the `$metadata` property value is expected to change along with it. In practical terms, this likely means a [`stream_get_meta_data()`](https://php.net/stream_get_meta_data)call on each access of `$metadata`.
    - **There are no `isReadable()`, etc. methods.** If necessary, such functionality can be determined by typehinting against the interface, or by checking `instanceof`, etc.
    - **The encapsulated resource is not exposed publicly here.** The encapsulated resource may remain private or protected. See the [*ResourceStream*](#resourcestream) interface for details on making the encapsulated resource publicly accessible.

#### *Stream* Properties

[](#stream-properties)

- ```
    public metadata_array $metadata { get; }
    ```

    - Represents the metadata for the encapsulated resource as if by [`stream_get_meta_data()`](https://php.net/stream_get_meta_data).
    - Directives:

        - Implementations MUST provide the most-recent metadata for the encapsulated resource at the moment of property access; if the encapsulated resource is closed, implementations MUST return an empty array.
        - Implementations MUST NOT allow `$metadata` to be publicly settable, either as a property or via property hook or method.

#### *Stream* Methods

[](#stream-methods)

- ```
    public function isClosed() : bool;
    ```

    - Returns `true` if the encapsulated resource has been closed, or `false` if not.
- ```
    public function isOpen() : bool;
    ```

    - Returns `true` if the encapsulated resource is still open, or `false` if not.

### *ResourceStream*

[](#resourcestream)

[*ResourceStream*](#resourcestream) extends [*Stream*](#stream) to afford direct access to the encapsulated resource.

- Notes:

    - **Not all [*Stream*](#stream) implementations need to expose the encapsulated resource.** Exposing the resource gives full control over it to consumers, who can then manipulate it however they like (e.g. close it, move the pointer, and so on). However, having access to the resource may be necessary for some consumers.
    - **Some [*Stream*](#stream) implementations might not encapsulate a resource.**Although a resource is the most common data source for a stream, other data sources may be used, in which cases [*ResourceStream*](#resourcestream)implementation is neither appropriate nor necessary.

#### *ResourceStream* Properties

[](#resourcestream-properties)

- ```
    public resource $resource { get; }
    ```

    - Represents the resource as if opened by [`fopen()`](https://php.net/fopen), [`fsockopen()`](https://php.net/fsockopen), [`popen()`](https://php.net/popen), etc.
    - Directives:

        - Implementations MUST ensure `$resource` is a `resource of type (stream)`; for example, as determined by [`get_resource_type()`](https://php.net/get_resource_type).
        - Implementations SHOULD NOT allow `$resource` to be publicly settable, either as a property or via property hook or method.

### *ClosableStream*

[](#closablestream)

[*ClosableStream*](#closablestream) extends [*Stream*](#stream) to afford closing the stream.

- Directives:

    - Implementations MAY close the encapsulated resource internally without affording [*ClosableStream*](#closablestream).
- Notes:

    - **Not all [*Stream*](#stream) implementations need to be closable.** It may be important for resource closing to be handled by a separate service or authority, and not be closable by [*Stream*](#stream) consumers.

#### *ClosableStream* Methods

[](#closablestream-methods)

- ```
    public function close() : void;
    ```

    - Closes the encapsulated resource as if by [`fclose()`](https://php.net/fclose), [`pclose()`](https://php.net/pclose), etc.
    - Directives:

        - Implementations MUST throw a [*StreamThrowable*](#streamthrowable) on failure.

### *SizableStream*

[](#sizablestream)

[*SizableStream*](#sizablestream) extends [*Stream*](#stream) to afford getting the full length of the stream in bytes.

- Directives:

    - Implementations MAY get the size of the encapsulated resource internally without affording [*SizableStream*](#sizablestream).
- Notes:

    - **Not all [*Stream*](#stream) implementations need to be sizable.** Some encapsulated resources may be unable to report a size; for example, remote or write-only resources.

#### *SizableStream* Methods

[](#sizablestream-methods)

- ```
    public function getSize() : ?int;
    ```

    - Returns the length of the encapsulated resource in bytes as if by the [`fstat()`](https://php.net/fstat) value for `size`, or `null` if indeterminate or on error.

### *ReadableStream*

[](#readablestream)

[*ReadableStream*](#readablestream) extends [*Stream*](#stream) to afford non-idempotent reading from the stream.

- Directives:

    - If the encapsulated resource is not readable at the time it becomes available to the [*ReadableStream*](#readablestream), implementations MUST throw a [*StreamThrowable*](#streamthrowable).
    - Implementations MAY read from the encapsulated resource internally without affording [*ReadableStream*](#readablestream).
- Notes:

    - **The `eof()` method is on [*ReadableStream*](#readablestream), not [*Stream*](#stream) or [*SeekableStream*](#seekablestream).** End-of-file is determined as a function of reading past the end of the file, not as of seeking to the end of the file. Cf. .
    - **These methods are non-idempotent.** They may return different results on repeated sequential calls, and may have side effects (e.g., changing the position of the pointer.)

#### *ReadableStream* Methods

[](#readablestream-methods)

- ```
    public function eof() : bool;
    ```

    - Tests for end-of-file on the encapsulated resource as if by [`feof()`](https://php.net/feof).
    - Directives:

        - Implementations MUST throw a [*StreamThrowable*](#streamthrowable) on failure.
- ```
    public function read(int $length) : string;
    ```

    - Returns up to `$length` bytes from the encapsulated resource as if by [`fread()`](https://php.net/fread).
    - Directives:

        - Implementations MUST throw a [*StreamThrowable*](#streamthrowable) on failure.
- ```
    public function getContents() : string;
    ```

    - Returns the remaining contents of the resource from the current pointer position as if by [`stream_get_contents()`](https://php.net/stream_get_contents).
    - Directives:

        - Implementations MUST throw a [*StreamThrowable*](#streamthrowable) on failure.

### *SeekableStream*

[](#seekablestream)

[*SeekableStream*](#seekablestream) extends [*Stream*](#stream) to afford moving the stream pointer position back and forth.

- Directives:

    - If the encapsulated resource is not seekable at the time it becomes available to the [*SeekableStream*](#seekablestream), implementations MUST throw a [*StreamThrowable*](#streamthrowable).

#### *SeekableStream* Methods

[](#seekablestream-methods)

- ```
    public function rewind() : void;
    ```

    - Moves the stream pointer position to the beginning of the stream as if by [`rewind()`](https://php.net/rewind).
    - Directives:

        - Implementations MUST throw a [*StreamThrowable*](#streamthrowable) on failure.
- ```
    public function seek(
        int $offset,
        SEEK_CUR|SEEK_SET|SEEK_END $whence = SEEK_SET,
    ) : void;
    ```

    - Moves the stream pointer position to the `$offset` as if by [`fseek()`](https://php.net/fseek).
    - Directives:

        - Implementations MUST throw a [*StreamThrowable*](#streamthrowable) on failure.
- ```
    public function tell() : int;
    ```

    - Returns the current stream pointer position as if by [`ftell()`](https://php.net/ftell).
    - Directives:

        - Implementations MUST throw a [*StreamThrowable*](#streamthrowable) on failure.

### *StringableStream*

[](#stringablestream)

[*StringableStream*](#stringablestream) extends [*Stream*](#stream) to afford idempotent reading from the encapsulated resource.

- Directives:

    - If the encapsulated resource is not readable at the time it becomes available to the [*StringableStream*](#stringablestream), implementations MUST throw a [*StreamThrowable*](#streamthrowable).
    - If the encapsulated resource is not seekable at the time it becomes available to the [*StringableStream*](#stringablestream), implementations MUST throw a [*StreamThrowable*](#streamthrowable).
    - Implementations MAY convert all or part of the encapsulated resource to a string internally without affording [*StringableStream*](#stringablestream).
- Notes:

    - **These methods are idempotent.** Repeated sequential calls to an unchanged resource will return the exact same result, without exposing side effects (such as the pointer position being changed).

#### *StringableStream* Methods

[](#stringablestream-methods)

- ```
    public function __toString() : string;
    ```

    - Returns the entire contents of the encapsulated resource as if by [`rewind()`](https://php.net/rewind)ing before returning [`stream_get_contents()`](https://php.net/stream_get_contents).
    - Directives:

        - After reading, implementations MUST reposition the encapsulated resource pointer to its initial location.
        - Implementations MUST throw a [*StreamThrowable*](#streamthrowable) on failure.
- ```
    public function subString(int $offset, ?int $length = null) : string;
    ```

    - Returns a string from the encapsulated resource as if by [`fseek()`](https://php.net/fseek)ing before reading.
    - Directives:

        - If the `$offset` is negative, implementations MUST begin reading at that many bytes from the end of the stream; otherwise, implementations MUST begin reading at that many bytes from the start of the stream.
        - If the `$length` is null, implementations MUST return all remaining bytes from the stream; otherwise, implementations MUST return up to that many bytes from the stream.
        - After reading, implementations MUST reposition the encapsulated resource pointer to its initial location.
        - Implementations MUST throw a [*StreamThrowable*](#streamthrowable) on failure.

### *WritableStream*

[](#writablestream)

[*WritableStream*](#writablestream) extends [*Stream*](#stream) to afford writing to the stream at the current pointer position.

- Directives:

    - If the encapsulated resource is not writable at the time it becomes available to the [*WritableStream*](#writablestream), implementations MUST throw a [*StreamThrowable*](#streamthrowable).
    - Implementations MAY write to the encapsulated resource internally without affording [*WritableStream*](#writablestream).

#### *WritableStream* Methods

[](#writablestream-methods)

- ```
    public function write(string|Stringable $data) : int;
    ```

    - Writes `$data` starting at the current stream pointer position, returning the number of bytes written, as if by [`fwrite()`](https://php.net/fwrite).
    - Directives:

        - Implementations MUST throw a [*StreamThrowable*](#streamthrowable) on failure.

### *ReadonlyStream*

[](#readonlystream)

[*ReadonlyStream*](#readonlystream) is a marker interface that extends [*Stream*](#stream) to indicate the implementation attempts to enforce readonly constraints on the encapsulated resource.

- Directives:

    - Implementations MUST open the encapsulated resource inside the [*ReadonlyStream*](#readonlystream).
    - Implementations MUST open the encapsulated resource as `php://input`or `php://memory`.
    - Implementations MAY open the encapsulated resource in a mode that allows writing (`rb+`, `w+`, etc.) to allow initialization.
    - Implementations MAY initialize the encapsulated resource after opening (e.g., by copying a constructor argument to the encapsulated resource).
    - Implementations MUST NOT modify, or allow modification of, the encapsulated resource content after initialization, whether by implementing [*WritableStream*](#writablestream) or by some other means.
    - Implementations MUST NOT expose the encapsulated resource, whether by implementing [*ResourceStream*](#resourcestream) or by some other means.
    - Implementations MAY allow closing of the encapsulated resource, whether by implementing [*ClosableStream*](#closablestream) or by some other means.
- Notes:

    - **The readonly constraints are necessarily strict.** Whereas readonly on scalar and array properties can be implemented relatively easily, readonly on a resource property is more difficult. The encapsulated resource, including both its content and its pointer, must be inaccessible from outside the [*ReadonlyStream*](#readonlystream) to ensure it cannot be modified from outside the [*ReadonlyStream*](#readonlystream).
    - **[*ReadonlyStream*](#readonlystream) implementations may be memory-intensive.** This is because they usually have to be initialized with a copy of the original resource, typically a file resource, thereby reading all of it into a `php://memory` resource.
    - **`php://input` is natively readonly.** It does not need to be copied to a `php://memory` resource, and does not need to be initialized.

### *ImmutableStream*

[](#immutablestream)

[*ImmutableStream*](#immutablestream) is a marker interface that extends [*ReadonlyStream*](#readonlystream)to indicate the implementation attempts to enforce immutability constraints on the encapsulated resource.

- Directives:

    - Implementations MUST adhere to all [*ReadonlyStream*](#readonlystream) constraints.
    - Implementations MUST NOT allow non-idempotent reading of the encapsulated resource, whether by implementing [*ReadableStream*](#readablestream) or by some other means.
    - Implementations MUST NOT expose the state of the encapsulated resource pointer, whether by implementing [*SeekableStream*](#seekablestream) or by some other means.
    - Implementations MUST NOT allow closing of the encapsulated resource before the [*ImmutableStream*](#immutablestream) is destructed, whether by implementing [*ClosableStream*](#closablestream) or by some other means.
    - Implementations MUST NOT allow mutation of the `$metadata` property.
- Notes:

    - **The immutability constraints are necessarily strict.** Immutability of a resource is incompatible with non-idempotent reading; doing so modifies its pointer position, thereby changing its state. Likewise, closing the resource changes its state. These constraints leave only [*StringableStream*](#stringablestream) and [*SizableStream*](#sizablestream) as compatible interfaces.

### *StreamThrowable*

[](#streamthrowable)

[*StreamThrowable*](#streamthrowable) is a marker interface that extends [*Throwable*](https://php.net/Throwable) to indicate an [*Exception*](https://php.net/Exception) is stream-related.

It adds no class members.

### *StreamTypeAliases*

[](#streamtypealiases)

[*StreamTypeAliases*](#streamtypealiases) defines custom PHPStan types to aid static analysis.

- ```
    metadata_array: array{
        timed_out: bool,
        blocked: bool,
        eof: bool,
        unread_bytes: int,
        stream_type: string,
        wrapper_type: string,
        wrapper_data: mixed,
        mode: string,
        seekable: bool,
        uri?: string,
        mediatype?: string,
        base64?: bool
    }

    ```

    - An `array` of stream metadata, as if by [`stream_get_meta_data()`](https://php.net/stream_get_meta_data).
- ```
    stat_array: array{
        dev: int,
        ino: int,
        mode: int,
        nlink: int,
        uid: int,
        gid: int,
        rdev: int,
        size: int,
        atime: int,
        mtime: int,
        ctime: int,
        blksize: int,
        blocks: int
    }

    ```

    - An `array` of resource stats, as if by [`fstat()`](https://php.net/fstat) or [`stat()`](https://php.net/stat).

Implementations
---------------

[](#implementations)

Implementations MAY encapsulate a string, or some other kind of data source, instead of a `resource`.

Implementations encapsulating something besides a `resource` MUST behave *as if* they encapsulate a resource.

Implementations advertised as readonly or immutable MUST be deeply readonly or immutable. With the exception of implementations meeting the specified [*ReadonlyStream*](#readonlystream) or [*ImmutableStream*](#immutablestream) conditions, they MUST NOT encapsulate any references, resources, mutable objects, objects or arrays encapsulating references or resources or mutable objects, and so on.

Implementations MAY define additional class members not defined in these interfaces; implementations advertised as readonly or immutable MUST make those additional class members deeply readonly or immutable.

Notes:

- **Reflection does not invalidate advertisements of readonly or immutable implementations.** The ability of a consumer to use Reflection to mutate an implementation advertised as readonly or immutable does not constitute a failure to comply with Stream-Interop.
- **Reference implementations** are available at .

Q &amp; A
---------

[](#q--a)

### What projects were used as reference points for Stream-Interop?

[](#what-projects-were-used-as-reference-points-for-stream-interop)

These are the reference projects for developing the above interfaces.

- amphp/byte-stream:
- fzaninotto/streamer:
- hoa/stream:
- kraken-php/stream:
- psr/http-message:
- react/stream:
- zenstruck/stream:

Please see [README-RESEARCH.md](./README-RESEARCH.md) for more information.

### What about filters?

[](#what-about-filters)

[Stream filters](https://www.php.net/manual/en/function.stream-filter-register.php) are a powerful aspect of stream resources. However, as they operate on resources directly, creating interfaces for them is out-of-scope for Stream-Interop. Further, none of the projects included in the Stream-Interop research implemented filters, making it difficult to rationalize adding filter interfaces.

Even so, consumers are free to register filters on the resources they inject into a [*Stream*](#stream). In addition, implementors are free to create filter mechanisms that intercept the input going into a [*WritableStream*](#writablestream) (e.g. via its `write()` method) or the output coming from a [*ReadableStream*](#readablestream) (e.g. via its `read()` method).

### Why is there no *Factory* interface?

[](#why-is-there-no-factory-interface)

The sheer volume of possible combinations of the various interfaces makes it difficult to provide a factory with proper return typehints. Implementors are encouraged to develop their own factories with proper typehinting.

---

###  Health Score

48

—

FairBetter than 93% of packages

Maintenance88

Actively maintained with recent releases

Popularity17

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity61

Established project with proven stability

 Bus Factor1

Top contributor holds 80.4% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~76 days

Recently: every ~95 days

Total

7

Last Release

61d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/25754?v=4)[Paul M. Jones](/maintainers/pmjones)[@pmjones](https://github.com/pmjones)

---

Top Contributors

[![pmjones](https://avatars.githubusercontent.com/u/25754?v=4)](https://github.com/pmjones "pmjones (78 commits)")[![nbish11](https://avatars.githubusercontent.com/u/1518821?v=4)](https://github.com/nbish11 "nbish11 (19 commits)")

---

Tags

streamresourceinterop

###  Code Quality

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/stream-interop-interface/health.svg)

```
[![Health](https://phpackages.com/badges/stream-interop-interface/health.svg)](https://phpackages.com/packages/stream-interop-interface)
```

###  Alternatives

[sandrokeil/interop-config

Provides interfaces and a concrete implementation to create instances depending on configuration via factory classes and ensures a valid config structure. It can also be used to auto discover factories and to create configuration files.

58482.2k37](/packages/sandrokeil-interop-config)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
