<nowiki>Recently, XML has gained ''popularity'' as a data-exchange and message-passing format. As web services become more widespread, XML plays an even more important role in a developer's life. With the help of a few extensions, PHP lets you read and write XML for every occasion. An italicized apostrophe:</nowiki>''<nowiki>foo'</nowiki>'' A reserved tag name: &lt;references> Not reserved: <reference> Reserved: &lt;ref> Not reserved: <refs> Tag names starting with "B": <Boo> and &lt;B>. A comment: &lt;!-- yep --> Inadvertant Wiki character reference: &#149;

+

Recently, XML has gained popularity as a data-exchange and message-passing format. As web services become more widespread, XML plays an even more important role in a developer's life. With the help of a few extensions, PHP lets you read and write XML for every occasion.

XML provides developers with a structured way to mark up data with tags arranged in a tree-like hierarchy. One perspective on XML is to treat it as CSV on steroids. You can use XML to store records broken into a series of fields. But, instead of merely separating each field with a comma, you can include a field name, type, and attributes alongside the data.

XML provides developers with a structured way to mark up data with tags arranged in a tree-like hierarchy. One perspective on XML is to treat it as CSV on steroids. You can use XML to store records broken into a series of fields. But, instead of merely separating each field with a comma, you can include a field name, type, and attributes alongside the data.

Introduction

Recently, XML has gained popularity as a data-exchange and message-passing format. As web services become more widespread, XML plays an even more important role in a developer's life. With the help of a few extensions, PHP lets you read and write XML for every occasion.

XML provides developers with a structured way to mark up data with tags arranged in a tree-like hierarchy. One perspective on XML is to treat it as CSV on steroids. You can use XML to store records broken into a series of fields. But, instead of merely separating each field with a comma, you can include a field name, type, and attributes alongside the data.

Another view of XML is as a document representation language. For instance, the PHP Cookbook was written using XML. The book is divided into chapters; each chapter into recipes; and each recipe into Problem, Solution, and Discussion sections. Within any individual section, we further subdivide the text into paragraphs, tables, figures, and examples. An article on a web page can similarly be divided into the page title and headline, the authors of the piece, the story itself, and any sidebars, related links, and additional content.

XML text looks similar to HTML. Both use tags bracketed by < and > for marking up text. But XML is both stricter and looser than HTML. It's stricter because all container tags must be properly closed. No opening elements are allowed without a corresponding closing tag. It's looser because you're not forced to use a set list of tags, such as <a>, <img>, and <h1>. Instead, you have the freedom to choose a series of tag names that best describe your data.

Other key differences between XML and HTML are case-sensitivity, attribute quoting, and whitespace. In HTML, <B> and <b> are the same bold tag; in XML, they're two different tags. In HTML, you can often omit quotation marks around attributes; XML, however, requires them. So, you must always write:

<element attribute="value">

Additionally, HTML parsers generally ignore whitespace, so a run of 20 consecutive spaces is treated the same as one space. XML parsers preserve whitespace, unless explicitly instructed otherwise. Because all elements must be closed, empty elements must end with />. For instance in HTML, the line break is <br>, while in XML, it's written as <br />.[1]

There is another restriction on XML documents. Since XML documents can be parsed into a tree of elements, the outermost element is known as the root element . Just as a tree has only one trunk, an XML document must have exactly one root element. In the previous book example, this means chapters must be bundled inside a book tag. If you want to place multiple books inside a document, you need to package them inside a bookcase or another container. This limitation applies only to the document root. Again, just like trees can have multiple branches off of the trunk, it's legal to store multiple books inside a bookcase.

This chapter doesn't aim to teach you XML; for an introduction to XML, see Learning XML, by Erik T. Ray. A solid nuts-and-bolts guide to all aspects of XML is XML in a Nutshell, by Elliotte Rusty Harold and W. Scott Means. Both books are published by O'Reilly & Associates.

Now that we've covered the rules, here's an example: if you are a librarian and want to convert your card catalog to XML, start with this basic set of XML tags:

From there, you can add new elements or modify existing ones. For example, <author> can be divided into first and last name, or you can allow for multiple records so two authors aren't placed in one field.

The first three recipes in this chapter cover writing and reading XML. Recipe 12.2 shows how to write XML without additional tools. To use the DOM XML extension to write XML in a standardized fashion, see Recipe 12.3. Reading XML using DOM is the topic of Recipe 12.4.

