Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
PHP SoapClient with timeout
//Drop-in replacement for PHP's SoapClient class supporting connect and response/transfer timeout
//Usage: Exactly as PHP's SoapClient class, except that 3 new options are available:
// timeout The response/transfer timeout in milliseconds; 0 == default SoapClient / CURL timeout
// connecttimeout The connection timeout; 0 == default SoapClient / CURL timeout
// sslverifypeer FALSE to stop SoapClient from verifying the peer's certificate
class SoapClientTimeout extends SoapClient
private $timeout = 0;
private $connecttimeout = 0;
private $sslverifypeer = true;
private $password = null;
private $login = null;
public function __construct($wsdl, $options) {
//"POP" our own defined options from the $options array before we call our parent constructor
//to ensure we don't pass unknown/invalid options to our parent
if (isset($options['timeout'])) {
if (isset($options['connecttimeout'])) {
if (isset($options['sslverifypeer'])) {
//Extract login information
if (isset($options['login']) && isset($options['password'])) {
$this->login = $options['login'];
$this->password = $options['password'];
//Now call parent constructor
parent::__construct($wsdl, $options);
public function __setTimeout($timeoutms)
if (!is_int($timeoutms) && !is_null($timeoutms) || $timeoutms<0)
throw new Exception("Invalid timeout value");
$this->timeout = $timeoutms;
public function __getTimeout()
return $this->timeout;
public function __setConnectTimeout($connecttimeoutms)
if (!is_int($connecttimeoutms) && !is_null($connecttimeoutms) || $connecttimeoutms<0)
throw new Exception("Invalid connecttimeout value");
$this->connecttimeout = $connecttimeoutms;
public function __getConnectTimeout()
return $this->connecttimeout;
public function __setSSLVerifyPeer($sslverifypeer)
if (!is_bool($sslverifypeer))
throw new Exception("Invalid sslverifypeer value");
$this->sslverifypeer = $sslverifypeer;
public function __getSSLVerifyPeer()
return $this->sslverifypeer;
public function __doRequest($request, $location, $action, $version, $one_way = FALSE)
if (($this->timeout===0) && ($this->connecttimeout===0))
// Call via parent because we require no timeout
$response = parent::__doRequest($request, $location, $action, $version, $one_way);
// Call via Curl and use the timeout
$curl = curl_init($location);
if ($curl === false)
throw new Exception('Curl initialisation failed');
$options = array(
CURLOPT_HTTPHEADER => array(sprintf('Content-Type: %s', $version == 2 ? 'application/soap+xml' : 'text/xml'), sprintf('SOAPAction: %s', $action)),
CURLOPT_SSL_VERIFYPEER => $this->sslverifypeer
if ($this->timeout>0) {
if (defined('CURLOPT_TIMEOUT_MS')) { //Timeout in MS supported?
$options[CURLOPT_TIMEOUT_MS] = $this->timeout;
} else { //Round(up) to second precision
$options[CURLOPT_TIMEOUT] = ceil($this->timeout/1000);
if ($this->connecttimeout>0) {
if (defined('CURLOPT_CONNECTTIMEOUT_MS')) { //ConnectTimeout in MS supported?
$options[CURLOPT_CONNECTTIMEOUT_MS] = $this->connecttimeout;
} else { //Round(up) to second precision
$options[CURLOPT_CONNECTTIMEOUT] = ceil($this->connecttimeout/1000);
// Set login information
if ($this->password && $this->login) {
$options[CURLOPT_USERPWD] = $this->login.':'.$this->password;
if (curl_setopt_array($curl, $options) === false)
throw new Exception('Failed setting CURL options');
$response = curl_exec($curl);
if (curl_errno($curl))
throw new Exception(curl_error($curl));
// Return?
if (!$one_way)
return ($response);
Copy link

staabm commented Aug 6, 2014

to mimic the behaviour php should have for SoapClient, this client should per default respect default_socket_timeout, no?

Copy link

hakre commented Apr 12, 2015

@staabm: Not really, default_socket_timeout is for socket based streams. If you compile curl as stream handler, you might perhaps expect it, but it's not a socket based stream handler at all. As the example class here is not stream handler at all as well, it shouldn't be expected to take over that value. You perhaps want to create an example that is re-using that value, that should work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment