The Oracle + PHP Cookbook

Creating Oracle-Powered SOAP Services in PHP


by John Coggeshall

Harness the native SOAP support in PHP5.

Downloads for this article:
 Oracle Database 10g Express Edition
 Zend Core for Oracle
 Apache Web Server 1.3.x

Published September 2006

The ability to provide data and functionality to other Internet-based Web applications via Web services is quickly becoming a requirement for serious development. While Oracle does provide a number of ways to host Web services, doing so is not always the most effective approach if you are already using PHP to develop your Web application. In this recipe, I’ll take you step-by-step through the development of a SOAP client and server in PHP using Oracle as a backend for your data.

To really understand the answer to this question, you need to understand the lifecycle of PHP script execution and how your Web server affects that lifecycle—and that's exactly where I'll begin this recipe.

Required Components

For the purposes of this recipe you will use a very simplistic database backend that will store some basic information regarding published books in a single table represented by the following CREATE statement:

CREATE TABLE books(isbn VARCHAR(32) PRIMARY KEY,
                   author VARCHAR(50),
                   title VARCHAR(50),
                   price FLOAT);
This table will serve as the data source for your SOAP server, which in turn will provide the data to one or more SOAP clients to use as they see fit. Although in any real-life application your database is likely to be considerably more complex, the techniques explained here will still apply.

With the database established (and hopefully you’ve put some dummy data in it), you’re now ready to start looking at what is involved in the development of a SOAP server in PHP.

How SOAP Services work in PHP

You have several options for developing SOAP services in PHP, all revolving around the SoapServer PHP class. This class is the centerpiece of any PHP-based SOAP service and has the following syntax:

$server = new SoapServer($wsdl [, $options]);
Where $wsdl is the location of the Web Service Description Language (WSDL) document describing the service being hosted and $options is an array of key / value pairs containing any setup options to be taken in consideration when creating the service. You will learn more about the WSDL document later; for now, take a look at the options available when creating a new SOAP service:
  • soap_version: The SOAP protocol version to use when communicating with clients. Possible options are the constants SOAP_1_1 for SOAP version 1.1 or SOAP_1_2 for SOAP version 1.2
  • encoding: The character encoding to use for this SOAP service (i.e. the string ISO-8859-1)
  • actor: The actor URI for this SOAP Service
  • classmap: An array itself of key/value pairs mapping WSDL data types to class names in PHP. When used, PHP will represent these classes to the connecting client as the type defined in the WSDL.
Thus, to create a SOAP service using the SOAP v1.2 protocol using a WSDL document named bookman.wsdl you would construct the server like this:
$server = new SoapServer(“bookman.wsdl”, array(‘soap_version’ => SOAP_1_2));
The next step in the process is to create service methods. In PHP this can be done using two primary methods. The first, which is the most flexible, is to specify each function to be hosted in the service manually using the addFunction() method and passing it the name of the function to expose to the client:
function add($a, $b) {
    return $a + $b;
}

$server->addFunction(‘add’);
You can also add multiple functions by providing an array of function names:
function add($a, $b) {
    return $a + $b;
}

function sub($a, $b) {
    return $a - $b;
}

$server->addFunction(array(‘add’, ‘sub’));
Finally, you can export all defined functions by passing the special constant SOAP_FUNCTIONS_ALL instead of a function name as shown:
function add($a, $b) {
    return $a + $b;
}

function sub($a, $b) {
    return $a - $b;
}

$server->addFunction(SOAP_FUNCTIONS_ALL);
AAs you can see from the examples above, functions exposed as SOAP services look identical to routine PHP functions. By definition, however, there are a few rules that apply to a function used in the context of a SOAP service that do not apply to routine PHP functions:
  • The function must accept the same input parameters, in the same order, as defined in the server’s provided WSDL document.
  • The function must not output anything (i.e. print/echo).
  • The function must return one or more values (multiple values are returned in the form of an associative array of key/value pairs).
Because it is not always architecturally or aesthetically wise to represent all your exposed service calls in procedural functions, PHP also provides a means of using objects to represent your SOAP service. Using the addClass() method, you can assign a class to represent the entire SOAP service’s functions wherein all public methods will automatically be exposed as service calls:
class math {
    public function add($a, $b) {
        return $a + $b;
    }

    public function sub($a, $b) {
        return $a - $b;
    }
}

$server->addClass("math");
As you'll see later, this method is the cleanest and most modular one of all.

To finish your SOAP server, you must instruct it to process any incoming requests that may occur from a connecting SOAP client. This is done via the handle() method, which requires no parameters.

In summary, creating a SOAP server in PHP is as simple as the following example:

<?php

class math {
    public function add($a, $b) {
        return $a + $b;
    }

    public function sub($a, $b) {
        return $a - $b;
    }
}

$server = new SoapServer(‘math.wsdl’);
$server->addClass(‘math’);
$server->handle();

?>

If Something Were to Go Wrong…

One issue that has yet to be addressed when creating a SOAP service in PHP is how errors are reported back to a client in the event of something going wrong. By specification in the SOAP protocol, errors during the request should be handled by returning a special SOAP Fault response back to the requesting client. In PHP, this is accomplished by throwing an instance of the SoapFault class, providing to it the error code and optional error message describing the nature of the error as shown:

public Function div($a, $b) {
    if($b == 0) {
        throw new SoapFault(-1, “Cannot divide by zero!”);
    }
    
    return $a / $b;
}
Note that both parameters to SoapFault have no meaning other then that expressed by your application itself. Thus, while I used negative one as my error code, I could have just as easily picked any other integer value I desired.

Generating WSDL

While the previous example is indeed a complete PHP script for creating a SOAP service, it does not address the issue of a WSDL document at all. Seeing as the WSDL document is a critical component of this entire process, generating the WSDL document will take some extra work.

Unfortunately, due to the typeless nature of PHP, there is no reasonable way to auto-generate WSDL documents by PHP on the fly as is the case with strongly-typed languages such as Java or .NET Services. WSDL documents must specify the type of each parameter, so you need an alternate way to express this in your script as your variables $a and $b provide no typing information. A number of options are available:

  • Write the WSDL document yourself by hand.
  • Use a Web-based WSDL generator to generate the file by manually entering each method and typing information.
  • Use Zend Studio’s automatic WSDL generator.
While all three options are perfectly viable, I will demonstrate how to generate the WSDL document using Zend Studio’s WSDL generator for two reasons. First, it is by far the easiest and most reliable way to generate WSDL documents, and second, Zend Studio is available in almost every serious PHP shop.

In order to generate WSDL documents using the Studio WSDL generator, first you must identify for each exposed method the typing information of its parameters and return values by using in-line documentation comments called PHPDoc (the PHP version of the popular JavaDoc). PHPDoc is simply a block comment placed at the start of every function using a particular parseable syntax used for automatic documentation generation. Zend Studio also uses this information to gather typing information required to generate the WSDL document.

Continuing our previous examples, below is the same math class used before, except this time with PHPDoc comments:

/**
 * A simple math utility class
 * @author John Coggeshall john@zend.com
 */
class math {
    /**
     * Add two integers together
     *
     * @param integer $a The first integer of the addition
     * @param integer $b The second integer of the addition
     * @return integer The sum of the provided integers
     */
    public function add($a, $b) {
        return $a + $b;
    }

    /**
     * Subtract two integers from each other
     *
     * @param integer $a The first integer of the subtraction
     * @param integer $b The second integer of the subtraction
     * @return integer The difference of the provided integers
     */
    public function sub($a, $b) {
        return $a - $b;
    }
}
With these PHPDoc comments in place, you can have Zend Studio automatically generate a suitable WSDL document for this class by executing the WSDL generator under the Tools menu in Studio:

Fig 1

With the PHPDoc comments in place, the otherwise tedious and unrewarding task of generating a WSDL document for your SOAP server is reduced to nothing more than following a very simple step-by-step wizard. When complete, Studio will open the WSDL document in Studio for your review and save it to the location of your choice.

After the document has been generated, it must reside in a place that it is accessible to the server (which requires it when the class is instantianted), as well as to any potential SOAP clients that may be using the service. Generally, this is most easily accomplished by placing the WSDL document in the same location as the endpoint PHP script hosting the SOAP service.

Creating Your BookManager Class

Now that you are familiar with everything required in the implementation of a SOAP service in PHP, it's time to bring your database back into the picture. For the purposes of this recipe I have created a class called BookManager. This class will serve the same purposes as the math class did in earlier examples, except it is designed to interact with the database and provide a SOAP service that allows you to perform general maintence and querying against the books table described at the start of this tutorial. Specifically, the BookManager class implements the following methods to be exposed as SOAP calls:

addBook($isbn, $author, $title, $price); // Adds a Book to the database
delBook($isbn); // Deletes a book by ISBN number
findBookISBNByAuthor($author); // Returns an array of ISBN numbers of books written by a         
                               // specific author
findBookISBNByTitle($title); // Returns an array of ISBN numbers of books whose title 
                             // matches the substring provided
getBookByISBN($isbn); // Returns the details of the book identified by ISBN
listAllBooks(); // Returns an array of all ISBN numbers in the database
While there are a few other methods in the class itself, the above six methods are the only declared public (except of course the constructor) and thus the only ones exposed as a SOAP service. While examining each of these methods in detail would be overkill for this tutorial (especially since they are all basically the same in form), in the name of completeness let’s take a look at the delBook() method:
/**
         * Delete a book from the database by ISBN
         *
         * @param string $isbn The ISBN serial number of the book to delete
         * 
         * @return mixed SOAP Fault on error, true on success
         */
        public function delBook($isbn) {
                
                $query = "DELETE FROM books
                                WHERE isbn = :isbn";
                
                $stmt = oci_parse($this->getDB(), $query);
                
                if(!$stmt) {
                        throw new SoapFault(-1, "Failed to prepare query (reason: " .  
                                         oci_error($stmt) . ")");
                }
                
                oci_bind_by_name($stmt, "isbn", $isbn, 32);

                if(!oci_execute($stmt)) {
                        oci_rollback($this->getDB());
                        throw new SoapFault(-1, "Failed to execute query (reason: " . 
                                          oci_error($stmt) . ")");
                }
                
                oci_commit($this->getDB());
                
                return true;
        }