But XML isn't an end by itself. Once you've gathered all your XML, the real question is "What do you do with it?" With an event-based parser, as described in Recipe 12.5, you can make element tags trigger actions, such as storing data into easily manipulated structures or reformatting the text.

With XSLT, you can take a XSL stylesheet and turn XML into viewable output. By separating content from presentation, you can make one stylesheet for web browsers, another for PDAs, and a third for cell phones, all without changing the content itself. This is the subject of Recipe 12.6.

You can use a protocol such as XML-RPC or SOAP to exchange XML messages between yourself and a server, or to act as a server yourself. You can thus put your card catalog on the Internet and allow other programmers to query the catalog and retrieve book records in a format that's easy for them to parse and display in their applications. Another use would be to set up an RSS feed that gets updated whenever the library gets a new book in stock. XML-RPC clients and servers are the subjects of Recipe 12.7 and Recipe 12.8, respectively. Recipe 12.9 and Recipe 12.10 cover SOAP clients and servers. WDDX, a data exchange format that originated with the ColdFusion language, is the topic of Recipe 12.11. Reading RSS feeds, a popular XML-based headline syndication format, is covered in Recipe 12.12.

As with many bleeding-edge technologies, some of PHP's XML tools are not feature-complete and bug-free. However, XML is an area of active development in the PHP community; new features are added and bugs are fixed on a regular basis. As a result, many XML functions documented here are still experimental. Sometimes, all that means is that the function is 99% complete, but there may be a few small bugs lying around. Other times, it means that the name or the behavior of the function could be completely changed. If a function is in a highly unstable state, we mention it in the recipe.

We've documented the functions as they're currently planned to work in PHP 4.3. Because XML is such an important area, it made no sense to omit these recipes from the book. Also, we wanted to make sure that the latest functions are used in our examples. This can, however, lead to small problems if the function names and prototypes change. If you find that a recipe isn't working as you'd expect it to, please check the online PHP manual or the errata section of the catalog page for the PHP Cookbook, http://www.oreilly.com/catalog/phpckbk.

Generating XML Manually

Problem

You want to generate XML. For instance, you want to provide an XML version of your data for another program to parse.

Solution

Loop through your data and print it out surrounded by the correct XML tags:

Discussion

Printing out XML manually mostly involves lots of foreach loops as you iterate through arrays. However, there are a few tricky details. First, you need to call header( ) to set the correct Content-Type header for the document. Since you're sending XML instead of HTML, it should be text/xml.

Next, depending on your settings for the short_open_tag configuration directive, trying to print the XML declaration may accidentally turn on PHP processing. Since the <? of <?xml version="1.0"?> is the short PHP open tag, to print the declaration to the browser you need to either disable the directive or print the line from within PHP. We do the latter in the Solution.

Last, entities must be escaped. For example, the & in the show Law & Order needs to be &amp;. Call htmlspecialchars( ) to escape your data.

Discussion

A single element is known as a node . Nodes can be of a dozen different types, but the three most popular are elements, attributes, and text. Given this:

<book cover="soft">PHP Cookbook</book>

PHP's DOM XML functions refer to book as type XML_ELEMENT_NODE , cover="soft" maps to an XML_ATTRIBUTE_NODE , and PHP Cookbook is a XML_TEXT_NODE.

For DOM parsing, PHP uses libxml, developed for the Gnome project. You can download it from http://www.xmlsoft.org. To activate it, configure PHP with --with-dom.

The revamped PHP 4.3 DOM XML functions follow a pattern. You create an object as either an element or a text node, add and set any attributes you want, and then append it to the tree in the spot it belongs.

Before creating elements, create a new document, passing the XML version as the sole argument:

$dom = domxml_new_doc('1.0');

Now create new elements belonging to the document. Despite being associated with a specific document, nodes don't join the document tree until appended:

Here a new book element is created and assigned to the object $book_element. To create the document root, append $book_element as a child of the $dom document. The result, $book, refers to the specific element and its location within the DOM object.

All nodes are created by calling a method on $dom. Once a node is created, it can be appended to any element in the tree. The element from which we call the append_child( ) method determines the location in the tree where the node is placed. In the previous case, $book_element is appended to $dom. The element appended to $dom is the top-level node, or the root node.

You can also append a new child element to $book. Since $book is a child of $dom, the new element is, by extension, a grandchild of $dom:

