14 Dec 2012, 11:36

SOAP Server with PHP

I had a need to build a SOAP server, and after reviewing implementations in different languages decided that PHP using the Zend Framework would best suit my needs:

  • minimal code required
  • supports auto generation of WSDL by performing ‘autodiscovery’ on the PHP code
  • PHP is widely deployed and well suited to web/cgi environments
    The examples below is an adaptation of the example found on this post - extended to support the Document/Literal style

My development environment is PHP 5.3 using Zend Framework 1.12, running on Linux + Apache.

SOAP Server

<?php
 
/* these are required for it all to work */
require("Zend/Soap/Server.php");
require("Zend/Soap/Wsdl.php");
require("Zend/Soap/Wsdl/Strategy/ArrayOfTypeComplex.php");
require("Zend/Soap/AutoDiscover.php");
 
$wsdlUrl = 'http://example.com/ws/?wsdl';
$serviceUrl = 'http://example.com/ws/';
 
class User
{
        /** @var string */
        public $name='';
}
 
class Response
{
        /** @var string */
        public $msg;
}
 
class MyService {
 
     /**
     * @param User $param
     * @return Response
     */
    public function GetCoupons($param) {
 
        $name = $param->param->name;
        $result = new Response();
        $result->msg = "Hello $name!";
 
        return array('GetCouponsResult'=>$result);
    }
}
 
// Generate WSDL relevant to code
if (isset($_GET['wsdl'])){
        $autodiscover = new Zend_Soap_AutoDiscover('Zend_Soap_Wsdl_Strategy_ArrayOfTypeComplex');
        $autodiscover->setOperationBodyStyle(array('use' => 'literal'));
        $autodiscover->setUri($serviceUrl);
        $autodiscover->setBindingStyle(array('style' => 'document'));
        $autodiscover->setClass('MyService');
        $autodiscover->handle();
} else {
        $server = new Zend_Soap_Server($wsdlUrl,array('soap_version'=>SOAP_1_2));
        $server->setClass('MyService');
        $server->setObject(new MyService());
        $server->handle();
}

Change $wsdlUrl & $serviceUrl to suit. $serviceUrl is where the client will be pointed to, so unless you’re running the client on the same host as the server then this should be changed otherwise the client will throw a connection error.

The WSDL autodiscovery takes the $param input variable and wraps that around ‘name’, thus the odd looking reference: $param->param->name . (I’d like to know if there’s a better way to do this)

SOAP Client

This client does not require Zend framework, so I’m just using PHP’s base SOAP library

<?php  
 ini_set("soap.wsdl_cache_enabled", 0);
 
$wsdlUrl = "http://example.com/ws/?wsdl";
 
$client = new
        SoapClient(
                $wsdlUrl,
                array(
                        'trace' => 1,
                        'soap_version' => SOAP_1_2
                )
        );
 
$user = array('name'=>'Bob');
$result = $client->GetCoupons(array('param'=>$user));
 
echo "<pre>\n";
print_r($result);
echo "</pre>\n";   

Running the above client code should result in the following output:

stdClass Object  
(  
    [GetCouponsResult] =&gt; stdClass Object  
        (  
            [msg] =&gt; Hello Bob!  
        )  
)

Final thoughts

I had a lot of trouble with PHP insisting on using the cached copy of WSDL, so I would highly recommend that you disable WSDL caching (set _soap.wsdl_cacheenabled = off in php.ini ) and remove any cache files from disk (rmĀ  /tmp/wsdl-*) - do this on both the server and the client .