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.
Real Simple Syndication (RSS): 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 Real 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 Magpie.
SimpleXML: New to PHP 5, the SimpleXML extension offers a new and highly practical method- ology for parsing XML. This section introduces this new feature and offers several practical examples demonstrating its powerful and intuitive capabilities.
SOAP: SOAP 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 avail- able 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.
361
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 serializa- tion 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 possi- bilities 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 appli- cations, 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 HTTP, 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 program- ming 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:
• http://www.amazon.com/webservices/
• http://www.google.com/apis/
• http://msdn.microsoft.com/mappoint/
Real 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 appli- cations, 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, and in some cases, several times daily. For each site, the process is almost identical: visit the URL, weave around a sea of adver- tisements, 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. Further- more, 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 maga- zine 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 limita- tions, 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 dissim- ilar 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:
• Browse the rendered feeds using a standalone RSS aggregator application. Examples of popular aggregators include RSS Bandit (http://www.rssbandit.org/), Straw (http:// www.gnome.org/projects/straw/), and SharpReader (http://www.sharpreader.net/). A screenshot of RSS Bandit is shown in Figure 20-1.
• Subscribe to any of the numerous Web-based RSS aggregators and view the feeds via a Web browser. Examples of popular online aggregators include Google Reader (http:// www.google.com/reader/), NewsIsFree (http://www.newsisfree.com/), and Bloglines (http:// www.bloglines.com/).
• Retrieve and republish the syndicated feed as part of a third-party Web application or service.
Later in this section, you’ll learn how this is accomplished using the Magpie RSS class library.
Figure 20-1. The RSS Bandit interface
WHO’S PUBLISHING RSS FEEDS?
Believe it or not, RSS has actually officially been around since early 1999, and in previous incarnations since 1996. However, like many emerging technologies, it remained a niche tool of the “techie” community, at least until recently. The emergence and growing popularity of news aggregation sites and tools has prompted an explosion in terms of the creation and publication of RSS feeds around the Web. These days, you can find RSS feeds just about everywhere, including within these prominent organizations:
• Yahoo! News: http://news.yahoo.com/rss/
• The Christian Science Monitor: http://www.csmonitor.com/rss/
• CNET News.com: http://www.news.com/
• BBC: http://www.bbc.co.uk/syndication/
• Wired.com: http://feeds.wired.com/wired/topheadlines
Given the adoption of RSS in such circles, it isn’t really a surprise that we’re hearing so much about this great technology these days.
RSS Syntax
If you’re not familiar with the general syntax of an RSS feed, Listing 20-1 offers an example that 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 Real 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.
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 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.
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.
Figure 20-3. Aggregating 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.
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 in the case that 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 docu- ment 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
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_dom() 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 the 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 docu- ment in the previous 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. 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:
array simplexml_element->xpath(string path)
XPath also offers a set of functions for selectively retrieving nodes based on value. Suppose you want to retrieve all authors found in the books.xml document:
<?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 is a lightweight protocol intended for exchanging structured information in a decen- tralized, 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.
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.
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.0. This section introduces this object-oriented extension and shows you how to create both a SOAP client and 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’ll also need to configure PHP with the --enable-soap extension. On Windows, you’ll 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 in WSDL mode, set it 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 url options are set when in non-WSDL mode:
actor: This parameter specifies the name, in URI format, of the role that a SOAP node must play in order to process the header.
compression: This parameter 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: This parameter turns on the exception-handling mechanism. It is enabled by default.
location: This parameter is used to specify the endpoint URL,when working in non-WSDL
mode.
login: This parameter specifies the username if HTTP authentication is used to access the
SOAP server.
password: This parameter specifies the password if HTTP authentication is used to access the
SOAP server.
proxy_host: This parameter specifies the name of the proxy host when connecting through a proxy server.
proxy_login: This parameter specifies the proxy server username if one is required. proxy_password: This parameter specifies the proxy server password if one is required. proxy_port: This parameter specifies the proxy server port when connecting through a proxy server. soap_version: This parameter specifies whether SOAP version 1.1 or 1.2 should be used. This
defaults to version 1.1.
trace: This parameter 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: This parameter 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.wjgilmore.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 docu- ment in the browser or a WSDL viewer by navigating to http://www.wjgilmore.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.wjgilmore.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(), and it requires that you pass in the name of a boxer,
returning 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 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 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.wjgilmore.com/boxing"
xmlns:tns="http://www.wjgilmore.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.wjgilmore.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 speci- fying 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:
$soapserver = new SoapServer("http://www.example.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. Let’s create this function and expose 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(). This method is introduced next.
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. Let’s create a class for the boxing quote service and export 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() will automatically direct 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: This mode specifies that PHP’s session-handling feature should be used to persist the object.
SOAP_PERSISTENCE_SESSION: This mode specifies that the object is destroyed at the end of the request.
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 simultaneously demonstrates both concepts. This SOAP service retrieves a famous quote from a particular boxer, and that boxer’s last name is requested using the exposed getQuote() method. It’s based on the boxing.wsdl file shown earlier in Listing 20-5. Let’s start with the server.
Creating the Boxing Server
The boxing server is simple but practical. Extending this to connect to a database server would be a trivial affair. Let’s 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.
Tuesday, July 14, 2009
Beginning PHP and Oracle From Novice to Professional by W. Jason Gilmore and Bob Bryla Chapter 20
Posted by Mr Procces at 9:40 AM
Labels: Oracle, PHP, Web Applications, Web Development
Subscribe to:
Post Comments (Atom)
0 comments:
Post a Comment