10 Commits
v4.1 ... v4.2.4

Author SHA1 Message Date
0e2c33faf8 Support of older PHP versions removed: MimeFilter does not sanitize PHP 5.4.x error 2017-02-22 12:23:24 +01:00
cf6e147c26 Merge remote-tracking branch 'remotes/origin/develop' 2017-02-18 00:14:21 +01:00
f276a30a47 Curl/WsdlGenerator - better error handling 2017-02-18 00:13:02 +01:00
4edc46e67f Parser fix - MimeMessages with CRLF caused iconv_mime_decode throwing Exceptions 2017-02-17 15:06:06 +01:00
a76526a5b6 DEFAULT_CONNECTION_TIMEOUT default value increased to 120 2017-02-17 11:20:09 +01:00
baf32c1350 Curl is now returning response body even on error
It is better to switch off CURLOPT_FAILONERROR and check response status manually by HTTP response code
2017-02-17 11:07:26 +01:00
5c0bf914e3 Unused SoapClientNativeDataTransferObject removed 2017-02-17 03:36:16 +01:00
01d10b89fd SoapClient now handles Attachments better
The inner storage is now the only possible way to handle attachments and hydrate responses by using ClassMap at the same time. To get the response objects from ClassMap, use SoapResponse->getResponseObject() method
2017-02-17 03:19:22 +01:00
e1b50ce914 SoapClient::__soapCall() must be compatible with \SoapClient::__soapCall() fix 2017-02-17 00:26:01 +01:00
68b41acc46 SoapClient - WSDL download fixes 2017-02-17 00:14:05 +01:00
11 changed files with 150 additions and 145 deletions

View File

