PHP Web Services Without SOAP
by Adam Trachtenberg, coauthor of PHP Cookbook10/30/2003
In an earlier article, I outlined the basics of Web services and how you can use PHP and SOAP to talk to Amazon.com. But SOAP isn't the only way a developer can query Amazon's database. The other method is known as REST, which stands for Representational State Transfer.
REST, unlike SOAP, doesn't require you to install a separate tool kit to send and receive data. Instead, the idea is that everything you need to use Web services is already available if you know where to look. HTTP lets you communicate your intentions through GET, POST, PUT, and DELETE requests. To access resources, you request URIs from Web servers. Further, what's wrong with the seemingly umpteen-gazillion already existing ways to process XML? Between SAX, DOM, XSLT, XSL-FO, XPath, XQuery, XPointer, and XWhatever, you should be able to harness at least one of them to turn XML into whatever form you desire.
Therefore, REST advocates claim, there's no need to layer the increasingly complicated SOAP specification on top of these fundamental tools. For more on REST, see the RESTwiki and Paul Prescod's pieces on XML.com.
There may be some community support for this philosophy. While SOAP gets all the press, there are signs REST is the Web service that people actually use. Since Amazon.com has both SOAP and REST APIs, they're a great way to measure usage trends. Sure enough, at OSCon, Jeff Barr, Amazon.com's Web Services Evangelist, revealed that Amazon handles more REST than SOAP requests. I forgot the exact percentage, but Tim O'Reilly blogged this number as 85%! The number I do remember from Jeff's talk, however, is 6, as in "querying Amazon using REST is 6 times faster than with SOAP".
In my previous article, I showed how to query Amazon using SOAP and then print HTML based on the data returned. In this article, I recreate the same results using REST instead of SOAP. This helps with a comparison between the two. If you haven't read the earlier article yet, now is a good time to skim it if you're unfamiliar with the basics of SOAP and the Amazon.Com Web services SOAP API.
The first task is finding the top selling O'Reilly books on Amazon.com. For that, you create an HTTP GET request to Amazon's Web Services URI. In REST, when a URI receives a GET request, it knows to process the request and return data to the client without altering the original information.
But before you can send the request, you need a way to let Amazon know what you're looking for. REST uses the query string to pass along the appropriate parameters, so you need to build it one argument at a time:
$base = 'http://xml.amazon.com/onca/xml3';
$query_string = '';
$params = array( 'ManufacturerSearch' => "O'Reilly",
'mode' => 'books',
'sort' => '+salesrank',
'page' => 1,
'type' => 'lite',
'f' => 'xml',
't' => 'trachtenberg-20' ,
'dev-t' => 'XXXXXXXXXXXXXX' ,
);
foreach ($params as $key => $value) {
$query_string .= "$key=" . urlencode($value) . "&";
}
$url = "$base?$query_string";
The $params array contains the details of the search: you want
to find books published by O'Reilly with the results ordered by
salesrank. These parameters are similar (differing in name) to
the information provided to Amazon earlier for the SOAP request.
The only new item is f, for format. When you use SOAP, Amazon
has to reply with a SOAP packet. When you use REST, Amazon is most likely
going to reply with an XML document, but this is less a rule than a guideline.
f is set to xml so that's what you get.
Later we'll see how to instruct Amazon to reformat the data using XSLT by
altering the value of f.
The foreach iterates through $params and constructs a properly
formatted and encoded query string. Terms like O'Reilly with its
apostrophe must be urlencoded into O%27Reilly or the request
won't be valid.
Next you need to send the request and receive Amazon's reply. The
file_get_contents() function is the easiest way to do this:
$xml = file_get_contents($url);
Unfortunately, file_get_contents() is only available on PHP
4.3.0 and above. Plus, fopen wrappers need to be enabled to let
file_get_contents() access remote files. An alternative method is
to use cURL:
$c = curl_init($url);
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
$xml = curl_exec($c);
curl_close($c);
When CURLOPT_RETURNTRANSFER is set to 1, cURL stores the file
as a variable instead of echoing it out.
The XML looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<ProductInfo
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation=
"http://xml.amazon.com/schemas3/dev-lite.xsd">
<Request>
<Args>
omitted for clarity
</Args>
</Request>
<TotalResults>828</TotalResults>
<TotalPages>83</TotalPages>
<Details url="http://www.amazon.com/...">
<Asin>0596004508</Asin>
<ProductName>Mac OS X: The Missing Manual...</ProductName>
<Catalog>Book</Catalog>
<Authors>
<Author>David Pogue</Author>
</Authors>
<ReleaseDate>October, 2002</ReleaseDate>
<Manufacturer>O'Reilly & Associates</Manufacturer>
<ImageUrlSmall>http://images.amazon.com/...</ImageUrlSmall>
<ImageUrlMedium>http://images.amazon.com/...</ImageUrlMedium>
<ImageUrlLarge>http://images.amazon.com/...</ImageUrlLarge>
<Availability>Usually ships within 24 hours</Availability>
<ListPrice>$29.95</ListPrice>
<OurPrice>$20.97</OurPrice>
<UsedPrice>$14.98</UsedPrice>
</Details>
insert nine more books here
</ProductInfo>
In order to parse the XML, you need to know its data layout. At the top of
the XML, there's a link to Amazon's lite schema.
You can also eyeball this document to discover the book-related information is
located inside of the <Details> elements below the top-level
<ProductInfo> root element.
Now that you have the data stored in $xml, it's time to
transform it into something useful. That's probably HTML, but it could be SQL
for database storage or RSS for weblog readers. Since this article
compares Amazon's SOAP and REST APIs, it's the same HTML from the SOAP
piece.
You can use any of PHP's XML parsing utilities to accomplish this task, but I use XSLT here for two reasons. Why? First, Amazon offers XSLT support, so this guarantees everyone can use XSLT. (I'll show how to do this later.) Second, I feel SAX is a tad bulky just to turn XML into HTML. Besides, XSLT exists for exactly this reason; if you not going to use it here, you're never going to use it. (That said, I'm quite sure more than one person is strongly on the side of never using it.)
If you're not familiar with XSLT, Bob DuCharme has a introductory primer on XML.Com. Before getting into the nitty gritty of XSLT processing in PHP, here's the XSL to create the HTML:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="/">
<xsl:apply-templates select="ProductInfo/Details"/>
</xsl:template>
<xsl:template match="Details">
<div style="clear:left; width: 300px; padding:5px;
margin:5px; background:#ddd;">
<a href="{@url}"><image src="{ImageUrlSmall}"
alt="{ProductName}" align="left"/></a>
<b><xsl:value-of select="ProductName"/></b><br/>
<xsl:apply-templates select="Authors"/><br/>
Amazon.com Price: $<xsl:value-of
select="substring(OurPrice, 2)"/><br/>
</div>
</xsl:template>
<xsl:template match="Authors">
By
<xsl:for-each select="Author">
<xsl:choose>
<xsl:when test="not(position() = 1)">
<xsl:text> and </xsl:text>
</xsl:when>
</xsl:choose>
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Without going into too much detail, this breaks down into a few pieces.
After the obligatory set of headers, you create a template that matches
/, the root element. Within that template only
ProductInfo and Details elements are handed off for
processing. Everything else is ignored. This is analogous to the foreach loop
on $hits->Details from last time.
The Details template does most of the reformatting. The HTML is embedded
within the template. Variable values are pulled out to fill in the changing
pieces of data. Things are a tad confusing because variables are accessed in
three different ways, as {@url}, {ImageUrlSmall}, and
<xsl:value-of select="ProductName">. In brief, the first
grabs an attribute of the existing element. The second finds the value of a
child of the current element. The last does the same thing, but it's used when
you're not filling in a tag's entities.
Unfortunately, processing the authors is a bit more complex. You want to
separate each author's name with and. However, since XSLT lacks
PHP's join() function, you can't just do:
join(' and ', $hit->Authors)
Instead, you need to write your own version of join(). The Authors template iterates through each Author using a foreach loop. For every author but the first, it adds " and " and
prints the author's name by selecting ".", the character data of
the current element.
To make things easier for later, this XSLT is stored locally in a file named
amazon.xsl and placed directly under the document root.
Now here's the code to use XSLT with PHP. This requires your copy of PHP to have XSLT support. If the XSLT extension isn't enabled, and you're unable to install it, just keep on reading, and you'll see how to solve this problem.
$xsl = file_get_contents('amazon.xsl');
$xslt = xslt_create();
// map the strings to arguments
$args = array('/_xml' => $xml, '/_xsl' => $xsl);
$results = xslt_process($xslt, 'arg:/_xml', 'arg:/_xsl', NULL, $args);
xslt_free($xslt);
Just as you loaded the XML file into $xml using
file_get_contents(), you also store the XSLT file in
$xsl. Then you instantiate an instance of the XSLT processor by
calling xslt_create().
When you call xslt_process(), the first argument is the
resource handle. The second through fourth arguments are the XML, XSLT, and
output files, respectively. Passing in NULL as the output filename
makes xslt_process() return the transformed document, where it's
stored in $results. The final argument is the $args
array. By default, xslt_process() loads documents from the file
system; using $args instructs the processor to access data stored
in the PHP variables $xml and $xsl instead.
Here are the results:

