Logging all Soap request and responses in PHP

I think the better way is to override SoapClient::__doRequest() (and not SoapClient::__soapCall()) as you'll have direct access to the request- as well as to the response-XML. But the general approach to subclass SoapClient should be the way to go.

class My_LoggingSoapClient extends SoapClient
{
    // logging methods

    function __doRequest($request, $location, $action, $version, $one_way = 0) 
    {
        $this->_logRequest($location, $action, $version, $request);
        $response = parent::__doRequest($request, $location, $action, $version, $one_way);
        $this->_logResponse($location, $action, $version, $response);
        return $response;
    }
}

EDIT

From an OOP-design / design pattern point of view a Decorator is obviously the better way to handle this kind of problem - please see Gordon's answer. But this is a little bit more difficult to implement.


Sorry to revisit such an old post, but I encountered some challenges with the accepted answer's implementation of a decorator that is responsible for the logging of the Soap requests and wanted to share in case anyone else runs into this.

Imagine you set up your instance like the following, using the SoapClientLogger class outlined in the accepted answer.

$mySoapClient = new SoapClientLogger(new SoapClient());

Presumably any method you call on the SoapClientLogger instance will get passed through the __call() method and executed on the SoapClient. However, typically you make use of a SoapClient by calling the methods generated from the WSDL, like this:

$mySoapClient->AddMember($parameters); // AddMember is defined in the WSDL

This usage will never hit the SoapClientLogger's _doRequest() method and thus the request will not be logged. Instead AddMember() is routed through $mySoapClient::_call() and then right on down to the SoapClient instance's _doRequest method.

I'm still searching for an elegant solution to this.


I second Aleksanders and Stefans suggestion but would not subclass SoapClient. Instead I'd wrap the regular SoapClient in a decorator, because logging is not a direct concern of the SoapClient. In addition, the loose coupling lets you easily substitute the SoapClient with a mock in your UnitTests, so you can concentrate on testing the logging functionality. If you only want to log specific calls, you can add some logic that filters requests and responses by $action or anything you see fit.

Edit since Stefan suggested to add some code, the decorator would probably look something like this, although I am not sure about the __call() method (see Stefans comments)

class SoapClientLogger
{
    protected $soapClient;

    // wrapping the SoapClient instance with the decorator
    public function __construct(SoapClient $client)
    {
        $this->soapClient = $client;
    }

    // Overloading __doRequest with your logging code
    function __doRequest($request, $location, $action, $version, $one_way = 0) 
    {
         $this->log($request, $location, $action, $version);

         $response = $this->soapClient->__doRequest($request, $location, 
                                                    $action, $version, 
                                                    $one_way);

         $this->log($response, $location, $action, $version);
         return $response;
    }

    public function log($request, $location, $action, $version)
    {
        // here you could add filterings to log only items, e.g.
        if($action === 'foo') {
            // code to log item
        }
    }

    // route all other method calls directly to soapClient
    public function __call($method, $args)
    {
        // you could also add method_exists check here
        return call_user_func_array(array($this->soapClient, $method), $args);
    }
}

Tags:

Php

Soap