UXML is an extremely simple PHP library for manipulating XML documents with ease while keeping overhead to a bare minimum.
It consist of just a single class which uses the PHP built-in DOMElement
and DOMDocument
classes under the hood.
composer require josemmo/uxml
Download source files from the GitHub repository:
git clone https://github.com/josemmo/uxml.git
Use the UXML class in your app:
use UXML\UXML;
require_once __DIR__ . "/uxml/src/UXML.php";
Why use this instead of sabre/xml or FluidXML?
Both those options are great and if they fit your project you should definetely use them! However, in my case I needed something more lightweight to put on top of LibXML's DOM to provide an alternative syntax.
Is UXML compatible with DOMElement
?
Yes, indeed! You can get the original DOMElement
instance from an UXML
object and vice versa.
I want UXML to do "X". Can you implement it?
My main goal with this project is not to implement all possible behaviors in the XML specification, you can use the DOM or SimpleXML libraries for that.
UXML does not distinguish between XML documents (DOMDocument
) and elements (DOMElement
). Instead, you can create a new document like so:
$xml = \UXML\UXML::newInstance('RootTagName');
You can also wrap an already existing DOMElement
:
$domElement = new DOMElement('TagName');
$xml = \UXML\UXML::fromElement($domElement);
By loading an XML string, UXML will return the root element of the document tree:
$source = <<<XML
<fruits>
<fruit>Banana</fruit>
<fruit>Apple</fruit>
<fruit>Tomato</fruit>
</fruits>
XML;
$xml = \UXML\UXML::fromString($source);
When adding an element, UXML will return a reference to the newly created element:
$xml = \UXML\UXML::newInstance('Parent');
$child = $xml->add('Child');
echo $child; // <Child />
echo $xml; // <Parent><Child /></Parent>
You can also define a value:
$child = $xml->add('Child', 'Hello World!');
echo $child; // <Child>Hello World!</Child>
And even attributes or namespaces:
$feed = \UXML\UXML::newInstance('feed', null, [
'xmlns' => 'https://www.w3.org/2005/Atom'
]);
echo $feed; // <feed xmlns="https://www.w3.org/2005/Atom" />
$link = $feed->add('link', 'Wow!', [
'href' => 'https://www.example.com'
]);
echo $link; // <link href="https://www.example.com">Wow!</link>
Because with every element insertion a reference to the new element is returned, you can chain multiple of these calls to create a tree:
$xml = \UXML\UXML::newInstance('people');
$xml->add('person')->add('name', 'Jane Doe');
echo $xml; // <people>
// <person>
// <name>Jane Doe</name>
// </person>
// </people>
Besides casting UXML
objects to a string
, there is a method for exporting the XML source of an element and its children:
$xml->asXML();
By default, exported strings include an XML declaration (except when casting UXML
instances to a string
).
UXML allows you to use XPath 1.0 queries to get a particular element from a document:
$xml = \UXML\UXML::newInstance('person');
$xml->add('name', 'Jane');
$xml->add('surname', 'Doe');
$xml->add('color', 'green', ['hex' => '#0f0']);
echo $xml->get('*[@hex]'); // <color hex="#0f0">green</color>
var_dump($xml->get('birthday')); // NULL
Or even multiple elements:
$xml = \UXML\UXML::fromString('<a><b>1</b><b>2</b><b>3</b></a>');
foreach ($xml->getAll('b') as $elem) {
echo "Element says: " . $elem->asText() . "\n";
}
Note all XPath queries are relative to current element:
$source = <<<XML
<movie>
<name>Inception</name>
<year>2010</year>
<director>
<name>Christopher</name>
<surname>Nolan</surname>
<year>1970</year>
</director>
</movie>
XML;
$xml = \UXML\UXML::fromString($source);
echo $xml->get('director/year'); // <year>1970</year>
echo $xml->get('director')->get('year'); // <year>1970</year>
echo $xml->get('year'); // <year>2010</year>
echo $xml->get('director')->get('//year'); // <year>2010</year>
Elements can be removed from the XML tree by calling the remove()
method on them.
After an element is removed, it becomes unusable:
$source = <<<XML
<project>
<public>
<name>Alpha</name>
</public>
<confidential>
<budget>1,000,000 USD</budget>
</confidential>
</project>
XML;
$xml = \UXML\UXML::fromString($source);
$xml->get('confidential')->remove();
echo $xml; // <project><public><name>Alpha</name></public></project>
Namespaces are assigned in the same way as other attributes:
$xml->add('TagName', null, [
'xmlns' => 'https://example.com',
'xmlns:abc' => 'urn:abc',
'attribute' => 'value'
])->add('abc:Child', 'Name');
echo $xml; // <TagName xmlns="https://example.com"
// xmlns:abc="urn:abc"
// attribute="value">
// <abc:Child>Name</abc:Child>
// </TagName>
However, when querying elements, the prefix defined in the document may not be the one you are expecting:
$xml = \UXML\UXML::fromString('<a xmlns:ns="urn:abc"><ns:b /></a>');
echo $xml->get('ns:b'); // <ns:b />
echo $xml->get('abc:b'); // Is NULL as the prefix does not exist
To fix this, you can make use of clark notation inside the XPath query:
echo $xml->get('{urn:abc}b'); // <ns:b />
For any other document manipulation outside the scope of this library, you can always interact with the DOMElement
instance:
$xml = \UXML\UXML::newInstance('Test');
$xml->element(); // Returns a [DOMElement] object