In my role as eBay Platform Evangelist, I spend a lot of time exploring different XML technologies.
SOAP is obviously the big one. I use the PHP 5 ext/soap extension, which is great, but there’s actually another PHP SOAP extension that might be even better. No, it’s not PEAR::SOAP or NuSOAP; it’s axis2.
If you haven’t heard of axis2, you’re not alone. That’s because it’s a pecl extension that’s still in beta, so there aren’t a lot of people using it yet. However, it’s a PHP version of the Apache Axis 2.0 SOAP stack.
Unlike ext/soap, which is recreating SOAP one feature at a time, Axis 2.0 already supports a good portion of the WS-* specifications. So all that needs to happen is for someone to write the hooks between PHP and Axis 2.0, which is far easier than actually writing the features themselves.
I met a number of people from WSO2, the company that’s writing Axis 2.0, and they’re quite excited about the extension. However, they did mention it’s still in beta, so I haven’t actually spent any time using it yet.
The other PHP XML extension I’ve been hearing a lot about is SDO. SDO is an attempt to provide a standard data interface regardless of the backend datasource. So, for example, you can interact with XML data in the exact same manner as information pulled from your database.
At OSCON and ApacheCon, I’ve run into a couple of people from IBM who have been doing the heavy lifting on this extension, and we’ve had a number of interesting chats with them about eBay Web services and SDO, but I hadn’t had any free time at work to install the extension.
Therefore, when I had a little free time today between the end of my session and lunch, I sat down and reproduced a short code example that I had wrote using ext/soap with SDO instead.
Here’s the key portion of the original code:
// Print Titles and Mileage
if (isset($response->SearchResultItemArray)) {
foreach($response->SearchResultItemArray as $item) {
printf("%s\n\t%d miles\n", $item, $item->ItemSpecific['Mileage']);
}
}
This iterates through a search result for eBay Motors listings and prints out the title and the mileage for each individual item.
Normally the code would be far more complex, but through a series of ext/soap class mappings I wrote that implement the IteratorAggregate and ArrayAccess interfaces, along with the __toString() magic method, I’ve managed to abstract away a number of the complexities.
Best I can tell, SDO doesn’t give me quite the same level of control, but it does implement a number of these features for me out-of-the-box.
Here’s my rewrite using SDO:
foreach ($root->SearchResultItemArray->SearchResultItem as $item) {
$title = $item->Item->Title;
$mileage = $item->ItemSpecific["NameValueList[Name='Mileage']"]->Value[0];
printf("%s\n\t%d miles\n", $title, $mileage);
}
It’s not quite as brief, but I do get this nice XPath-like filtering that lets me pull out the the value of the car’s Mileage in one line. Pretty handy.
I had to implement the ArrayAccess interface to get this to work under ext/soap, which included this method:
public function offsetGet($name) {
if (! is_array($this->NameValueList)) {
$this->NameValueList = array($this->NameValueList);
}
foreach ($this->NameValueList as $NameValueList) {
if ($NameValueList->Name == $name) {
return $NameValueList->Value;
}
}
return null;
}
Not the hardest thing in the world to write, but this is just one of the custom class maps that will arise in our data schema, and SDO takes care of them all automatically.
Still, right now I think I prefer my ability the greater control I have over the interface with ext/soap. Our SOAP schema isn’t that pretty since there are lots of list, array, and hash wrappers. Through classmaps and interfaces I can turn these into native-looking PHP arrays and hashes.
I may be introducing a leaky abstraction, but I think this is better than exposing a NameValueListArrayType for people to wrangle with.
Like ext/soap, SDO requires you to define your data using an XML Schema. While we actually publish a stand alone XML Schema file, SDO will also happily parse a WSDL file, too, which is nice.
However, it will not directly consume a SOAP message because the WSDL doesn’t include any mention of the SOAP envelope wrapper. I needed to rip out the contents of the SOAP body into a separate XML document in order to get SDO to parse my data. Oddly, this corresponds perfectly with our “XML API,” so I could use that to retrieve properly formatted data that I can pass directly to SDO.
Where SDO really falls down for me is performance. Admittedly, eBay is a pathological case, but our WSDL file is 2.94 Megs in size. When I feed that to SDO, it takes 8.5 seconds to process the XML Schema data. Yikes!
I don’t mind a one-time start up hit, but it doesn’t appear that SDO can cache a parsed version of the schema. In contrast, ext/soap has both an on-disk and in-memory WSDL cache.
This means it takes SDO about 8.7 seconds to process the schema, load in data, and print out the information — and all with locally stored files.
In contrast, ext/soap takes as little as 0.55 seconds to pull in a cached version of the WSDL from the disk, go out over the network to query eBay via SOAP, wait for eBay’s SOAP server to run a database query on its end and send back a SOAP response, parse the SOAP envelope, and print out the exact same data. If I was using the in-memory cache, I think it’d be even faster, and I’m sure the big bottleneck here is talking to eBay.
Now, I’ve spent all of 60 minutes playing with SDO, so it’s quite possible that I’m missing some obvious configuration flag. If not, I hope IBM can do something to help speed up the performance, since while I could probably write a script to break apart our schema to create individual files that contain all the possible types on a per-call basis, I am hoping I don’t need to.
In the meantime, I’m going to continue exploring SDO to see what else I can do with it because I’ve sure I’ve only grazed its surface.
[Update: I cannot get the axis2 extension to compile, despite trying a number of different versions of Axis2c and both Linux and Mac OS X. I may try again tomorrow.]