The only parameter dump_mem( ) takes is an optional boolean value. An empty value or false means "return the string as one long line." A true value causes the XML to be nicely formatted with child nodes indented, like this:

You can pass up to three values to dump_file( ) . The first one, which is mandatory, is the filename. The second is whether the file should be compressed with gzip. The final value is the same pretty formatting option as dump_mem( ).

Parsing XML with the DOM

Problem

You want to parse an XML file using the DOM API. This puts the file into a tree, which you can process using DOM functions. With the DOM, it's easy to search for and retrieve elements that fit a certain set of criteria.

Solution

Use PHP's DOM XML extension. Here's how to read XML from a file:

$dom = domxml_open_file('books.xml');

Here's how to read XML from a variable:

$dom = domxml_open_mem($books);

You can also get just a single node. Here's how to get the root node:

$root = $dom->document_element( );

Here's how to do a depth-first recursion to process all the nodes in a document:

Discussion

The W3C's DOM provides a platform- and language-neutral method that specifies the structure and content of a document. Using the DOM, you can read an XML document into a tree of nodes and then maneuver through the tree to locate information about a particular element or elements that match your criteria. This is called tree-based parsing . In contrast, the non-DOM XML functions allow you to do event-based parsing.

Additionally, you can modify the structure by creating, editing, and deleting nodes. In fact, you can use the DOM XML functions to author a new XML document from scratch; see Recipe 12.3

One of the major advantages of the DOM is that by following the W3C's specification, many languages implement DOM functions in a similar manner. Therefore, the work of translating logic and instructions from one application to another is considerably simplified. PHP 4.3 comes with an updated series of DOM functions that are in stricter compliance with the DOM standard than previous versions of PHP. However, the functions are not yet 100% compliant. Future PHP versions should bring a closer alignment, but this may break some applications that need minor updates. Check the DOM XML material in the online PHP Manual at http://www.php.net/domxml for changes. Functions available in earlier versions of PHP are available, but deprecated.

The DOM is large and complex. For more information, read the specification at http://www.w3.org/DOM/ or pick up a copy of XML in a Nutshell; Chapter 18 discusses the DOM.

For DOM parsing, PHP uses libxml , developed for the Gnome project. You can download it from http://www.xmlsoft.org. To activate it, configure PHP with --with-dom.

DOM functions in PHP are object-oriented. To move from one node to another, call methods such as $node->child_nodes( ) , which returns an array of node objects, and $node->parent_node( ) , which returns the parent node object. Therefore, to process a node, check its type and call a corresponding method:

The get_elements_by_tagname( ) function returns an array of element node objects. By looping through each element's children, you can get to the text node associated with that element. From there, you can pull out the node values, which in this case are the names of the book authors, such as Sklar and Trachtenberg.

Discussion

These XML parsing functions require the expat library. However, because Apache 1.3.7 and later is bundled with expat, this library is already installed on most machines. Therefore, PHP enables these functions by default, and you don't need to explicitly configure PHP to support XML.

expat parses XML documents and allows you to configure the parser to call functions when it encounters different parts of the file, such as an opening or closing element tag or character data (the text between tags). Based on the tag name, you can then choose whether to format or ignore the data. This is known as event-based parsing and contrasts with DOM XML, which use a tree-based parser.

A popular API for event-based XML parsing is SAX: Simple API for XML. Originally developed only for Java, SAX has spread to other languages. PHP's XML functions follow SAX conventions. For more on the latest version of SAX — SAX2 — see SAX2 by David Brownell (O'Reilly).

PHP supports two interfaces to expat: a procedural one and an object-oriented one. Since the procedural interface practically forces you to use global variables to accomplish any meaningful task, we prefer the object-oriented version. With the object-oriented interface, you can bind an object to the parser and interact with the object while processing XML. This allows you to use object properties instead of global variables.

Here's an example application of expat that shows how to process an RSS feed and transform it into HTML. For more on RSS, see Recipe 12.12. The script starts with the standard XML processing code, followed by the objects created to parse RSS specifically:

After creating a new XML parser and an instance of the pc_RSS_parser class, configure the parser. First, bind the object to the parser; this tells the parser to call the object's methods instead of global functions. Then call xml_set_element_handler( ) and xml_set_character_data_handler( ) to specify the method names the parser should call when it encounters elements and character data. The first argument to both functions is the parser instance; the other arguments are the function names. With xml_set_element_handler( ), the middle and last arguments are the functions to call when a tag opens and closes, respectively. The xml_set_character_data_handler( ) function takes only one additional argument — the function to call when it processes character data.

