Journalisation de toutes les requêtes Soap et réponses en PHP

est-ce que quelqu'un sait comment enregistrer toutes les requêtes et réponses avec le SoapClient builtin en PHP? Je pourrais en fait tout enregistrer manuellement avec SoapClient::__getLastRequest() et SoapClient::__getLastResponse() mais nous avons autant de requêtes soap dans notre système que je cherche d'autres possibilités.

Note: j'utilise le mode wsdl donc utiliser une méthode qui tunnels tout au long de SoapClient::__soapCall() n'est pas une option

9
demandé sur ChrisR 2009-11-13 16:57:51

5 réponses

Je seconde Aleksanders et Stefans suggestion, mais ne serait pas sous-classe SoapClient. Au lieu de cela, j'envelopperais le SoapClient régulier dans un décorateur, parce que la journalisation n'est pas une préoccupation directe du SoapClient. De plus, l'accouplement lâche vous permet de remplacer facilement le SoapClient par un mock dans vos UnitTests, de sorte que vous pouvez vous concentrer sur le test de la fonctionnalité de journalisation. Si vous voulez seulement enregistrer des appels spécifiques, vous pouvez ajouter une logique qui filtre les requêtes et les réponses par $action ou autre chose. vous voyez l'ajustement.

Edit puisque Stefan a suggéré d'ajouter du code, le décorateur ressemblerait probablement à quelque chose comme ça, bien que je ne suis pas sûr de la méthode _ _ call () (voir les commentaires de Stefans)

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);
    }
}
17
répondu Gordon 2009-11-13 15:18:57

je pense que le meilleur moyen est de remplacer SoapClient::__doRequest() (et non SoapClient::__soapCall() ) car vous aurez un accès direct à la requête - ainsi qu'à la réponse-XML. Mais l'approche générale de la sous-classe SoapClient devrait être la voie à suivre.

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;
    }
}

MODIFIER

à Partir d'un programmation orientée objet-design / modèle de conception point de vue Décorateur est de toute évidence, la meilleure façon de traiter ce genre de problème - voir réponse de Gordon . Mais c'est un peu plus difficile à mettre en œuvre.

7
répondu Stefan Gehrig 2017-05-23 12:00:21

désolé de revisiter un poste aussi vieux, mais j'ai rencontré quelques défis avec la mise en œuvre de la réponse acceptée d'un décorateur qui est responsable de la journalisation des demandes de Soap et voulait partager au cas où quelqu'un d'autre se heurte à cela.

Imaginez que vous configuriez votre instance comme suit, en utilisant la classe SoapClientLogger décrite dans la réponse acceptée.

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

probablement n'importe quelle méthode que vous appelez sur le SoapClientLogger l'instance sera passée par la méthode _ _ call () et exécutée sur le SoapClient. Cependant, vous utilisez généralement un SoapClient en appelant les méthodes générées par la WSDL, comme ceci:

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

cet usage ne sera jamais utilisé avec la méthode _dorequest() de SoapClientLogger et la requête ne sera donc pas journalisée. Au lieu de cela, AddMember () est routé par $mySoapClient::_call () et ensuite directement à la méthode _doRequest de L'instance SoapClient.

je suis toujours à la recherche d'une solution élégante à cela.

6
répondu stereoscott 2010-10-15 02:33:05

concernant la question soulevée dans https://stackoverflow.com/a/3939077/861788 je suis venu avec la solution suivante ( source complète ):

<?php

namespace Lc5\Toolbox\LoggingSoapClient;

use Psr\Log\LoggerInterface;

/**
 * Class LoggingSoapClient
 *
 * @author Łukasz Krzyszczak <lukasz.krzyszczak@gmail.com>
 */
class LoggingSoapClient
{

    const REQUEST  = 'Request';
    const RESPONSE = 'Response';

    /**
     * @var TraceableSoapClient
     */
    private $soapClient;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @param TraceableSoapClient $soapClient
     * @param LoggerInterface $logger
     */
    public function __construct(TraceableSoapClient $soapClient, LoggerInterface $logger)
    {
        $this->soapClient = $soapClient;
        $this->logger     = $logger;
    }

    /**
     * @param string $method
     * @param array $arguments
     * @return string
     */
    public function __call($method, array $arguments)
    {
        $result = call_user_func_array([$this->soapClient, $method], $arguments);

        if (!method_exists($this->soapClient, $method) || $method === '__soapCall') {
            $this->logger->info($this->soapClient->__getLastRequest(), ['type' => self::REQUEST]);
            $this->logger->info($this->soapClient->__getLastResponse(), ['type' => self::RESPONSE]);
        }

        return $result;
    }

    /**
     * @param string $request
     * @param string $location
     * @param string $action
     * @param int $version
     * @param int $oneWay
     * @return string
     */
    public function __doRequest($request, $location, $action, $version, $oneWay = 0)
    {
        $response = $this->soapClient->__doRequest($request, $location, $action, $version, $oneWay);

        $this->logger->info($request, ['type' => self::REQUEST]);
        $this->logger->info($response, ['type' => self::RESPONSE]);

        return $response;
    }
}

Utilisation:

use Lc5\Toolbox\LoggingSoapClient\LoggingSoapClient;
use Lc5\Toolbox\LoggingSoapClient\TraceableSoapClient;
use Lc5\Toolbox\LoggingSoapClient\MessageXmlFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;

$handler = new StreamHandler('path/to/your.log');
$handler->setFormatter(new MessageXmlFormatter());

$logger = new Logger('soap');
$logger->pushHandler($handler);

$soapClient = new LoggingSoapClient(new TraceableSoapClient('http://example.com'), $logger);
Le client SOAP

enregistrera alors chaque requête et réponse à l'aide d'un logger PSR-3.

3
répondu Łukasz Krzyszczak 2017-05-23 12:25:19

est-ce que quelque chose comme ça pourrait marcher?

class MySoapClient extends SoapClient
{
    function __soapCall($function_name, $arguments, $options = null, $input_headers = null, &$output_headers = null) 
    {
        $out = parent::__soapCall($function_name, $arguments, $options, $input_headers, $output_headers);

        // log request here...
        // log response here...

        return $out;
    }
}

puisque SoapClient envoie déjà toutes les requêtes par __soapCall, vous pouvez les intercepter en sous-classant SoapClient et en le remplaçant. Bien sûr , pour que cela fonctionne, vous devez aussi remplacer chaque new SoapClient(...) dans votre code par new MySoapClient(...) , mais cela semble être une tâche de recherche et de remplacement assez facile.

1
répondu Aleksander Kmetec 2009-11-13 14:26:28