# Streams and Decorators

PSR-7 request and response bodies are streams. This page covers stream creation, cursor and I/O behavior, built-in stream decorators, and wrapping PSR-7 streams as PHP resources.

Streams allow HTTP messages to represent small strings, large files, generated data, remote resources, and other body sources through a common interface.

The PSR-7 `Psr\Http\Message\StreamInterface` exposes methods that let consumers read, write, seek, and inspect body data without requiring the entire body to be loaded into memory.

Streams expose their capabilities using `isReadable()`, `isWritable()`, and `isSeekable()`. These methods help collaborators determine whether a stream supports the operations they need.

## Creating Streams

Use `GuzzleHttp\Psr7\Utils::streamFor()` to create streams from common PHP values. It accepts strings, resources returned from `fopen()`, objects that implement `__toString()`, iterators, callable arrays, closures, invokable objects, and existing `Psr\Http\Message\StreamInterface` instances.

Strings and `null` are stored in `php://temp` streams. PHP keeps `php://temp` data in memory until the stream exceeds 2 MB, then spills to a temporary file on disk. Non-string scalars such as integers, floats, and booleans are rejected; cast them to strings first.

Callable sources receive a suggested read length, may return fewer or more bytes, and end the stream by returning `false` or `null`. Strings remain literal body contents, even when they name a callable.

```php
use GuzzleHttp\Psr7\Utils;

$stream = Utils::streamFor('string data');
echo $stream;
// string data
echo $stream->read(3);
// str
echo $stream->getContents();
// ing data
var_export($stream->eof());
// true
var_export($stream->tell());
// 11
```

You can create streams from iterators. The iterator can yield any number of bytes per iteration. Any excess bytes returned by the iterator that were not requested by a stream consumer will be buffered until a subsequent read.

```php
use GuzzleHttp\Psr7\Utils;

$generator = function ($bytes) {
    for ($i = 0; $i < $bytes; $i++) {
        yield '.';
    }
};

$stream = Utils::streamFor($generator(1024));
echo $stream->read(3);
// ...
```

## Metadata

Streams expose stream metadata through `getMetadata()`. This method provides the data returned by PHP's [stream_get_meta_data()](https://www.php.net/manual/en/function.stream-get-meta-data.php), and can optionally expose custom metadata.

```php
use GuzzleHttp\Psr7\Utils;

$resource = Utils::tryFopen('/path/to/file', 'r');
$stream = Utils::streamFor($resource);

echo $stream->getMetadata('uri');
// /path/to/file
var_export($stream->isReadable());
// true
var_export($stream->isWritable());
// false
var_export($stream->isSeekable());
// true
```

## AppendStream

`GuzzleHttp\Psr7\AppendStream`

Reads from multiple streams, one after the other.

```php
use GuzzleHttp\Psr7;

$a = Psr7\Utils::streamFor('abc, ');
$b = Psr7\Utils::streamFor('123.');
$composed = new Psr7\AppendStream([$a, $b]);

$composed->addStream(Psr7\Utils::streamFor(' Above all listen to me'));

echo $composed; // abc, 123. Above all listen to me.
```


## BufferStream

`GuzzleHttp\Psr7\BufferStream`

Provides a buffer stream that can be written to fill a buffer, then read
from it to remove bytes from the buffer.

This stream returns a "hwm" metadata value that tells upstream consumers
what the configured high water mark of the stream is, or the maximum
preferred size of the buffer.

```php
use GuzzleHttp\Psr7;

// When the buffer reaches or exceeds 1024 bytes, it will begin returning 0 to
// writes. This is an indication that writers should slow down.
$buffer = new Psr7\BufferStream(1024);
```


## CachingStream

The CachingStream is used to allow seeking over previously read bytes on
non-seekable streams. This can be useful when transferring a non-seekable
entity body fails due to needing to rewind the stream (for example, resulting
from a redirect). Data that is read from the remote stream will be buffered in
a PHP temp stream so that previously read bytes are cached first in memory,
then on disk.

```php
use GuzzleHttp\Psr7;

$original = Psr7\Utils::streamFor(fopen('http://www.google.com', 'r'));
$stream = new Psr7\CachingStream($original);

$stream->read(1024);
echo $stream->tell();
// 1024

$stream->seek(0);
echo $stream->tell();
// 0
```

By default the bytes are cached in a `php://temp` stream. You can supply your own
cache target as the second constructor argument, but it is used as a random-access
byte buffer to replay the remote stream, so it must be readable, writable, and
seekable, report an accurate position and size, and store writes losslessly. Lossy
or non-seekable streams such as `BufferStream` and `DroppingStream` are not valid
targets.


## DroppingStream

`GuzzleHttp\Psr7\DroppingStream`

Stream decorator that begins dropping data once the size of the underlying
stream becomes too full.

```php
use GuzzleHttp\Psr7;

// Create an empty stream
$stream = Psr7\Utils::streamFor();

// Start dropping data when the stream has more than 10 bytes
$dropping = new Psr7\DroppingStream($stream, 10);

$dropping->write('01234567890123456789');
echo $stream; // 0123456789
```


## FnStream

`GuzzleHttp\Psr7\FnStream`

Compose stream implementations based on a hash of callables.

Allows for easy testing and extension of a provided stream without needing
to create a concrete class for a simple extension point.

```php

use GuzzleHttp\Psr7;

$stream = Psr7\Utils::streamFor('hi');
$fnStream = Psr7\FnStream::decorate($stream, [
    'rewind' => function () use ($stream) {
        echo 'About to rewind - ';
        $stream->rewind();
        echo 'rewound!';
    }
]);

$fnStream->rewind();
// Outputs: About to rewind - rewound!
```