@ -62,7 +62,7 @@ class Curl
curl_setopt_array($curlSession, [
CURLOPT_ENCODING => '',
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_FAILONERROR => true,
CURLOPT_FAILONERROR => false,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_HEADER => true,
@ -147,6 +147,8 @@ class Curl
$headerSize = curl_getinfo($curlSession, CURLINFO_HEADER_SIZE);
$httpResponseCode = curl_getinfo($curlSession, CURLINFO_HTTP_CODE);
$httpResponseContentType = curl_getinfo($curlSession, CURLINFO_CONTENT_TYPE);;
$responseBody = substr($executeSoapCallResponse, $headerSize);
$responseHeaders = substr($executeSoapCallResponse, 0, $headerSize);
preg_match('/HTTP\/(1\.[0-1]+) ([0-9]{3}) (.*)/', $executeSoapCallResponse, $httpResponseMessages);
$httpResponseMessage = trim(array_pop($httpResponseMessages));
$curlErrorMessage = sprintf(
@ -156,7 +158,7 @@ class Curl
$location
);
if ($executeSoapCallResponse === false) {
if (!is_integer($httpResponseCode) || $httpResponseCode >= 400 || $httpResponseCode === 0) {
return new CurlResponse(
$httpRequestHeadersAsString,
@ -164,6 +166,8 @@ class Curl
$httpResponseMessage,
$httpResponseContentType,
self::CURL_FAILED,
$responseHeaders,
$responseBody,
$curlErrorMessage
);
}
@ -174,9 +178,8 @@ class Curl
$httpResponseMessage,
$httpResponseContentType,
self::CURL_SUCCESS,
null,
substr($executeSoapCallResponse, 0, $headerSize),
substr($executeSoapCallResponse, $headerSize)
$responseHeaders,
$responseBody
);
}

View File

@ -13,7 +13,7 @@ use Exception;
class CurlOptionsBuilder
{
const DEFAULT_MAX_REDIRECTS = 10;
const DEFAULT_CONNECTION_TIMEOUT = 10;
const DEFAULT_CONNECTION_TIMEOUT = 120;
public static function buildDefault()
{

View File

@ -19,9 +19,9 @@ class CurlResponse
$httpResponseStatusMessage,
$httpResponseContentType,
$curlStatus,
$curlErrorMessage = null,
$responseHeader = null,
$responseBody = null
$responseHeader,
$responseBody,
$curlErrorMessage = null
) {
$this->httpRequestHeaders = $httpRequestHeaders;
$this->httpResponseStatusCode = $httpResponseStatusCode;
@ -78,21 +78,11 @@ class CurlResponse
return $this->curlErrorMessage;
}
public function hasResponseHeader()
{
return $this->responseHeader !== null;
}
public function getResponseHeader()
{
return $this->responseHeader;
}
public function hasResponseBody()
{
return $this->responseBody !== null;
}
public function getResponseBody()
{
return $this->responseBody;

View File

@ -71,7 +71,7 @@ class MimeFilter implements SoapRequestFilter, SoapResponseFilter
$soapPart = $multiPartMessage->getMainPart();
$attachments = $multiPartMessage->getAttachments();
$response->setContent($this->sanitizePhpExceptionOnHrefs($soapPart));
$response->setContent($soapPart->getContent());
$response->setContentType($soapPart->getHeader('Content-Type'));
if (count($attachments) > 0) {
$response->setAttachments($attachments);

View File

@ -41,6 +41,10 @@ class SoapClient extends \SoapClient
protected $soapClientOptions;
protected $soapOptions;
private $curl;
/** @var SoapAttachment[] */
private $soapAttachmentsOnRequestStorage;
/** @var SoapResponse */
private $soapResponseStorage;
public function __construct(SoapClientOptions $soapClientOptions, SoapOptions $soapOptions)
{
@ -51,20 +55,20 @@ class SoapClient extends \SoapClient
);
try {
@parent::__construct(
$this->loadWsdl(
$this->curl,
$soapOptions->getWsdlFile(),
$soapOptions->getWsdlCacheType()
),
$soapClientOptions->toArray() + $soapOptions->toArray()
$wsdlPath = $this->loadWsdl(
$this->curl,
$soapOptions->getWsdlFile(),
$soapOptions->getWsdlCacheType(),
false
);
} catch (Exception $e) {
throw new SoapFault(
SoapFaultEnum::SOAP_FAULT_SOAP_CLIENT_ERROR,
'Could not create SoapClient instance with message: '.$e->getMessage()
'Unable to load WsdlPath ('.$soapOptions->getWsdlFile().') with message: '.$e->getMessage().' in file: '.$e->getFile().' (line: '.$e->getLine().')'
);
}
@parent::__construct($wsdlPath, $soapClientOptions->toArray() + $soapOptions->toArray());
}
/**
@ -82,20 +86,20 @@ class SoapClient extends \SoapClient
/**
* Using __soapCall returns only response string, use soapCall instead.
*
* @param string $functionName
* @param string $function_name
* @param array $arguments
* @param array|null $options
* @param null $inputHeaders
* @param array|null $outputHeaders
* @param null $input_headers
* @param array|null $output_headers
* @return string
*/
public function __soapCall($functionName, $arguments, $options = null, $inputHeaders = null, &$outputHeaders = null)
public function __soapCall($function_name, $arguments, $options = null, $input_headers = null, &$output_headers = null)
{
return $this->soapCall($functionName, $arguments, $options, $inputHeaders, $outputHeaders)->getContent();
return $this->soapCall($function_name, $arguments, $options, $input_headers, $output_headers)->getResponseContent();
}
/**
* @param string $functionName
* @param string $functionName
* @param array $arguments
* @param array|null $options
* @param SoapAttachment[] $soapAttachments
@ -105,17 +109,13 @@ class SoapClient extends \SoapClient
*/
public function soapCall($functionName, array $arguments, array $soapAttachments = [], array $options = null, $inputHeaders = null, array &$outputHeaders = null)
{
ob_start();
parent::__soapCall($functionName, $arguments, $options, $inputHeaders, $outputHeaders);
$nativeSoapClientRequest = $this->mapNativeDataJsonToDto(ob_get_clean());
$this->setSoapAttachmentsOnRequestToStorage($soapAttachments);
$soapResponseAsObject = parent::__soapCall($functionName, $arguments, $options, $inputHeaders, $outputHeaders);
return $this->performSoapRequest(
$nativeSoapClientRequest->request,
$nativeSoapClientRequest->location,
$nativeSoapClientRequest->action,
$nativeSoapClientRequest->version,
$soapAttachments
);
$soapResponse = $this->getSoapResponseFromStorage();
$soapResponse->setResponseObject($soapResponseAsObject);
return $soapResponse;
}
/**
@ -131,34 +131,16 @@ class SoapClient extends \SoapClient
*/
public function __doRequest($request, $location, $action, $version, $oneWay = 0)
{
$soapClientNativeDataTransferObject = new SoapClientNativeDataTransferObject(
$soapResponse = $this->performSoapRequest(
$request,
$location,
$action,
$version
$version,
$this->getSoapAttachmentsOnRequestFromStorage()
);
echo json_encode($soapClientNativeDataTransferObject);
$this->setSoapResponseToStorage($soapResponse);
return $request;
}
/**
* Custom request method to be able to modify the SOAP messages.
* $oneWay parameter is not used at the moment.
*
* @param mixed $request Request object
* @param string $location Location
* @param string $action SOAP action
* @param int $version SOAP version
* @param SoapAttachment[] $soapAttachments SOAP attachments array
*
* @return SoapResponse
*/
public function performSoapRequest($request, $location, $action, $version, array $soapAttachments = [])
{
$soapRequest = $this->createSoapRequest($location, $action, $version, $request, $soapAttachments);
return $this->performHttpSoapRequest($soapRequest);
return $soapResponse->getResponseContent();
}
/** @deprecated */
@ -201,6 +183,25 @@ class SoapClient extends \SoapClient
);
}
/**
* Custom request method to be able to modify the SOAP messages.
* $oneWay parameter is not used at the moment.
*
* @param mixed $request Request object
* @param string $location Location
* @param string $action SOAP action
* @param int $version SOAP version
* @param SoapAttachment[] $soapAttachments SOAP attachments array
*
* @return SoapResponse
*/
private function performSoapRequest($request, $location, $action, $version, array $soapAttachments = [])
{
$soapRequest = $this->createSoapRequest($location, $action, $version, $request, $soapAttachments);
return $this->performHttpSoapRequest($soapRequest);
}
/**
* @param string $location Location
* @param string $action SOAP action
@ -248,7 +249,7 @@ class SoapClient extends \SoapClient
];
} else {
$headers = [
'Content-Type:' . $soapRequest->getContentType() . '; action="' . $soapRequest->getAction() . '"',
'Content-Type:' . $soapRequest->getContentType() . '; action="' . $soapRequest->getAction() . '"',
];
}
$curlResponse = $this->curl->executeCurlWithCachedSession(
@ -311,14 +312,13 @@ class SoapClient extends \SoapClient
*/
private function loadWsdl(Curl $curl, $wsdlPath, $wsdlCacheType, $resolveRemoteIncludes = true)
{
$wsdlDownloader = new WsdlDownloader($curl);
$wsdlDownloader = new WsdlDownloader();
try {
$loadedWsdlFilePath = $wsdlDownloader->getWsdlPath($curl, $wsdlPath, $wsdlCacheType, $resolveRemoteIncludes);
} catch (Exception $e) {
throw new SoapFault(
SoapFaultEnum::SOAP_FAULT_WSDL,
'Unable to load WsdlPath: ' . $e->getMessage()
'Unable to load WsdlPath ('.$wsdlPath.') with message: '.$e->getMessage().' in file: '.$e->getFile().' (line: '.$e->getLine().')'
);
}
@ -338,18 +338,6 @@ class SoapClient extends \SoapClient
return $filters;
}
private function mapNativeDataJsonToDto($nativeDataJson)
{
$nativeData = json_decode($nativeDataJson);
return new SoapClientNativeDataTransferObject(
$nativeData->request,
$nativeData->location,
$nativeData->action,
$nativeData->version
);
}
private function returnSoapResponseByTracing(
$isTracingEnabled,
SoapRequest $soapRequest,
@ -407,4 +395,33 @@ class SoapClient extends \SoapClient
throw new Exception('SoapClientOptions tracing disabled, turn on trace attribute');
}
}
private function setSoapResponseToStorage(SoapResponse $soapResponseStorage)
{
$this->soapResponseStorage = $soapResponseStorage;
}
/**
* @param SoapAttachment[] $soapAttachments
*/
private function setSoapAttachmentsOnRequestToStorage(array $soapAttachments)
{
$this->soapAttachmentsOnRequestStorage = $soapAttachments;
}
private function getSoapAttachmentsOnRequestFromStorage()
{
$soapAttachmentsOnRequest = $this->soapAttachmentsOnRequestStorage;
$this->soapAttachmentsOnRequestStorage = null;
return $soapAttachmentsOnRequest;
}
private function getSoapResponseFromStorage()
{
$soapResponse = $this->soapResponseStorage;
$this->soapResponseStorage = null;
return $soapResponse;
}
}

View File

@ -1,19 +0,0 @@
<?php
namespace BeSimple\SoapClient;
class SoapClientNativeDataTransferObject
{
public $request;
public $location;
public $action;
public $version;
public function __construct($request, $location, $action, $version)
{
$this->request = $request;
$this->location = $location;
$this->action = $action;
$this->version = $version;
}
}

View File

@ -6,8 +6,38 @@ use BeSimple\SoapCommon\SoapResponse as CommonSoapResponse;
class SoapResponse extends CommonSoapResponse
{
/** @var mixed */
protected $responseObject;
/** @var SoapResponseTracingData */
protected $tracingData;
public function getResponseContent()
{
return $this->getContent();
}
public function getResponseObject()
{
return $this->responseObject;
}
public function setResponseObject($responseObject)
{
$this->responseObject = $responseObject;
}
public function hasTracingData()
{
return $this->tracingData !== null;
}
public function getTracingData()
{
return $this->tracingData;
}
public function setTracingData(SoapResponseTracingData $tracingData)
{
$this->tracingData = $tracingData;
}
}

View File

@ -75,7 +75,7 @@ class WsdlDownloader
$curlResponse = $curl->executeCurlWithCachedSession($wsdlPath);
if ($curlResponse->curlStatusSuccess()) {
if (mb_strlen($curlResponse->getResponseBody()) === 0) {
throw new Exception('Could not write WSDL cache file: curl response empty');
throw new Exception('Could not write WSDL cache file: empty curl response from: '.$wsdlPath);
}
if ($resolveRemoteIncludes === true) {
$document = $this->getXmlFileDOMDocument($curl, $cacheType, $curlResponse->getResponseBody(), $wsdlPath);
@ -180,26 +180,28 @@ class WsdlDownloader
foreach ($nodes as $node) {
/** @var DOMElement $node */
$locationPath = $node->getAttribute($locationAttributeName);
if ($this->isRemoteFile($locationPath)) {
$node->setAttribute(
$locationAttributeName,
$this->getWsdlPath(
$curl,
$locationPath,
$cacheType,
true
)
);
} else if ($parentFilePath !== null) {
$node->setAttribute(
$locationAttributeName,
$this->getWsdlPath(
$curl,
$this->resolveRelativePathInUrl($parentFilePath, $locationPath),
$cacheType,
true
)
);
if ($locationPath !== '') {
if ($this->isRemoteFile($locationPath)) {
$node->setAttribute(
$locationAttributeName,
$this->getWsdlPath(
$curl,
$locationPath,
$cacheType,
true
)
);
} else if ($parentFilePath !== null) {
$node->setAttribute(
$locationAttributeName,
$this->getWsdlPath(
$curl,
$this->resolveRelativePathInUrl($parentFilePath, $locationPath),
$cacheType,
true
)
);
}
}
}
}
@ -218,7 +220,7 @@ class WsdlDownloader
$urlParts = parse_url($base);
// combine base path with relative path
if (isset($urlParts['path']) && '/' === $relative{0}) {
if (isset($urlParts['path']) && mb_strlen($relative) > 0 && '/' === $relative{0}) {
// $relative is absolute path from domain (starts with /)
$path = $relative;
} elseif (isset($urlParts['path']) && strrpos($urlParts['path'], '/') === (strlen($urlParts['path']) )) {

View File

@ -110,7 +110,8 @@ class Parser
}
if (strpos($currentHeader, ':') !== false) {
list($headerName, $headerValue) = explode(':', $currentHeader, 2);
$headerValue = iconv_mime_decode($headerValue, 0, Part::CHARSET_UTF8);
$headerValueWithNoCrAtTheEnd = trim($headerValue);
$headerValue = iconv_mime_decode($headerValueWithNoCrAtTheEnd, 0, Part::CHARSET_UTF8);
$parsedMimeHeaders = ContentTypeParser::parseContentTypeHeader($headerName, $headerValue);
foreach ($parsedMimeHeaders as $parsedMimeHeader) {
$currentPart->setHeader(

View File

@ -13,8 +13,6 @@
namespace BeSimple\SoapCommon;
use BeSimple\SoapClient\SoapResponseTracingData;
/**
* SOAP response message.
*
@ -23,21 +21,4 @@ use BeSimple\SoapClient\SoapResponseTracingData;
*/
class SoapResponse extends SoapMessage
{
/** @var SoapResponseTracingData */
protected $tracingData;
public function hasTracingData()
{
return $this->tracingData !== null;
}
public function getTracingData()
{
return $this->tracingData;
}
public function setTracingData(SoapResponseTracingData $tracingData)
{
$this->tracingData = $tracingData;
}
}

View File

@ -38,7 +38,7 @@ class MimeFilter implements SoapRequestFilter, SoapResponseFilter
$soapPart = $multiPartMessage->getMainPart();
$attachments = $multiPartMessage->getAttachments();
$request->setContent($this->sanitizePhpExceptionOnHrefs($soapPart));
$request->setContent($soapPart->getContent());
$request->setContentType($soapPart->getHeader('Content-Type'));
if (count($attachments) > 0) {
$request->setAttachments($attachments);