有没有人知道如何使用PHP内置的SoapClient记录所有请求和响应?实际上,我可以使用SoapClient::__getLastRequest()
和SoapClient::__getLastResponse()
手动记录所有内容但是我们的系统中有很多肥皂请求,我正在寻找其他可能性。
注意:我正在使用wsdl模式,因此使用隧道传输到SoapClient::__soapCall()
的方法不是一个选项
答案 0 :(得分:17)
我是Aleksanders和Stefans的第二个建议,但不会将SoapClient子类化。相反,我将常规SoapClient包装在装饰器中,因为日志记录不是SoapClient的直接关注点。此外,松散耦合使您可以轻松地在UnitTests中使用模拟替换SoapClient,因此您可以专注于测试日志记录功能。如果您只想记录特定的调用,可以添加一些逻辑,通过$ action或您认为合适的任何内容来过滤请求和响应。
编辑,因为Stefan建议添加一些代码,装饰器可能看起来像这样,虽然我不确定__call()方法(参见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);
}
}
答案 1 :(得分:7)
我认为更好的方法是覆盖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;
}
}
修改强>
从 OOP-design / 设计模式的角度来看,Decorator显然是处理此类问题的更好方法 - 请参阅{{ 3}}。但实施起来有点困难。
答案 2 :(得分:6)
很抱歉重新访问这么老的帖子,但是我遇到了一些挑战,接受了答案的装饰器的实现,负责记录Soap请求,并希望分享以防其他人遇到此问题。
想象一下,使用接受的答案中概述的SoapClientLogger类,设置如下所示的实例。
$mySoapClient = new SoapClientLogger(new SoapClient());
假设您在SoapClientLogger实例上调用的任何方法都将通过__call()方法传递并在SoapClient上执行。但是,通常通过调用从WSDL生成的方法来使用SoapClient,如下所示:
$mySoapClient->AddMember($parameters); // AddMember is defined in the WSDL
此用法永远不会触及SoapClientLogger的_doRequest()方法,因此不会记录请求。相反,AddMember()通过$ mySoapClient :: _ call()进行路由,然后直接进入SoapClient实例的_doRequest方法。
我仍然在寻找一个优雅的解决方案。
答案 3 :(得分:3)
解决https://stackoverflow.com/a/3939077/861788中提出的问题我提出了以下解决方案(full source):
<?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记录器记录每个请求和响应。
答案 4 :(得分:1)
这样的事情会起作用吗?
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(...)
,但这似乎是一个非常简单的搜索和替换任务。
答案 5 :(得分:-2)
我用WireShark检查了请求和响应。