Being a web development company quite often we end up working with various APIs. Some of these are relatively simple, but many involve sending and receiving messages in XML format. This in itself is not too much of a problem, PHP comes with many tools to handle XML. And to be honest, most of the time the XML messages are simple and straight forward enough that integration is a breeze. And with these simpler integrations it is rare to find an XML Schema to define the messages being sent back and forth.
However, recently work started on an integration with a SOAP based API which involved about 20 different requests. Each request was a block of XML contained within a SOAP envolop and defined by it’s own schema; and many of the schemas had upwards of 1000 lines defining the structure of the XML payloads.
To communicate with the API we needed to build up an object with properties matching the XML nodes, send that to the SOAP client, and it would handle the mapping to XML.
--------------------------------------- $client = new Zend_Soap_Client(); $params->node1->node2_1 = 'foo'; $params->node1->node2_2 = 'bar'; $client->methodName($params); ---------------------------------------
Would result in XML similar the following being sent
---------------------------------------
<params>
<node1>
<node2_1>foo</node2_1>
<node2_2>bar</node2_2>
</node1>
</params>
---------------------------------------
This was fine as a proof of concept for communicating with the API. Not the prettiest by any means, but we were communicating
A second iteration of the code we started looking at doing it along the lines of
--------------------------------------- $node1 = new stdClass(); $node1 ->node2_1 = 'foo' $params = new stdclass(); $params->node1 = $node1; ---------------------------------------
I liked this in that it felt cleaner, everything was being defined, and there were no E_STRICT messages appearing (If you aren’t using E_STRICT with your PHP you should be). I still wasn’t happy though. Ideally $node1 should be of a properly defined type, not just stdClass. So I was looking at creating a class for each non-leaf node in the XML tree. For the first API call we were looking at that would have amounted to about 80 different classes I would have to create, by hand.
While I certainly could have jumped in and written all those classes, I knew there had to be an easier way. Why couldn’t I just parse the XML schema and generate all the different classes? A few hours later I had a basic script which was generating code along the lines of the following
---------------------------------------
class node1type
{
var $node2_1;
var $node2_2;
public __construct(
$node2_1 = null,
$node2_2 = null
)
{
$this->node2_1 = $node2_1;
$this->node2_2 = $node2_2;
}
}
---------------------------------------
This was a good start; and it allowed me to now do something along the lines of the following, and my code was becoming a lot more developer friendly to read.
---------------------------------------
$params->node1 =
new node1type('val1', 'val2');
---------------------------------------
Following on from this I added the following features to the script
- Validation of parameters based on minOccurs and maxOccurs attributes in the schema
- Handling of arrays of objects where maxOccurs > 1
- Type checking against objects passed in
- Validation of integers, decimals and strings
- Doc comments on the constructor for when your IDE supports that kind of thing
- Generated code should not generate any errors (there will be line length warnings) when checking against the PEAR coding standard with PHP Codesniffer.
The end result is a tool which in seconds generates a list of classes which I know work; and when are on my include path, my IDE will give me type hints when I am creating instances of them. There is not full support for the XML schema spec, and some things could be handled a little differently, but it works for all schemas I need it to work on, and the generated code has already, and will in the future save us a lot of development time. I hope it can be of use to others too.
The code is released under the GPL and is available at https://github.com/davidgillen/XSD2PHP