## InflateStream

`GuzzleHttp\Psr7\InflateStream`

Uses PHP's zlib.inflate filter to inflate zlib (HTTP deflate, RFC1950) or gzipped (RFC1952) content.

This stream decorator converts the provided stream to a PHP stream resource,
appends the zlib.inflate filter, and wraps the filtered resource as a stream.

Closing an `InflateStream` also closes the compressed source stream it decorates; `detach()` leaves the source stream open.


## LazyOpenStream

`GuzzleHttp\Psr7\LazyOpenStream`

Lazily reads from or writes to a file that is opened only after an I/O operation
takes place on the stream.

```php
use GuzzleHttp\Psr7;

$stream = new Psr7\LazyOpenStream('/path/to/file', 'r');
// The file has not yet been opened...

echo $stream->read(10);
// The file is opened and read from only when needed.
```


## LimitStream

`GuzzleHttp\Psr7\LimitStream`

LimitStream can be used to read a subset or slice of an existing stream object.
This can be useful for breaking a large file into smaller pieces to be sent in
chunks (e.g. Amazon S3's multipart upload API).

```php
use GuzzleHttp\Psr7;

$original = Psr7\Utils::streamFor(fopen('/tmp/test.txt', 'r+'));
echo $original->getSize();
// >>> 1048576

// Limit the size of the body to 1024 bytes and start reading from byte 2048
$stream = new Psr7\LimitStream($original, 1024, 2048);
echo $stream->getSize();
// >>> 1024
echo $stream->tell();
// >>> 0
```


## MultipartStream

`GuzzleHttp\Psr7\MultipartStream`

A stream that returns bytes for a streaming multipart or multipart/form-data
body when read.

Each multipart element must contain a `name` and `contents` key. `contents` may
be any non-array value accepted by `GuzzleHttp\Psr7\Utils::streamFor()`,
including closures and invokable objects. Array contents are recursively
expanded into nested form fields.


## NoSeekStream

`GuzzleHttp\Psr7\NoSeekStream`

NoSeekStream wraps a stream and does not allow seeking.

```php
use GuzzleHttp\Psr7;

$original = Psr7\Utils::streamFor('foo');
$noSeek = new Psr7\NoSeekStream($original);

echo $noSeek->read(3);
// foo
var_export($noSeek->isSeekable());
// false

try {
    $noSeek->seek(0);
} catch (\RuntimeException $e) {
    echo $e->getMessage();
    // Cannot seek a NoSeekStream
}
```


## PumpStream

`GuzzleHttp\Psr7\PumpStream`

Provides a read-only stream that pumps data from a PHP callable.

When invoking the provided callable, the PumpStream will pass the suggested
number of bytes to read to the callable. The callable can choose to ignore
this value and return fewer or more bytes than requested. Any extra data
returned by the provided callable is buffered internally until drained using
the `read()` method of the PumpStream. The provided callable MUST return a
non-empty string to provide data, and MUST return false or null when there is
no more data to read. Returning an empty string causes a RuntimeException
because it cannot satisfy a positive-length read.

Userland callables that declare no parameters are tolerated by PHP, but
length-aware callables remain the recommended formal shape.


## Implementing Stream Decorators

Creating a stream decorator is very easy thanks to the
`GuzzleHttp\Psr7\StreamDecoratorTrait`. This trait provides methods that
implement `Psr\Http\Message\StreamInterface` by proxying to an underlying
stream. Just `use` the `StreamDecoratorTrait` and implement your custom
methods.

For example, let's say we wanted to call a specific function each time the last
byte is read from a stream. This could be implemented by overriding the
`read()` method.

```php
use Psr\Http\Message\StreamInterface;
use GuzzleHttp\Psr7\StreamDecoratorTrait;

class EofCallbackStream implements StreamInterface
{
    use StreamDecoratorTrait;

    private $callback;

    private $stream;

    public function __construct(StreamInterface $stream, callable $cb)
    {
        $this->stream = $stream;
        $this->callback = $cb;
    }

    public function read(int $length): string
    {
        $result = $this->stream->read($length);

        // Invoke the callback when EOF is hit.
        if ($this->eof()) {
            ($this->callback)();
        }

        return $result;
    }
}
```

This decorator could be added to any existing stream and used like so:

```php
use GuzzleHttp\Psr7;

$original = Psr7\Utils::streamFor('foo');

$eofStream = new EofCallbackStream($original, function () {
    echo 'EOF!';
});

$eofStream->read(2);
$eofStream->read(1);
// echoes "EOF!"
$eofStream->seek(0);
$eofStream->read(3);
// echoes "EOF!"
```


## PHP StreamWrapper

You can use the `GuzzleHttp\Psr7\StreamWrapper` class if you need to use a
PSR-7 stream as a PHP stream resource.

Use the `GuzzleHttp\Psr7\StreamWrapper::getResource()` method to create a PHP
stream from a PSR-7 stream.

```php
use GuzzleHttp\Psr7\StreamWrapper;

$stream = GuzzleHttp\Psr7\Utils::streamFor('hello!');
$resource = StreamWrapper::getResource($stream);
echo fread($resource, 6); // outputs hello!
```

## Related

- [PSR-7 Messages](psr-7-messages.md)
- [Stream Helpers](stream-helpers.md)
- [PSR-17 Factories](psr-17-factories.md)
- [URI Helpers](uri-helpers.md)
