XMLTransformer is a PHP library for transforming any kind of input XML into an output string. This output string does not have to be XML, but can also be, for instance, HTML or plain text.
XMLTransformer is able to …
In my opinion, XMLTransformer performs very well if the input XML and the output to be produced are similarly structured. Moreover, if data from the input XML has to be processed by an existing PHP codebase, it is possibly cleaner and simpler to use XMLTransformer instead of XSL-T.
When the input data has to be re-arranged, you are probably better off with XSL-T, as this is something that XMLTransformer does not provide. (Although to some extent it can be done with appropriate callback code.) Of course you are free to combine XSL-T with XMLTransformer to get the best of both worlds, if one is not enough.
The recommended way to install this library is through Composer. For this, add "bluem/xmltransformer": "~2.0"
to the requirements in your composer.json
file. As the library uses semantic versioning, you will get fixes and feature additions, but not changes which break the API.
Alternatively, you can clone the repository using git or download an archived release.
You pass the input XML and the name of a callback function or a callback method (specified as usual, using [$object, 'methodName']
syntax) or an anonymous function / closure to XMLTransformer
.
For each tag (opening, closing or empty) the callback function will be called with the tag’s name, its attributes and information on whether it is an opening, empty or closing tag. Now, your function / method / closure can return one of three things:
false
(meaning: discard this tag, its attributes as well as any tags and any child elements)null
(meaning: don’t modify anything – this is the default behaviour, i.e.: if the callback returns nothing, nothing is changed.The callback function / method / closure is called with three arguments:
XMLTransformer::ELEMENT_OPEN
for an opening tag, XMLTransformer::ELEMENT_EMPTY
for an empty tag and XMLTransformer::ELEMENT_CLOSE
for a closing tag.Please note that the attributes will always be given, even for a closing tag.
When you wish to perform a transformation, you must return an associative array. In this case, the following keys can be used:
XMLTransformer::RULE_TAG
: Returning false for key “tag” removes the tag (incl. its attributes, of course), but keeps any enclosed content. Returning a string will set the tag name to that string.XMLTransformer::RULE_ADD_BEFORE
: Will insert the given string before the opening tagXMLTransformer::RULE_ADD_AFTER
: Will insert the given string after the closing tagXMLTransformer::RULE_ADD_START
: Will insert the given string right after the opening tagXMLTransformer::RULE_ADD_END
: Will insert the given string right before the closing tagXMLTransformer::RULE_TRANSFORM_OUTER
: Value must be a closure, which will be passed the element itself incl. all its content as a string. The closure’s return value will replace the element.XMLTransformer::RULE_TRANSFORM_INNER
: Value must be a closure, which will be passed the element’s content as a string. The closure’s return value will replace the element.Additionally, for handling attributes, array keys in the form of “@
For instance, this return array …
return [
XMLTransformer::RULE_TAG => 'demo',
'@xml:id' => 'id',
'@foo' => false,
XMLTransformer::RULE_ADD_AFTER => '!',
];
… means:
Please note that (as XMLTransformer
is not restricted to produce XML) no automatic escaping is done to values returned by the array. Only exception: attribute values, as XMLTransformer assumes that if you set attribute values, you want XML or HTML output.
The callback can accept the arguments’ array by reference, therefore allowing direct manipulation of the attributes. This can be handy when changing or removing a large number of attributes or when only a prefix or suffix (or namespace) of attributes’ names is known in advance.
See below for an example.
All of the examples below assume that your code includes the usual boilerplate code for Composer autoloading:
require 'vendor/autoload.php';
use BlueM\XMLTransformer;
echo XMLTransformer::transformString(
'<root><element>Hello world</element></root>',
function($tag, $attributes, $opening) {
return [
XMLTransformer::RULE_TAG => false, // <-- Removes tag, but keeps content
];
}
);
// Result: “Hello World”.
use BlueM\XMLTransformer;
function transform($tag, $attributes, $opening) {
if ('hello-world' == $tag) {
if (isset($attributes['xml:lang']) and
'de' == $attributes['xml:lang']) {
$str = 'Hallo Welt';
} else {
$str = 'Hello world';
}
return [
XMLTransformer::RULE_TAG => false, // <-- Remove the tag, keep content
XMLTransformer::RULE_ADD_BEFORE => $str, // <- Insert literal content
];
}
if ('root' == $tag) {
// We do not want the enclosing <root> tags in the output
return [XMLTransformer::RULE_TAG => false];
}
}
echo XMLTransformer::transformString(
'<root><hello-world xml:lang="de" /></root>',
'transform'
);
// Result: “Hallo Welt”
echo XMLTransformer::transformString(
'<root><hello-world xml:lang="en" /></root>',
'transform'
);
// Result: “Hello world”
echo XMLTransformer::transformString(
'<root><remove>Hello </remove>World</root>',
function($tag, $attributes, $opening) {
switch ($tag) {
case 'remove':
return false; // <-- Removes tag incl. content
case 'root':
case 'keep':
return [XMLTransformer::RULE_TAG => false]; // <-- Remove tag, keep content
break;
default:
// Returning null is not necessary, as this
// is the default behaviour. It is equivalent
// to "Do not change anything."
return null;
}
}
);
// Result: “World”
echo XMLTransformer::transformString(
'<root abc="def"></root>',
function($tag, $attributes, $opening) {
return [
'@abc' => 'xyz'
];
}
);
// Result: “<root abc="xyz"></root>”
// Please note that empty tags will always be returned with
// a space before the slash.
echo XMLTransformer::transformString(
'<root xml:id="abc"><bla xml:id="def" blah="yes"/></root>',
function($tag, $attributes, $opening) {
return [
'@foo' => 'bar', // Add attribute "foo" with value "bar"
'@blah' => false, // Remove attribute "blah"
'@xml:id' => '@id', // Rename attribute "xml:id" to "id"
];
}
);
// Result: “<root id="abc" foo="bar"><bla id="def" foo="bar" /></root>”
// Please note that empty tags will always be returned with
// a space before the slash.
echo XMLTransformer::transformString(
'<root xml:a="a" xml:b="b" id="foo">Content</root>',
function($tag, &$attributes, $opening) {
foreach ($attributes as $name => $value) {
if ('xml:' === substr($name, 0, 4)) {
unset($attributes[$name]); // Drop attributes in "xml" namespace
}
}
}
);
// Result: “<root id="foo">Content</root>”
This code was written by Carsten Blüm (www.bluem.net) and licensed under the BSD2 license.
null
is returned as value for XMLTransformer::RULE_TAG
(which is not supposed to be done)..travis.yml
, as Travis CI does not support PHP 8 yet).insend
to XMLTransformer::RULE_ADD_END
insafter
to XMLTransformer::RULE_ADD_AFTER
insbefore
to XMLTransformer::RULE_ADD_BEFORE
insstart
to XMLTransformer::RULE_ADD_START
transformInner
to XMLTransformer::RULE_TRANSFORM_INNER
transformOuter
to XMLTransformer::RULE_TRANSFORM_OUTER
tag
to XMLTransformer::RULE_TAG
XMLTransformer::ELOPEN
was renamed to XMLTransformer::ELEMENT_OPEN
, XMLTransformer::ELEMPTY
was renamed to XMLTransformer::ELEMENT_EMPTY
and XMLTransformer::ELCLOSE
was renamed to XMLTransformer::ELEMENT_CLOSE
.transformString()
to false, CDATA content is replaced with but as PCDATA content with <
and >
and &
escaped.