Making .NET compatible PHP SOAP services with Zend Framework

The default binding style for SOAP webservices in PHP is RPC. When you consume such a webservice using the PHP SoapClient it will work perfectly.

Unfortunately Microsoft .NET prefers a different kind of SOAP service. The binding style it works with is document. When you work with complex types and arrays and maybe even arrays of complex types, you’ll see it all go south in the blink of eye. You’ll get some weird SOAP serialization errors and you’ll be scratching your head.

Where to start

In order to support document style webservices, you’ll need to have the right WSDL file. Of course you can create one from scratch, but that’s way to complicated. Fortunately, Zend Framework offers ways to simplify WSDL generation. The autodiscovery and WSDL components allow dynamic WSDL generation based on docblocks.

Docblocks

Wikipedia describes it as follows:

A DocBlock is an extended C++-style PHP comment that begins with “/**” and has an “*” at the beginning of every line. DocBlocks precede the element they are documenting. Any line within a DocBlock that doesn’t begin with a * will be ignored.

Basically you use them to add the type of properties, parameters and return values of classes. Due to the fact that PHP is not strictly typed, this is the best way to determine the type of a variable. Here’s an example:

<?php
class MyClass
{
    /**
     * @var int
     */
    protected $_one;
    /**
     * @var int
     */
    protected $_two;
    /**
     * @param int $one
     * @return MyClass 
     */
    public function setOne($one)
    {
        $this->_one = $one;
        return $this;
    }
    /**
     * @param int $two
     * @return MyClass 
     */
    public function setTwo($two)
    {
        $this->_two = $two;
        return $this;
    }
    /**
     * @return int
     */
    public function add()
    {
        return $this->_one+$this->_two;
    }
}

Let’s see some SOAP

Defining a SOAP server implies that you will be interfacing with some sort of service. The service layer of your app will expose the business logic and consists of classes/objects. You’ ll use the docblocks to show the SOAP server how to present the data in the WSDL file. Here’s some code.

Code

if($_SERVER['REQUEST_METHOD'] == 'GET') {
    $autodiscover = new Zend_Soap_AutoDiscover('Zend_Soap_Wsdl_Strategy_ArrayOfTypeSequence');
    $autodiscover->setBindingStyle(array('style'=>'document'));
    $autodiscover->setOperationBodyStyle(array('use' => 'literal'));
    $autodiscover->setClass(get_class($service));
    $autodiscover->handle();
} elseif($_SERVER['REQUEST_METHOD'] == 'POST') {
    $schema = "http";
    if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
        $schema = 'https';
    }
    $wsdl = $schema.'://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
    $server = new Zend_Soap_Server($wsdl,array('cache_wsdl'=>false));
    $server->registerFaultException("Your_Exception");
    $server->setObject($proxyService);
    $server->handle();
} else {
    echo "Something went wrong";
}

Highlights

The code example above has some particularities. Let’s run through them:

$autodiscover = new Zend_Soap_AutoDiscover('Zend_Soap_Wsdl_Strategy_ArrayOfTypeSequence');

.NET prefers arrays to be represented as sequences. That’s why we choose the Zend_Soap_Wsdl_Strategy_ArrayOfTypeSequence WSDL strategy.

$autodiscover->setBindingStyle(array('style'=>'document'));
$autodiscover->setOperationBodyStyle(array('use' => 'literal'));

These setters are essential: they decide the binding style and the style of the body. Set them to document and literal to make it compatible.

Modifying your service layer

Once you have your SOAP server up and running, you’ll need to make some changes to your service layer. Whereas with RPC style services the arguments are separated, you’ll get a single argument object using document style. Changing your service layer is a huge task.

That’s where a service proxy could come in handy. It wraps around your original service layer and proxies the method calls. The basic task of this proxy class is to transform input and output in a compliant way.

<?php
class Service_Soap
{
    protected $_service;
    public function  __construct($someDependency)
    {
        $this->_service = new Service($someDependency);
    }
    public function __call($name, $arguments)
    {
        $params = array();
        if(count($arguments) > 0){
            foreach($arguments[0] as $property=>$value){
                $params[$property] = $value;
            }
        }
        $result = call_user_func_array(array($this->_service,$name), $params);
        return array("{$name}Result"=>$result);
    }
}

As you can see, we favor composition over inheritance and use the magic __call method to perform the transformation. In the parameter objects will be transformed into separate arguments and passed as such to the real service layer. Output will get the necessary return block.

And finally it’s important to use the right service layer in the right context. You’ll notice it when examining my Zend_Soap_Server code snippet. All WSDL calls have to be made to the actual service layer. That’s because each separate method has the required docblock to describe the type. SOAP method calls however have to be sent to the proxy service layer because it does the input/output transformation.

Because actual method calls don’t require API reflection using WSDL, you don’t have to re-implement each method in your proxy layer. You can just use __call to take care of it.


Speaking at DPC11

The title says it all: I’ll be speaking at DPC11. For those of you who don’t know what this means: it’s the 2011 edition of the Dutch PHP Conference. DPC11 is the leading European PHP conference and attracks a big crowd and an awesome speaker lineup. Last year I spoke at the event as well


Dispatching HTTP requests manually in Zend Framework

I recently developed a custom Zend Framework view helper and I needed to find a way to get pieces of rendered HTML out of an action controller. You could to this in a simple way by performing a full HTTP call via cURL. That’s not the best way to get it done, so I started


My custom Zend Framework ESI view helper

I’ve been a fan of Zend Framework for some time now and I’ve recently become quite a fan of Varnish. As you all know, Varnish is an extremely powerful reverse caching proxy (although the Varnish people prefer the term HTTP accelerator). Varnish supports a subset of the ESI language and I’ve written a custom Zend


Microsoft Webcafe 2 in retrospect

Yesterday Microsoft hosted the second edition of Microsoft Webcafe. The event was held at Combell and we made our conference room available as well as our bar. The event aims to gather developers in an informal way. The webcafe concept is quite simple: a couple of short talks with a 10 minute timeslot each followed


Speaking at ConFoo

I’m very proud to announce that I’ll speaking at the 2011 edition of ConFoo in Montreal (Canada) in the beginning of March.


Speaking at PHP UK Conference 2011

That’s right folks, I’m delighted to announce that I’ll be speaking at the 2011 edition of the PHP UK Conference. The conference takes place in London and is a must on my schedule. This year it will be the third time I’ll attend and I’m looking forward to it. My talk is called Varnish In


Speaking at FeWeb10

I’m proud to announce that I’ll be speaking at 2010 edition of the FeWeb conference. FeWeb is the Belgian federation for webdesigners & web developers. They host an annual conference with parallel tracks, keynotes and of course the “state of the union” by the FeWeb chairman. I’ll be doing the intro & outro for the


Zend_Soap_AutoDiscover & eAccelerator causes trouble

Last week I used Zend_Soap_AutoDiscover for the first time. It’s pretty awesome, because your entire WSDL file dynamically generated based on docblock reflection. Unfortunately, it all went wrong when I deployed my code to staging. Apparently the issue was caused by eAccelerator. But first things first: how did I get into trouble, what caused it


Speaking at Syntra West

Tonight I’m speaking at Syntra West in Roeselare. The talk will be in Dutch and is called ‘Van hosting tot cloud computing’. This is the second time I present this talk and it’s the remake of the talk I usually give at Syntra. During this workshop I’ll sketch a clear view on hosting and the