Because an object has been associated with our parser, when that parser finds the string <tag>data</tag>, it calls $rss->start_element( ) when it reaches <tag>; $rss->character_data( ) when it reaches data; and $rss->end_element( ) when it reaches </tag>. The parser can't be configured to automatically call individual methods for each specific tag; instead, you must handle this yourself. However, the PEAR package XML_Transform provides an easy way to assign handlers on a tag-by-by basis.

The last XML parser configuration option tells the parser not to automatically convert all tags to uppercase. By default, the parser folds tags into capital letters, so <tag> and <TAG> both become the same element. Since XML is case-sensitive, and most feeds use lowercase element names, this feature should be disabled.

In order to curb memory usage, load the file in 4096-byte chunks, and feed each piece to the parser one at a time. This requires you to write the handler functions that will accommodate text arriving in multiple calls and not assume the entire string comes in all at once.

Last, while PHP cleans up any open parsers when the request ends, you can also manually close the parser by calling xml_parser_free( ) .

Now that the generic parsing is properly set up, add the pc_RSS_item and pc_RSS_parser classes, as shown in Examples Example 12-1 and Example 12-2, to handle a RSS document.

The pc_RSS_item class provides an interface to an individual feed item. This removes the details of displaying each item from the general parsing code and makes it easy to reset the data for a new item by calling unset( ).

The pc_RSS_item::display( ) method prints out an HTML-formatted RSS item. It calls htmlspecialchars( ) to reencode any necessary entities, because expat decodes them into regular characters while parsing the document. This reencoding, however, breaks on feeds that place HTML in the title and description instead of plaintext.

Within pc_RSS_parser( ), the start_element( ) method takes three parameters: the XML parser, the name of the tag, and an array of attribute/value pairs (if any) from the element. PHP automatically supplies these values to the handler as part of the parsing process.

The start_element( ) method checks the value of $tag. If it's item, the parser's found a new RSS item, and a new pc_RSS_item object is instantiated. Otherwise, it checks to see if $this->item is empty( ); if it isn't, the parser is inside an item element. It's then necessary to record the tag's name, so that the character_data( ) method knows which property to assign its value to. If it is empty, this part of the RSS feed isn't necessary for our application, and it's ignored.

When the parser finds a closing item tag, the corresponding end_element( ) method first prints the RSS item, then cleans up by deleting the object.

Finally, the character_data( ) method is responsible for assigning the values of title, description, and link to the RSS item. After making sure it's inside an item element, it checks that the current tag is one of the properties of pc_RSS_item. Without this check, if the parser encountered an element other than those three, its value would also be assigned to the object. The { } s are needed to set the object property dereferencing order. Notice how trim($data) is appended to the property instead of a direct assignment. This is done to handle cases in which the character data is split across the 4096-byte chunks retrieved by fread( ); it also removes the surrounding whitespace found in the RSS feed.

Transforming XML with XSLT

Problem

You have a XML document and a XSL stylesheet. You want to transform the document using XSLT and capture the results. This lets you apply stylesheets to your data and create different versions of your content for different media.

Discussion

XML documents describe the content of data, but they don't contain any information about how those data should be displayed. However, when XML content is coupled with a stylesheet described using XSL (eXtensible Stylesheet Language), the content is displayed according to specific visual rules.

The glue between XML and XSL is XSLT, which stands for eXtensible Stylesheet Language Transformations. These transformations apply the series of rules enumerated in the stylesheet to your XML data. So, just as PHP parses your code and combines it with user input to create a dynamic page, an XSLT program uses XSL and XML to output a new page that contains more XML, HTML, or any other format you can describe.

There are a few XSLT programs available, each with different features and limitations. PHP currently supports only the Sablotron XSLT processor, but in the future you'll be able to use other programs, such as Xalan and Libxslt. You can download Sablotron from http://www.gingerall.com. To enable Sablotron for XSLT processing, configure PHP with both --enable-xslt and --with-xslt-sablot.

Processing documents takes a few steps. First, you need to grab a handle to a new instance of an XSLT processor with xslt_create( ) . Then, to transform the files, use xslt_process( ) to make the transformation and check the results:

