CHAPTER 20

Web Services

This chapter discusses some of the more applicable implementations of Web Services technologies and shows you how to use PHP to start incorporating them into your Web application development strategy right now. To accomplish this goal without actually turning this chapter into a book unto itself, the discussion that follows isn't intended to offer an in-depth introduction to the general concept, and advantages, of Web Services.

Even if you have no prior experience with or knowledge of Web Services, hopefully you'll find this chapter quite easy to comprehend. The intention here is to demonstrate the utility of Web Services through numerous practical demonstrations. Specifically, the following topics are discussed:

Why Web Services?
For the uninitiated, this section very briefly touches upon the reasons for all of the work behind Web Services and how they change the landscape of application development.
Really Simple Syndication:
The originators of the World Wide Web had little idea that their accomplishments in this area would lead to what is certainly one of the greatest technological leaps in the history of humankind. However, the extraordinary popularity of the medium caused the capabilities of the original mechanisms to be stretched in ways never intended by their creators. As a result, new methods for publishing information over the Web have emerged and are starting to have as great an impact on the way we retrieve and review data as did their predecessors. One such technology is known as Really Simple Syndication, or RSS. This section introduces RSS and demonstrates how you can incorporate RSS feeds into your development acumen using a great tool called MagpieRSS.
SimpleXML:
New to PHP 5, the SimpleXML extension offers a new and highly practical methodology for parsing XML. This section introduces this new feature and offers several practical examples demonstrating its powerful and intuitive capabilities.
SOAP:
The SOAP protocol plays an enormously important role in the implementation of Web Services. This section discusses its advantages and introduces PHP's SOAP extension, which was made available with the version 5 release.

Why Web Services?

Although the typical developer generally adheres to a loosely defined set of practices and tools, much as an artist generally works with a particular medium and style, he tends to create software in the way he sees most fit. As such, it doesn't come as a surprise that although many programs resemble one another in look and behavior, the similarities largely stop there. Numerous deficiencies arise as a result of this refusal to follow generally accepted programming principles, with software being developed at a cost of maintainability, scalability, extensibility, and interoperability.

This problem of interoperability has become even more pronounced over the past few years, given the incredible opportunities for cooperation that the Internet has opened up to businesses around the world. However, fully exploiting an online business partnership often, if not always, involves some level of system integration. Therein lies the problem: if the system designers never consider the possibility that they might one day need to tightly integrate their application with another, how will they ever really be able to exploit the Internet to its fullest advantage? Indeed, this has been a subject of considerable discussion almost from the onset of this new electronic age.

Web Services technology is today's most promising solution to the interoperability problem. Rather than offer up yet another interpretation of the definition of Web Services, here's an excellent interpretation provided in the W3C's "Web Services Architecture" document (http://www.w3.org/TR/ws-arch/):

A Web service is a software system designed to support interoperable machine-to-machine interaction over a network. It has an interface described in a machine-processable format (specifically WSDL). Other systems interact with the Web service in a manner prescribed by its description using SOAP messages, typically conveyed using HTTP with an XML serialization in conjunction with other Web-related standards.

Some of these terms may be alien to the newcomer; not to worry, because they're introduced later in the chapter. What is important to keep in mind is that Web Services open up endless possibilities to the enterprise, a sampling of which follows:

Software as a service:
Imagine building an e-commerce application that requires a means for converting currency among various exchange rates. However, rather than take it upon yourself to devise some means for automatically scraping the Federal Reserve Bank's Web page (http://www.federalreserve.gov/releases/) for the daily released rate, you instead take advantage of its (hypothetical) Web Service for retrieving these values. The result is far more readable code, with much less chance for error from presentational changes on the Web page.
Significantly lessened Enterprise Application Integration (EAI)horrors:
Developers currently are forced to devote enormous amounts of time to hacking together often complex solutions to integrate disparate applications. Contrast this with connecting two Web Service-enabled applications, in which the process is highly standardized and reusable no matter the language.
Write once, reuse everywhere:
Because Web Services offer platform-agnostic interfaces to exposed application methods, they can be simultaneously used by applications running on a variety of operating systems. For example, a Web Service running on an e-commerce server might be used to keep the CEO abreast of inventory numbers via both a Windows-based client application and a Perl script running on a Linux server that generates daily e-mails that are sent to the executive team.
Ubiquitous access:
Because Web Services typically travel over the HTTP protocol, firewalls can be bypassed because port 80 (and 443 for HTTPS) traffic is almost always allowed. Although debate rages as to whether this is really prudent, for the moment it is indeed an appealing solution to the often difficult affair of firewall penetration.

Such capabilities are tantalizing to the developer. Believe it or not, as is demonstrated throughout this chapter, you can actually begin taking advantage of Web Services right now.

Ultimately, only one metric will determine the success of Web Services: acceptance. Interestingly, several global companies have already made quite a stir by offering Web Services application programming interfaces (APIs) to their treasured data stores. Among the most interesting offers include those provided by the online superstore Amazon.com, Google, and Microsoft, stirring the imagination of the programming industry with their freely available standards-based Web Services. Since their respective releases, all three implementations have sparked the imaginations of programmers worldwide, who have gained valuable experience working with a well-designed Web Services architecture plugged into an enormous amount of data.

Follow these links to learn more about these popular APIs:

Really Simple Syndication

