BeSimpleSoap/src/BeSimple/SoapClient/SoapClient.php

409 lines
14 KiB
PHP
Raw Normal View History

<?php
2011-10-03 21:03:46 +02:00
/*
* This file is part of the BeSimpleSoapClient.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapClient;
use BeSimple\SoapBundle\Soap\SoapAttachment;
use BeSimple\SoapBundle\Soap\SoapAttachmentList;
use BeSimple\SoapClient\Curl\Curl;
use BeSimple\SoapClient\Curl\CurlOptionsBuilder;
use BeSimple\SoapClient\Curl\CurlResponse;
use BeSimple\SoapClient\SoapOptions\SoapClientOptions;
use BeSimple\SoapCommon\Fault\SoapFaultEnum;
use BeSimple\SoapCommon\Mime\PartFactory;
use BeSimple\SoapCommon\SoapKernel;
use BeSimple\SoapCommon\SoapOptions\SoapOptions;
use BeSimple\SoapCommon\SoapRequest;
use BeSimple\SoapCommon\SoapRequestFactory;
use Exception;
use SoapFault;
2011-12-11 21:20:35 +01:00
/**
2011-10-02 12:23:59 +02:00
* Extended SoapClient that uses a a cURL wrapper for all underlying HTTP
* requests in order to use proper authentication for all requests. This also
* adds NTLM support. A custom WSDL downloader resolves remote xsd:includes and
* allows caching of all remote referenced items.
*
2011-10-22 11:28:15 +02:00
* @author Andreas Schamberger <mail@andreass.net>
* @author Petr Bechyně <mail@petrbechyne.com>
*/
class SoapClient extends \SoapClient
{
protected $soapClientOptions;
protected $soapOptions;
private $curl;
public function __construct(SoapClientOptions $soapClientOptions, SoapOptions $soapOptions)
{
$this->soapClientOptions = $soapClientOptions;
$this->soapOptions = $soapOptions;
$this->curl = new Curl(
CurlOptionsBuilder::buildForSoapClient($soapClientOptions)
);
2011-10-16 19:49:24 +02:00
try {
2017-02-17 00:14:05 +01:00
$wsdlPath = $this->loadWsdl(
$this->curl,
$soapOptions->getWsdlFile(),
$soapOptions->getWsdlCacheType()
);
} catch (Exception $e) {
throw new SoapFault(
SoapFaultEnum::SOAP_FAULT_SOAP_CLIENT_ERROR,
2017-02-17 00:14:05 +01:00
'Unable to load WsdlPath ('.$soapOptions->getWsdlFile().') with message: '.$e->getMessage().' in file: '.$e->getFile().' (line: '.$e->getLine().')'
);
}
2017-02-17 00:14:05 +01:00
@parent::__construct($wsdlPath, $soapClientOptions->toArray() + $soapOptions->toArray());
}
2011-10-16 19:49:24 +02:00
/**
* Avoid using __call directly, it's deprecated even in \SoapClient.
2011-10-16 19:49:24 +02:00
*
* @deprecated
*/
public function __call($function_name, $arguments)
{
throw new Exception(
'The __call method is deprecated. Use __soapCall/soapCall instead.'
);
}
2011-10-16 19:49:24 +02:00
/**
* Using __soapCall returns only response string, use soapCall instead.
2011-10-16 19:49:24 +02:00
*
2017-02-17 00:14:05 +01:00
* @param string $function_name
* @param array $arguments
* @param array|null $options
2017-02-17 00:14:05 +01:00
* @param null $input_headers
* @param array|null $output_headers
* @return string
*/
public function __soapCall($function_name, array $arguments, array $options = null, $input_headers = null, array &$output_headers = null)
{
2017-02-17 00:14:05 +01:00
return $this->soapCall($function_name, $arguments, $options, $input_headers, $output_headers)->getContent();
}
/**
* @param string $functionName
* @param array $arguments
* @param array|null $options
* @param SoapAttachment[] $soapAttachments
* @param null $inputHeaders
* @param array|null $outputHeaders
* @return SoapResponse
*/
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());
return $this->performSoapRequest(
$nativeSoapClientRequest->request,
$nativeSoapClientRequest->location,
$nativeSoapClientRequest->action,
$nativeSoapClientRequest->version,
$soapAttachments
);
}
/**
* This is not performing any HTTP requests, but it is getting data from SoapClient that are needed for this Client
*
* @param string $request Request string
* @param string $location Location
* @param string $action SOAP action
* @param int $version SOAP version
* @param int $oneWay 0|1
*
* @return string
*/
public function __doRequest($request, $location, $action, $version, $oneWay = 0)
{
$soapClientNativeDataTransferObject = new SoapClientNativeDataTransferObject(
$request,
$location,
$action,
$version
);
echo json_encode($soapClientNativeDataTransferObject);
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);
}
/** @deprecated */
public function __getLastRequestHeaders()
{
$this->checkTracing();
throw new Exception(
'The __getLastRequestHeaders method is now deprecated. Use callSoapRequest instead and get the tracing information from SoapResponseTracingData.'
);
}
/** @deprecated */
public function __getLastRequest()
{
$this->checkTracing();
throw new Exception(
'The __getLastRequest method is now deprecated. Use callSoapRequest instead and get the tracing information from SoapResponseTracingData.'
);
}
/** @deprecated */
public function __getLastResponseHeaders()
{
$this->checkTracing();
throw new Exception(
'The __getLastResponseHeaders method is now deprecated. Use callSoapRequest instead and get the tracing information from SoapResponseTracingData.'
);
}
/** @deprecated */
public function __getLastResponse()
{
$this->checkTracing();
throw new Exception(
'The __getLastResponse method is now deprecated. Use callSoapRequest instead and get the tracing information from SoapResponseTracingData.'
);
2011-10-16 19:49:24 +02:00
}
/**
* @param string $location Location
* @param string $action SOAP action
* @param int $version SOAP version
* @param string $request SOAP request body
* @param SoapAttachment[] $soapAttachments array of SOAP attachments
*
* @return SoapRequest
*/
private function createSoapRequest($location, $action, $version, $request, array $soapAttachments = [])
{
$soapAttachmentList = new SoapAttachmentList($soapAttachments);
$soapRequest = SoapRequestFactory::create($location, $action, $version, $request);
if (count($soapAttachments) > 0) {
if ($this->soapOptions->hasAttachments() === true) {
$soapRequest->setAttachments(PartFactory::createAttachmentParts($soapAttachments));
$soapRequest = SoapKernel::filterRequest(
$soapRequest,
$this->getAttachmentFilters(),
$this->soapOptions->getAttachmentType()
);
} else {
throw new Exception(
'Non SWA SoapClient cannot handle SOAP action '.$action.' with attachments: '.implode(', ', $soapAttachmentList->getSoapAttachmentIds())
);
}
}
return $soapRequest;
}
/**
* Perform HTTP request with cURL.
*
2011-12-17 16:05:25 +01:00
* @param SoapRequest $soapRequest SoapRequest object
2011-12-11 21:20:35 +01:00
* @return SoapResponse
* @throws SoapFault
*/
private function performHttpSoapRequest(SoapRequest $soapRequest)
{
if ($soapRequest->getVersion() === SOAP_1_1) {
$headers = [
2013-08-30 22:07:44 +02:00
'Content-Type:' . $soapRequest->getContentType(),
'SOAPAction: "' . $soapRequest->getAction() . '"',
];
2013-08-30 22:07:44 +02:00
} else {
$headers = [
'Content-Type:' . $soapRequest->getContentType() . '; action="' . $soapRequest->getAction() . '"',
];
2013-08-30 22:07:44 +02:00
}
$curlResponse = $this->curl->executeCurlWithCachedSession(
2011-12-11 21:20:35 +01:00
$soapRequest->getLocation(),
$soapRequest->getContent(),
$headers
);
$soapResponseTracingData = new SoapResponseTracingData(
$curlResponse->getHttpRequestHeaders(),
$soapRequest->getContent(),
$curlResponse->getResponseHeader(),
$curlResponse->getResponseBody()
2011-12-17 16:05:25 +01:00
);
2011-12-11 21:20:35 +01:00
if ($curlResponse->curlStatusSuccess()) {
$soapResponse = $this->returnSoapResponseByTracing(
$this->soapClientOptions->getTrace(),
$soapRequest,
$curlResponse,
$soapResponseTracingData
);
if ($this->soapOptions->hasAttachments()) {
return SoapKernel::filterResponse(
$soapResponse,
$this->getAttachmentFilters(),
$this->soapOptions->getAttachmentType()
);
} else {
2014-07-01 15:47:46 +02:00
return $soapResponse;
}
} else if ($curlResponse->curlStatusFailed()) {
return $this->throwSoapFaultByTracing(
$this->soapClientOptions->getTrace(),
SoapFaultEnum::SOAP_FAULT_HTTP.'-'.$curlResponse->getHttpResponseStatusCode(),
$curlResponse->getCurlErrorMessage(),
$soapResponseTracingData
);
} else {
return $this->throwSoapFaultByTracing(
$this->soapClientOptions->getTrace(),
SoapFaultEnum::SOAP_FAULT_SOAP_CLIENT_ERROR,
'Cannot process curl response with unresolved status: ' . $curlResponse->getCurlStatus(),
$soapResponseTracingData
);
}
}
2011-12-11 21:20:35 +01:00
/**
* @param Curl $curl
* @param string $wsdlPath
* @param int $wsdlCacheType
* @param bool $resolveRemoteIncludes
2011-12-17 16:05:25 +01:00
*
* @return string
*/
private function loadWsdl(Curl $curl, $wsdlPath, $wsdlCacheType, $resolveRemoteIncludes = true)
{
2017-02-17 00:14:05 +01:00
$wsdlDownloader = new WsdlDownloader();
2011-10-02 12:23:59 +02:00
try {
$loadedWsdlFilePath = $wsdlDownloader->getWsdlPath($curl, $wsdlPath, $wsdlCacheType, $resolveRemoteIncludes);
} catch (Exception $e) {
throw new SoapFault(
SoapFaultEnum::SOAP_FAULT_WSDL,
2017-02-17 00:14:05 +01:00
'Unable to load WsdlPath ('.$wsdlPath.') with message: '.$e->getMessage().' in file: '.$e->getFile().' (line: '.$e->getLine().')'
);
}
2011-12-11 21:20:35 +01:00
return $loadedWsdlFilePath;
}
private function getAttachmentFilters()
{
$filters = [];
if ($this->soapOptions->getAttachmentType() !== SoapOptions::SOAP_ATTACHMENTS_TYPE_BASE64) {
$filters[] = new MimeFilter();
}
if ($this->soapOptions->getAttachmentType() === SoapOptions::SOAP_ATTACHMENTS_TYPE_MTOM) {
$filters[] = new XmlMimeFilter();
}
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,
CurlResponse $curlResponse,
SoapResponseTracingData $soapResponseTracingData,
array $soapAttachments = []
) {
if ($isTracingEnabled === true) {
return SoapResponseFactory::createWithTracingData(
$curlResponse->getResponseBody(),
$soapRequest->getLocation(),
$soapRequest->getAction(),
$soapRequest->getVersion(),
$curlResponse->getHttpResponseContentType(),
$soapResponseTracingData,
$soapAttachments
);
} else {
return SoapResponseFactory::create(
$curlResponse->getResponseBody(),
$soapRequest->getLocation(),
$soapRequest->getAction(),
$soapRequest->getVersion(),
$curlResponse->getHttpResponseContentType(),
$soapAttachments
);
}
}
private function throwSoapFaultByTracing($isTracingEnabled, $soapFaultCode, $soapFaultMessage, SoapResponseTracingData $soapResponseTracingData)
{
if ($isTracingEnabled === true) {
throw new SoapFaultWithTracingData(
$soapFaultCode,
$soapFaultMessage,
$soapResponseTracingData
);
} else {
throw new SoapFault(
$soapFaultCode,
$soapFaultMessage
);
}
}
private function checkTracing()
{
if ($this->soapClientOptions->getTrace() === false) {
throw new Exception('SoapClientOptions tracing disabled, turn on trace attribute');
}
}
}