有誰知道如何記錄所有的請求和PHP內置的SoapClient響應?事實上,我可以用SoapClient::__getLastRequest()
和SoapClient::__getLastResponse()
手動記錄所有內容。但我們在系統中有很多肥皂請求,我正在尋找其他可能性。在PHP中記錄所有Soap請求和響應
注:我使用WSDL模式,以便使用該隧道全線貫通到SoapClient::__soapCall()
是不是一種選擇
有誰知道如何記錄所有的請求和PHP內置的SoapClient響應?事實上,我可以用SoapClient::__getLastRequest()
和SoapClient::__getLastResponse()
手動記錄所有內容。但我們在系統中有很多肥皂請求,我正在尋找其他可能性。在PHP中記錄所有Soap請求和響應
注:我使用WSDL模式,以便使用該隧道全線貫通到SoapClient::__soapCall()
是不是一種選擇
我第二次Aleksanders和Stefans的建議,但不會子類SoapClient。相反,我會將常規SoapClient包裝在裝飾器中,因爲日誌不是SoapClient的直接關注點。另外,鬆耦合使您可以輕鬆地將SoapClient替換爲UnitTest中的模擬對象,以便您可以專注於測試日誌記錄功能。如果您只想記錄特定的調用,則可以添加一些邏輯,通過$ action或您認爲合適的任何內容過濾請求和響應。
編輯因爲斯特凡建議添加一些代碼,裝飾可能會是這個樣子,雖然我不知道的__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);
}
}
會是這樣的工作方法?
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(...)
,但這似乎是一個非常簡單的搜索和替換任務。
這不起作用......你有什麼洞察力,爲什麼__soapCall不能被覆蓋了嗎? – 2015-03-16 16:02:04
我認爲更好的方法是覆蓋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設計/設計模式來看一個Decorator顯然是更好的方式來處理這類問題 - 請Gordon's answer。但是這實施起來有點困難。
我想這是一個優於裝飾者的解決方案。由於'SoapClient'不是真正的OOP-aish,並且它沒有實現任何接口,所以從語言的角度來看,它實際上沒有辦法讓裝飾器與它兼容,如果soap客戶端依賴被type-hinted'\ SoapClient '。如果你自己編寫一個使用這個客戶端的庫,當然最好包裝它並實現一些接口。但是如果你想在一個已經存在的庫中替換對象,那麼期望一個類型爲「SoapClient」的對象,裝飾器不會這樣做(但是如果沒有類型提示,你可以使用它)。 – sevavietl 2018-02-15 16:20:32
@sevavietl是的。實現一個與'SoapClient'類型提示兼容的裝飾器可能會有問題(由於缺少接口和'SoapClient' API的動態特性)。從來沒有嘗試過,所以我不能判斷一般情況下是否可能。 – 2018-02-16 07:40:44
對不起重溫這樣一箇舊帖子,但我遇到了一些挑戰,接受答案的裝飾器的實現負責記錄Soap請求,並希望共享以防其他人遇到此問題。
想象一下,使用接受的答案中列出的SoapClientLogger類設置您的實例,如下所示。
$mySoapClient = new SoapClientLogger(new SoapClient());
想必您對SoapClientLogger實例調用任何方法會通過__call()方法傳遞並在SoapClient的執行。但是,通常您使用一個SoapClient類的調用由WSDL生成的方法,就像這樣:
$mySoapClient->AddMember($parameters); // AddMember is defined in the WSDL
這種用法永遠不會撞到SoapClientLogger的_doRequest()方法,因此請求將不會被記錄。相反,AddMember()通過$ mySoapClient :: _ call()路由,然後直到SoapClient實例的_doRequest方法。
我仍在尋找一個優雅的解決方案。
Adressing在https://stackoverflow.com/a/3939077/861788提出我想出了以下解決方案(full source)問題:
<?php
namespace Lc5\Toolbox\LoggingSoapClient;
use Psr\Log\LoggerInterface;
/**
* Class LoggingSoapClient
*
* @author Łukasz Krzyszczak <[email protected]>
*/
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記錄儀。
使用裝飾器是一個非常好的主意。實際上,如果我必須解決同樣的問題,我會自己選擇裝飾解決方案。但我認爲,子類化解決方案更容易理解。 – 2009-11-13 14:49:10
如果你添加一個代碼示例,它可能會幫助OP。 – 2009-11-13 14:50:00
然後有一個PHP問題,如果你在一個裝飾器或要裝飾的類中使用方法重載特性__call(),則不允許裝飾器堆疊*(在此處的'SoapClient'案件)。 – 2009-11-13 14:59:36