thenorthmemory/xml
Lightweight XML transformer for PHP: parse XML into arrays and build XML back from arrays. Supports repeated elements/lists, optional pretty printing, custom root nodes, and wrapping arrays to control tag output. Extracted from wechatpay-php for general use.
Install the package via Composer:
composer require thenorthmemory/xml
First Use Case: Convert an XML string to an array for processing:
use TheNorthMemory\Xml\Transformer;
// Parse XML to array
$xml = '<xml><hello>world</hello></xml>';
$data = Transformer::toArray($xml);
// Build array back to XML
$newXml = Transformer::toXml($data);
Where to Look First:
// In a Laravel service or controller
public function handleXmlResponse(string $xmlPayload): array
{
return Transformer::toArray($xmlPayload);
}
// Usage in a controller
public function processPaymentGatewayResponse(Request $request)
{
$xmlResponse = $request->getContent();
$data = Transformer::toArray($xmlResponse);
// Process $data (e.g., save to DB, return to client)
}
// Generate XML for a third-party API
public function buildPaymentRequest(array $paymentData): string
{
return Transformer::toXml(
$paymentData,
headless: false, // Include root tag
indent: true, // Pretty-print
root: 'PaymentRequest'
);
}
// Parse XML with repeated <item> tags
$xml = '<root><items><item>1</item><item>2</item></items></root>';
$data = Transformer::toArray($xml);
// $data['items']['item'] = ['1', '2'] (array of values)
// Rebuild with custom item tag
$data['items'] = Transformer::wrap($data['items']['item'], true, 'customItem');
$xml = Transformer::toXml($data, false, true, 'root');
Laravel Facade (Optional): Create a facade for cleaner syntax:
// app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\Facade;
use TheNorthMemory\Xml\Transformer;
class AppServiceProvider extends ServiceProvider {
public function boot() {
Facade::register('Xml', function () {
return new Transformer();
});
}
}
Usage:
$data = Xml::toArray($xml);
Service Container Binding: Bind the transformer to Laravel’s container for dependency injection:
$this->app->bind('xml.transformer', function () {
return new Transformer();
});
Request/Response Macros:
Extend Laravel’s Request or Response classes to auto-parse/build XML:
// app/Http/Requests/XmlRequest.php
use TheNorthMemory\Xml\Transformer;
class XmlRequest extends FormRequest {
public function getXmlData(): array
{
return Transformer::toArray($this->getContent());
}
}
Form Request Validation: Validate XML structure before processing:
public function rules()
{
return [
'xml_data' => 'required|string',
];
}
public function withValidator($validator)
{
$validator->after(function ($validator) {
$xml = $this->input('xml_data');
try {
$data = Transformer::toArray($xml);
// Add custom validation logic on $data
} catch (\Exception $e) {
$validator->errors()->add('xml_data', 'Invalid XML format');
}
});
}
Artisan Commands: Use the transformer in CLI tools for bulk XML processing:
// app/Console/Commands/ProcessXmlFiles.php
use TheNorthMemory\Xml\Transformer;
class ProcessXmlFiles extends Command {
protected $signature = 'xml:process {path}';
public function handle() {
$files = File::files($this->argument('path'));
foreach ($files as $file) {
$xml = File::get($file);
$data = Transformer::toArray($xml);
// Process $data (e.g., save to DB)
}
}
}
Repeated Tags Without wrap():
XML like <root><item>1</item><item>2</item></root> becomes:
['item' => ['1', '2']] // Not nested under 'root'
Fix: Use Transformer::wrap() to control the structure:
$data['item'] = Transformer::wrap($data['item'], true, 'item');
Headless XML Output:
Transformer::toXml() defaults to headless: true, omitting the root tag. Set headless: false to include it:
$xml = Transformer::toXml($data, headless: false);
Attribute Handling:
The package does not parse XML attributes by default. Use SimpleXML or DOMDocument for attribute-heavy XML:
// Fallback for attributes
$simpleXml = simplexml_load_string($xml);
$attributes = $simpleXml->attributes();
Namespace Support:
The transformer ignores XML namespaces. For namespaced XML, pre-process with SimpleXML:
$xml = simplexml_load_string($xml);
$xml->registerXPathNamespace('ns', 'http://example.com/ns');
Malformed XML:
Transformer::toArray() may throw cryptic errors for invalid XML. Sanitize input first:
$sanitizedXml = Transformer::sanitize($xml);
$data = Transformer::toArray($sanitizedXml);
Large XML Files:
The package loads XML into memory. For files >10MB, use XMLReader or chunked processing:
$reader = new XMLReader();
$reader->open($filePath);
while ($reader->read()) {
if ($reader->nodeType === XMLReader::ELEMENT) {
// Process nodes incrementally
}
}
PHP Warnings:
Suppress libxml warnings (e.g., for invalid characters) with:
libxml_use_internal_errors(true);
$data = Transformer::toArray($xml);
libxml_clear_errors();
Validate XML Structure:
Use Transformer::toArray() + print_r() to inspect parsed data:
$data = Transformer::toArray($xml);
print_r($data);
Pretty-Print XML: Enable indentation for debugging:
$xml = Transformer::toXml($data, indent: true);
Compare Outputs: Test against known-good XML/array pairs to catch regressions:
$expectedArray = ['key' => 'value'];
$actualArray = Transformer::toArray($xml);
$this->assertEquals($expectedArray, $actualArray);
Log Transformations: Log XML ↔ array conversions for auditing:
\Log::debug('XML Parsed', ['xml' => $xml, 'data' => $data]);
Custom Wrapping Logic:
Extend LabeledArrayIterator for custom array-to-XML mapping:
class CustomArrayIterator extends \TheNorthMemory\Xml\LabeledArrayIterator {
public function current(): string {
return '<custom>' . parent::current() . '</custom>';
}
}
Pre/Post-Processing:
Wrap Transformer methods in Laravel middleware or decorators:
class XmlTransformerDecorator {
public function parse(string $xml): array {
$sanitized = Transformer::sanitize($xml);
return Transformer::toArray($sanitized);
}
}
Event Listeners: Trigger events before/after XML transformations:
// app/Providers/EventServiceProvider.php
protected $listen = [
'xml.parsing' => [
\App\Listeners\LogXmlParsing::class,
],
];
Testing Helpers: Create a test trait for XML assertions:
// tests/TestCase.php
use function TheNorthMemory\Xml\Transformer;
trait XmlAssertions {
protected function assertXmlEquals(string $expectedXml, array $data): void {
$actualXml = Transformer::toXml($data);
$this->assertXmlStringEqualsXmlString($expectedXml, $actualXml);
}
}
How can I help you explore Laravel packages today?