Article
Instant XML with PHP and PEAR::XML_Serializer
And the resulting XML is as follows:
<?xml version="1.0" encoding="ISO-8859-1"?>
<palette>
<red>45</red>
<green>240</green>
<blue>120</blue>
</palette>
Notice that the tag names now correspond to the keys of the $palette variable. If I use this example again, adding the 'scalarAsAttributes' option (see example file palette3.php), here's the XML I get back:
<?xml version="1.0" encoding="ISO-8859-1"?>
<palette blue="120" green="240" red="45" />
The scalar integer values now become attributes of the root tag rather than being represented as separate tags.
Another example shows what happens when you serialize objects and take advantage of the 'typeHints' option:
<?php
// Set error reporting to ignore notices
error_reporting(E_ALL ^ E_NOTICE);
// Include XML_Serializer
require_once 'XML/Serializer.php';
// A class to store color information
class ColorInformation {
var $hue;
var $value;
function ColorInformation($hue = NULL, $value = NULL) {
$this->hue = $hue;
$this->value = $value;
}
}
// Some data to transform
$palette = array();
$palette[] = &new ColorInformation('red', 45);
$palette[] = &new ColorInformation('green', 240);
$palette[] = &new ColorInformation('blue', 120);
// An array of serializer options
$serializer_options = array (
'addDecl' => TRUE,
'encoding' => 'ISO-8859-1',
'indent' => ' ',
'indentAttributes' => '_auto',
'rootName' => 'palette',
'defaultTagName' => 'color',
'typeHints' => TRUE,
);
// Instantiate the serializer with the options
$Serializer = &new XML_Serializer($serializer_options);
// Serialize the data structure
$status = $Serializer->serialize($palette);
// Check whether serialization worked
if (PEAR::isError($status)) {
die($status->getMessage());
}
// Display the XML document
header('Content-type: text/xml');
echo $Serializer->getSerializedData();
?>
Filename: palette4.php
The corresponding XML looks like this:
<?xml version="1.0" encoding="ISO-8859-1"?>
<palette _type="array">
<color _class="colorinformation"
_originalKey="0"
_type="object">
<hue _type="string">red</hue>
<value _type="integer">45</value>
</color>
<color _class="colorinformation"
_originalKey="1"
_type="object">
<hue _type="string">green</hue>
<value _type="integer">240</value>
</color>
<color _class="colorinformation"
_originalKey="2"
_type="object">
<hue _type="string">blue</hue>
<value _type="integer">120</value>
</color>
</palette>
With 'typeHints' switched on, attributes are added that describe the original data structure is some detail, '_type' referring to the original PHP variable type, '_class' storing the class name of any serialized objects, and '_originalKey' being the key of the indexed array in which this element was found, if applicable.
The 'typeHints' functionality is useful when you need to make sure that when you unserialize the document, you get back exactly what you started with. You might want to use 'typeHints' if you're using PEAR::XML_Serializer to store persistent data that your code will retrieve later. Note that when you need a precise representation of a PHP data structure using typeHints, it's a good idea to avoid using the 'scalarAsAttributes' option, which loses information about scalar types.
Finally, the following example shows how you can add DOCTYPE declarations, in this example, to render XHTML:
<?php
// Set error reporting to ignore notices
error_reporting(E_ALL ^ E_NOTICE);
// Include XML_Serializer
require_once 'XML/Serializer.php';
// PHP data structure representing an XHTML document
$xhtml = array
(
'head' => array (
'title' => 'XHTML with XML_Serializer',
),
'body' => array (
'h1' => 'XHTML with XML_Serializer',
'p' => 'It\'s possible but not recommended',
),
);
// XML_Serializer options
$serializer_options = array (
'addDecl' => TRUE,
'encoding' => 'ISO-8859-1',
'indent' => ' ',
'rootName' => 'html',
'rootAttributes' => array (
'xmlns' => 'http://www.w3.org/1999/xhtml',
'lang' => 'en',
'xml:lang' => 'en'
),
'addDoctype' => TRUE,
'doctype' => array (
'id' => '-//W3C//DTD XHTML 1.0 Strict//EN',
'uri' => 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
),
);
// Create the serializer
$Serializer = &new XML_Serializer($serializer_options);
// Serialize the XHTML
$status = $Serializer->serialize($xhtml);
// Check for errors
if (PEAR::isError($status)) {
die($status->getMessage());
}
// Send the right HTTP header
// See http://www.juicystudio.com/tutorial/xhtml/mime.asp for more info
if (stristr($_SERVER[HTTP_ACCEPT], 'application/xhtml+xml')) {
header('Content-Type: application/xhtml+xml; charset=ISO-8859-1');
} else {
header('Content-Type: text/html; charset=ISO-8859-1');
}
// Display the XML document
echo $Serializer->getSerializedData();
?>
Filename: xhtml.php
Notice the 'doctype' option. The array defined here is actually determined by PEAR::XML_Util in its getDocTypeDeclaration() method. Notice also that I used the 'rootAttributes' option to add attributes to the root html tag. Here's the resulting XHTML:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>XHTML with XML_Serializer</title>
</head>
<body>
<h1>XHTML with XML_Serializer</h1>
<p>It's possible but not recommended</p>
</body>
</html>
This example is meant purely to show how DOCTYPE declarations can be used. In practice, I don't recommend you generate XHTML with XML_Serializer, as the API does not provide you with fine grained control over the transformation, and you're likely to spend a lot of time fiddling with giant PHP arrays and generally losing hair.
The XML_Unserializer API
You've seen the essentials of the XML_Serializer API; XML_Unserializer, which is used to transform XML to PHP data structures, is essentially its "mirror image".
The main public methods are:
object XML_Unserializer([array options]) This constructor accepts an optional array of options (described below).mixed unserialize(mixed data, [boolean isFile], [array options]) The first argument, data, can be a string containing the XML document, the path to a file containing the XML (in which case the second argument isFile must be set to true) or a PHP resource implementing the steams API, such as a file you've already opened or an instance of one of PEAR's stream wrappers. The third optional argument, options, is an array of options as described below. The value returned fromunserialize()will either be TRUE on success or a PEAR Error object.mixed getUnserializedData()This returns either the PHP data structure represented by the XML document or a PEAR Error object.mixed getRootName()This method returns the name of the root element (typically invisible in the data structure returnedfrom getUnserializedData()) or a PEAR Error object.void setOption(string name, mixed value) Use this to set an individual option.void resetOptions()This method resets all options to their default state.
The options available for use with XML_Unserializer are:
parseAttributes(default = FALSE) whether tag attributes should be turned into arrays; if set to false, attributes are ignoredattributesArray(default = FALSE) the name to use to identify the array into switch attributes will be placedprependAttributes(default = "") use to prepend the keys of the array, into which attributes will be placedcomplexType(default = "array") if no typeHint is found, complex types (tags containing a mix of CDATA and other child tags) will be converted to arrayscontentName(default = "_content") for complex types, CDATA inside the tag will be placed in the resulting arraytagMap(default = array()) allows you to map an XML tag name to the name of a PHP class (see below for example); overrides the value of the classAttribute if there are typeHints in the XML document being unserializedkeyAttribute(default = "_originalKey") identifies the attribute used as the typeHint for the indexed array key (see the XML_Serializer option of the same name)typeAttribute(default = "_type") identifies the PHP data type, used as the typeHint (see the XML_Serializer option of the same name)classAttribute(default = "_class") identifies the PHP name of a PHP class that this tag represents an object of (see the XML_Serializer option of the same name)
As with the XML_Serializer class, the 'overrideOptions' option can be passed to the unserialize() method to ignore values already passed to the constructor.
To provide a basic illustration of XML_Unserializer, I'll take the output from XML_Serializer examples you saw above and see what we get back when we unserialize them.