Given that the entire concept of Web Services largely sprung out of the notion that XML- and HTTP-driven applications would be harnessed to power the next generation of business-to-business applications, it's rather ironic that the first widespread implementation of the Web Services technologies happened on the end-user level. RSS solves a number of problems that both Web developers and Web users have faced for years.

All of us can relate to the considerable amount of time consumed by our daily surfing ritual. Most people have a stable of Web sites that they visit on a regular basis—in some cases, several times daily. For each site, the process is almost identical: visit the URL, weave around a sea of advertisements, navigate to the section of interest, and finally actually read the news story. Repeat this process numerous times, and the next thing you know, a fair amount of time has passed. Furthermore, given the highly tedious process, it's easy to miss something of interest. In short, leave the process to a human and something is bound to get screwed up.

Developers face an entirely different set of problems. Once upon a time, attracting users to your Web site involved spending enormous amounts of money on prime-time commercials and magazine layouts, and throwing lavish holiday galas. Then the novelty wore off (and the cash disappeared) and those in charge of the Web sites were forced to actually produce something substantial for their site visitors. Furthermore, they had to do so while working with the constraints of bandwidth limitations, the myriad of Web-enabled devices that sprung up, and an increasingly finicky (and time-pressed) user. Enter RSS.

RSS offers a formalized means for encapsulating a Web site's content within an XML-based structure, known as a feed. It's based on the premise that most site information shares a similar format, regardless of topic. For example, although sports, weather, and theater are all vastly dissimilar topics, the news items published under each would share a very similar structure, including a title, an author, a publication date, a URL, and a description. A typical RSS feed embodies all such attributes, and often much more, forcing an adherence to a presentation-agnostic format that can in turn be retrieved, parsed, and formatted in any means acceptable to the end user, without actually having to visit the syndicating Web site. With just the feed's URL, the user can store it, along with others if he likes, into a tool that is capable of retrieving and parsing the feed, allowing the user to do as he pleases with the information. Working in this fashion, you can use RSS feeds to do the following:

image

Figure 20-1. The RSS Bandit interface

Understanding RSS Syntax

