Back to Article List

Originally published January 2004 [ Publisher Link ]

PHP Web Services with PEAR


PHP -- short for PHP Hypertext Preprocessor -- was, along with Perl, one of the frontrunners in server-side programming long before any JSP/Servlet or ASP technology came to be. It is often the language of choice for those using Apache's Web server, which runs almost 70% of sites on the Web. Due to its pervasiveness, it seems obvious that it should support the most recent standards, such as SOAP, which are also adopted by major technology vendors. In this article, we will describe how Web services can be implemented in PHP.

In a previous article, we explored how to design and deploy Web services based on Java using the Axis SOAP engine and Tomcat J2EE-Servlet/JSP container. Although not a prerequisite for the following article, reading it will give you a firmer grasp on the whole Web services picture, since you will be able to use the PHP Web service described here to communicate with another service written in Java, and vice versa, allowing you to experiment firsthand with Web services interoperability.

PHP Web services suites

PHP has various ways in which you can implement Web services, including NuSOAP, PHP-SOAP, and PEAR (PHP Extension and Application Repository). Some of these packages are more mature than others, but in essence they achieve the same goal, which is to deploy Web services in PHP. We will use PEAR, which is is not a standalone package for Web services but more of a framework for building PHP applications, and which includes a Web services module.

PEAR download and installation

To begin, you need an Apache Server -- either the 1.x branch or the 2.x version enabled with PHP. Depending upon the PHP version you have installed, you may still need to download the PEAR Package Manager, which is in charge of administering all of PEAR's modules and functionality.

You can query your PHP version by executing the command php --version from a shell prompt. If the version is lower than 4.3, you need to invoke the following command from lynx, the console-based Web browser: lynx -source http://go-pear.org/ | php . You will be prompted with various download questions; select the default options. You must use lynx because the interactive prompts do not allow you to use a GUI browser.

Once you have installed the PEAR Package Manager you can proceed to installing the necessary PEAR modules for using SOAP in PHP, this is done with the pear install [Module_Name] command. If you execute this command while you have an active connection to the Internet, it will automatically download and install the module in your local environment. In case you don't have an active connection, you will need to download the SOAP package for PEAR as well as some prerequisite PEAR modules from http://pear.php.net/packages.php in order to install the SOAP package, including Mail_Mime, Net_URL, HTTP_Request, and Net_DIME.

Now that you have installed the SOAP PEAR module, we are ready to start designing our Web service.

The Web service

The following service is quite similar to the one described in my earlier Java Web services article. It is designed to return a particular result based on two integers provided by a Web services client. For simplicity, all data is coded directly in the PHP script; a more comprehensive Web service would normally include a database connection for extracting information in real time, but since this is not our primary intention, the information is static in nature.

<?php
require_once('SOAP/Server.php');
require_once('SOAP/Disco.php');

class Sales
{
     var $__dispatch_map = array();

     function Sales() {
      // Define the signature of the dispatch map on 
      //  the Web services method

      // Necesary for WSDL creation
      $this->__dispatch_map['onsale'] =
        array('in' => array('low' => 'integer', 'high' =>
'integer'),
             'out' => array('sales' => 'string'),
                   );
     }

     function onsale($low, $high)  {
       if ($low < 1) {
         return "We have some bargains, but not at 
              those prices!";
       } else if ( $high < 100 ) {
           return "We have a few gadgets which fit your 
                price range,visit our site 
                for more details and pictures!";
       } else {
           return "Thats more than spare change, have you 
                considered donating some of 
                it to an Open-Source project ? ";
       }
   }
}

$server = new SOAP_Server();
$webservice = new Sales();
$server->addObjectMap($webservice,
'http://schemas.xmlsoap.org/soap/envelope/');

if (isset($_SERVER['REQUEST_METHOD'])  &&

     $_SERVER['REQUEST_METHOD']=='POST') {
     $server->service($HTTP_RAW_POST_DATA);
} else {
     // Create the DISCO server
     $disco = new SOAP_DISCO_Server($server,'Sales');
     header("Content-type: text/xml");
     if (isset($_SERVER['QUERY_STRING']) &&
         strcasecmp($_SERVER['QUERY_STRING'],'wsdl') == 0) {
         echo $disco->getWSDL();
     } else {
         echo $disco->getDISCO();
     }
}
exit;

?>

First off we declare the use of two PHP libraries. SOAP/Server.php is used in the creation of PHP Web services, and SOAP/Disco.php contains a series of methods employed in the generation of a Web Services Description Language (WSDL) file, necessary for creating the corresponding Web services client.

After declaring our main Web services class named Sales, we define an array field named $__dispatch_map and a corresponding constuctor class. These two declarations form an integral part in the generation of the WSDL file for our Web service. Through the constructor we are guaranteed that when the class is instantiated the __dispatch_map will be populated with a specific set of data. Notice that this information corresponds to the input and output fields of the onsale method.The onsale method contains the core functionality of our Web service, as it returns an output based on the parameters it is passed on invocation.

Once our main Sales class is defined, we create an instance for it, as well as for the SOAP_Server class. We then call the addObjectMap method, which is included in SOAP_Server, to associate our Sales instance named $webservice with http://schemas.xmlsoap.org/soap/envelope/, which is the standard namespace for sending SOAP message through the wire.