You start by defining variables to store the filenames for the XML data and the XSL stylesheet. They're the first two parameters to the transforming function, xslt_process( ). If the fourth argument is missing, as it is here, or set to NULL, the function returns the results. Otherwise, it writes the resulting data to the filename passed:

xslt_process($xslt, $xml, $xsl, 'data.html');

If you want to provide your XML and XSL data from variables instead of files, call xslt_process( ) with a fifth parameter, which allows you to substitute string placeholders for your files:

When reading and writing files, Sablotron supports two types of URIs. The PHP default is file:, so Sablotron looks for the data on the filesystem. Sablotron also uses a custom URI of arg:, which allows users to alternatively pass in data using arguments. That's the feature used here.

In the previous example, the data for the XML and XSL comes from a database, but, it can arrive from anywhere, such as a remote URL or POSTed data. Once you've obtained the data, create the $args array. This sets up mappings between the argument names and the variable names. The keys of the associative array are the argument names passed to xslt_process( ); the values are the variables holding the data. By convention, /_xml and /_xsl are the argument names; however, you can use others.

Then call xslt_process( ) and in place of data.xml, use arg:/_xml, with arg: being the string that lets the extension know to look in the $args array. Because you're passing in $args as the fifth parameter, you need to pass NULL as the fourth argument; this makes sure the function returns the results.

Sending XML-RPC Requests

Problem

You want to be an XML-RPC client and make requests of a server. XML-RPC lets PHP make function calls to web servers, even if they don't use PHP. The retrieved data is then automatically converted to PHP variables for use in your application.

Solution

Use PHP's built-in XML-RPC extension with some helper functions. As of PHP 4.1, PHP bundles the xmlrpc-epi extension. Unfortunately, xmlrpc-epi does not have any native C functions for taking a XML-RPC formatted string and making a request. However, the folks behind xmlrpc-epi have a series of helper functions written in PHP available for download at http://xmlrpc-epi.sourceforge.net/xmlrpc_php/index.php. The only file used here is the one named index.php, which is located in xmlrpc_php/. To install it, just copy that file to a location where PHP can find it in its include_path.

Here's some client code that calls a function on an XML-RPC server that returns state names:

Discussion

XML-RPC, a format created by Userland Software, allows you to make a request to a web server using HTTP. The request itself is a specially formatted XML document. As a client, you build up an XML request to send that fits with the XML-RPC specification. You then send it to the server, and the server replies with an XML document. You then parse the XML to find the results. In the Solution, the XML-RPC server returns a state name, so the code prints:

I love New York!

Unlike earlier implementations of XML-RPC, which were coded in PHP, the current bundled extension is written in C, so there is a significant speed increase in processing time. To enable this extension while configuring PHP, add --with-xmlrpc.

The server settings tell PHP which web site to contact to make the request. The $host is the hostname of the machine; $port is the port the web server is running on, which is usually port 80; and $uri is the pathname to the XML-RPC server you wish to contact. This request is equivalent to http://betty.userland.com:80/RPC2. If no port is given, the function defaults to port 80, and the default URI is the web server root, /.

The request settings are the function to call and the data to pass to the function. The method examples.getStateName takes an integer from 1 to 50 and returns a string with the name of the U.S. state, in alphabetical order. In XML-RPC, method names can have periods, while in PHP, they cannot. If they could, the PHP equivalent to passing 32 as the argument to the XML-RPC call to examples.getStateName is calling a function named examples.getStateName( ):

The xu_rpc_http_concise( ) function makes the XML-RPC call and returns the results. Since the return value is a string, you can print $results directly. If the XML-RPC call returns multiple values, xu_rpc_http_concise( ) returns an array.

There are 10 different parameters that can be passed in the array to xu_rpc_http_concise( ), but the only one that's required is host. The parameters are shown in Table 12-1.

Table 12-1. Parameters for xu_rpc_http_concise( )

Name

Description

host

Server hostname

uri

Server URI (default /)

port

Server port (default 80)

method

Name of method to call

args

Arguments to pass to method

debug

Debug level (0 to 2: 0 is none, 2 is lots)

timeout

Number of seconds before timing out the request; a value of 0 means never timeout

user

Username for Basic HTTP Authentication, if necessary

pass

Password for Basic HTTP Authentication, if necessary

secure

Use SSL for encrypted transmissions; requires PHP to be built with SSL support (pass any true value)

Discussion

Since the bundled XML-RPC extension, xmlrpc-epi, is written in C, it processes XML-RPC requests in a speedy and efficient fashion. Add --with-xmlrpc to your configure string to enable this extension during compile time. For more on XML-RPC, see Recipe 12.7.

