This package is a set of XML utilities for PHP.
Examples of how the writer converts arrays to XML.
Use toString
to get the XML body string or toDomDocument
to get a PHP DOMDocument object.
use Skraeda\Xmlary\XmlWriter;
$writer = new XmlWriter;
$array = [
'People' => [
// String value should become <Marie>Unknown</Marie>
'Marie' => 'Unknown',
// Null value should become <Lao/>
'Lao' => null,
// Boolean value should become <Peter>1</Peter>
'Peter' => true,
// Nested value should become <John><Age>20</Age></John>
'John' => [
'Age' => 20,
]
]
];
echo $writer->toString($array);
outputs
<People>
<Marie>Unknown</Marie>
<Lao/>
<Peter>1</Peter>
<John>
<Age>20</Age>
</John>
</People>
$array = [
'Root' => [
'ArrayOfValues' => [
// Should create duplicate <Values> elements
'Values' => [
'String1',
2,
true,
null
]
],
'ArrayOfNestedValues' => [
// Should create duplicate <Values> elements
'Values' => [
[
'StringValue' => 'String1'
],
[
'BoolValue' => true
],
[
'NullValue' => null
],
[
'NumericValue' => 123
],
[
'Nested' => [1, 2]
]
]
]
]
];
echo $writer->toString($array);
outputs
<Root>
<ArrayOfValues>
<Values>String1</Values>
<Values>2</Values>
<Values>1</Values>
<Values/>
</ArrayOfValues>
<ArrayOfNestedValues>
<Values>
<StringValue>String1</StringValue>
</Values>
<Values>
<BoolValue>1</BoolValue>
</Values>
<Values>
<NullValue/>
</Values>
<Values>
<NumericValue>123</NumericValue>
</Values>
<Values>
<Nested>1</Nested>
<Nested>2</Nested>
</Values>
</ArrayOfNestedValues>
</Root>
This section describes some ways you can customize the XmlWriter
. You can configure the XmlWriter through interfaces, configure the inner DOMDocument
object before or after generation or add keywords
to handle individual elements differently.
You can set a custom configuration object, validator or value converter via the XmlWriter
constructor or through setter methods.
use Skraeda\Xmlary\Contracts\XmlValueConverterContract;
/**
* Custom value converter to change boolean values to true / false strings instead of 1 / 0
*/
class CustomXmlValueConverter implements XmlValueConverterContract
{
public function convert($value)
{
if (is_bool($value)) {
return $value ? 'true' : 'false';
}
return $value;
}
}
// Outputs <Root>false</Root>
echo (new XmlWriter)
->setConverter(new CustomXmlValueConverter)
->toString(['Root' => false]);
You can add middleware before or after DOMDocument
creation.
// Add namespaced attribute to root element.
$writer->pushAfterMiddleware(function ($doc) {
$namespace = $doc->createAttributeNS('http://example.com', 'example:attr');
$namespace->value = 'foo';
$doc->firstChild->appendChild($namespace);
});
// Outputs <foo:Root xmlns:example="http://example.com" example:attr="foo">value</foo:Root>
$writer->toString(['foo:Root' => 'value']);
Extend the XmlWriter
with Keywords to add some custom functionality on the element level.
use Skraeda\Xmlary\Contracts\XmlKeyword;
class CDataKeyword implements XmlKeyword
{
public function handle(DOMDocument $doc, DOMNode $parent, $value): void
{
$parent->appendChild($doc->createCDATASection($value));
}
}
echo $writer->extend('cdata', new CDataKeyword)->toString([
'Root' => [
'Element' => [
'@cdata' => 'bar',
]
]
]);
outputs
<Root><Element><![CDATA[bar]]></Element></Root>
You can use the bootstrap
method on the writer to configure it with default keywords.
attributes
: Set element attributesvalue
: Set element value (Useful if using other keywords in the same node)cdata
: Wraps a value in CDATA blockcomment
: Wraps a value in comment blockhandler
: Add a customer function handler for a node block
$writer = (new XmlWriter)->bootstrap();
$arr = [
'Root' => [
'@attributes' => [
'xmlns:example' => 'http://example.com',
'example:attr' => 'foo'
],
'Element' => [
'@attributes' => [
'foo' => 'bar'
],
'@value' => 'Value'
],
'CData' => [
'@cdata' => 'Wrapped',
'@value' => 'NotWrapped'
],
'Comment' => [
'@value' => 'Value',
'@comment' => 'Comment'
],
'Handler' => [
'@handler' => function (DOMDocument $doc, DOMNode $parent) {
$parent->appendChild($doc->createElement('Name', 'Value'));
}
]
]
];
echo $writer->toString($arr);
Examples of how the reader converts XML to arrays.
$xml = <<<XML
<Root>
<Element id="1">Value</Element>
</Root>
XML;
(new XmlReader)->parse($xml));
outputs
[
'Root' => [
'Element' => [
'@attributes' => [
'id' => '1'
],
'@value' => Value
]
]
]
$xml = <<<XML
<Order>
<Items>
<Item id="1">
<Price>2.00</Price>
<Type>Drink</Type>
<Supplier>
<Organization>
<OrganizationName />
</Organization>
</Supplier>
</Item>
<Item id="3">
<Price><![CDATA[1.00]]></Price>
<Type>Misc</Type>
<Supplier />
</Item>
</Items>
</Order>
XML;
outputs
[
'Order' => [
'Items' => [
'Item' => [
[
'@attributes' => [
'id' => 1
],
'Price' => [
'@value' => '2.00'
],
'Supplier' => [
'Organization' => [
'OrganizationName' => [
'@value' => null
]
]
]
],
[
'@attributes' => [
'id' => 3
],
'Price' => [
'@value' => '1.00'
]
'Type' => [
'@value' => 'Misc'
],
'Supplier' => [
'@value' => null
]
]
]
]
]
]
You can provide a configuration mapping for the nodes for some basic changes to the generated array.
use Skraeda\Xmlary\XmlReaderNodeConfiguration as Config;
$xml = <<<XML
<Root>
<Element>1</Element>
<Inner><Sheet><Data>2</Data></Sheet></Inner>
</Root>
XML;
$reader = new XmlReader;
$config = [
'Root' => [
// Provide config to rename Root element
'@config' => new Config('NewRoot'),
'Element' => [
// Provide config to ensure Element is always an array element and add custom value converter to cast to int and add +2 to the value.
'@config' => new Config(null, true, function ($oldValue) {
return ((int) $oldValue) + 2;
})
],
'Inner' => [
'@config' => new Config(null, false, function ($oldValue) use ($reader) {
return $reader->parse(html_entity_decode($oldValue));
}, function (XmlReaderNode $node) {
// Callback handler to execute when the ReaderNode is created..
})
]
]
];
$reader->parse($xml, $config);
outputs
[
'NewRoot' => [
'Element' => [
[
'@value' => 3
]
],
'Inner' => [
'@value' => [
'Sheet' => [
'Data' => [
'@value' => '2'
]
]
]
]
]
]
You can create custom config classes to reduce repitition if you need.
Either extend the reader node configuration class or implement the interface.
This section describes how you can customize the XmlReader
.
You can set a custom configuration object through the constructor or setter methods.
Interface you can define on a model so it can be formatted as XML by an XmlWriter
.
You need to define xmlSerialize
on your model which should return an array similar to the above examples.
Trait you can add on a model to give it a default xmlSerialize
handler using reflection.
Abstract base class you can extend to give your object a default xmlSerialize
handler using reflection.
use Skraeda\Xmlary\XmlMessage;
class MyMessage extends XmlMessage
{
public $Public = 'PublicValue';
protected $Protected = 'ProtectedValue';
private $Private = 'PrivateValue';
public $Array;
public $Inner;
public $Changer = 'WillBeChanged';
public $Attrs = null;
public function __construct(?MyMessage $Inner = null)
{
$this->Array = ['Field' => 'Value'];
$this->Inner = $Inner;
}
protected function ChangerMutator()
{
return 'ChangedValue';
}
// Uses AttributeKeyword and ValueKeyword
protected function AttrsAttributes()
{
return [
'value' => 'AttributeValue'
];
}
protected function AttrsTag()
{
return 'AttributeTag';
}
protected function attributes()
{
return [
'foo' => 'bar'
];
}
}
echo $writer->toString(new MyMessage(new MyMessage));
outputs
<MyMessage foo="bar">
<Public>PublicValue</Public>
<Protected>ProtectedValue</Protected>
<Private>PrivateValue</Private>
<Array>
<Field>Value</Field>
</Array>
<Inner>
<MyMessage foo="bar">
<Public>PublicValue</Public>
<Protected>ProtectedValue</Protected>
<Private>PrivateValue</Private>
<Array>
<Field>Value</Field>
</Array>
<Inner/>
<Changer>ChangedValue</Changer>
<AttributeTag name="AttributeValue"/>
</MyMessage>
</Inner>
<Changer>ChangedValue</Changer>
<AttributeTag name="AttributeValue">
</MyMessage>
PHP7.2 CLI dockerfile included, use it to test any new functionality.
docker-compose up
docker-compose exec app vendor/bin/phpunit