For those of you familiar with the Oracle API for PHP, the method shown above should be very straightforward. For the rest of you, let’s examine some of the key points in this function starting with the oci_parse() method. This method accepts an SQL query in the form of a string (with, if desired, placeholders for each variable in the query) and returns a statement resource representing it. From here, this statement resource’s placeholders can be mapped directly to PHP variables by using the oci_bind_by_name() method, which accepts the statement, placeholder name, corresponding PHP variable, and an optional maximum length of the column in question as parameters. Once PHP has bound each placeholder to a PHP variable the statement can be executed and the results acted upon. Of course, because this operation is a write operation against your table, you complete your successful function execution by committing the changes to the database and returning a successful state.

For your reference, below is the entire BookManager class and corresponding server script that uses it to expose the SOAP service.

Your browser does not appear to support inline frames. You can view this listing here.

Creating SOAP Clients in PHP

With creating SOAP services in PHP explained, let’s also take a moment to review how to create SOAP clients so that your server has something to communicate with.

While there are numerous approaches to executing a remote procedure call via SOAP using the PHP SOAP implementation, the recommended way is through the use of a WSDL document. You had to generate this document already to make the SOAP service to work, so it is easily available.

To create a SOAP client in PHP, you must create an instance of the SoapClient class, which has the following constructor:

$client = new SoapClient($wsdl [, $options]);
As was the case with the SoapServer class, the $wsdl parameter is the location of the WSDL document for the service being accessed and the optional parameter $options is an array of key/value pairs configuring the client connection. Here are some of the options available (see www.php.net/ for a complete list):
  • soap_version: The version of SOAP protocol to use, either the constant SOAP_1_1 or SOAP_1_2
  • login: If using HTTP authentication on the SOAP server, this is the login name to use
  • password: If using HTTP authentication on the SOAP server, this is the password to use
  • proxy_host: If connecting via a Proxy server, this is the address of the server
  • proxy_port: If connecting via a Proxy server, this is the port of the proxy is listening on
  • proxy_login: If connecting via a Proxy server, this is the username to login with
  • proxy_password: If connecting via a Proxy server, this is the password to login with
  • local_cert: If connecting to a SOAP server that communicates via secure HTTP (https), this is the location of the local certification file
  • passphrase: Used in conjunction with local_cert to provide the passphrase for the certification file if any
  • compression: If set to true PHP will attempt to communicate with the SOAP server using a compressed HTTP request
  • classmap: An array of key/value pairs mapping WSDL data types to PHP classes for use in the client
Once a SOAP client in PHP is instantiated via a WSDL document, the client object returned can be used to call the methods exposed on the SOAP server as if they were native PHP calls and deal with any SOAP Faults that may occur as native PHP exceptions. For instance, returning to our original math SOAP service example, the following is a complete PHP SOAP client:
<?php

    $client = new SoapClient(“http://www.example.com/math.wsdl”);
    
    try {
        $result = $client->div(10,rand(0,5); // will cause a Soap Fault if divide by zero
        print “The answer is: $result”;
    } catch(SoapFault $f) {
        print “Sorry an error was caught executing your request: {$e->getMessage()}”;
    }
?>

As you can see, accessing SOAP services (regardless if they are implemented in PHP) is simple using the SoapClient Class. In fact, creating a Web-based management system of your books database via a SOAP service is almost trivial! As shown below, clearly much more coding was required to develop the logic and interface of a simple querying interface then to actually make it work with the SOAP services directly:

Your browser does not appear to support inline frames. You can view this listing here.

This, when executed, provides an ugly, yet completely functional, interface to your Oracle database via PHP-powered Web services:

Fig 2

Conclusion

You should now be equipped with everything you need to take your Oracle-backed databases and combine them with the power of SOAP in PHP to create powerful Web services. As the Internet evolves closer to the mythical Web 2.0, these services constitute a critical aspect of the Service Oriented Architecture that is the hallmark of rich client-side Internet experiences. While we did not cover every detail of the SOAP functionality in PHP, we only avoided those aspects of the functionality which would only be useful in rare circumstances (such as connecting to a service without a WSDL document). John Coggeshall [ http://www.coggeshall.org/)] is a Senior Technical Consultant for Zend Technologies where he provides professional services to clients around the world. He got started with PHP in 1997 and is the author of three published books and over 100 articles on PHP technologies with some of the biggest names in the industry such as Sams Publishing, Apress, and O'Reilly. John also is a active contributor to the PHP core as the author of the tidy extension, a member of the Zend Education Advisory Board, and frequent speaker at PHP-related conferences worldwide.

Send us your comments