sabre/xml
sabre/xml is a lightweight, specialized XML reader and writer for PHP. It makes it easy to parse XML into structured data and generate XML output with namespace support. Version 3 adds strict type declarations and supports PHP 7.4+ and PHP 8.
Installation:
composer require sabre/xml
Target v4.x for PHP 8+ or v3.x for PHP 7.4+ with type safety.
First Use Case: Parse a simple XML string into a PHP array:
use Sabre\Xml\Reader;
use Sabre\Xml\Service;
$xml = '<root><item>value</item></root>';
$reader = new Reader();
$service = new Service();
$result = $service->parse($reader->parse($xml));
// Returns: ['root' => ['item' => 'value']]
Where to Look First:
Sabre\Xml\Service (core class) and Sabre\Xml\Writer for output.$service = new Service();
$data = $service->parse($reader->parse('<root><key>value</key></root>'));
// ['root' => ['key' => 'value']]
deserialize() with custom mappings:
$service->deserialize($reader->parse($xml), MyModel::class);
$reader->setNamespace('soap', 'http://schemas.xmlsoap.org/soap/envelope/');
$service->parse($reader->parse($soapXml));
$writer = new Writer();
$writer->write(['root' => ['item' => 'value']]);
// Outputs: <root><item>value</item></root>
$writer->setIndent(2);
$writer->write($data);
$writer->setNamespace('ns', 'http://example.com/ns');
$writer->write(['ns:root' => ['ns:item' => 'value']]);
Service Provider:
// app/Providers/XmlServiceProvider.php
public function register()
{
$this->app->singleton(Service::class, function () {
return new Service();
});
}
API Responses:
return response()->xml($data)->header('Content-Type', 'application/xml');
(Use a facade or helper for response()->xml().)
Background Jobs:
ParseXmlJob::dispatch($xmlFilePath)
->onQueue('xml-processing');
$service->addDeserializer('date', function ($value) {
return new DateTime($value);
});
$reader = new Reader();
$reader->open($largeXmlFilePath);
while ($reader->read()) {
$service->parse($reader->getCurrentElement());
}
Resource Handling:
fclose()) will throw exceptions. Reopen or use streams:
$reader->open($filePath); // Instead of passing a closed resource.
is_resource($handle) before parsing.Namespace Conflicts:
$reader->setNamespace('ns', 'http://example.com/ns');
Empty XML Elements:
<tag></tag>). Use trimWhitespace:
$reader->setTrimWhitespace(true);
Type Mismatches (v3+):
class CustomService extends Service {
public function customParse(array $data): array { ... }
}
PHP 8+ Deprecations:
xml_parse_into_struct() (deprecated). Use Sabre\Xml\Reader instead.libxml_use_internal_errors(true);
$reader->parse($xml);
$errors = libxml_get_errors();
libxml_get_last_error() to catch parsing issues early.Custom Writers:
Extend Sabre\Xml\Writer to add domain-specific formatting:
class ApiWriter extends Writer {
public function writeArray(array $data, string $root = 'response') {
$this->startElement($root);
foreach ($data as $key => $value) {
$this->writeElement($key, $value);
}
$this->endElement();
}
}
Deserializer Hooks:
Override Service::deserialize() to inject logic:
$service->addDeserializer('callback', function ($value, $name) {
return call_user_func($value, $name);
});
Event Listeners:
Use Sabre\Xml\Reader::onRead() to intercept parsing:
$reader->onRead(function ($element) {
if ($element['name'] === 'secret') {
$element['value'] = '****';
}
});
Reader::read() in a loop instead of parse() to avoid loading the entire file into memory.$reader->setNamespaces([
'ns1' => 'http://example.com/ns1',
'ns2' => 'http://example.com/ns2',
]);
Caching Parsed XML:
$cacheKey = 'xml_' . md5($xml);
$data = Cache::remember($cacheKey, 3600, function () use ($xml) {
return $service->parse($reader->parse($xml));
});
Form Request Validation:
public function rules()
{
return [
'xml_data' => 'required|xml', // Custom validator for XML structure.
];
}
Artisan Commands:
class ParseXmlCommand extends Command {
protected $service;
public function __construct(Service $service) {
parent::__construct();
$this->service = $service;
}
public function handle() {
$xml = file_get_contents($this->argument('file'));
$data = $this->service->parse($reader->parse($xml));
// Process $data...
}
}
How can I help you explore Laravel packages today?