If you're not familiar with the general syntax of an RSS feed, Listing 20-1 offers an example, which will be used as input for the scripts that follow. Although a discussion of RSS syntax specifics is beyond the scope of this book, you'll nonetheless find the structure and tags to be quite intuitive (after all, that's why they call it Really Simple Syndication).

Listing 20-1. A Sample RSS Feed (blog.xml)

<?xml version="1.0" encoding="iso-8859-1"?>
   <rss version="2.0">
   <channel>
      <title>Inside Open Source</title>
      <link>http://opensource.apress.com/</link>

   <item>
      <title>Killer Firefox Tip #294</title>
      <link>http://opensource.apress.com/article/190/</link>
      <author>W. Jason Gilmore</author>
      <description>Like most of you, I spend bunches of time downloading large
          files from the Web, typically podcasts and PDF documents...</description>
   </item>

   <item>
      <title>Beginning Ubuntu Linux wins Linux Journal Award!</title>
      <link>http://opensource.apress.com/article/189/</link>
      <author>Keir Thomas</author>
      <description>Woo hoo! My book, Beginning Ubuntu Linux, has won an award
          in the Linux Journal Editor's Choice 2006 awards!
          More precisely...</description>
   </item>
<item>
      <title>Forms Validation with CakePHP</title>
      <link>http://opensource.apress.com/article/188/</link>
      <author>W. Jason Gilmore</author>
      <description>Neglecting to validate user input is akin to foregoing
          any defensive
          gameplan for containing the NFL's leading rusher. Chances are
          sooner or later...</description>
   </item>
   </channel>
   </rss>

This example doesn't take advantage of all available RSS elements. For instance, other feeds might contain elements describing the feed's update interval, language, and creator. However, for the purposes of the examples found in this chapter, it makes sense to remove those components that have little bearing on instruction.

Now that you're a bit more familiar with the purpose and advantages of RSS, you'll next learn how to use PHP to incorporate RSS into your own development strategy. Although there are numerous RSS tools written for the PHP language, one in particular offers an amazingly effective solution for retrieving, parsing, and displaying feeds: MagpieRSS.

Introducing MagpieRSS

MagpieRSS (Magpie for short) is a powerful RSS parser written in PHP by Kellan Elliott-McCrea. It's freely available for download via http://magpierss.sourceforge.net/ and is distributed under the GPL license. Magpie offers developers an amazingly practical and easy means for retrieving and rendering RSS feeds, as you'll soon see. In addition, Magpie offers to users a number of cool features, including the following:

Simplicity:
Magpie gets the job done with a minimum of effort by the developer. For example, typing a few lines of code is all it takes to begin retrieving, parsing, and converting RSS feeds into an easily readable format.
Nonvalidating:
If the feed is well formed, Magpie will successfully parse it. This means that it supports all tag sets found within the various RSS versions, as well as your own custom tags.
Bandwidth-friendly:
By default, Magpie caches feed contents for 60 minutes, cutting down on use of unnecessary bandwidth. You're free to modify the default to fit caching preferences on a per-feed basis. If retrieval is requested after the cache has expired, Magpie will retrieve the feed only if it has been changed (by checking the Last-Modified and ETag headers provided by the Web server). In addition, Magpie recognizes HTTP's Gzip content-negotiation ability when supported.

Installing Magpie

Like most PHP classes, Magpie is as simple to install as placing the relevant files within a directory that can later be referenced from a PHP script. The instructions for doing so follow:

  1. Download Magpie from http://magpierss.sourceforge.net/.
  2. Extract the package contents to a location convenient for inclusion from a PHP script. For instance, consider placing third-party classes within an aptly named directory located within the PHP_INSTALL_DIR/includes/ directory. Note that you can forgo the hassle of typing out the complete path to the Magpie directory by adding its location to the include_path directive found in the php.ini file.
  3. Include the Magpie class (magpie.php) within your script:
    require('magpie/magpie.php'),

That's it. You're ready to begin using Magpie.

How Magpie Parses a Feed

Magpie parses a feed by placing it into an object consisting of four fields: channel, image, items, and textinput. In turn, channel is an array of associative arrays, while the remaining three are associative arrays. The following script retrieves the blog.xml feed, outputting it using the print_r() statement:

<?php
    require("magpie/magpie.php");
    $url = "http://localhost/book/20/blog.xml";
    $rss = fetch_rss($url);
    print_r($rss);

?>

This returns the following output (formatted for readability):


Magpie_Feed Object (
    [items] => Array (
        [0] => Array (
            [title] => Killer Firefox Tip #294
            [title_detail] => Array (
                [type] => text
                [value] => Killer Firefox Tip #294
            )
            [link] => http://opensource.apress.com/article/190/
            [links] => Array (
                [0] => Array (
                    [rel] => alternate [href] =>  
                        http://opensource.apress.com/article/190/
                )
            )
            [author] => W. Jason Gilmore
            [description] => Like most of you, I spend bunches of time
                                      downloading large files from the Web,
                                      typically podcasts and PDF documents...
        )

        [1] => Array (
            [title] => Beginning Ubuntu Linux wins Linux Journal Award!
            [title_detail] => Array (
                [type] => text
                [value] => Beginning Ubuntu Linux wins Linux Journal Award!
            )
            [link] => http://opensource.apress.com/article/189/
            [links] => Array (
                [0] => Array (
                    [rel] => alternate [
                    href] => http://opensource.apress.com/article/189/
                )
            )
[author] => Keir Thomas
            [description] => Woo hoo! My book, Beginning Ubuntu Linux, has
                               won an award in the Linux Journal Editor's Choice
                                  2006 awards! More precisely...
        )

        [2] => Array (
            [title] => Forms Validation with CakePHP
            [title_detail] => Array (
                [type] => text
                [value] => Forms Validation with CakePHP
            )
            [link] => http://opensource.apress.com/article/188/
            [links] => Array (
                [0] => Array (
                    [rel] => alternate
                    [href] => http://opensource.apress.com/article/188/
                )
            )
            [author] => W. Jason Gilmore
          [description] => Neglecting to validate user input is akin to foregoing
                             any defensive gameplan for containing the NFL's
                             leading rusher. Chances are sooner or later...
        )
    )
    [feed] => Array (
        [title] => Inside Open Source
        [title_detail] => Array (
            [type] => text
            [value] => Inside Open Source
        )
        [link] => http://opensource.apress.com/
        [links] => Array (
            [0] => Array (
                [rel] => alternate
                [href] => http://opensource.apress.com/
            )
        )
    )
[feed_type] =>
    [feed_version] =>
    [_namespaces] => Array ( )
    [from_cache] =>
    [_headers] => Array (
        [date] => Sun, 12 Nov 2006 21:11:12 GMT
        [server] => Apache/2.0.58 (Win32) PHP/5.1.4
        [last-modified] => Sun, 12 Nov 2006 21:10:41 GMT
        [etag] => "ad43-4f5-37c15b77"
        [accept-ranges] => bytes
        [content-length] => 1269
        [connection] => close
        [content-type] => application/xml
    )
    [_etag] => "ad43-4f5-37c15b77"
    [_last_modified] => Sun, 12 Nov 2006 21:10:41 GMT
    [output_encoding] => utf-8
    [channel] => Array (
        [title] => Inside Open Source
        [title_detail] => Array (
            [type] => text
            [value] => Inside Open Source
        )
        [link] => http://opensource.apress.com/
        [links] => Array (
            [0] => Array (
                [rel] => alternate
                [href] => http://opensource.apress.com/
            )
        )
    )
)

An object named Magpie_Feed is returned, containing several attributes. This means you can access the feed content and other attributes using standard object-oriented syntax. The following examples demonstrate how the data is peeled from this object and presented in various fashions.

Retrieving and Rendering an RSS Feed

Based on your knowledge of Magpie's parsing behavior, rendering the feed components should be trivial. Listing 20-2 demonstrates how easy it is to render a retrieved feed within a standard browser.

Listing 20-2. Rendering an RSS Feed with Magpie

<?php
    require("magpie/magpie.php");

    // RSS feed location?
    $url = "http://localhost/book/20/blog.xml";
    // Retrieve the feed
    $rss = fetch_rss($url);

    // Format the feed for the browser
    $feedTitle = $rss->channel['title'];
    echo "Latest News from <strong>$feedTitle</strong>";
    foreach ($rss->items as $item) {
       $link = $item['link'];
       $title = $item['title'];
       // Not all items necessarily have a description, so test for one.
       $description = isset($item['description']) ? $item['description'] : "";
       echo "<p><a href="$link">$title</a><br />$description</p>";
}

?>

Note that Magpie does all of the hard work of parsing the RSS document, placing the data into easily referenced arrays. Figure 20-2 shows the fruits of this script.

image

Figure 20-2. Rendering an RSS feed within the browser

As you can see in Figure 20-2, each feed item is formatted with the title linking to the complete entry. So, for example, following the Killer Firefox Tip #294 link will take the user to http://opensource.apress.com/article/190/.

Aggregating Feeds

Of course, chances are you're going to want to aggregate multiple feeds and devise some means for viewing them simultaneously. To do so, you can simply modify Listing 20-2, passing in an array of feeds. A bit of CSS may also be added to shrink the space required for output. Listing 20-3 shows the rendered version.

Listing 20-3. Aggregating Multiple Feeds with Magpie

<style><!--
p { font: 11px arial,sans-serif; margin-top: 2px;}
//-->
</style>

<?php
require("magpie/magpie.php");
// Compile array of feeds
$feeds = array(
"http://localhost/book/20/blog.xml",
"http://news.com.com/2547-1_3-0-5.xml",
"http://rss.slashdot.org/Slashdot/slashdot");

// Iterate through each feed
foreach ($feeds as $feed) {

   // Retrieve the feed
   $rss = fetch_rss($feed);

   // Format the feed for the browser
   $feedTitle = $rss->channel['title'];
   echo "<p><strong>$feedTitle</strong><br />";

   foreach ($rss->items as $item) {
      $link = $item['link'];
      $title = $item['title'];
      $description = isset($item['description']) ? $item['description'].
                     "<br />" : "";
      echo "<a href="$link">$title</a><br />$description";
   }
   echo "</p>";

}

?>

Figure 20-3 depicts the output based on these three feeds.

Although the use of a static array for containing feeds certainly works, it might be more practical to maintain them within a database table, or at the very least a text file. It really all depends upon the number of feeds you'll be using and how often you intend on managing the feeds themselves.

image

Figure 20-3. Aggregating feeds

Limiting the Number of Displayed Headlines

Some Web site developers are so keen on RSS that they wind up dumping quite a bit of information into their published feeds. However, you might be interested in viewing only the most recent items and ignoring the rest. Because Magpie relies heavily on standard PHP language features such as arrays and objects for managing RSS data, limiting the number of headlines is trivial because you can call upon one of PHP's default array functions for the task. The function array_slice() should do the job quite nicely. For example, suppose you want to limit total headlines displayed for a given feed to three. You can use array_slice() to truncate it prior to iteration, like so:

$rss->items = array_slice($rss->items, 0, 3);

Caching Feeds

One final topic to discuss regarding Magpie is its caching feature. By default, Magpie caches feeds for 60 minutes, on the premise that the typical feed will likely not be updated more than once per hour. Therefore, even if you constantly attempt to retrieve the same feeds, say once every 5 minutes, any updates will not appear until the cached feed is at least 60 minutes old. However, some feeds are published more than once an hour, or the feed might be used to publish somewhat more pressing information. (RSS feeds don't necessarily have to be used for browsing news headlines; you could use them to publish information about system health, logs, or any other data that could be adapted to its structure. It's also possible to extend RSS as of version 2.0, but this matter is beyond the scope of this book.) In such cases, you may want to consider modifying the default behavior.

To completely disable caching, disable the constant MAGPIE_CACHE_ON, like so:

define('MAGPIE_CACHE_ON', 0);

To change the default cache time (measured in seconds), you can modify the constant MAGPIE_CACHE_AGE, like so:

define('MAGPIE_CACHE_AGE',1800);

Finally, you can opt to display an error instead of a cached feed if the fetch fails, by enabling the constant MAGPIE_CACHE_FRESH_ONLY:

define('MAGPIE_CACHE_FRESH_ONLY', 1)

You can also change the default cache location (by default, the same location as the executing script) by modifying the MAGPIE_CACHE_DIR constant:

define('MAGPIE_CACHE_DIR', '/tmp/magpiecache/'),

SimpleXML

Everyone agrees that XML signifies an enormous leap forward in data management and application interoperability. Yet how come it's so darned hard to parse? Although powerful parsing solutions are readily available, DOM, SAX, and XSLT to name a few, each presents a learning curve that is just steep enough to cause considerable gnashing of the teeth among those users interested in taking advantage of XML's practicalities without an impractical time investment. Leave it to an enterprising PHP developer (namely, Sterling Hughes) to devise a graceful solution. SimpleXML offers users a very practical and intuitive methodology for processing XML structures and is enabled by default as of PHP 5. Parsing even complex structures becomes a trivial task, accomplished by loading the document into an object and then accessing the nodes using field references, as you would in typical object-oriented fashion.

The XML document displayed in Listing 20-4 is used to illustrate the examples offered in this section.

Listing 20-4. A Simple XML Document

<?xml version="1.0" standalone="yes"?>
<library>
   <book>
      <title>Pride and Prejudice</title>
      <author gender="female">Jane Austen</author>
      <description>Jane Austen's most popular work.</description>
   </book>
   <book>
      <title>The Conformist</title>
      <author gender="male">Alberto Moravia</author>
      <description>Alberto Moravia's classic psychological novel.</description>
   </book>
   <book>
      <title>The Sun Also Rises</title>
      <author gender="male">Ernest Hemingway</author>
      <description>The masterpiece that launched Hemingway's
      career.</description>
   </book>
</library>

Loading XML

A number of SimpleXML functions are available for loading and parsing the XML document. These functions are introduced in this section, along with several accompanying examples.


Note To take advantage of SimpleXML when using PHP versions older than 6.0, you need to disable the PHP directive zend.ze1_compatibility_mode.


Loading XML from a File

The simplexml_load_file() function loads an XML file into an object. Its prototype follows:

object simplexml_load_file(string filename [, string class_name])

If a problem is encountered loading the file, FALSE is returned. If the optional class_name parameter is included, an object of that class will be returned. Of course, class_name should extend the SimpleXMLElement class. Consider an example:

<?php
    $xml = simplexml_load_file("books.xml");
    var_dump($xml);
?>

This code returns the following:


object(SimpleXMLElement)#1 (1) {
  ["book"]=>
  array(3) {
    [0]=>
    object(SimpleXMLElement)#2 (3) {
      ["title"]=>
      string(19) "Pride and Prejudice"
      ["author"]=>
      string(11) "Jane Austen"
      ["description"]=>
      string(32) "Jane Austen's most popular work."
    }
    [1]=>
    object(SimpleXMLElement)#3 (3) {
      ["title"]=>
      string(14) "The Conformist"
      ["author"]=>
      string(15) "Alberto Moravia"
      ["description"]=>
      string(46) "Alberto Moravia's classic psychological novel."
    }
