jstewmc/stream
jstewmc/stream is a small PHP library that provides a simple Stream abstraction for working with PHP stream resources. It helps with reading/writing, buffering, and common stream operations behind a cleaner, object-oriented API.
Installation
composer require jstewmc/stream
No additional configuration is required—just autoload the package.
First Use Case: Streaming a Large File
use JSTewMc\Stream\Stream;
$filePath = storage_path('app/large-file.txt');
$stream = new Stream($filePath);
while ($stream->valid()) {
$char = $stream->current();
// Process character (e.g., log, analyze, or emit)
$stream->next();
}
First Use Case: Streaming a String
$string = "Hello, 世界!";
$stream = new Stream($string);
while ($stream->valid()) {
$char = $stream->current();
// Process character
$stream->next();
}
Where to Look First
JSTewMc\Stream\Stream methods (valid(), current(), next(), key(), rewind()).File Streaming (Chunked Processing)
$stream = new Stream(storage_path('app/logs/big.log'));
$lineBuffer = '';
while ($stream->valid()) {
$char = $stream->current();
if ($char === "\n") {
// Process $lineBuffer (e.g., parse, store, or emit)
$lineBuffer = '';
} else {
$lineBuffer .= $char;
}
$stream->next();
}
String Transformation
$stream = new Stream("Input String");
$output = '';
while ($stream->valid()) {
$char = strtoupper($stream->current()); // Example transformation
$output .= $char;
$stream->next();
}
Integration with Laravel Events/Jobs
// In a job or event listener:
$stream = new Stream($filePath);
while ($stream->valid()) {
dispatch(new ProcessCharacterJob($stream->current()));
$stream->next();
}
Memory-Efficient Pagination
$stream = new Stream($filePath);
$batchSize = 1000;
$batch = [];
while ($stream->valid()) {
$batch[] = $stream->current();
$stream->next();
if (count($batch) >= $batchSize) {
Process::chunk($batch, 100); // Laravel's chunk processing
$batch = [];
}
}
Custom Iterators
Extend Stream or wrap it in a custom iterator for domain-specific logic:
class LineStream extends Stream {
public function __construct($source) {
parent::__construct($source);
$this->buffer = '';
}
public function next() {
if ($this->current() === "\n") {
$this->buffer = '';
} else {
$this->buffer .= $this->current();
}
parent::next();
}
public function getLine() {
return $this->buffer;
}
}
Resource Cleanup
Ensure streams are closed (especially for files) using PHP’s finally or Laravel’s try-catch-finally:
$stream = new Stream($filePath);
try {
while ($stream->valid()) { /* ... */ }
} finally {
if ($stream instanceof \SplFileObject) {
$stream->close();
}
}
Multi-Byte Character Handling
"你好"). Edge cases may arise with surrogate pairs or incomplete sequences.mb_* functions for critical operations.File Pointers and rewind()
rewind() on a file stream resets the file pointer but does not reset memory buffers in custom iterators.Stream object if you need a fresh read.Resource Leaks
SplFileObject) may leak if not closed. Use finally blocks or Laravel’s withFile() helper:
withFile($filePath, function ($file) {
$stream = new Stream($file);
// Process...
}); // Auto-closes file
Performance with Large Files
Empty Inputs
Stream may throw exceptions or behave unexpectedly with empty strings/files.if (empty($source)) {
return; // or throw new \InvalidArgumentException();
}
Inspect Stream State
Use var_dump($stream->key()) to track position (useful for debugging infinite loops).
Log Characters For troubleshooting, log characters with their byte values:
$char = $stream->current();
\Log::debug("Char: {$char} | Bytes: " . bin2hex($char));
Test Edge Cases
Custom Stream Sources
Extend Stream to support new sources (e.g., HTTP streams, database cursors):
class HttpStream extends Stream {
protected $handle;
public function __construct($url) {
$this->handle = fopen($url, 'r');
parent::__construct($this->handle);
}
public function __destruct() {
fclose($this->handle);
}
}
Lazy Evaluation
Combine with Laravel’s LazyCollection for functional-style processing:
$stream = new Stream($filePath);
LazyCollection::make(function () use ($stream) {
while ($stream->valid()) {
yield $stream->current();
$stream->next();
}
})->filter(fn($char) => $char !== ' ')->take(100);
Progress Tracking Wrap the stream to add progress callbacks:
class ProgressStream {
public function __construct(private Stream $stream, private callable $callback) {}
public function next() {
$this->callback($this->stream->key());
$this->stream->next();
}
}
How can I help you explore Laravel packages today?