Next, we initiate a conditional to check what type of request is being made on the PHP page. If we detect an explicit request method then the service method is invoked on the $server instance defined previously, which returns a Web service result. If the previous conditional does not hold true, then a SOAP_DISCO_Server instance is created on the Web service, which facilitates returning a WSDL file for the requester. At this juncture, we create one more conditional to inspect the URL and see if it contains a wsdl extension. If it does, then the corresponding WSDL is returned; if not, then an absolute URL is returned indicating where to extract the WSDL contract.

All set? Place the Web service PHP script, which from here on we will refered to as Sales.php, on your server.

WSDL for the Web service

WSDL is the standard language in which you describe Web services. It is an XML-based language that allows you to map a particular service in a language-agnostic manner.

In PEAR's case, you can obtain a WSDL contract for your Web service through its SOAP_DISCO_Server, described in the earlier code. Unfortunately you do have to write some plumbing code to facilitate this mechanism, unlike with Java's Axis procedure, in which simply appending the ?wsdl snippet to the URL is enough to generate the WSDL contract.

The Web service we have designed in PHP allows you to use this same mechanism of appending the ?wsdl to the request in order to obtain the WSDL. However, you do need to code a constructor method for this task, as well as write some scaffolding to achieve it, all of this directly coded in your Web service. This can be a considerable burden, espcially for more complex Web services, but it is the only way to achieve this behavior of obtaining a WSDL contract directly in PEAR's current SOAP version.

The Web service client

When creating a Web services client you normally feed the WSDL contract for the Web service you wish to contact into a specific language tool, which eases the writing of your code. Since our Web service is so simple we will be directly inspecting the contract and coding the client without any specific tools.

The following PHP-based Web service client is designed to be invoked from the command line:

#!/usr/local/bin/php
<?php
require('SOAP/Client.php');

   if ($argc != 3 ) {

    printf("Usage : Customer lowestprice highestprice");
    echo "\n";

   } else {

    $endpoint = 'http://localhost/Sales.php';

    # We could also use the URL's for the Java Web services
    # deployed via Axis/Tomcat
    #$endpoint = 'http://localhost:8080/axis/Sales.jws';
    #$endpoint = 'http://localhost:8080/axis/services/Sales';

    $customer = new SOAP_Client($endpoint);
    $method = 'onsale';
    $int1 = (integer) $argv[1];
    $int2 = (integer) $argv[2];
    $params = array('lowprice' => $int1, 'highprice' => $int2);
    $ans = $customer->call($method, $params);
    printf($ans);
    echo "\n";

}

?>

The Web service client first imports the SOAP/Client.php library used in coding PEAR's Web services clients. Afterwords, we check the parameters provided by the user at the command line to assure an appropriate invocation.

Once the input is revised [ do you mean received? No, I mean checking the input for validity,e.g if the user inputs 2 integers ], we declare a string that holds the location of the Web service variable, which is later used to instaniate the SOAP_Client class used in invoking a Web service. We then define the method which is to be called on the service, as well as an array containing the integers provided at run-time by the user.

Finally, we invoke the call method on the SOAP_Client instance Customer to directly call the Web service. This last method takes both the function that is to be called on the Web service and the input parameters. The results of this call are then printed out to the console.

The WSDL-Querying Web service client

The last example showed you the basics of invoking a Web service from a PHP environment. There is one more way you can create a Web service client from PEAR -- by directly querying the WSDL of a Web service. With this technique the Web service characteristics are taken and parsed directly from the WSDL file. As automatic as this may seem compared to the previous method, you still have to figure out somehow what parameters to pass to the Web service, which obviously requires a manual or tool-based inspection, so though it is different, it is not entirely automatic. Take a look for yourself:


#!/usr/local/bin/php
<?php
require_once 'SOAP/Client.php';

 if ($argc != 3 ) {

  printf("Usage : Customer lowestprice highestprice");
  echo "\n";
 } else {

 $wsdl = new SOAP_WSDL ('http://localhost/Sales.php?wsdl');

 # We could also use the URL's for the Java Web services WSDL
 # deployed via Axis/Tomcat
 #$wsdl = 
 # new SOAP_WSDL ('http://localhost:8080/axis/Sales.jws?wsdl');
 #$wsdl = new SOAP_WSDL(
 #'http://localhost:8080/axis/services/Sales?wsdl');


   $Sales = $wsdl->getProxy();
   $int1 = (integer) $argv[1];
   $int2 = (integer) $argv[2];
   echo ( $Sales->onsale($int1,$int2) );
   echo "\n";
}
?>

This Web service client is a command line program that begins by importing the SOAP/Client.php library, then further checks to see if the required parameters were indicated upon execution. Once the input validation is done, we create a SOAP_WSDL instance which takes the URL where the WSDL contract resides. We later invoke the getProxy method on this instance and assign it to the $Sales variable. We place the two integers passed at run-time into the corresponding variables $int1 and $int2, and finally, we print to console the results of calling the onsale method on the $Sales instance which takes these last integers as input.

Notice that even though we queried the WSDL directly, we knew beforehand that an onsale method existed and that it took two integers as input. The actual Web service end-point is extracted from the WSDL as well as other information, so it's not all that different from the first Web services client we coded.

You have just deployed an end-to-end Web service using PHP --congratulations!


Originally published January 2004 [ Publisher Link ]

Back to Article List