[2]=>
    object(SimpleXMLElement)#4 (3) {
      ["title"]=>
      string(18) "The Sun Also Rises"
      ["author"]=>
      string(16) "Ernest Hemingway"
      ["description"]=>
      string(55) "The masterpiece that launched Hemingway's
      career."
    }
  }
}

Note that dumping the XML will not cause the attributes to show. To view attributes, you need to use the attributes() method, introduced later in this section.

Loading XML from a String

If the XML document is stored in a variable, you can use the simplexml_load_string() function to read it into the object. Its prototype follows:

object simplexml_load_string(string data)

This function is identical in purpose to simplexml_load_file(), except that the lone input parameter is expected in the form of a string rather than a file name.

Loading XML from a DOM Document

The Document Object Model (DOM) is a W3C specification that offers a standardized API for creating an XML document, and subsequently navigating, adding, modifying, and deleting its elements. PHP provides an extension capable of managing XML documents using this standard, titled the DOM XML extension. You can use the simplexml_import_om() function to convert a node of a DOM document into a SimpleXML node, subsequently exploiting use of the SimpleXML functions to manipulate that node. Its prototype follows:

object simplexml_import_dom(domNode node)

Parsing XML

Once an XML document has been loaded into an object, several methods are at your disposal. Presently, four methods are available, each of which is introduced in this section.