The Solution begins with a definition of the PHP function to associate with the XML-RPC method. The name of the function is return_time( ) . This is later linked with the get_time( ) XML-RPC method:

function return_time($method, $args) {
return date('Ymd\THis');
}

The function returns an ISO 8601-formatted string with the current date and time. We escape the T inside the call to date( ) because the specification requires a literal T to divide the date part and the time part. For August 21, 2002 at 3:03:51 P.M., the return value is 20020821T150351.

The function is automatically called with two parameters: the name of the XML-RPC method the server is responding to and an array of method arguments passed by the XML-RPC client to the server. In this example, the server ignores both variables.

We create a new server and assign it to $server, then call xmlrpc_server_register_method( ) with three parameters. The first is the newly created server, the second is the name of the method to register, and the third is the name of the PHP function to handle the request.

Now that everything is configured, tell the XML-RPC server to dispatch the method for processing and print the results to the client:

The client request comes in as POST data. PHP converts HTTP POST data to variables, but this is XML-RPC data, so the server needs to access the unparsed data, which is stored in $GLOBALS['HTTP_RAW_POST_DATA']. In this example, the request XML looks like this:

Thus, the server is responding to the get_time( ) method, and it expects no parameters.

We also configure the response options to output the results in XML and interpret the request as XML-RPC. These two variables are then passed to xmlrpc_server_call_method( ) along with the XML-RPC server, $server. The third parameter to this function is for any user data you wish to provide; in this case, there is none, so we pass NULL.

The xmlrpc_server_call_method( ) function decodes the variables, calls the correct function to handle the method, and encodes the response into XML-RPC. To reply to the client, all you need to do is print out what xmlrpc_server_call_method( ) returns.

Finally, clean up with a call to xmlrpc_server_destroy( ):

xmlrpc_server_destroy($server);

Using the XML-RPC client code from Recipe 12.7, you can make a request and find the time, as follows:

It is legal to associate multiple methods with a single XML-RPC server. You can also associate multiple methods with the same PHP function. For example, we can create a server that replies to two methods: get_gmtime( ) and get_time( ). The first method, get_gmtime( ), is similar to get_time( ), but it replies with the current time in GMT. To handle this, you can extend get_time( ) to take an optional parameter, which is the name of a time zone to use when computing the current time.

Here's how to change the return_time( ) function to handle both methods:

This function uses both the $method and $args parameters. At the top of the function, we check if the request is for get_gmtime. If so, the time zone is set to GMT. If it isn't, see if an alternate time zone is specified as an argument by checking $args[0]. If neither check is true, we keep the current time zone.

To configure the server to handle the new method, add only one new line:

xmlrpc_server_register_method($server, 'return_time', 'get_gmtime');

This maps get_gmtime( ) to return_time( ).

Here's an example of a client in action. The first request is for get_time( ) with no parameters; the second calls get_time( ) with a time zone of PST8PDT, which is three hours behind the server; the last request is for the new get_gmtime( ) method, which is four hours ahead of the server's time zone.

Discussion

The Simple Object Access Protocol (SOAP), is, like XML-RPC, a method for exchanging information over HTTP. It uses XML as its message format, which makes it easy to create and parse. As a result, because it's platform- and language-independent, SOAP is available on many platforms and in many languages, including PHP. To make a SOAP request, you instantiate a new SOAP_Client object and pass the constructor the location of the page to make the request:

$soap = new SOAP_Client('http://api.google.com/search/beta2');

Currently, two different types of communications methods are supported: HTTP and SMTP. Secure HTTP is also allowed, if SSL is built into your version of PHP. To choose one of these methods, begin your URL with http, https, or mailto.

After creating a SOAP_Client object, you use its call( ) method to call a remote function:

The $params array holds a collection of SOAP_Value objects. A SOAP_Value object is instantiated with three arguments: the name, type, and value of the parameter you're passing to the SOAP server. These vary from message to message, depending upon the SOAP functions available on the server.

The real action happens with the SOAP_Client::call( ) method, which takes a few arguments. The first is the method you want the server to execute; here, it's doGoogleSearch. The second argument is an array of parameters that gets passed to the function on the SOAP server. The third argument, urn:GoogleSearch, is the SOAP namespace; it allows the server to know that doGoogleSearch belongs in the GoogleSearch namespace. With namespaces, a more generally named search method doesn't cause a conflict with another more specific search method.

