jstewmc/chunker
Multi-byte safe chunked reading for huge files or strings in PHP. Avoids breaking UTF-8 characters by adjusting chunk boundaries so each chunk is valid text, reducing memory use while processing streams sequentially.
Installation:
composer require jstewmc/chunker
Ensure ext-mbstring is enabled in your PHP environment.
First Use Case: Process a large UTF-8 encoded file without breaking multi-byte characters:
use Jstewmc\Chunker\File;
$chunker = new File('large-file.csv', 'UTF-8', 8192); // 8KB chunks
while (false !== ($chunk = $chunker->current())) {
process($chunk); // Your logic here
$chunker->next();
}
Where to Look First:
0.2.1 for fixes related to malformed byte sequences and idempotency.File Processing:
$chunker = new File('data/export.jsonl', 'UTF-8', 4096);
while ($chunker->hasNextChunk()) {
$data = json_decode($chunker->current(), true);
$chunker->next();
// Process line-by-line JSON data
}
String Chunking:
$chunker = new Text($largeString, 'UTF-8', 2000); // 2000 chars
foreach ($chunker as $chunk) {
// Process text in 2000-character blocks
}
Memory-Efficient CSV Parsing:
$chunker = new File('huge.csv', 'UTF-8', 1024);
$headers = null;
while (false !== ($chunk = $chunker->current())) {
if (!$headers) {
$headers = str_getcsv($chunk);
$chunker->next();
continue;
}
$row = str_getcsv($chunk);
// Process row with $headers
$chunker->next();
}
Laravel Filesystem:
Combine with Laravel's Storage facade for cloud storage:
use Illuminate\Support\Facades\Storage;
use Jstewmc\Chunker\File;
$path = Storage::path('backups/large-backup.sql');
$chunker = new File($path, 'UTF-8', 16384); // 16KB chunks
Queue Jobs: Process chunks in background jobs:
$chunker = new File('data/process', 'UTF-8', 8192);
while ($chunker->hasNextChunk()) {
ProcessChunkJob::dispatch($chunker->current())
->afterRelease(fn() => $chunker->next());
}
Streaming Responses:
$chunker = new File('reports/generated.pdf', null, 8192);
return response()->stream(fn() => yield $chunker->current(), $chunker->next());
Encoding Assumptions:
mb_internal_encoding(), which may cause issues if your app uses UTF-8 but the file uses ISO-8859-1.$chunker = new File('file.txt', 'ISO-8859-1'); // Force correct encoding
Chunk Size Limits:
size=0 or negative values throws InvalidArgumentException.$size = max(1, (int)$request->input('chunk_size', 8192));
Navigation Edge Cases:
next()/previous() are idempotent but not reversible (forward/backward chunks may differ).reset() before bidirectional traversal:
$chunker->next(); // Move forward
$chunker->reset(); // Reset to start
Malformed Bytes:
?.mb_convert_encoding():
$cleanText = mb_convert_encoding($dirtyText, 'UTF-8', 'UTF-8//IGNORE');
Verify Chunk Counts:
$chunker = new Text($largeString, 'UTF-8', 100);
dd($chunker->countChunks()); // Debug total chunks
Inspect Chunk Boundaries:
$chunker = new File('file.txt', 'UTF-8');
while ($chunker->hasNextChunk()) {
$chunk = $chunker->current();
$chunkLength = mb_strlen($chunk, 'UTF-8');
dd($chunk, $chunkLength); // Check for unexpected lengths
$chunker->next();
}
Performance Profiling:
memory_get_usage() to compare chunk sizes:
$chunker = new File('large.log', 'UTF-8', 8192);
while ($chunker->hasNextChunk()) {
$start = memory_get_usage();
$chunk = $chunker->current();
$usage = memory_get_usage() - $start;
dd($usage); // Monitor memory per chunk
$chunker->next();
}
Custom Chunk Validators:
$chunker = new File('data.json', 'UTF-8');
while ($chunker->hasNextChunk()) {
$chunk = $chunker->current();
if (!json_validate($chunk)) { // Custom validation
throw new \RuntimeException("Invalid JSON chunk");
}
$chunker->next();
}
Lazy-Loading with Generators:
function chunkGenerator(File $chunker) {
while ($chunker->hasNextChunk()) {
yield $chunker->current();
$chunker->next();
}
}
foreach (chunkGenerator($chunker) as $chunk) {
// Process lazily
}
Event-Based Processing:
$chunker = new File('stream.log', 'UTF-8');
$chunker->on('chunk', fn($chunk) => log($chunk));
$chunker->on('end', fn() => notify('Processing complete'));
while ($chunker->hasNextChunk()) {
$chunker->next();
}
(Note: Requires extending the class to support events.)
Progress Tracking:
$totalChunks = $chunker->countChunks();
$processed = 0;
while ($chunker->hasNextChunk()) {
$chunk = $chunker->current();
$progress = ($processed++ / $totalChunks) * 100;
reportProgress($progress);
$chunker->next();
}
mb_convert_encoding() to normalize before chunking:
$normalized = mb_convert_encoding($text, 'UTF-8', 'UTF-8');
$chunker = new Text($normalized, 'UTF-8');
How can I help you explore Laravel packages today?