"Tech Moxy" ~ Moxune is the Surgical Team most capable of delivering your next complex PHP Application on time & under budget.

Debugging Soap Services with Zend Studio

September 6th, 2011

How do you get the debugger in Zend Studio to step through SoapServer code?

Inevitably as you develop Soap services with PHP you have to deal with a bug in the server side code. Since you can embed SoapClient in a script that renders a Web page and hit said page from a browser, it’s easy to debug SoapClient code using the debug toolbar. Doing the same for SoapServer code doesn’t work; you’ll see the problem in a moment. Conventional techniques like error_log et al to track down problems can seem painful compared to Zend Studio’s interactive debugger. Once you’re comfortable with the debugger and the advantages it offers this SoapServer enigma becomes very annoying, especially when you’ve got a bug to fix!

Reverse engineering the debug toolbar

When you click that magic ‘Next page’ button in the Zend debug toolbar to indicate the next request should invoke a debug session, the browser is appending specific headers to the request through a cookie. When these headers are retrieved by a server running the Zend debug extension, Zend Studio is invoked (if installed and running).

Zend Framework Firefox Debug Toolbar

Zend Debug Toolbar

The easiest way to view the data that invokes the debugger in Zend studio is to use Firebug, Developer Tools etc. and keep an eye on the network panel. Look for the part of the request that sets a string resembling

ZDEDebuggerPresent=php,phtml,php3; debug_host=10.40.1.85,127.0.0.1; ....

You’ll want to use values from the debug toolbar on your system when editing the string for use in the custom SoapClient.

Inspect Zend Toolbar debug Cookie

Using Firebug's Net tag to inspect the Zend Debug toolbar's debug Cookie

So you can see all that’s happening is the debug toolbar is sending over metadata for the debugger with the request. To debug SoapServer code, a SoapClient subclass that passes the same information to the server running ZendDebugger will have the same effect. Googling for a bit reveals a SoapClient subclass that overrides __doRequest(). This will allow us to modify the request from SoapClient before sending it off, we’ll just add the debug cookie.

Subclassing SoapClient

Look at this file as a template, it should be easy to modify for your needs:

class ZendDebugSoapClient extends SoapClient
{
    private $_sUserAgent;
    private $_sDebugCookie;

    public function setUserAgent($sUserAgent)
    {
        $this->_sUserAgent = $sUserAgent;
    }

    public function setDebugCookie($sCookie)
    {
        $this->_sDebugCookie = $sCookie;
    }

    public function __doRequest(
        $request, $location, $action, $version, $one_way=0
    ) {
        $aHeaders = array(
            'Method: POST',
            'Connection: Close',
            'User-Agent: ' . $this->_sUserAgent,
            'Content-Type: text/xml',
            'SOAPAction: "'.$action.'"',
            'Cookie: ' . $this->_sDebugCookie
        );

        $ch = curl_init($location);
        curl_setopt_array(
            $ch,
            array(
                CURLOPT_VERBOSE        => false,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_POST           => true,
                CURLOPT_POSTFIELDS     => $request,
                CURLOPT_HEADER         => false,
                CURLOPT_HTTPHEADER     => $aHeaders,
                CURLOPT_SSL_VERIFYPEER => false,
            )
        );

        return curl_exec($ch);
    }
}

Customizing & Debugging

Now all you have to do to get this working is supply a user-agent, and a modified cookie for your environment, then use the SoapClient like you normally would. If you point the SoapClient at a Soap service hosted on your workstation that is also running ZendStudio, issuing requests through it will invoke the debugger (assuming you configured the debug cookie correctly)!

$sUrl    = 'http://mysite.local/soapservice';
$oClient = new ZendDebugSoapClient($sUrl);
$oClient->setUserAgent($sUserAgent);
$oClient->setDebugCookie(
    'ZDEDebuggerPresent=php,phtml,php3; ' .
    'debug_host=10.40.1.85,127.0.0.1; ' .
    'debug_fastfile=1; ' .
    'debug_port=10137; ' .
    'start_debug=1; ' .
    'send_debug_header=1; ' .
    'send_sess_end=1; ' .
    'debug_jit=1; ' .
    'original_url=' . $sUrl . ';' .
    'debug_stop=1; ' .
    'use_remote=1; ' .
    'debug_session_id=16281615'
);

// this will invoke the debugger
$oClient->removeMethod('param1', 2, true);

Retrospect

Debuggers are useful with systems that don’t have unit tests or have tightly coupled components that are difficult to isolate for testing. Generally, unit tests are ideal. Well designed service code will only treat the Soap interface as a slim facade, so most bugs with SoapServer code will most likely be in objects aggregated by SoapServer instances. If you find yourself needing to debug SoapServer code though, try ZendDebugSoapClient.

Share

If you enjoyed this post please consider sharing it!

  • Twitter
  • LinkedIn
  • Facebook
  • Reddit
  • Digg

5 Responses to Debugging Soap Services with Zend Studio

  1. ed says:

    removeMethod is not defined, I suppose it will call __doRequest() ?

  2. Nathan Nobbe says:

    The nature of SoapClient is such that it uses __call() internally to provide methods for the service it’s attached to during construction (assuming you provide a WSDL address). So for example, if you pointed SoapClient at the National Weather Service’s WSDL:
    http://graphical.weather.gov/xml/DWMLgen/wsdl/ndfdXML.wsdl
    it would have methods like CornerPoints and GmlLatLonList etc. which are defined in the service.

    To your point these will invoke __doRequest, so if you have a system running the Zend Debugger and providing Soap services via SoapServer, then you can send a request to those services with ZendDebugSoapClient and it will invoke a debug session in Zend Studio.

    Also to your point removeMethod is probably not the best choice for an example. Thanks for your comment!

  3. ed says:

    Yep, I have noticed.
    Understand the script better now.

    Thanks for your tutorial, it has helped me a lot.

  4. Hi,
    there is a bug in your code which doesn’t make to work.
    You don’t assign the variable $sCookie to the class property.

    public function setDebugCookie($sCookie)
    {
    $this->_sDebugCookie = $sCookie;
    }

    Otherwise, thank you very much to have shared the code, very nice :-)

  5. Nathan Nobbe says:

    This snippet was adapted from production code; some edits have been made to protect its origins. Thanks for identifying that bug. I’ve updated the code in the post as well now. ;)

Leave a Response