prewk/xml-string-streamer
Stream huge XML files with minimal memory by iterating one node at a time. XmlStringStreamer reads from a file/buffered stream and returns each matched element as a string for processing (e.g., SimpleXML). Includes StringWalker and faster UniqueNode parsers.
Installation
composer require prewk/xml-string-streamer
Add to composer.json if using a monorepo or custom package management.
First Use Case: Streaming Large XML
use Prewk\XmlStringStreamer\Streamer;
$streamer = new Streamer();
$streamer->streamFromFile('path/to/large.xml', function ($node) {
// Process each node as it's encountered
if ($node->name === 'record') {
// Handle record data
}
});
Key Files to Review
src/Streamer.php – Core class with streaming logic.tests/ – Example use cases (e.g., handling namespaces, attributes).README.md – Quickstart and CLI examples.Streaming from Files
$streamer->streamFromFile('file.xml', function ($node) {
// Node is a stdClass with properties: name, attributes, children, text
});
Streaming from Strings
$streamer->streamFromString($xmlString, function ($node) {
// Process node
});
Event-Based Processing
$streamer->on('startElement', function ($name, $attrs) {
// Handle element start
});
Integration with Laravel
dispatch(new ProcessLargeXmlJob('file.xml'));
class ProcessLargeXmlJob implements ShouldQueue {
public function handle() {
$streamer = new Streamer();
$streamer->streamFromFile(storage_path('app/large.xml'), fn($node) => {
// Process and store in DB
});
}
}
$this->app->singleton(Streamer::class, fn() => new Streamer());
Transforming XML to Collections
$records = collect();
$streamer->streamFromFile('data.xml', function ($node) use (&$records) {
if ($node->name === 'user') {
$records->push((object) [
'id' => $node->attributes->id,
'name' => $node->text,
]);
}
});
Memory Leaks with Closures
use (&$var) sparingly.$bigArray = range(1, 1_000_000);
$streamer->streamFromFile('file.xml', function ($node) use ($bigArray) {
// $bigArray is copied into closure scope; inefficient
});
Namespace Handling
if ($node->name === '{http://example.com}record') {
// Handle namespaced record
}
Attribute Access
$node->attributes). Use isset() or property_exists() to check for keys:
if (property_exists($node->attributes, 'id')) {
$id = $node->attributes->id;
}
Encoding Issues
$streamer->setEncoding('UTF-8');
Performance with Deeply Nested XML
$streamer->streamFromFile('file.xml', function ($node) {
if ($node->name === 'deeply_nested_tag') {
// Process and reset state to avoid stack overflow
}
});
Log Node Structures
$streamer->streamFromFile('file.xml', function ($node) {
\Log::debug('Node:', [
'name' => $node->name,
'attrs' => (array) $node->attributes,
'text' => substr($node->text, 0, 100), // Truncate long text
]);
});
Validate XML First
Use libxml_use_internal_errors() to catch malformed XML before streaming:
libxml_use_internal_errors(true);
$doc = simplexml_load_file('file.xml');
if ($doc === false) {
foreach (libxml_get_errors() as $error) {
\Log::error($error->message);
}
throw new \RuntimeException('Invalid XML');
}
Test with Small Files Start with a 10KB XML file to verify logic before scaling to GB-sized files.
Custom Node Processing
Extend Streamer to add pre/post-processing hooks:
class CustomStreamer extends Streamer {
protected function beforeProcessNode($node) {
// Modify node before callbacks
}
}
Plugin System Use Laravel’s service container to bind custom processors:
$this->app->bind(Streamer::class, function ($app) {
$streamer = new Streamer();
$streamer->addProcessor(new CustomXmlProcessor());
return $streamer;
});
Event Dispatching Integrate with Laravel Events for cross-cutting concerns:
$streamer->on('startElement', function ($name) {
event(new XmlElementStarted($name));
});
How can I help you explore Laravel packages today?