Learning More About an Element

XML attributes provide additional information about an XML element. In the sample XML document presented earlier, in Listing 20-4, only the author node possesses an attribute, namely gender, used to offer information about the author's gender. You can use the attributes() method to retrieve these attributes. Its prototype follows:

object simplexml_element->attributes()

For example, suppose you want to retrieve the gender of each author:

<?php
    $xml = simplexml_load_file("books.xml");
    foreach($xml->book as $book) {
        printf("%s is %s. <br />",$book->author, $book->author->attributes());
    }
?>

This example returns the following:


Jane Austen is female.
Alberto Moravia is male.
Ernest Hemingway is male.

You can also directly reference a particular book author's gender. For example, suppose you want to determine the gender of the author of the second book in the XML document:

echo $xml->book[2]->author->attributes();

This example returns the following:


male

Often a node possesses more than one attribute. For example, suppose the author node looks like this:

<author gender="female" age="20">Jane Austen</author>

It's easy to output the attributes with a for loop:

foreach($xml->book[0]->author->attributes() AS $a => $b) {
    printf("%s = %s <br />", $a, $b);

}

This example returns the following:


gender = female
age = 20

Creating XML from a SimpleXML Object

The asXML() method returns a well-formed XML 1.0 string based on the SimpleXML object. Its prototype follows:

string simplexml_element->asXML()

An example follows:

<?php
    $xml = simplexml_load_file("books.xml");
    echo htmlspecialchars($xml->asXML());

?>

This example returns the original XML document, except that the newline characters have been removed, and the characters have been converted to their corresponding HTML entities.

Learning About a Node's Children

Often, you might be interested in only a particular node's children. Using the children() method, retrieving them becomes a trivial affair. Its prototype follows:

object simplexml_element->children()

Suppose for example that the books.xml document is modified so that each book includes a cast of characters. The Hemingway book might look like the following:

<book>
   <title>The Sun Also Rises</title>
   <author gender="male">Ernest Hemingway</author>
  <description>The masterpiece that launched Hemingway's career.</description>
   <cast>
      <character>Jake Barnes</character>
      <character>Lady Brett Ashley</character>
      <character>Robert Cohn</character>
      <character>Mike Campbell</character>
   </cast>
</book>

Using the children() method, you can easily retrieve the characters:

<?php
    $xml = simplexml_load_file("books.xml");
    foreach($xml->book[2]->cast->children() AS $character) {
        echo "$character<br />";
    }
?>

This example returns the following:


Jake Barnes
Lady Brett Ashley
Robert Cohn
Mike Campbell

Using XPath to Retrieve Node Information

XPath is a W3C standard that offers an intuitive, path-based syntax for identifying XML nodes. SimpleXML offers a method called xpath() for doing so, and its prototype follows:

array simplexml_element->xpath(string path)

XPath also offers a set of functions for selectively retrieving nodes based on value. For example, referring to the books.xml document, you could use the xpath() method to retrieve all author nodes using the expression /library/book/author:

<?php
    $xml = simplexml_load_file("books.xml");
    $authors = $xml->xpath("/library/book/author");
    foreach($authors AS $author) {
        echo "$author<br />";
    }
?>

This example returns the following:


Jane Austen
Alberto Moravia
Ernest Hemingway

You can also use XPath functions to selectively retrieve a node and its children based on a particular value. For example, suppose you want to retrieve all book titles where the author is named Ernest Hemingway:

<?php
    $xml = simplexml_load_file("books.xml");
    $book = $xml->xpath("/library/book[author='Ernest Hemingway']");
    echo $book[0]->title;

?>

This example returns the following:


The Sun Also Rises

SOAP

The Postal Service is amazingly effective at transferring a package from party A to party B, but its only concern is ensuring the safe and timely transmission. The Postal Service is oblivious to the nature of the transaction, provided that it is in accordance with the Postal Service's terms of service. As a result, a letter written in English might be sent to a fisherman in China, and that letter will indeed arrive without issue, but the recipient would probably not understand a word of it. The same holds true if the fisherman were to send a letter to you written in his native language; chances are you wouldn't even know where to begin.

