Я думаю, что это действительно зависит от языка. На некоторых языках, Lisp, например, рекурсия часто является естественным ответом на проблему (и часто с языками, где дело обстоит так, компилятор оптимизирован для контакта с рекурсией).
общий шаблон в Lisp выполнения операции на первом элементе списка и затем вызывания функции на остальной части списка для или накопления значения или нового списка является довольно изящным и самым естественным способом сделать много вещей на том языке. В Java, не так.
Я повторяю предложение Александера и Стефанса, но не буду создавать подкласс SoapClient. Вместо этого я бы обернул обычный SoapClient в декоратор, потому что ведение журнала не является прямой проблемой SoapClient. Кроме того, слабая связь позволяет вам легко заменить SoapClient на макет в ваших модульных тестах, так что вы можете сосредоточиться на тестировании функциональности ведения журнала. Если вы хотите регистрировать только определенные вызовы, вы можете добавить некоторую логику, которая фильтрует запросы и ответы по $ 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);
}
}
Я думаю, что лучший способ - переопределить 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;
}
}
РЕДАКТИРОВАТЬ
Из ООП-дизайна / паттерна проектирования точки зрения Декоратор , очевидно, лучший способ справиться с подобными проблемами - см. ответ Гордона . Но это немного сложнее реализовать.
Что-нибудь вроде этого сработает?
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 и переопределив его. Конечно, чтобы он заработал, вам нужно также заменить каждый новый SoapClient (...)
в вашем коде на новый MySoapClient (...)
, но это выглядит довольно неплохо. удобный поиск и замена задачи.