Figure 1. Reformatted book data from the Amazon database
If your copy of PHP doesn't support XSLT, all is not lost. Amazon.com makes an XSLT service available, which you can use instead. It's easy to do.
First, place the stylesheet in a publicly accessible place on your Web
server, such as http://www.example.com/amazon.xsl. Next, instead
of assigning the f parameter in your REST request to
xml, set it to the URL of your XSLT document. This causes Amazon to
request the stylesheet, pass it to its XSLT processor, and return the
transformed file to you instead of the XML.
$base = 'http://xml.amazon.com/onca/xml3';
$query_string = '';
$params = array( 'ManufacturerSearch' => "O'Reilly",
'mode' => 'books',
'sort' => '+salesrank',
'page' => 1,
'type' => 'lite',
'f' => 'http://www.example.com/amazon.xsl',
't' => 'trachtenberg-20',
'dev-t'=> 'XXXXXXXXXXXXXX',
);
foreach ($params as $k => $v) {
$query_string .= "$k=" . urlencode($v) . "&";
}
$url = "$base?$query_string";
$results = file_get_contents($url);
Foisting your XSLT onto Amazon drastically simplifies your PHP. There's no need to manually invoke the XSLT program or even touch the XML. On the other hand, you're forced to use XSLT, which some might claim is a faustian bargain.
To make a more complex search, such as finding books published by O'Reilly
on PHP, turn ManufacturerSearch into PowerSearch and update the search
terms:
'PowerSearch' => "publisher:O'Reilly AND keywords:PHP"
This one line change filters our results down to four books:

