Skip to content

Instantly share code, notes, and snippets.

@treehousetim
Forked from RobThree/SoapClientTimeout.class.php
Created August 20, 2012 18:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save treehousetim/3406693 to your computer and use it in GitHub Desktop.
Save treehousetim/3406693 to your computer and use it in GitHub Desktop.
PHP SoapClient with timeout
<?
// https://gist.github.com/gists/3406693
// 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
// 2012-08-20 - treehousetim added an additional option to support xignite.com api key auth
// ->extraGet
// used like this:
/*
// authenticate using an API key
$apiKey = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
$client->extraGet = '?Header_Username=' $apiKey );
*/
// 2012-08-20 - treehousetim also added custom exceptions so that the different errors can be caught using multiple catch levels
// used like this:
/*
try
{
// instantiate client and call wsdl method here
}
catch( stConnectionException $e )
{
// handle connection timeout here
}
catch( stOperationException $e )
{
// handle operation timeout here
}
*/
class SoapClientTimeout extends SoapClient
{
private $timeout = 10;
private $connecttimeout = 3;
private $sslverifypeer = true;
public $extraGet = '';
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'])) {
$this->__setTimeout($options['timeout']);
unset($options['timeout']);
}
if (isset($options['connecttimeout'])) {
$this->__setConnectTimeout($options['connecttimeout']);
unset($options['connecttimeout']);
}
if (isset($options['sslverifypeer'])) {
$this->__setSSLVerifyPeer($options['sslverifypeer']);
unset($options['sslverifypeer']);
}
//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);
}
else
{
// Call via Curl and use the timeout
$curl = curl_init($location . $this->extraGet );
if ($curl === false)
throw new Exception('Curl initialization failed');
$options = array(
CURLOPT_VERBOSE => false,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $request,
CURLOPT_HEADER => false,
CURLOPT_NOSIGNAL => true, //http://www.php.net/manual/en/function.curl-setopt.php#104597
CURLOPT_HTTPHEADER => array(sprintf('Content-Type: %s', $version == 2 ? 'application/soap+xml' : 'text/xml'), sprintf('SOAPAction: %s', $action)),
CURLOPT_SSL_VERIFYPEER => $this->sslverifypeer
);
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 (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);
}
if (curl_setopt_array($curl, $options) === false)
throw new Exception('Failed setting CURL options');
$response = curl_exec($curl);
$errNo = curl_errno($curl);
if ( $errNo )
{
if ( $errNo == 7 )
{
throw new stConnectionException( curl_error( $curl ) );
}
if ( $errNo == 28 )
{
throw new stOperationException( curl_error( $curl ) );
}
// failsafe
throw new stGeneralException( curl_error( $curl ) );
}
curl_close($curl);
}
// Return?
if (!$one_way)
return ($response);
}
}
//------------------------------------------------------------------------
// custom exceptions for soap timeout by tim gallagher "treehousetim" everywhere
//------------------------------------------------------------------------
interface stIException
{
/* Protected methods inherited from Exception class */
public function getMessage(); // Exception message
public function getCode(); // User-defined Exception code
public function getFile(); // Source filename
public function getLine(); // Source line
public function getTrace(); // An array of the backtrace()
public function getTraceAsString(); // Formated string of trace
/* Overrideable methods inherited from Exception class */
public function __toString(); // formated string for display
public function __construct( $message = null, $code = 0 );
}
//------------------------------------------------------------------------
abstract class stException extends Exception implements stIException
{
protected $message = 'Generic SoapTimeout Exception'; // Exception message
private $string; // Unknown
protected $code = 0; // User-defined exception code
protected $file; // Source filename of exception
protected $line; // Source line of exception
private $trace; // Unknown
protected $errno; // error number aka error level - integer
//------------------------------------------------------------------------
public function __construct( $message = null, $code = 0 )
{
if ( ! $message )
{
throw new $this( 'Unknown '. get_class( $this ) );
}
parent::__construct( $message, $code );
}
//------------------------------------------------------------------------
public function __toString()
{
return PHP_EOL . get_class( $this ) .
":\n{$this->message}\n\nin {$this->file}({$this->line})\n" .
"{$this->getTraceAsString()}";
}
}
//------------------------------------------------------------------------
// now the actual custom exceptions
//------------------------------------------------------------------------
class stGeneralException extends stException { }
class stConnectionException extends stException { }
class stOperationException extends stException { }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment