SOAP Webサービスを使用してユーザーからの情報を確認するプロジェクトに取り組んでいます。現在、Webサービスから応答を受け取っていると仮定してエラーを処理していますが、また、サービスタイムアウトまたは使用不能のEdgeケースを処理する必要があります。
タイムアウトまたはサービスが利用できない場合、リクエストが成功した(Webサービスが情報を承認した)ふりをする必要がありますが、どの例外がスローされるのか明確ではありません。
いくつかの擬似コード:
// $client is PHP's SoapClient class
try {
$response = $client->SomeSoapRequest();
}
catch(SoapFault $e){
// handle issues returned by the web service
}
catch(Exception $e){
// handle PHP issues with the request
}
私が見つけられないように見えるのは:
SoapFault
ですか?もしそうなら、タイムアウトエラーとWebサービスの問題(タイプエラーなど)を区別する最良の方法は何ですか?メッセージが「ヘッダーの読み込みエラー」の結果であるというエラーについて言及しているページを見つけましたが、これがSOAPエラーかどうかについては言及していません。_default_socket_timeout
_ のように見えますSOAP HTTPS経由の呼び出しを行う場合:
執筆時点で開いているバグ。削除された回答で参照されたブログ投稿Robert Ludwickへのコメントとして Timing Out PHP Soap Calls(2009年10月21日、発行者Robert F. Ludwick) 指摘、投稿の回避策( SoapClient::__doRequest()
をcurlリクエストでオーバーライド)がこのバグを回避する。
別の関連するバグは次のとおりです。
ブログの投稿で言及されているコードはいくつかの変更が加えられており、GithubのHTTP認証をサポートする最新の形式で見つけることができます。
いずれの場合も、この問題はPHP SOAPClient拡張機能で修正されているため、回避策はもう必要ありません。
サービスのタイムアウトに対処するには
$client = new SoapClient($wsdl, array("connection_timeout"=>10));
// SET SOCKET TIMEOUT
if(defined('RESPONSE_TIMEOUT') && RESPONSE_TIMEOUT != '') {
ini_set('default_socket_timeout', RESPONSE_TIMEOUT);
}
私の経験から、$e->getMessage
は「HTTPヘッダーのフェッチエラー」であり、ネットワークタイムアウトを処理しています。
$e->getMessage
は「ホストに接続できません」のようなもので、到達しようとしているサービスがダウンしています。
次に、「XMLドキュメントを取得していないように見えます」があります。
SoapClient拡張を取得してNice例外をスローするために、2つの要因を使用しました。メッセージとリクエストが返されるまでにかかった時間。エラーメッセージ「Error Fetching http headers」は他のいくつかのケースでも発生する可能性があるため、時間をチェックします。
次のコードはほぼ正しいはずです
class SoapClientWithTimeout extends SoapClient {
public function __soapCall ($params, ---) {
$time_start = microtime(true);
try {
$result = parent::__soapCall ($params, ---);
}
catch (Exception $e) {
$time_request = (microtime(true)-$time_start);
if(
$e->getMessage() == 'Error Fetching http headers' &&
ini_get('default_socket_timeout') < $time_request
) {
throw new SoapTimeoutException(
'Soap request most likly timed out.'.
' It took '.$time_request.
' and the limit is '.ini_get('default_socket_timeout')
);
}
// E: Not a timeout, let's rethrow the original exception
throw $e;
}
// All good, no exception from the service or PHP
return $result;
}
}
class SoapTimeoutException extends Exception {}
次に、SoapClientWithTimeoutを使用します
$client = new SoapClientWithTimeout();
try {
$response = $client->SomeSoapRequest();
var_dump($response);
}
catch(SoapTimeoutException $e){
echo 'We experienced a timeout! '. $e->getMessage();
}
catch(Exception $e) {
echo 'Exception: '.$e->getMessage();
}
サービスのタイムアウトをデバッグするには。サービスを呼び出す前に次の行を追加します
ini_set('default_socket_timeout', 1);
「stream_context」を使用して、WSDLロードのタイムアウト設定も設定します(前にSoapClient $ options ['connection_timeout']を設定する必要があります)。
class SoapClient2 extends SoapClient
{
public function __construct($wsdl, $options=null)
{
if(isset($options['connection_timeout']))
{
$s_options = array(
'http' => array(
'timeout' => $options['connection_timeout']
)
);
$options['stream_context'] = stream_context_create($s_options);
}
parent::__construct($wsdl, $options);
}
}
私は少し遅れていると思いますが、誰かがまだPHP SOAPクライアントでタイムアウトの解決策を探している場合-ここに私のために働いているものがあります:
基本的にPHPタイムアウトを設定したSoapClientをcURLに置き換えます。覚えておいてください。WSはHTTPヘッダーで指定されたアクションを予期する場合があります。
default_socket_timeout
iniを介してグローバルにあなたが望むことをしないかもしれません。これはSOAPリクエストに影響しますが、DB接続を含む他の発信接続にも影響します。代わりに、SoapClientの__doRequest()メソッドをオーバーライドしてHTTP接続を自分で作成します。ソケット、それを検出し、トラップして処理できる例外をスローします。
class SoapClientWithTimeout extends SoapClient {
public function __construct ($wsdl, $options = null) {
if (!$options) $options = [];
$this->_connectionTimeout =
@$options['connection_timeout']
?: ini_get ('default_socket_timeout');
$this->_socketTimeout =
@$options['socket_timeout']
?: ini_get ('default_socket_timeout');
unset ($options['socket_timeout']);
parent::__construct($wsdl, $options);
}
/**
* Override parent __doRequest to add a timeout.
*/
public function __doRequest (
$request, $location, $action, $version, $one_way = 0
) {
// Extract Host, port, and scheme.
$url_parts = parse_url ($location);
$Host = $url_parts['Host'];
$port =
@$url_parts['port']
?: ($url_parts['scheme'] == 'https' ? 443 : 80);
$length = strlen ($request);
// Form the HTTP SOAP request.
$http_req = "POST $location HTTP/1.0\r\n";
$http_req .= "Host: $Host\r\n";
$http_req .= "SoapAction: $action\r\n";
$http_req .= "Content-Type: text/xml; charset=utf-8\r\n";
$http_req .= "Content-Length: $length\r\n";
$http_req .= "\r\n";
$http_req .= $request;
// Need to tell fsockopen to use SSL when requested.
if ($url_parts['scheme'] == 'https')
$Host = 'ssl://'.$Host;
// Open the connection.
$socket = @fsockopen (
$Host, $port, $errno, $errstr, $this->_connectionTimeout
);
if (!$socket)
throw new SoapFault (
'Client',
"Failed to connect to SOAP server ($location): $errstr"
);
// Send the request.
stream_set_timeout ($socket, $this->_socketTimeout);
fwrite ($socket, $http_req);
// Read the response.
$http_response = stream_get_contents ($socket);
// Close the socket and throw an exception if we timed out.
$info = stream_get_meta_data ($socket);
fclose ($socket);
if ($info['timed_out'])
throw new SoapFault (
'Client',
"HTTP timeout contacting $location"
);
// Extract the XML from the HTTP response and return it.
$response = preg_replace (
'/
\A # Start of string
.*? # Match any number of characters (as few as possible)
^ # Start of line
\r # Carriage Return
$ # End of line
/smx',
'', $http_response
);
return $response;
}
}