There's a fourth parameter that's unused here: soapAction. If you want to provide the SOAP server with a URI indicating the intent of the request, you can add one here. Unfortunately, the definition of the word "intent" varies from implementation to implementation. The current consensus is that soapAction shouldn't be used until its meaning is further clarified. The PEAR SOAP server doesn't use this field, but other vendors may assign their own meanings.

Upon successful execution, the function returns an object containing the server's response. If an error occurs, the function returns a PEAR_Error object. Google returns all sorts of information, but here we just iterate through the $resultElements array and pull out the URL and title of each hit for display:

This code is equivalent to the longer previous example. The SOAP_WSDL object takes a URL for the GoogleSearch WSDL file and automatically loads the specification from that URL. Instead of making $soap a SOAP_Client, call SOAP_WSDL::getProxy( ) to create a GoogleSearch object.

This new object has methods with the same name as the GoogleSearch SOAP methods. So, instead of passing doGoogleSearch as the first parameter to SOAP_Client::call( ), you call $soap->doGoogleSearch( ). The $params array becomes the arguments for the method, without any array encapsulation or SOAP_Value instantiations necessary. Also, because it's set in the WSDL file, the namespace doesn't need to be specified.

Discussion

There are three steps to creating a SOAP server with PEAR's SOAP_Server class:

Create a class to process SOAP methods and instantiate it

Create an instance of a SOAP server and associate the processing object with the server

Instruct the SOAP server to process the request and reply to the SOAP client

The PEAR SOAP_Server class uses objects to handle SOAP requests. A request-handling class needs a $method_namespace property that specifies the SOAP namespace for the class. In this case, it's urn:pc_SOAP_return_time. Object methods then map to SOAP procedure names within the namespace. The actual PHP class name isn't exposed via SOAP, so the fact that both the name of the class and its $method_namespace are identical is a matter of convenience, not of necessity:

Once the class is defined, you create an instance of the class to link methods with the SOAP server object. Before mapping the procedures to the class methods, however, you first must instantiate a SOAP_Server object:

Once that's done, call SOAP_Server::addObjectMap( ) with the object to tell the SOAP server about the methods the object provides. Now the server is ready to reply to all SOAP requests within the namespace for which you've defined methods.

To tell the server to respond to the request, call SOAP_Server::service( ) and pass the SOAP envelope. Because the envelope arrives via POST, you pass $GLOBALS['HTTP_RAW_POST_DATA']. This provides the server with the complete request, because the class takes care of the necessary parsing.

To extend the method to read in parameters, you need to alter the method prototype to include parameter names and then modify the client request to include data for the additional arguments. This example modifies the SOAP procedure to accept an optional time zone argument:

Discussion

WDDX stands for Web Distributed Data eXchange and was one of the first XML formats to share information in a language-neutral fashion. Invented by the company behind ColdFusion, WDDX gained a lot of popularity in 1999, but doesn't have much momentum at the present.

Instead, many people have begun to use SOAP as a replacement for WDDX. But WDDX does have the advantage of simplicity, so if the information you're exchanging is basic, WDDX may be a good choice. Also, due to its origins, it's very easy to read and write WDDX packets in ColdFusion, so if you need to communicate with a ColdFusion application, WDDX is helpful.

Variables are wrapped inside <var> tags with the variable name assigned as the value for the name attribute. Inside there is another set of tags that indicate the variable type: string, number, dateTime, boolean, array, binary, or recordSet. Finally, you have the data itself.

You can also serialize one variable at a time using wddx_serialize_value :

Discussion

RSS, which stands for RDF Site Summary, is an easy-to-use headline or article syndication format written in XML.[2] Many news web sites, such as Slashdot and O'Reilly's Meerkat, provide RSS feeds that update whenever new stories are published. Weblogs have also embraced RSS and having an RSS feed for your blog is a standard feature. The PHP web site also publishes RSS feeds for most PHP mailing lists.

This foreach loop creates an unordered list of items with the item title linking back to the URL associated with the complete article, as shown in Figure 12-1. Besides the required title and link fields, an item can have an optional description field that contains a brief write-up about the item.

Figure 12-1. php.announce RSS feed

Each channel also has an entry with information about the feed, as shown in Figure 12-2. To retrieve that data, call XML_RSS::getChannelInfo( ) :