2009-11-13 5 views
8

누구나 PHP에서 내장 SoapClient를 사용하여 모든 요청 및 응답을 기록하는 방법을 알고 있습니까? 실제로 수동으로 SoapClient::__getLastRequest()SoapClient::__getLastResponse()으로 모든 것을 기록 할 수 있습니다. 그러나 우리는 우리 시스템에서 다른 가능성을 찾고있는 비누 요청을 많이 받았습니다.PHP에서 비누 요청 및 응답 모두 로깅

참고 : 나는 그래서 모든 SoapClient::__soapCall()이 옵션이 작품 같은

답변

14

두 번째로 Aleksanders와 Stefans는 제안하지만 SoapClient를 하위 클래스로 분류하지는 않습니다. 로깅은 SoapClient의 직접적인 관심사가 아니기 때문에 대신 데코레이터에 일반 SoapClient를 래핑합니다. 또한 느슨한 결합을 사용하면 UnitTests의 모의 객체로 SoapClient를 쉽게 대체 할 수 있으므로 로깅 기능 테스트에 집중할 수 있습니다. 특정 호출 만 기록하려는 경우 $ action 또는 적합하다고 판단되는 항목으로 요청 및 응답을 필터링하는 논리를 추가 할 수 있습니다. 스테판 이후

편집

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); 
    } 
} 
+0

데코레이터를 사용하는 것이 좋습니다. 사실 나는 같은 문제를 해결해야만한다면 데코레이터 솔루션으로 직접 갈 것입니다. 하지만 서브 클래 싱 솔루션은 좀 더 이해하기 쉽습니다. –

+1

아마도 코드 샘플을 추가하면 OP가 도움이 될 것입니다. –

+0

그리고 꾸미기 중 하나 또는 꾸미기 클래스에서 method-overloading-feature'__call()'을 사용한다면 데코레이터의 * stacking *을 허용하지 않는 PHP 문제가 있습니다 ('SoapClient' in this 케이스). –

1

겠습니까의 것이 아닙니다에 통해 터널링 방법을 사용하여 WSDL 모드를 사용하고 있습니다?

SoapClient 이미 __soapCall을 통해 모든 요청을 전송하기 때문에
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를 서브 클래 싱하고 오버라이드 (override)를 가로 챌 수 있습니다. 물론 코드가 작동하도록하려면 코드에있는 new SoapClient(...)을 모두 new MySoapClient(...)으로 바꿔야하지만 꽤 쉬운 검색 및 바꾸기 작업처럼 보입니다.

+0

이것은 작동하지 않습니다 ... __soapCall을 더 이상 무시할 수없는 이유에 대한 통찰력이 있습니까? –

6

응답 - XML뿐 아니라 요청에 직접 액세스 할 수 있으므로 더 좋은 방법은 SoapClient::__doRequest() (및 SoapClient::__soapCall()이 아닌)을 무시하는 것입니다. 그러나 하위 클래스 인 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; 
    } 
} 

Decorator 분명히 이런 종류의 문제를 처리 할 수있는 더 좋은 방법이다보기의 OOP 디자인/디자인 패턴 점에서 편집

- Gordon's answer를 참조하십시오. 그러나 이것은 구현하기가 조금 더 어렵습니다.

+0

이것이 데코레이터보다 바람직한 해결책이라고 생각합니다. 'SoapClient'는 실제로 OOP-aish가 아니기 때문에 어떤 인터페이스도 구현하지 않습니다. soap 클라이언트 의존성이'\ SoapClient '로 타입 힌팅 되었다면 데코레이터가 언어의 관점에서 호환 될 수있는 방법이 아닙니다. '. 이 클라이언트를 직접 사용하는 라이브러리를 작성하는 경우 물론,이를 래핑하고 일부 인터페이스를 구현하는 것이 좋습니다. 하지만 이미 존재하는 라이브러리에서 객체를 대체하기를 원한다면, 객체는'SoapClient' 타입의 객체를 기대합니다. 데코레이터는하지 않을 것입니다. (타입 힌트가 없다면 다시 할 수 있습니다.) – sevavietl

+0

@sevavietl 맞습니다. 'SoapClient' 유형의 힌트와 호환되는 데코레이터를 구현하는 것은 문제가 될 수 있습니다 (인터페이스가 누락되고'SoapClient' API의 동적 인 특성으로 인해). 나 자신을 시도하지 않았기 때문에 일반적으로 가능한지 여부를 판단 할 수 없었습니다. –

5

죄송 재 방문 (Stefans 코멘트 참조) 내가 __call() 메소드에 대해 확실하지 않지만, 데코레이터는 아마 다음과 같이 보일 것입니다 몇 가지 코드를 추가 제안 그런 오래된 게시물,하지만 난 비누 요청의 로깅에 대한 책임이 있고 다른 사람이 이걸로 실행하는 경우에 공유하고 싶었 데 데코레이터의 허용 대답의 구현과 몇 가지 문제가 발생했습니다.

허용 된 대답에 설명 된 SoapClientLogger 클래스를 사용하여 다음과 같이 인스턴스를 설정한다고 가정 해보십시오.

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

은 아마도 당신이 SoapClientLogger 인스턴스에서 호출있는 방법은 __call() 메서드를 통해 전달되고 SoapClient에서 실행 얻을 것이다. 그러나, 일반적으로이 같은 WSDL에서 생성되는 방법을 호출하여 SoapClient를 사용합니다 :이 사용이 SoapClientLogger의 _doRequest() 메소드 따라서 요청이 기록에 남지 않습니다 충돌하지 않습니다

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

.대신 AddMember()는 $ mySoapClient :: _ call()을 통해 라우팅 된 다음 SoapClient 인스턴스의 _doRequest 메서드를 호출합니다.

나는이 문제에 대한 우아한 해결책을 찾고 있습니다.

3

나는 다음과 같은 솔루션과 함께 https://stackoverflow.com/a/3939077/861788에서 제기 된 문제 (full source)를 Adressing :

<?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 나무꾼.