Регистрация всех запросов и ответов на мыло в PHP - PullRequest
10 голосов
/ 13 ноября 2009

Кто-нибудь знает, как регистрировать все запросы и ответы с помощью встроенного SoapClient в PHP? На самом деле я мог бы вручную регистрировать все с помощью SoapClient::__getLastRequest() и SoapClient::__getLastResponse() Но в нашей системе столько запросов на мыло, что я ищу другие возможности.

Примечание: я использую режим wsdl, поэтому использование метода, который туннелирует до SoapClient::__soapCall(), не вариант

Ответы [ 5 ]

17 голосов
/ 13 ноября 2009

Я поддерживаю предложение Александерс и Стефанс, но не буду подклассом SoapClient. Вместо этого я бы обернул обычный SoapClient в декораторе, потому что ведение журнала не является прямой задачей SoapClient. Кроме того, слабая связь позволяет вам легко заменить SoapClient макетом в ваших UnitTests, чтобы вы могли сосредоточиться на тестировании функциональности регистрации. Если вы хотите регистрировать только определенные вызовы, вы можете добавить некоторую логику, которая фильтрует запросы и ответы по $ action или как угодно.

Редактировать , поскольку Стефан предложил добавить некоторый код, декоратор, вероятно, будет выглядеть примерно так, хотя я не уверен насчет метода __call () (см. Комментарии Стефана)

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);
    }
}
7 голосов
/ 13 ноября 2009

Я думаю, что лучший способ - переопределить SoapClient::__doRequest() (а не SoapClient::__soapCall()), поскольку у вас будет прямой доступ к запросу, а также к XML-ответу. Но общий подход к подклассу SoapClient должен быть подходящим.

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

Из ООП-дизайна / шаблон проектирования точка зрения Декоратор , очевидно, является лучшим способом решения этой проблемы - смотрите Гордон ответ . Но это немного сложнее реализовать.

6 голосов
/ 15 октября 2010

Извините, что вернулся к такому старому сообщению, но я столкнулся с некоторыми трудностями при реализации принятого ответа о декораторе, который отвечает за ведение журнала запросов Soap, и хотел бы поделиться им, если кто-нибудь еще столкнется с этим.

Представьте, что вы настроили свой экземпляр следующим образом, используя класс SoapClientLogger, описанный в принятом ответе.

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

Предположительно, любой метод, который вы вызываете в экземпляре SoapClientLogger, будет передан через метод __call () и выполнен в SoapClient. Однако обычно вы используете SoapClient, вызывая методы, сгенерированные из WSDL, например:

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

Это использование никогда не повлияет на метод _doRequest () SoapClientLogger и, следовательно, запрос не будет зарегистрирован. Вместо этого AddMember () направляется через $ mySoapClient :: _ call (), а затем прямо в метод _doRequest экземпляра SoapClient.

Я все еще ищу элегантное решение для этого.

3 голосов
/ 19 апреля 2016

Решение проблемы, поднятой в https://stackoverflow.com/a/3939077/861788 Я пришел со следующим решением ( полный источник ):

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

Использование:

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

SOAP-клиент будет затем регистрировать каждый запрос и ответ, используя любой регистратор PSR-3.

1 голос
/ 13 ноября 2009

Хотелось бы что-нибудь подобное?

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

Поскольку SoapClient уже отправляет все запросы через __soapCall, вы можете перехватить их, создав подкласс SoapClient и переопределив его. Конечно, чтобы это работало, вам также необходимо заменить все new SoapClient(...) в вашем коде на new MySoapClient(...), но это выглядит как довольно простая задача поиска и замены.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...