guzzle/stream
Lightweight stream abstraction for PHP and Guzzle, offering common stream utilities and decorators to read, write, and transform data consistently. Useful for building HTTP clients, middleware, and I/O workflows that need simple, testable stream handling.
Installation
Add to composer.json:
"require": {
"guzzle/stream": "^3.0"
}
Run composer update.
First Use Case Stream a large file (e.g., from S3 or a remote server) without loading it entirely into memory:
use Guzzle\Stream\Stream;
$stream = Stream::fromFile('large_file.zip');
$chunkSize = 1024; // 1KB chunks
$stream->rewind();
while (!$stream->eof()) {
$chunk = $stream->read($chunkSize);
// Process chunk (e.g., upload to another service)
}
Key Classes to Know
Guzzle\Stream\Stream: Core stream abstraction.Guzzle\Stream\StreamInterface: Contract for streams.Guzzle\Stream\StreamDecorator: Wrap existing streams (e.g., for logging).Guzzle\Stream\PhpStream: PHP-native stream wrapper.Pattern: Use Stream::fromFile() or Stream::fromResource() to avoid memory overload.
$stream = Stream::fromFile(storage_path('logs/large.log'));
$stream->rewind();
while (!$stream->eof()) {
$data = $stream->read(8192); // 8KB chunks
// Process $data incrementally
}
Pattern: Decorate streams to add behavior (e.g., compression, logging).
use Guzzle\Stream\StreamDecorator;
$originalStream = Stream::fromFile('input.txt');
$decoratedStream = new StreamDecorator($originalStream, function ($chunk) {
return strtoupper($chunk); // Example: Convert to uppercase
});
Pattern: Use streams with Guzzle HTTP client for chunked uploads/downloads.
$client = new \GuzzleHttp\Client();
$response = $client->get('https://example.com/large-file.zip', [
'sink' => 'downloads/large-file.zip',
'stream' => true,
]);
Pattern: Stream directly from/to remote URLs or resources.
$remoteStream = Stream::fromCurl('https://example.com/stream-data');
$localStream = Stream::toFile('local-copy.txt');
stream_copy_to_stream($remoteStream, $localStream);
Pattern: Use Stream::fromFile() with memory_map for zero-copy reads.
$stream = Stream::fromFile('huge-dataset.bin', ['memory_map' => true]);
// Access data via $stream->read() without loading entire file
Resource Leaks
$stream->close() when done to free resources.finally blocks or context managers (e.g., Laravel’s Storage facade) to ensure cleanup.Stream Positioning
rewind() resets the stream pointer. Forgetting this can cause skipped data.tell() returns the current position; useful for resuming interrupted transfers.Chunk Size Trade-offs
PHP Stream Wrappers
php://temp) require explicit closing.guzzle/stream with native PHP streams (e.g., fopen) unless necessary.Error Handling
try-catch:
try {
$stream->read(1024);
} catch (\RuntimeException $e) {
// Handle stream errors (e.g., disk full)
}
$stream->meta(); // Returns array of stream properties (e.g., size, mode)
StreamDecorator to log chunks:
$stream = new StreamDecorator($originalStream, function ($chunk) {
\Log::debug('Chunk:', ['size' => strlen($chunk)]);
return $chunk;
});
if (!$stream->isReadable()) {
throw new \RuntimeException('Stream is not readable');
}
Custom Stream Sources
Implement StreamInterface for new stream types (e.g., database blobs):
class DatabaseStream implements StreamInterface {
// Implement read(), write(), etc.
}
Stream Filters Chain decorators for complex transformations:
$stream
->pipe(new StreamDecorator($stream, fn($c) => gzdecode($c))) // Decompress
->pipe(new StreamDecorator($stream, fn($c) => str_replace('foo', 'bar', $c))); // Replace text
Laravel Integration
Storage facade for disk streams:
$stream = Stream::fromResource(fopen(storage_path('file.txt'), 'r'));
Storage::disk('s3')->writeStream('remote-file.txt', $stream);
Http client for chunked uploads:
$client->post('https://api.example.com/upload', [
'body' => Stream::fromFile('large-file.zip'),
]);
['memory_map' => true] for large files, but ensure PHP has sufficient memory_map limits.StreamDecorator to wrap with a timeout:
$stream = new StreamDecorator($originalStream, function () use (&$timeout) {
if ($timeout-- <= 0) throw new \RuntimeException('Timeout');
return $this->stream->read(1024);
}, 100); // 100ms timeout per chunk
How can I help you explore Laravel packages today?