veewee/xml
Type-safe, declarative XML toolkit for PHP. Work with DOM safely, encode/decode XML like JSON, handle errors, and stream large files with memory-safe reader/writer. Includes XSD schema tools and XSLT transformations. Spec-compliant from v4 (PHP 8.4+).
Installation:
composer require veewee/xml
Ensure PHP 8.4+ for full spec compliance (or use v3.x for older PHP versions).
First Use Case: Generate a simple XML file:
use VeeWee\Xml\Writer\Writer;
use function VeeWee\Xml\Writer\Builder\document;
use function VeeWee\Xml\Writer\Builder\element;
use function VeeWee\Xml\Writer\Builder\value;
$writer = Writer::forFile('output.xml');
$writer->write(
document('1.0', 'UTF-8',
element('root', value('Hello, XML!'))
)
);
Output: output.xml with <root>Hello, XML!</root>.
Key Entry Points:
Writer::forFile(), Writer::inMemory()).element(), attribute(), children().indentation().Pattern: Use builders to construct XML hierarchically.
$xml = element('user',
attribute('id', 123),
children([
element('name', value('John Doe')),
element('email', value('john@example.com')),
])
);
Writer::inMemory()->write($xml)->map(memory_output());
When to Use: For structured data (e.g., API responses, config files).
Pattern: Stream data using generators or iterables.
function generateItems(): Generator {
foreach (DB::query('SELECT * FROM items') as $item) {
yield element('item', value($item['name']));
}
}
Writer::forFile('large.xml')->write(
element('items', children(generateItems()))
);
When to Use: Processing large datasets (e.g., CSV-to-XML conversion).
Pattern: Centralize namespace declarations.
$xml = element('catalog',
namespace_attribute('http://example.com/ns', 'ex'),
children([
prefixed_element('ex', 'product', value('Widget')),
])
);
When to Use: Working with SOAP, XSD, or domain-specific XML schemas.
Pattern: Treat XML like JSON for serialization.
$xmlString = xml_encode(['user' => ['name' => 'Alice']]);
$data = xml_decode($xmlString);
When to Use: Quick serialization/deserialization (e.g., caching, config).
Pattern: Query and modify XML using DOM methods.
$dom = new \VeeWee\Xml\Dom\Dom('input.xml');
$nodes = $dom->query('//user[name="John"]');
foreach ($nodes as $node) {
$node->setAttribute('active', 'true');
}
$dom->save('output.xml');
When to Use: Parsing/transforming existing XML (e.g., XSLT-like operations).
Pattern: Wrap XML operations in try-catch.
try {
$writer = Writer::forFile('invalid.xml');
$writer->write(element('root', value('<?malformed'))); // Throws on write
} catch (\VeeWee\Xml\Exception\XmlException $e) {
log::error($e->getMessage());
}
When to Use: User-uploaded XML or untrusted sources.
Raw XML Injection:
raw() bypasses escaping. Malicious input can break XML or inject scripts.value() for user-provided data.
// UNSAFE
raw($_GET['input']);
// SAFE
value(htmlspecialchars($_GET['input']));
Namespace Collisions:
namespace_attribute() before prefixed_* builders.
// WRONG (missing xmlns)
prefixed_attribute('ns', 'attr', 'value');
// CORRECT
element('root',
namespace_attribute('http://ns.com', 'ns'),
prefixed_attribute('ns', 'attr', 'value')
);
Memory Leaks with inMemory():
Writer::forStream() with a temporary file or database BLOB.PHP 8.4+ Spec Compliance:
libxml_use_internal_errors(true) if migrating from v3.
libxml_use_internal_errors(true);
$dom = new \VeeWee\Xml\Dom\Dom('file.xml');
Encoding Mismatches:
xml_encode()/xml_decode() default to UTF-8. Non-UTF-8 data may corrupt.$xml = xml_encode(['data' => '€'], 'ISO-8859-1');
Validate XML:
Use libxml_get_errors() to debug malformed XML:
libxml_use_internal_errors(true);
$dom = new \VeeWee\Xml\Dom\Dom('file.xml');
foreach (libxml_get_errors() as $error) {
error_log($error->message);
}
Inspect Builders: Convert builders to XML strings for debugging:
$xmlString = Writer::inMemory()
->write($yourBuilder)
->map(memory_output());
dd($xmlString);
XSD Validation: Validate against schemas:
$validator = new \VeeWee\Xml\Xsd\Validator('schema.xsd');
$isValid = $validator->validate('data.xml');
Custom Builders:
Extend functionality by implementing the Builder interface:
class CustomTag implements \VeeWee\Xml\Writer\Builder\Builder {
public function __invoke(XMLWriter $writer): Generator {
yield $writer->startElement('custom');
yield $writer->text('dynamic content');
yield $writer->endElement();
}
}
Use with children([new CustomTag()]).
Configurators: Add global settings (e.g., auto-indentation):
Writer::configure(
\VeeWee\Xml\Writer\Configurator\indentation(' ')
);
Mappers: Convert XML to other formats (e.g., JSON):
$mapper = new class implements \VeeWee\Xml\Writer\Mapper\Mapper {
public function __invoke(string $xml): array {
return json_decode(xml_decode($xml), true);
}
};
Writer::inMemory()->write($xml)->map($mapper);
Reuse Writers:
Instantiate Writer once and reuse for multiple writes (e.g., in a loop).
$writer = Writer::forFile('output.xml');
foreach ($items as $item) {
$writer->write(element('item', value($item)));
}
Disable Output Buffering: For large files, disable PHP buffering:
Writer::forFile('large.xml', \VeeWee\Xml\Writer\Configurator\output_buffering(false));
Lazy Loading: Use generators for nested elements to defer processing:
function lazyChildren(): Generator {
foreach (fetchData() as $data) {
yield element('child', value($data));
}
}
How can I help you explore Laravel packages today?