sabre/xml
sabre/xml is a specialized PHP library for reading and writing XML. It offers a flexible reader/writer API with good namespace support, modern type declarations in v3, and compatibility with PHP 7.4 and 8 for building reliable XML-based integrations.
Installation:
composer require sabre/xml
Ensure your composer.json targets PHP 8.2+ (for v4.x) or PHP 7.4+ (for v3.x).
First Use Case: Parse a simple XML string into an associative array:
use Sabre\Xml\Reader;
$reader = new Reader();
$reader->parse('<root><user><name>John</name><email>john@example.com</email></user></root>');
$data = $reader->getParsedData();
// Output: ['user' => ['name' => 'John', 'email' => 'john@example.com']]
Where to Look First:
Use the Reader class with deserializers for complex data:
$reader = new Reader();
$reader->parse('<order><items><item id="101">Laptop</item></items></order>');
// Register a custom deserializer for 'item' elements
$reader->addDeserializer('item', function ($element) {
return [
'id' => $element['@id'],
'name' => $element['#text']
];
});
$orders = $reader->getParsedData();
// Output: ['order' => ['items' => [['id' => '101', 'name' => 'Laptop']]]]
Leverage the Writer for structured XML output:
use Sabre\Xml\Writer;
$writer = new Writer();
$writer->write('<users>', [
'user' => [
['name' => 'Alice', 'role' => 'admin'],
['name' => 'Bob', 'role' => 'user']
]
]);
echo $writer->getXmlString();
// Output: <users><user><name>Alice</name><role>admin</role></user>...</users>
Use Service to parse and rewrite XML in one step:
use Sabre\Xml\Service;
$service = new Service();
$service->parse('<config><database>mysql</database></config>');
$data = $service->getParsedData(); // Parse
$service->write('<new_config>', $data); // Rewrite
Wrap the library in a service provider or facade for consistency:
// app/Providers/SabreXmlServiceProvider.php
namespace App\Providers;
use Sabre\Xml\Reader;
use Illuminate\Support\ServiceProvider;
class SabreXmlServiceProvider extends ServiceProvider {
public function register() {
$this->app->singleton('sabre.xml.reader', function () {
return new Reader();
});
}
}
Register in config/app.php and inject via Laravel’s DI:
$reader = app('sabre.xml.reader');
Use parseClarkNotation for namespace-aware parsing:
$reader = new Reader();
$reader->parse('<ns:root xmlns:ns="http://example.com/ns"><ns:item>Value</ns:item></ns:root>');
$data = $reader->parseClarkNotation('ns:item');
Catch Sabre\Xml\Exception for malformed XML:
try {
$reader->parse('<invalid>xml</invalid>');
} catch (\Sabre\Xml\Exception $e) {
report($e); // Log via Laravel's error handler
}
Validation:
Combine with Laravel’s Validator for schema validation:
use Illuminate\Support\Facades\Validator;
$data = $reader->getParsedData();
$validator = Validator::make($data, [
'user.name' => 'required|string',
'user.email' => 'required|email'
]);
Caching: Cache parsed XML responses (e.g., for SOAP APIs):
$cacheKey = 'soap_response_' . md5($request->getContent());
$data = Cache::remember($cacheKey, now()->addHours(1), function () use ($reader) {
return $reader->parse($request->getContent())->getParsedData();
});
Testing:
Use Laravel’s XmlReader mocks in PHPUnit:
$mockReader = $this->createMock(\Sabre\Xml\Reader::class);
$mockReader->method('getParsedData')->willReturn(['test' => 'data']);
$this->app->instance('sabre.xml.reader', $mockReader);
SOAP Integration:
Pair with php-soap for SOAP services:
$client = new \SoapClient($wsdl);
$response = $client->__doRequest($xml, $location, $action, $version);
$reader = new Reader();
$reader->parse($response);
Closed Resource Handling:
parse() or expect() throws an exception.fopen()/tmpfile():
$resource = fopen('php://temp', 'r+');
fwrite($resource, $xmlString);
rewind($resource);
$reader->parse($resource);
Namespace Quirks:
getParsedData() by default.parseClarkNotation() or enable namespace handling:
$reader->setNamespaceHandling(true);
Empty XML Elements:
<tag></tag>) may cause infinite loops in older versions.$reader->addDeserializer('empty_tag', function () {
return null; // Explicitly return null for empty tags
});
Type Declarations (v3+):
class CustomReader extends \Sabre\Xml\Reader {
public function customParse(string $xml): array {
return $this->parse($xml)->getParsedData();
}
}
PHP 8.2+ Compatibility:
TypeError if not using v4.x.sabre/xml:^4.0 in composer.json for PHP 8.2+:
"require": {
"sabre/xml": "^4.0"
}
Enable Verbose Errors:
Configure libxml for detailed error messages:
libxml_use_internal_errors(true);
$reader->parse($xml);
$errors = libxml_get_errors();
foreach ($errors as $error) {
report("XML Error: {$error->message}");
}
libxml_clear_errors();
Inspect Parsed Structure:
Use debug() to dump the parsed data:
$data = $reader->getParsedData();
\Illuminate\Support\Facades\Log::debug('Parsed XML:', $data);
Check for Malformed XML: Validate XML before parsing:
if (!\Sabre\Xml\Reader::isValidXml($xmlString)) {
throw new \InvalidArgumentException('Invalid XML provided.');
}
$reader->addDeserializer('invoice', function ($element) {
return [
'amount' => (float) $element['@amount'],
'currency' => $element['@currency'],
'items' => array_map(function ($item) {
return ['product' => $item['#text'],
How can I help you explore Laravel packages today?