Jason Houle
Web Site Development
Who is Jason Houle?
I am a developer (not so much a designer) who makes a living as a Java developer for an entertainment company located in Orlando, FL. In my spare time I work as a freelance web site developer specializing in PHP and MySQL powered web sites. You can view some of my work on my portfolio site which has just been redesigned.The site is built using PHP with a MySQL database and the layout is done using CSS. AJAX has been used in some areas of the site to improve usability.
Converting PHP5 Objects to XML
During my re-write of TheWeddingVendor.com I decided to use the Model-View-Controller (MVC) design. I also wanted to greatly enhance the user experience through AJAX calls and JavaScript effects. I found myself using alot of XML and JSON to represent my data. I began to look for a way to convert my PHP objects to XML along the lines of JAXB in the Java world (although JAXB can be a little more complicated than what I needed).
I found a couple PEAR packages, such as XML/Serializer, which I did not immediately have access to. Rather than spend a lot of time searching for something that would meet my needs I decided to simply tackle the task myself. It turned out to be a lot easier than I had originally thought it would be. A little magic with Iterators and DOMDocument and it was done.
The first thing I did was to create an ObjectToXML class. This class has one private field which is going to be our DOMDocument. Since there really should be no need to manipulate the DOM, since this is a straight Object to XML conversion, I have made this field private. I would suggest doing the same and if you need to modify the DOM in any way do it through methods or after the document has been returned. There are no getter / setter methods because this just didn't make sense. We already have the PHP object so there is no reason to need to retrieve data from the XML document.
ObjectToXML.php
class ObjectToXML {
private $dom;
}
Next we need to write our constructor. Our constructor must take in an object and create a DOMDocument so that is exactly what we will do. We will also set the root of the XML document to be the class name of the object passed to the constructor. Let's override the __toString() method as well, so we can output our XML.
ObjectToXML.php
class ObjectToXML {
private $dom;
// constructor takes an object and creates a DOMDocument
public function __construct($obj) {
$this->dom = new DOMDocument("1.0", "UTF8");
$root = $this->dom->createElement(get_class($obj));
$this->dom->appendChild($root);
}
// override the __toString() method to output the XML
public function __toString() {
return $this->dom->saveXML();
}
}
This isn't much yet but let's see what it does. First we create a Car class which we will use to test our conversion to XML.
Test.php
require_once("ObjectToXML.php");
class Car {
public $make;
public $model;
public $color;
public $numDoors;
public function __construct($make, $model, $color) {
$this->make = $make;
$this->model = $model;
$this->color = $color;
}
}
// create our car object
$car = new Car("Ford", "Thunderbird SC", "red", 2);
// use the converter
$xml = new ObjectToXML($car);
// output the XML
header('Content-type: text/xml');
echo $xml;
You should see a very simple XML document <Car/>. So as you can see we have already turned our PHP Object into an XML document. Of course this isn't very helpful because we do not have any of the fields, or properties, that make up our Car object.
One of the great things about PHP Objects is the fact that they are iterable, meaning that we can loop through their properties as we would an array. This means we simply need to loop through the fields and create a node for each one. Since we are going to do this multiple times it only makes sense to create a seperate method to handle this work for us.
ObjectToXML.php
class ObjectToXML {
private $dom;
// constructor takes an object and creates a DOMDocument
public function __construct($obj) {
$this->dom = new DOMDocument("1.0", "UTF8");
$root = $this->dom->createElement(get_class($obj));
// loop through the objects properties
foreach($obj as $key=>$value) {
$node = $this->createNode($key, $value);
if($node != NULL) {
$root->appendChild($node);
}
}
$this->dom->appendChild($root);
}
// creates a node for each property
private function createNode($key, $value) {
$node = NULL;
$node = $this->dom->createElement($key, $value);
return $node;
}
// override the __toString() method to output the XML
public function __toString() {
return $this->dom->saveXML();
}
}
So far, so good. Notice that we did not assign a value for $numDoors but an empty node was still created. This is exactly the behavior we want for this type of situation. There are a few things that we did not account for here. What if one of our properties is a boolean? Our current code works great for strings and numeric types but will not work for booleans, arrays, or objects. We need to fix our createNode() function to account for these types.
ObjectToXML.php
class ObjectToXML {
...
private function createNode($key, $value) {
$node = NULL;
// check to see if value is a string or number
if(is_string($value) || is_numeric($value) || is_bool($value) || $value == NULL) {
// check for NULL values
if($value == NULL) {
$node = $this->dom->createElement($key);
} else {
$node = $this->dom->createElement($key, (string)$value);
}
// value is an object or an array
} else {
$node = $this->dom->createElement($key);
foreach($value as $key=>$value) {
// This function calls itself
$sub = $this->createNode($key, $value);
if($sub != NULL) {
$node->appendChild($sub);
}
}
}
return $node;
}
...
}
Now that we have added the ability to add elements for arrays, booleans, and objects let's give it a try. One thing to not is that our conversion of a boolean to a string will result in a 1 or a 0. If you wish to use the text "true" or "false" then you must explicitly do so. $value = ($value) ? "true" : "false"; would work just fine. Let's modify our Car class to two more fields, one a boolean name $isConvertible, and an object called $engine.
Test.php
require_once("ObjectToXML.php");
class Car {
public $make;
public $model;
public $color;
public $numDoors;
public $isConvertible = false;;
public $engine;
public function __construct($make, $model, $color) {
$this->make = $make;
$this->model = $model;
$this->color = $color;
}
}
class Engine {
public $cylinders;
public $displacement;
public $numValves;
public function __construct($cyl, $disp, $valves) {
$this->cylinders = $cyl;
$this->displacement = $disp;
$this->numValves = $valves;
}
}
// create our car object
$car = new Car("Ford", "Thunderbird SC", "red", 2);
$car->isConvertible = true;
$car->engine = new Engine(6, 3.2, 16);
// use the converter
$xml = new ObjectToXML($car);
// output the XML
header('Content-type: text/xml');
echo $xml;
Output
<Car>
<make>Ford</make>
<model>Thunderbird SC</model>
<color>red</color>
<numDoors/>
<isConvertible>1</isConvertible>
<engine>
<cylinders>6</cylinders>
<displacement>3.2</displacement>
<numValves>16</numValves>
</engine>
</Car>
And there you have it. In about 30 lines of code we have converted our PHP objects into XML. Below you can find the completed ObjectToXML class.
ObjectToXML.php
class ObjectToXML {
private $dom;
public function __construct($obj) {
$this->dom = new DOMDocument("1.0", "UTF8");
$root = $this->dom->createElement(get_class($obj));
foreach($obj as $key=>$value) {
$node = $this->createNode($key, $value);
if($node != NULL) $root->appendChild($node);
}
$this->dom->appendChild($root);
}
private function createNode($key, $value) {
$node = NULL;
if(is_string($value) || is_numeric($value) || is_bool($value) || $value == NULL) {
if($value == NULL) $node = $this->dom->createElement($key);
else $node = $this->dom->createElement($key, (string)$value);
} else {
$node = $this->dom->createElement($key);
if($value != NULL) {
foreach($value as $key=>$value) {
$sub = $this->createNode($key, $value);
if($sub != NULL) $node->appendChild($sub);
}
}
}
return $node;
}
public function __toString() {
return $this->dom->saveXML();
}
}
Posted by JasonHoule
06-24-2008 03:06:14

