I\'m working on a project where I am verifying information from a user with a SOAP web service. I currently am taking care of errors assuming that I\'m receiving responses from
Guess I'm little late, but in case someone is still looking for solution to timeouts in php soap client - here's what's worked for me:
Basicly replacing PHP SoapClient with cURL with set timeout. Just keep in mind, sometimes WS expects action specified in HTTP header. Original solution posted on that website doesn't include that (check comments).
To deal with timeouts in the service
$client = new SoapClient($wsdl, array("connection_timeout"=>10));
// SET SOCKET TIMEOUT
if(defined('RESPONSE_TIMEOUT') && RESPONSE_TIMEOUT != '') {
ini_set('default_socket_timeout', RESPONSE_TIMEOUT);
}
just use the "stream_context" to set the timeout setting also for WSDL loading (you need to set the SoapClient $options['connection_timeout'] before):
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);
}
}
Simply setting the default_socket_timeout
globally via the ini may not do what you want. This would affect SOAP requests, but would also affect other outgoing connections, including DB connections. Instead, override SoapClient's __doRequest() method to make the HTTP connection yourself. You can then set your own timeout on the socket, detect it, and throw exceptions that you can trap and handle.
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;
}
}
Looks like default_socket_timeout is not taken into account when making SOAP calls over HTTPS:
Bug open at the time of writing. As a comment on the blog post Robert Ludwick referenced in a deleted answer Timing Out PHP Soap Calls (21 Oct 2009; by Published by Robert F. Ludwick) points out, the workaround the post discusses (overriding SoapClient::__doRequest() with a curl request) works around this bug also.
Another related bug is:
The code mentioned in the blog post has undergone some changes and can be found in it's latest form with support of HTTP authentication here on Github:
In any case, the workaround shouldn't be needed any longer as this problem has been fixed in the PHP SOAPClient extension.
From my experience, if $e->getMessage
is "Error Fetching http headers", you are dealing with a network timeout.
If $e->getMessage
is something like "Cannot connect to host", the service you are trying to reach is down.
Then there is "Looks like we got no XML document", which is more cryptic an can mean different things.