Initial commit for basic SoapClient enhancements.
This commit is contained in:
323
src/BeSimple/SoapClient/Curl.php
Normal file
323
src/BeSimple/SoapClient/Curl.php
Normal file
@ -0,0 +1,323 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of the BeSimpleSoapClient.
|
||||
*
|
||||
* (c) Christian Kerl <christian-kerl@web.de>
|
||||
* (c) Francis Besset <francis.besset@gmail.com>
|
||||
* (c) Andreas Schamberger <mail@andreass.net>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*
|
||||
* @link https://github.com/BeSimple/BeSimpleSoapClient
|
||||
*/
|
||||
|
||||
namespace BeSimple\SoapClient;
|
||||
|
||||
/**
|
||||
* cURL wrapper class for doing HTTP requests that uses the soap class options.
|
||||
*
|
||||
* @author Andreas Schamberger
|
||||
*/
|
||||
class Curl
|
||||
{
|
||||
/**
|
||||
* HTTP User Agent.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const USER_AGENT = 'PHP-SOAP/\BeSimple\SoapClient';
|
||||
|
||||
/**
|
||||
* Curl resource.
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
private $ch;
|
||||
|
||||
/**
|
||||
* Maximum number of location headers to follow.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $followLocationMaxRedirects;
|
||||
|
||||
/**
|
||||
* Request response data.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $response;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $options
|
||||
* @param int $followLocationMaxRedirects
|
||||
*/
|
||||
public function __construct( array $options, $followLocationMaxRedirects = 10 )
|
||||
{
|
||||
// set the default HTTP user agent
|
||||
if ( !isset( $options['user_agent'] ) )
|
||||
{
|
||||
$options['user_agent'] = self::USER_AGENT;
|
||||
}
|
||||
$this->followLocationMaxRedirects = $followLocationMaxRedirects;
|
||||
|
||||
// make http request
|
||||
$this->ch = curl_init();
|
||||
$curlOptions = array(
|
||||
CURLOPT_ENCODING => '',
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_FAILONERROR => false,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
||||
CURLOPT_HEADER => true,
|
||||
CURLOPT_USERAGENT => $options['user_agent'],
|
||||
CURLINFO_HEADER_OUT => true,
|
||||
);
|
||||
curl_setopt_array( $this->ch, $curlOptions );
|
||||
if ( isset( $options['compression'] ) && !( $options['compression'] & SOAP_COMPRESSION_ACCEPT ) )
|
||||
{
|
||||
curl_setopt( $this->ch, CURLOPT_ENCODING, 'identity' );
|
||||
}
|
||||
if ( isset( $options['connection_timeout'] ) )
|
||||
{
|
||||
curl_setopt( $this->ch, CURLOPT_CONNECTTIMEOUT, $options['connection_timeout'] );
|
||||
}
|
||||
if ( isset( $options['proxy_host'] ) )
|
||||
{
|
||||
$port = isset( $options['proxy_port'] ) ? $options['proxy_port'] : 8080;
|
||||
curl_setopt( $this->ch, CURLOPT_PROXY, $options['proxy_host'] . ':' . $port );
|
||||
}
|
||||
if ( isset( $options['proxy_user'] ) )
|
||||
{
|
||||
curl_setopt( $this->ch, CURLOPT_PROXYUSERPWD, $options['proxy_user'] . ':' . $options['proxy_password'] );
|
||||
}
|
||||
if ( isset( $options['login'] ) )
|
||||
{
|
||||
curl_setopt( $this->ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
|
||||
curl_setopt( $this->ch, CURLOPT_USERPWD, $options['login'].':'.$options['password'] );
|
||||
}
|
||||
if ( isset( $options['local_cert'] ) )
|
||||
{
|
||||
curl_setopt( $this->ch, CURLOPT_SSLCERT, $options['local_cert'] );
|
||||
curl_setopt( $this->ch, CURLOPT_SSLCERTPASSWD, $options['passphrase'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
curl_close( $this->ch );
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute HTTP request.
|
||||
* Returns true if request was successfull.
|
||||
*
|
||||
* @param string $location
|
||||
* @param string $request
|
||||
* @param array $requestHeaders
|
||||
* @return bool
|
||||
*/
|
||||
public function exec( $location, $request = null, $requestHeaders = array() )
|
||||
{
|
||||
curl_setopt( $this->ch, CURLOPT_URL, $location);
|
||||
|
||||
if ( !is_null( $request ) )
|
||||
{
|
||||
curl_setopt( $this->ch, CURLOPT_POST, true );
|
||||
curl_setopt( $this->ch, CURLOPT_POSTFIELDS, $request );
|
||||
}
|
||||
|
||||
if ( count( $requestHeaders ) > 0 )
|
||||
{
|
||||
curl_setopt( $this->ch, CURLOPT_HTTPHEADER, $requestHeaders );
|
||||
}
|
||||
|
||||
$this->response = $this->execManualRedirect( $this->followLocationMaxRedirects );
|
||||
|
||||
return ( $this->response === false ) ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom curl_exec wrapper that allows to follow redirects when specific
|
||||
* http response code is set. SOAP only allows 307.
|
||||
*
|
||||
* @param resource $ch
|
||||
* @param int $maxRedirects
|
||||
* @param int $redirects
|
||||
* @return mixed
|
||||
*/
|
||||
private function execManualRedirect( $redirects = 0 )
|
||||
{
|
||||
if ( $redirects > $this->followLocationMaxRedirects )
|
||||
{
|
||||
// TODO Redirection limit reached, aborting
|
||||
return false;
|
||||
}
|
||||
curl_setopt( $this->ch, CURLOPT_HEADER, true );
|
||||
curl_setopt( $this->ch, CURLOPT_RETURNTRANSFER, true );
|
||||
$response = curl_exec( $this->ch );
|
||||
$httpResponseCode = curl_getinfo( $this->ch, CURLINFO_HTTP_CODE );
|
||||
if ( $httpResponseCode == 307 )
|
||||
{
|
||||
$headerSize = curl_getinfo( $this->ch, CURLINFO_HEADER_SIZE );
|
||||
$header = substr( $response, 0, $headerSize );
|
||||
$matches = array();
|
||||
preg_match( '/Location:(.*?)\n/', $header, $matches );
|
||||
$url = trim( array_pop( $matches ) );
|
||||
// @parse_url to suppress E_WARNING for invalid urls
|
||||
if ( ( $url = @parse_url( $url ) ) !== false )
|
||||
{
|
||||
$lastUrl = parse_url( curl_getinfo( $this->ch, CURLINFO_EFFECTIVE_URL ) );
|
||||
if ( !isset( $url['scheme'] ) )
|
||||
{
|
||||
$url['scheme'] = $lastUrl['scheme'];
|
||||
}
|
||||
if ( !isset( $url['host'] ) )
|
||||
{
|
||||
$url['host'] = $lastUrl['host'];
|
||||
}
|
||||
if ( !isset( $url['path'] ) )
|
||||
{
|
||||
$url['path'] = $lastUrl['path'];
|
||||
}
|
||||
$newUrl = $url['scheme'] . '://' . $url['host'] . $url['path'] . ( $url['query'] ? '?' . $url['query'] : '' );
|
||||
curl_setopt( $this->ch, CURLOPT_URL, $newUrl );
|
||||
return $this->execManualRedirect( $redirects++ );
|
||||
}
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Error code mapping from cURL error codes to PHP ext/soap error messages
|
||||
* (where applicable)
|
||||
*
|
||||
* http://curl.haxx.se/libcurl/c/libcurl-errors.html
|
||||
*
|
||||
* @var array(int=>string)
|
||||
*/
|
||||
protected function getErrorCodeMapping()
|
||||
{
|
||||
return array(
|
||||
1 => 'Unknown protocol. Only http and https are allowed.', //CURLE_UNSUPPORTED_PROTOCOL
|
||||
3 => 'Unable to parse URL', //CURLE_URL_MALFORMAT
|
||||
5 => 'Could not connect to host', //CURLE_COULDNT_RESOLVE_PROXY
|
||||
6 => 'Could not connect to host', //CURLE_COULDNT_RESOLVE_HOST
|
||||
7 => 'Could not connect to host', //CURLE_COULDNT_CONNECT
|
||||
9 => 'Could not connect to host', //CURLE_REMOTE_ACCESS_DENIED
|
||||
28 => 'Failed Sending HTTP SOAP request', //CURLE_OPERATION_TIMEDOUT
|
||||
35 => 'Could not connect to host', //CURLE_SSL_CONNECT_ERROR
|
||||
41 => 'Can\'t uncompress compressed response', //CURLE_FUNCTION_NOT_FOUND
|
||||
51 => 'Could not connect to host', //CURLE_PEER_FAILED_VERIFICATION
|
||||
52 => 'Error Fetching http body, No Content-Length, connection closed or chunked data', //CURLE_GOT_NOTHING
|
||||
53 => 'SSL support is not available in this build', //CURLE_SSL_ENGINE_NOTFOUND
|
||||
54 => 'SSL support is not available in this build', //CURLE_SSL_ENGINE_SETFAILED
|
||||
55 => 'Failed Sending HTTP SOAP request', //CURLE_SEND_ERROR
|
||||
56 => 'Error Fetching http body, No Content-Length, connection closed or chunked data', //CURLE_RECV_ERROR
|
||||
58 => 'Could not connect to host', //CURLE_SSL_CERTPROBLEM
|
||||
59 => 'Could not connect to host', //CURLE_SSL_CIPHER
|
||||
60 => 'Could not connect to host', //CURLE_SSL_CACERT
|
||||
61 => 'Unknown Content-Encoding', //CURLE_BAD_CONTENT_ENCODING
|
||||
65 => 'Failed Sending HTTP SOAP request', //CURLE_SEND_FAIL_REWIND
|
||||
66 => 'SSL support is not available in this build', //CURLE_SSL_ENGINE_INITFAILED
|
||||
67 => 'Could not connect to host', //CURLE_LOGIN_DENIED
|
||||
77 => 'Could not connect to host', //CURLE_SSL_CACERT_BADFILE
|
||||
80 => 'Error Fetching http body, No Content-Length, connection closed or chunked data', //CURLE_SSL_SHUTDOWN_FAILED
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the curl error message.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getErrorMessage()
|
||||
{
|
||||
$errorCodeMapping = $this->getErrorCodeMapping();
|
||||
$errorNumber = curl_errno( $this->ch );
|
||||
if ( isset( $errorCodeMapping[$errorNumber] ) )
|
||||
{
|
||||
return $errorCodeMapping[$errorNumber];
|
||||
}
|
||||
return curl_error( $this->ch );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the request headers as a string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRequestHeaders()
|
||||
{
|
||||
return curl_getinfo( $this->ch, CURLINFO_HEADER_OUT );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the whole response (including headers) as a string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the response body as a string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getResponseBody()
|
||||
{
|
||||
$headerSize = curl_getinfo( $this->ch, CURLINFO_HEADER_SIZE );
|
||||
return substr( $this->response, $headerSize );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the response content type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getResponseContentType()
|
||||
{
|
||||
return curl_getinfo( $this->ch, CURLINFO_CONTENT_TYPE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the response headers as a string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getResponseHeaders()
|
||||
{
|
||||
$headerSize = curl_getinfo( $this->ch, CURLINFO_HEADER_SIZE );
|
||||
return substr( $this->response, 0, $headerSize );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the response http status code.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getResponseStatusCode()
|
||||
{
|
||||
return curl_getinfo( $this->ch, CURLINFO_HTTP_CODE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the response http status message.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getResponseStatusMessage()
|
||||
{
|
||||
preg_match( '/HTTP\/(1\.[0-1]+) ([0-9]{3}) (.*)/', $this->response, $matches );
|
||||
return trim( array_pop( $matches ) );
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user