ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


PHP Cookbook

A PHP Web Services Client

by Adam Trachtenberg, coauthor of PHP Cookbook
07/03/2003

Web services allow you to exchange information over HTTP using XML. When you want to find out the weather forecast for New York City, the current stock price of IBM, or the best-selling DVD according to Amazon.com, you can write a short script to gather that data in a format you can easily manipulate. From a developer's perspective, it's as if you're calling a local function that returns a value.

A major advantage of web services is ubiquity across platforms and languages. A PHP script running on Linux can talk to an IIS server on a Windows box using ASP without any communication problems. When the server switches over to Solaris, Apache, and JSP, everything transitions without a glitch.

SOAP is the most popular web services format. It's a W3C standard for passing messages across the network and calling functions on remote computers. This article shows how to create a SOAP client in PHP. The examples below demonstrate how to query Amazon's SOAP server to pull data from their site onto yours. From there, you can include a listing of newly released books on PHP on your web site, gather information about your CD collection, or even make a customized wedding registry site. The options are only limited by your imagination and Amazon's lawyers.

For a more detailed introduction to web services, see Venu Vasudevan's Web Services Primer on XML.com. XML.com also has an article where Don Box deconstructs SOAP, called Inside SOAP.

PHP does not come with a bundled SOAP extension. Before you can begin, you need to download and install files that let you easily integrate SOAP into your applications. There are three major SOAP implementations for PHP: PEAR::SOAP, NuSOAP, and PHP-SOAP. The first two have more features, but they're written in 100% PHP, so they're slowish. (Not slow, mind you, just slowish. They're perfectly fine for medium-traffic sites.) PHP-SOAP, however, is written in C, so it's much faster. That said, in this case, I prefer comprehensiveness and correctness over speed; therefore, I use PEAR::SOAP because it's simple to install and works for me.

If pear, the PEAR package manager, is installed on your machine, run the following command in your shell:

% pear install SOAP

This will download, unzip, and install PEAR::SOAP. Depending on which packages you've yet to install, you may get an dependency error. That means SOAP relies upon another package, like Net_URL, to handle some aspect of its business, but you don't have this package on your machine. If this happens, you'll see a message similar to this:

downloading SOAP-0.7.3.tgz ...
...done: 73,630 bytes
requires package `Net_URL'
SOAP: dependencies failed

Solving the problem is easy. Just enter:

% pear install Net_URL SOAP

Now, pear will first install Net_URL and then SOAP. If a different package name is printed, substitute that name for Net_URL. If pear echoes multiple dependencies, place all of the package names on the command line before SOAP. (Depending upon your configuration, you may need to be the superuser to install PEAR packages.) See PHP's PEAR on Mac OS X or Chapter 21 of PHP Cookbook for additional information on PEAR.

Next, you need to head over to Amazon's web services site to download their developer's kit and apply for a developer's token. The application requires you to agree to a set of legal restrictions, such as not bombarding Amazon's servers with requests or using their data to construct a store that lets you buy things from Barnes and Noble.

Amazon's developer archive contains a series of folders containing example code and applications in a several languages, including PHP, Java, Visual Basic, and even XSL. Right now, you're not going to use any of the files, but they'll come in handy in the future. As Amazon offers many different ways of searching and sends back lots of information, the complete API documentation is a much-needed reference.

Whew! Now that you've completed the set up, you can move on to the fun stuff. As I said in the introduction, with SOAP you can make requests of Amazon.com's database just as if you're calling a local function. You just need to know what function to call and what parameters to pass it. For example, to find all the books published by O'Reilly, you make a ManufacturerSearchRequest(). A KeywordSearchRequest(), on the other hand, is used to find everything written about PHP. (Searches aren't restricted to books, you can search for anything sold on Amazon: music, DVDs, electronics, software, and even kitchen supplies.) A full list is included in the documentation.

In PHP using PEAR::SOAP, here's the code to find all of O'Reilly's books sorted from best-selling to worst-selling:

require_once 'SOAP/Client.php';

$wsdl_url = 
  'http://soap.amazon.com/schemas3/AmazonWebServices.wsdl';
$WSDL     = new SOAP_WSDL($wsdl_url); 
$client   = $WSDL->getProxy(); 

$params   = array(
    'manufacturer' => "O'Reilly",
    'mode'         => 'books',
    'sort'         => '+title',
    'page'         => 1,
    'type'         => 'lite',
    'tag'          => 'trachtenberg-20',
    'devtag'       => 'XXXXXXXXXXXXXX',
);

$books    = $client->ManufacturerSearchRequest($params);

The first line loads in PEAR::SOAP's client classes. This let you make SOAP requests to other servers. If you have trouble loading the files, make sure your include_path contains the folder where PEAR files are stored.

Then, you use WSDL (Web Services Definition Language) to create an object whose methods are the different functions Amazon understands. WSDL is a particularly cool part of web services. Not only does it let you manipulate an object as you would a PHP class, it even knows what parameters each method takes and each parameter's type. Since unlike PHP, SOAP is a strictly typed language; this allows PEAR::SOAP to coerce variables into the appropriate types Amazon expects to receive, without any action on your part.

You first instantiate a new SOAP_WSDL object by passing $wsdl_url, the location of Amazon's WSDL file, to the constructor. Next, SOAP_WSDL::getProxy() is called, and it returns a client object, $client. This is what you use to make SOAP requests.

Now that the object is up and running, there's still the matter of making the actual ManufacturerSearchRequest() query. This method takes a few arguments, which are passed in as an (associative) array. Parameter names are the array's keys, and their values are the, well, array values.

Parameters like manufacturer and mode control what you're searching for — in this case — books manufactured by O'Reilly. The sort, page, and type options specify what data is to be returned.

Results can optionally be ordered from best- to worst-selling, alphabetically, by price, and more. (The + in front of salesrank means ordered from best-selling; -salesrank reverses the order.) To conserve resources, Amazon only returns ten items per request. Setting page to 1 means return books 1 through 10; page 2 has books 11 through 20; and so forth. If you want more than ten items on your page, just create a loop and increment page each time through. Also, since Amazon has so much information, they've created two different DTDs that specify what information they'll return: lite and heavy. Here, you get the lite results, which have the product name, author, price, three different image URLs, and the manufacturer. The heavy DTD has everything, including all of the posted reviews, any ListMania lists containing the work, and links to music clips for CDs.

Also, you pass in a tag and a devtag. The tag is an Amazon associate name, so you can collect referral fees. The devtag is the developer's token you received at the beginning of the article.

You don't need to worry about the parameter order. That's all taken care of for you behind the scenes. That's not all that's done behind closed doors, however. When you finally make the request, by calling $client->ManufacturerSearchRequest($params), PEAR:SOAP converts your PHP data structures to a SOAP message written in XML and sends an HTTP request to Amazon's server. After Amazon receives and processes your query, it replies with a SOAP message of its own. PEAR::SOAP listens for this response and parses the XML into a PHP object, which is then returned by our method and stored in $books.

When you're unsure what's in a variable, the easiest way to inspect it is to use print_r(). Calling print_r($books) echoes:

stdClass Object
(
  [TotalResults] => 822
  [TotalPages] => 83
  [Details] => Array
    (
      [0] => stdClass Object
        (
          [Url] => http://www.amazon.com/...
          [Asin] => 0596004478
          [ProductName] => Google Hacks
          [Catalog] => Book
          [Authors] => Array
            (
              [0] => Tara Calishain
              [1] => Rael Dornfest
            )

          [ReleaseDate] => 01 February, 2003
          [Manufacturer] => O'Reilly & Associates
          [ImageUrlSmall] => http://images.amazon.com/...
          [ImageUrlMedium] => http://images.amazon.com/...
          [ImageUrlLarge] => http://images.amazon.com/...
          [ListPrice] => $24.95
          [OurPrice] => $17.47
          [UsedPrice] => $15.95
        )

      [insert nine additional books here]

    )

)

The object has three properties: TotalResults, the number of items that the search matches; TotalPages, the number of times you need to increment $page and requery Amazon to gather all the data; and Details, an array containing the actual catalog information. Each element of Details is itself another array, with the properties you can use to do things like making your own Amazon store or printing information about the pile of books next to your bed. (I've shortened some of the data so it fits nicely in the screen.)

For example, here's a quick-and-dirty loop to create HTML showing the book title, author(s), and price next to a JPEG picture of the cover:

foreach ($hits->Details as $hit) {
    $ProductName = html_entities($hit->ProductName);
    $Authors     = join(' and ', $hit->Authors);

    print <<< _HTML_
    <div style="clear:left; width: 300px; padding:5px; 
                margin:5px; background:#ddd;">
    <a href="$hit->Url"><image src="$hit->ImageUrlSmall" 
                alt="$ProductName" align="left"></a>
    <b>$ProductName</b><br/>
    By $Authors<br/>
    Amazon.com Price: $hit->OurPrice<br/>
    </div>    
_HTML_;
}

This iterates through $hits::Details and places each book inside of its own <div>. (Of course, a cleaner solution is to place the style information in a class instead of embedding it inside of each <div>.) Within the loop, you can pull out the data you want to show. If it's already formatted how you like, print it directly. Or, if it needs a little massaging, like the authors array, reformat it, as is done above by calling join(). You also need to call html_entities() on ProductName and other text fields, because you need to encode characters like the & in O'Reilly & Associates.

You can see the results in Figure 1.

reformatted book data
Figure 1. Reformatted book data from the Amazon database

Amazon also allows more complex queries that filter the listings on multiple criteria. You can use PowerSearchRequest() with Boolean logic to construct a search. For example, to restrict your O'Reilly search to only books on PHP:

$params = array(
    'power'  => "publisher:O'Reilly AND keywords:PHP",
    'mode'   => 'books',
    'sort'   => '+title',
    'page'   => 1,
    'type'   => 'lite',
    'tag'    => 'trachtenberg-20',
    'devtag' => 'XXXXXXXXXXXXXX',
);

$hits  = $client->PowerSearchRequest($params);

Only the first array element has changed. It's now power instead of manufacturer. Also, its value, publisher:O'Reilly AND keywords:PHP, makes sure that the publisher is O'Reilly AND that the book is on PHP. You can also use OR and NOT as well as group expressions using (parenthesis).

Now, the same display function generates new output:

a filtered search
Figure 2. A filtered search

Conclusion

As you've seen, it's very simple to use SOAP and WSDL with PHP. These clients allow you to gather information from across the net to use in your scripts. Amazon.com is not the only major company to provide a SOAP front end to its data. Google lets you search their listings up to 1,000 times a day. Additionally, XMethods has a large directory of SOAP servers that you can experiment with and use.

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."

PHP Cookbook

Related Reading

PHP Cookbook
By David Sklar, Adam Trachtenberg

Return to the PHP DevCenter.

Copyright © 2009 O'Reilly Media, Inc.