Figure 2. A filtered search
The REST PowerSearch parameter accepts all the syntactical
variations as its SOAP counterpart. That makes it just as powerful and easy for
you to port your query logic from SOAP to REST and vice versa.
That's a quick tour of using Amazon.Com's REST Web services API. Unlike SOAP, an additional extension isn't necessary, nor is there all the associated overhead that comes from converting data structures from PHP to XML and back again. Instead, you have a leaner process that increases transparency and efficiency. However, that comes at the expense of requiring additional lines of code. Plus, you need to understand more about using HTTP and XML processing.
That can be a plus, not a minus. Once you have a good grasp of the basics, debugging REST is simple, since you can easily understand what's going on. In contrast, with SOAP, tracking down a bug can be a complicated mess, as you trace through SOAP packets and specifications.
Adam Trachtenberg is the manager of technical evangelism for eBay and is the author of two O'Reilly books, "Upgrading to PHP 5" and "PHP Cookbook." In February he will be speaking at Web Services Edge 2005 on "Developing E-Commerce Applications with Web Services" and at the O'Reilly booth at LinuxWorld on "Writing eBay Web Services Applications with PHP 5."
|
Related Reading PHP Cookbook |
Return to the PHP DevCenter.
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 14 of 14.
-
Where to locate php fiel on web service
2008-04-14 15:53:14 japuentem [Reply | View]
i build a web service and i want to create a client to consuming the service, the client are created in php but i don't know where to locate the php file
-
Passing parameters in URL itself, is it safe?
2007-01-29 23:18:38 Raja.p.Nagarajan [Reply | View]
Dear sir!
In REST style web services we are passing parameters to particular web service. I think this kind of passing doesn't have well security messure.
Please Security issues in REST style web services. Since i dont know about that clearly.
-
How Unaware users know about the parameters of the particular web service?
2007-01-29 23:11:31 Raja.p.Nagarajan [Reply | View]
Dear Sir!
Suppose one new user trying to access some web service in REST way. But he/she dont have clear idea about the parameters need to pass to that web service.
I read one statement from one site.In that WSDL not important for REST based web services. So how can new user aware of parameters need to pass to a particular web service?
-
Source code
2006-11-15 12:14:26 Loreen [Reply | View]
Please, can you put a complete example of this?
I mean, what code must be in wich file.
Cause maybe I don't understand it all.
Whatever, thanks and good work =)
-
error
2006-07-23 10:25:52 digitalusmedia [Reply | View]
i keep getting a permission error:
[function.file-get-contents]: failed to open stream: Permission denied in...
is there something i need to configure on my server to use this function?
-
Amazon Sales Rank
2005-08-11 15:54:59 romej.com [Reply | View]
track sales rank data using tips from this article at http://www.romej.com/rankforest
-
SOAP v. REST
2005-03-03 08:17:55 jlines79@yahoo.com [Reply | View]
This was a great article, I wanted to a make a comment about the fact that Amazon processes more REST requests than SOAP.
In my own personal experience the AWS soap implementation is fairly unconventional. I tried getting it to work using both PEAR:SOAP and nuSoap. Neither one was able to properly consume the AWS SOAP. There are also documented problems using MS .Net with amazon soap. I don't think AWS is a very good example of a SOAP webservice.
-
Connect to SOAP without SOAP
2004-11-23 02:07:39 lemms [Reply | View]
Amazon gives SOAP APIs, but assuming it does not have REST APIs, how do I connect to Amazon if REST is all I do? Does that mean I have to write out my own parser?
I'm only familar with PHP and general XML. Not familiar with SOAP so I hope this don't sound silly!
-
saops vs PHP
2004-10-14 14:59:01 bizopp [Reply | View]
My webmaster is proficient with PHP, but I would like to know what are the main advantages to SOAP over PHP?
david
-
Source Code
2004-01-20 17:37:06 bvalk [Reply | View]
I think REST seems great, and I'm trying to implement it. I've copied the code listed in the article and read up on the functions I needed more info on, but sadly, it doesn't want to work for me I get only a blank page, with just the html header tags in the source of my page. Is there anywhere available that can show me rest working? Particularly is the source code for the example in this article available anywhere? Is there any tutorial availble that shows how to set up a server.
-
other values as params
2003-11-05 03:44:37 anonymous2 [Reply | View]
I was wondering where I could get more information on what values the $params->key would accept.
Suppose I want to list all the top selling video games on amazon, how do I proceed?
-
Very interesting
2003-11-05 03:42:00 spencerpieters [Reply | View]
Hi,
Great article, got great results in no time. Are there websites that describe how to create the scripts that run on the other side of the story, ie how do we write our own webservices in PHP for example ?
-
Intro to REST
2003-11-01 04:34:44 peej [Reply | View]
I wrote an introduction to REST a while ago that may interest people: http://www.peej.co.uk/rest/
-
Screen shaping
2003-10-31 05:33:33 anonymous2 [Reply | View]
If content is output with XHTML and makes wise use of CSS, by definition is makes it ideal for screen shaping.
Whether it is ideal in terms of being a "coarse grained API" is another question (users don't like being flooded with information) - with a REST based API as you've described, there's alot more room to publish a fat API.
That said the advantage of using XHTML, with data clearly defined with id/class attributes, is the provider only needs to build one interface.
http://simon.incutio.com/archive/2003/10/21/xpathRocks