This isn't unlike what might occur if two applications attempt to talk to each other across a network. Although they could employ messaging protocols such as HTTP and SMTP in much the same way that we make use of the Postal Service, it's quite unlikely one protocol will be able to say anything of discernible interest to the other. However, if the parties agree to send data using the same messaging language, and both are capable of understanding messages sent to them, the dilemma is resolved. Granted, both parties might go about their own way of interpreting that language (more about that in a bit), but nonetheless the commonality is all that's needed to ensure comprehension. Web Services often employ the use of something called SOAP as that common language. Here's the formalized definition of SOAP, as stated within the SOAP 1.2 specification (http://www.w3.org/TR/SOAP12-part1/):

SOAP Version 1.2 (SOAP) is a lightweight protocol intended for exchanging structured information in a decentralized, distributed environment. It uses XML technologies to define an extensible messaging framework providing a message construct that can be exchanged over a variety of underlying protocols. The framework has been designed to be independent of any particular programming model and other implementation-specific semantics.

Introducing SOAP Messages

Keep in mind that SOAP is only responsible for defining the construct used for the exchange of messages; it does not define the protocol used to transport that message, nor does it describe the features or purpose of the Web Service used to send or receive that message. This means that you could conceivably use SOAP over any protocol, and in fact could route a SOAP message over numerous protocols during the course of transmission. A sample SOAP message is offered in Listing 20-5 (formatted for readability).

Listing 20-5. A Sample SOAP Message

<?xml version="1.0" encoding="ISO-8859-1" ?>
   <SOAP-ENV:Envelope SOAP
                ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
                xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
                xmlns:si="http://soapinterop.org/xsd">
      <SOAP-ENV:Body>
         <getRandQuoteResponse>
            <return xsi:type="xsd:string">
            "My main objective is to be professional but to kill him.",
                Mike Tyson (2002)
            </return>
        </getRandQuoteResponse>
     </SOAP-ENV:Body>
  </SOAP-ENV:Envelope>

If you're new to SOAP, it would certainly behoove you to take some time to become familiar with the protocol. A simple Web search will turn up a considerable amount of information pertinent to this pillar of Web Services. Regardless, you should be able to follow along with the ensuing discussion quite easily because the PHP SOAP extension does a fantastic job of taking care of most of the dirty work pertinent to the assembly, parsing, submission, and retrieval of SOAP messages.

Introducing PHP's SOAP Extension

In response to the community clamor for Web Services-enabled applications, and the popularity of third-party SOAP extensions, a native SOAP extension was available as of PHP 5, and enabled by default as of PHP 6. This section introduces this object-oriented extension and shows you how to create both a SOAP client and a SOAP server. Along the way you'll learn more about many of the functions and methods available through this extension. Before you can follow along with the accompanying examples, you need to take care of a few prerequisites, which are discussed next.

Prerequisites

PHP's SOAP extension requires the GNOME XML library. You can download the latest stable libxml2 package from http://www.xmlsoft.org/. Binaries are also available for the Windows platform. Version 2.5.4 or greater is required. If you're running a version of PHP older than 6.0, you also need to configure PHP with the −-enable-soap extension. On Windows, you need to add the following line to your php.ini file:

extension=php_soap.dll

Instantiating the Client

The SoapClient() constructor instantiates a new instance of the SoapClient class. The prototype looks like this:

object SoapClient->SoapClient(mixed wsdl [, array options])

The wsdl parameter determines whether the class will be invoked in WSDL or non-WSDL mode; if invoked in WSDL mode, set wsdl to the WSDL file URI; otherwise set it to NULL. The options parameter is an array that accepts the following parameters. It's optional for WSDL mode and requires that at least the location and uri options be set when in non-WSDL mode.

actor:
Specifies the name, in URI format, of the role that a SOAP node must play in order to process the header.
compression:
Specifies whether data compression is enabled. Presently, Gzip and x-gzip are supported. According to the TODO document, support is planned for HTTP compression.
exceptions:
Turns on the exception-handling mechanism. It is enabled by default.
location:
Specifies the endpoint URL, when working in non-WSDL mode.
login:
Specifies the username if HTTP authentication is used to access the SOAP server.
password:
Specifies the password if HTTP authentication is used to access the SOAP server.
proxy_host:
Specifies the name of the proxy host when connecting through a proxy server.
proxy_login:
Specifies the proxy server username if one is required.
proxy_password:
Specifies the proxy server password if one is required.
proxy_port:
Specifies the proxy server port when connecting through a proxy server.
soap_version:
Specifies whether SOAP version 1.1 or 1.2 should be used. This defaults to version 1.1.
trace:
Specifies whether you'd like to examine SOAP request and response envelopes. If so, you'll need to enable this by setting it to 1.
uri:
Specifies the SOAP service namespace when not working in WSDL mode.

Establishing a connection to a Web Service is trivial. The following example shows you how to use the SoapClient object to connect to a sports-related Web Service I've created to retrieve a random boxing quote:

<?php
    $ws = "http://www.beginningphpandmysql.com/boxing.wsdl";
    $client = new SoapClient($ws);
?>

However, just referencing the Web Service really doesn't do you much good. You'll want to learn more about the methods exposed by this Web Service. Of course, you can open up the WSDL document in the browser or a WSDL viewer, by navigating to http://www.beginningphpandmysql.com/boxing.wsdl. However, you can also retrieve the methods programmatically using the __getFunctions() method, introduced next.

Retrieving the Exposed Methods

The __getFunctions() method returns an array consisting of all methods exposed by the service referenced by the SoapClient object. The prototype looks like this:

array SoapClient->__getFunctions()

The following example establishes a connection to the boxing quotation SOAP server and retrieves a list of available methods:

<?php
   $ws = "http://www.beginningphpandmysql.com/boxing.wsdl";
   $client = new SoapClient($ws);
   var_dump($client->__getFunctions());
?>

This example returns the following (formatted for readability):


array(1) {
   [0]=> string(30) "string getQuote(string $boxer)"
}

One method is exposed, getQuote(), which requires you to pass in the name of a boxer, and returns a string (presumably a quotation).

In the following sections you'll learn how the boxing quotation SOAP server was created and see it in action.

Creating a SOAP Server

Creating a SOAP server with the native SOAP extension is easier than you think. Although several server-specific methods are provided with the SOAP extension, only three methods are required to create a complete WSDL-enabled server. This section introduces these and other methods, guiding you through the process of creating a functional SOAP server as the section progresses. The next section, "SOAP Client and Server Interaction," offers a complete working example of the interaction between a WSDL-enabled client and server created using this extension. To illustrate this, the examples found in the remainder of this chapter refer to Listing 20-6, which offers a sample WSDL file. Directly following the listing, a few important SOAP configuration directives are introduced that you need to keep in mind when building SOAP services using this extension.

Listing 20-6. A Sample WSDL File (boxing.wsdl)

<?xml version="1.0" ?>
  <definitions name="boxing"
               targetNamespace="http://www.beginningphpandmysql.com/boxing"
    xmlns:tns="http://www.beginningphpandmysql.com/boxing"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns="http://schemas.xmlsoap.org/wsdl/">
<message name="getQuoteRequest">
      <part name="boxer" type="xsd:string" />
     </message>

     <message name="getQuoteResponse">
       <part name="return" type="xsd:string" />
     </message>

     <portType name="QuotePortType">
       <operation name="getQuote">
         <input message="tns:getQuoteRequest" />
         <output message="tns:getQuoteResponse" />
       </operation>
     </portType>

     <binding name="QuoteBinding" type="tns:QuotePortType">
       <soap:binding
             style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />
       <operation name="getQuote">
         <soap:operation soapAction="" />
           <input>
             <soap:body use="encoded"
              encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
           </input>
           <output>
             <soap:body use="encoded"
             encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" />
           </output>
       </operation>
     </binding>

    <service name="boxing">
      <documentation>Returns quote from famous pugilists</documentation>
      <port name="QuotePort" binding="tns:QuoteBinding">
         <soap:address
          location="http://www.beginningphpandmysql.com/boxingserver.php" />
      </port>
    </service>
  </definitions>

The SoapServer() constructor instantiates a new instance of the SoapServer class in WSDL or non-WSDL mode. Its prototype looks like this:

object SoapServer->SoapServer(mixed wsdl [, array options])

If you require WSDL mode, you need to assign the wsdl parameter the WSDL file's location, or else set it to NULL. The optional options parameter is an array used to set the following options:

actor:
Identifies the SOAP server as an actor, defining its URI.
encoding:
Sets the character encoding.
soap_version:
Determines the supported SOAP version and must be set with the syntax SOAP_x_y, where x is an integer specifying the major version number, and y is an integer specifying the corresponding minor version number. For example, SOAP version 1.2 would be assigned as SOAP_1_2.

The following example creates a SoapServer object referencing the boxing.wsdl file:

$soapserver = new SoapServer("boxing.wsdl");

If the WSDL file resides on another server, you can reference it using a valid URI. For example:

$soapserver = new SoapServer("http://www.beginningphpandmysql.com/boxing.wsdl");

Next, you need to export at least one function, a task accomplished using the addFunction() method, introduced next.


Note If you're interested in exposing all methods in a class through the SOAP server, use the method setClass(), introduced later in this section.


Adding a Server Function

You can make a function available to clients by exporting it using the addFunction() method. In the WSDL file, there is only one function to implement, getQuote(). It takes $boxer as a lone parameter and returns a string. The following creates this function and exposes it to connecting clients:

<?php
    function getQuote($boxer) {
        if ($boxer == "Tyson") {
            $quote = "My main objective is to be professional
                      but to kill him. (2002)";
        } elseif ($boxer == "Ali") {
            $quote = "I am the greatest. (1962)";
        } elseif ($boxer == "Foreman") {
            $quote = "Generally when there's a lot of smoke,
                      there's just a whole lot more smoke. (1995)";
        } else {
            $quote = "Sorry, $boxer was not found.";
        }
        return $quote;
    }

    $soapserver = new SoapServer("boxing.wsdl");

    $soapserver->addFunction("getQuote");
?>

When two or more functions are defined in the WSDL file, you can choose which ones are to be exported by passing them in as an array, like so:

$soapserver->addFunction(array("getQuote","someOtherFunction");

Alternatively, if you would like to export all functions defined in the scope of the SOAP server, you can pass in the constant, SOAP_FUNCTIONS_ALL, like so:

$soapserver->addFunction(array(SOAP_FUNCTIONS_ALL);

It's important to understand that exporting the functions is not all that you need to do to produce a valid SOAP server. You also need to properly process incoming SOAP requests, a task handled for you via the method handle(), introduced later in this chapter.

Adding Class Methods

Although the addFunction() method works fine for adding functions, what if you want to add class methods? This task is accomplished with the setClass() method. Its prototype follows:

void SoapServer->setClass(string class_name [, mixed args])

The class_name parameter specifies the name of the class, and the optional args parameter specifies any arguments that will be passed to a class constructor. The following creates a class for the boxing quote service and exports its methods using setClass():

<?php
    class boxingQuotes {
        function getQuote($boxer) {
            if ($boxer == "Tyson") {
                $quote = "My main objective is to be professional
                          but to kill him. (2002)";
            } elseif ($boxer == "Ali") {
                $quote = "I am the greatest. (1962)";
            } elseif ($boxer == "Foreman") {
                $quote = "Generally when there's a lot of smoke,
                          there's just a whole lot more smoke. (1995)";
            } else {
                $quote = "Sorry, $boxer was not found.";
            }
            return $quote;
        }
    }

    $soapserver = new SoapServer("boxing.wsdl");

    $soapserver->setClass("boxingQuotes");
    $soapserver->handle();
?>

The decision to use setClass() instead of addFunction() is irrelevant to any requesting clients.

Directing Requests to the SOAP Server

Incoming SOAP requests are received by way of either the input parameter soap_request or the PHP global $HTTP_RAW_POST_DATA. Either way, the method handle() automatically directs the request to the SOAP server for you. Its prototype follows:

void SoapServer->handle([string soap_request])

It's the last method executed in the server code. You call it like this:

$soapserver->handle();

Persisting Objects Across a Session

One really cool feature of the SOAP extension is the ability to persist objects across a session. This is accomplished with the setPersistence() method. Its prototype follows:

void SoapServer->setPersistence(int mode)

This method only works in conjunction with setClass(). Two modes are accepted:

SOAP_PERSISTENCE_REQUEST:
Specifies that PHP's session-handling feature should be used to persist the object.
SOAP_PERSISTENCE_SESSION:
Specifies that the object is destroyed at the end of the request.

Understanding SOAP Client and Server Interaction

Now that you're familiar with the basic premises of using this extension to create both SOAP clients and servers, this section presents an example that demonstrates both concepts simultaneously. This SOAP service retrieves a famous quote from a particular boxer, and that boxer's last name is requested using the exposed getQuote() method. This example is based on the boxing.wsdl file shown earlier in Listing 20-6.

Creating the Boxing Server

The boxing server is simple but practical. Extending this to connect to a database server would be a trivial affair. Consider the code:

<?php
    class boxingQuotes {
        function getQuote($boxer) {
            if ($boxer == "Tyson") {
                $quote = "My main objective is to be professional
                          but to kill him. (2002)";
            } elseif ($boxer == "Ali") {
                $quote = "I am the greatest. (1962)";
            } elseif ($boxer == "Foreman") {
                $quote = "Generally when there's a lot of smoke,
                          there's just a whole lot more smoke. (1995)";
            } else {
                $quote = "Sorry, $boxer was not found.";
            }
return $quote;
        }
    }

    $soapserver = new SoapServer("boxing.wsdl");

    $soapserver->setClass("boxingQuotes");
    $soapserver->handle();
?>

The client, introduced next, will consume this service.

Executing the Boxing Client

The boxing client consists of just two lines, the first instantiating the WSDL-enabled SoapClient() class, and the second executing the exposed method getQuote(), passing in the parameter "Ali":

<?php
    $client = new SoapClient("boxing.wsdl");
    echo $client->getQuote("Ali");
?>

Executing the client produces the following output:


I am the greatest. (1962)

Summary

The promise of Web Services and other XML-based technologies has generated an incredible amount of work in this area, with progress regarding specifications and the announcement of new products and projects happening all the time. No doubt such efforts will continue, given the incredible potential that this concentration of technologies has to offer.

In the next chapter, you'll turn your attention to the security-minded strategies that developers should always keep at the forefront of their development processes.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.225.220