6 Commits
v4.2.6 ... v4.3

Author SHA1 Message Date
8db9b374e4 SoapFault handling refactored: client now returns server fault codes + more details in message 2017-05-26 10:53:41 +02:00
f669c18c7f Merge pull request #3 from ceesco53/patch-1
composer.json refers to upstream not this fork (README.md fix)
2017-05-25 19:07:26 +02:00
524d1f3fd7 composer.json refers to upstream not this fork
Using the composer.json instructions as is will cause the rest of your instructions to fail.  It points to upstream package.  I've got some other changes coming soon.  Happy to help.
2017-05-25 12:13:58 -04:00
ecffdc18fd Minor bugfixes of SoapFault handling
SoapFault is now properly prefixed so that SoapFaultSourceGetter identifies them correctly, missing exceptions option is now processed in SoapServer
2017-05-10 09:15:27 +02:00
b45202f40a Project composer description update 2017-04-26 13:00:40 +02:00
ab83642f06 Parser fix: single line MimeMessages are now parsed correctly 2017-04-06 15:51:29 +02:00
17 changed files with 551 additions and 213 deletions

View File

@ -47,7 +47,7 @@ Create a `composer.json` file:
```json
{
"require": {
"besimple/soap": "0.2.*@dev"
"tuscanicz/soap": "^4.2"
}
}
```
@ -144,4 +144,4 @@ See a simplified implementation of ``dummyServiceMethod`` to get a clue:
}
```
For further information and getting inspiration for your implementation, see the unit tests in ``tests`` dir.
For further information and getting inspiration for your implementation, see the unit tests in ``tests`` dir.

View File

@ -1,7 +1,7 @@
{
"name": "tuscanicz/soap",
"type": "library",
"description": "A fork of the abandoned besimple/soap package with added support for Symfony 3.0",
"description": "A largely refactored besimple/soap used to build SOAP and WSDL based web services. This fork fixes a lot of errors and provides better API, robust, stable and modern codebase.",
"keywords": ["soap", "soap server", "soap client"],
"license": "MIT",
"authors": [

View File

@ -152,10 +152,11 @@ class Curl
preg_match('/HTTP\/(1\.[0-1]+) ([0-9]{3}) (.*)/', $executeSoapCallResponse, $httpResponseMessages);
$httpResponseMessage = trim(array_pop($httpResponseMessages));
$curlErrorMessage = sprintf(
'Curl error "%s" with message: %s occurred while connecting to %s',
'Curl error "%s" with message: %s occurred while connecting to %s with HTTP response code %s',
curl_errno($curlSession),
curl_error($curlSession),
$location
$location,
$httpResponseCode
);
if (!is_integer($httpResponseCode) || $httpResponseCode >= 400 || $httpResponseCode === 0) {

View File

@ -19,6 +19,7 @@ use BeSimple\SoapClient\Curl\CurlOptionsBuilder;
use BeSimple\SoapClient\Curl\CurlResponse;
use BeSimple\SoapClient\SoapOptions\SoapClientOptions;
use BeSimple\SoapCommon\Fault\SoapFaultEnum;
use BeSimple\SoapCommon\Fault\SoapFaultParser;
use BeSimple\SoapCommon\Fault\SoapFaultPrefixEnum;
use BeSimple\SoapCommon\Fault\SoapFaultSourceGetter;
use BeSimple\SoapCommon\Mime\PartFactory;
@ -40,16 +41,14 @@ use SoapFault;
*/
class SoapClient extends \SoapClient
{
use SoapClientNativeMethodsTrait;
/** @var SoapClientOptions */
protected $soapClientOptions;
/** @var SoapOptions */
protected $soapOptions;
/** @var Curl */
private $curl;
/** @var SoapAttachment[] */
private $soapAttachmentsOnRequestStorage;
/** @var SoapResponse */
private $soapResponseStorage;
public function __construct(SoapClientOptions $soapClientOptions, SoapOptions $soapOptions)
{
@ -76,33 +75,6 @@ class SoapClient extends \SoapClient
@parent::__construct($wsdlPath, $soapClientOptions->toArray() + $soapOptions->toArray());
}
/**
* Avoid using __call directly, it's deprecated even in \SoapClient.
*
* @deprecated
*/
public function __call($function_name, $arguments)
{
throw new Exception(
'The __call method is deprecated. Use __soapCall/soapCall instead.'
);
}
/**
* Using __soapCall returns only response string, use soapCall instead.
*
* @param string $function_name
* @param array $arguments
* @param array|null $options
* @param null $input_headers
* @param array|null $output_headers
* @return string
*/
public function __soapCall($function_name, $arguments, $options = null, $input_headers = null, &$output_headers = null)
{
return $this->soapCall($function_name, $arguments, $options, $input_headers, $output_headers)->getResponseContent();
}
/**
* @param string $functionName
* @param array $arguments
@ -125,95 +97,13 @@ class SoapClient extends \SoapClient
} catch (SoapFault $soapFault) {
if (SoapFaultSourceGetter::isNativeSoapFault($soapFault)) {
$soapResponse = $this->getSoapResponseFromStorage();
if ($soapResponse instanceof SoapResponse) {
$soapFault = $this->throwSoapFaultByTracing(
SoapFaultPrefixEnum::PREFIX_PHP . '-' . $soapFault->getCode(),
$soapFault->getMessage(),
new SoapResponseTracingData(
'Content-Type: ' . $soapResponse->getRequest()->getContentType(),
$soapResponse->getRequest()->getContent(),
'Content-Type: ' . $soapResponse->getContentType(),
$soapResponse->getResponseContent()
)
);
} else {
$soapFault = new SoapFault(
SoapFaultPrefixEnum::PREFIX_PHP . '-unresolved',
'Got SoapFault message with no response: '.$soapFault->getMessage()
);
}
$soapFault = $this->decorateNativeSoapFault($soapFault);
}
throw $soapFault;
}
}
/**
* 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)
{
$soapResponse = $this->performSoapRequest(
$request,
$location,
$action,
$version,
$this->getSoapAttachmentsOnRequestFromStorage()
);
$this->setSoapResponseToStorage($soapResponse);
return $soapResponse->getResponseContent();
}
/** @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.'
);
}
/**
* Custom request method to be able to modify the SOAP messages.
* $oneWay parameter is not used at the moment.
@ -226,7 +116,7 @@ class SoapClient extends \SoapClient
*
* @return SoapResponse
*/
private function performSoapRequest($request, $location, $action, $version, array $soapAttachments = [])
protected function performSoapRequest($request, $location, $action, $version, array $soapAttachments = [])
{
$soapRequest = $this->createSoapRequest($location, $action, $version, $request, $soapAttachments);
@ -273,22 +163,10 @@ class SoapClient extends \SoapClient
*/
private function performHttpSoapRequest(SoapRequest $soapRequest)
{
if ($soapRequest->getVersion() === SOAP_1_1) {
$headers = [
'Content-Type: ' . $soapRequest->getContentType(),
'SOAPAction: "' . $soapRequest->getAction() . '"',
'Connection: ' . ($this->soapOptions->isConnectionKeepAlive() ? 'Keep-Alive' : 'close'),
];
} else {
$headers = [
'Content-Type: ' . $soapRequest->getContentType() . '; action="' . $soapRequest->getAction() . '"',
'Connection: ' . ($this->soapOptions->isConnectionKeepAlive() ? 'Keep-Alive' : 'close'),
];
}
$curlResponse = $this->curl->executeCurlWithCachedSession(
$soapRequest->getLocation(),
$soapRequest->getContent(),
$headers
$this->getHttpHeadersBySoapVersion($soapRequest)
);
$soapResponseTracingData = new SoapResponseTracingData(
$curlResponse->getHttpRequestHeaders(),
@ -310,26 +188,42 @@ class SoapClient extends \SoapClient
$this->getAttachmentFilters(),
$this->soapOptions->getAttachmentType()
);
} else {
return $soapResponse;
}
} else if ($curlResponse->curlStatusFailed()) {
return $soapResponse;
}
if ($curlResponse->curlStatusFailed()) {
if ($curlResponse->getHttpResponseStatusCode() >= 500) {
$soapFault = SoapFaultParser::parseSoapFault(
$curlResponse->getResponseBody()
);
return $this->throwSoapFaultByTracing(
$soapFault->faultcode,
sprintf(
'SOAP HTTP call failed: %s with Message: %s and Code: %s',
$curlResponse->getCurlErrorMessage(),
$soapFault->getMessage(),
$soapFault->faultcode
),
$soapResponseTracingData
);
}
return $this->throwSoapFaultByTracing(
SoapFaultPrefixEnum::PREFIX_DEFAULT.'-'.SoapFaultEnum::SOAP_FAULT_HTTP.'-'.$curlResponse->getHttpResponseStatusCode(),
SoapFaultEnum::SOAP_FAULT_HTTP.'-'.$curlResponse->getHttpResponseStatusCode(),
$curlResponse->getCurlErrorMessage(),
$soapResponseTracingData
);
} else {
return $this->throwSoapFaultByTracing(
SoapFaultPrefixEnum::PREFIX_DEFAULT.'-'.SoapFaultEnum::SOAP_FAULT_SOAP_CLIENT_ERROR,
'Cannot process curl response with unresolved status: ' . $curlResponse->getCurlStatus(),
$soapResponseTracingData
);
}
return $this->throwSoapFaultByTracing(
SoapFaultEnum::SOAP_FAULT_SOAP_CLIENT_ERROR,
'Cannot process curl response with unresolved status: ' . $curlResponse->getCurlStatus(),
$soapResponseTracingData
);
}
/**
@ -383,18 +277,16 @@ class SoapClient extends \SoapClient
$soapResponseTracingData,
$soapAttachments
);
} else {
return SoapResponseFactory::create(
$curlResponse->getResponseBody(),
$soapRequest->getLocation(),
$soapRequest->getAction(),
$soapRequest->getVersion(),
$curlResponse->getHttpResponseContentType(),
$soapAttachments
);
}
return SoapResponseFactory::create(
$curlResponse->getResponseBody(),
$soapRequest->getLocation(),
$soapRequest->getAction(),
$soapRequest->getVersion(),
$curlResponse->getHttpResponseContentType(),
$soapAttachments
);
}
/**
@ -412,49 +304,59 @@ class SoapClient extends \SoapClient
$soapFaultMessage,
$soapResponseTracingData
);
}
throw new SoapFault(
$soapFaultCode,
$soapFaultMessage
);
}
private function decorateNativeSoapFault(SoapFault $nativePhpSoapFault)
{
$soapResponse = $this->getSoapResponseFromStorage();
if ($soapResponse instanceof SoapResponse) {
$tracingData = new SoapResponseTracingData(
'Content-Type: ' . $soapResponse->getRequest()->getContentType(),
$soapResponse->getRequest()->getContent(),
'Content-Type: ' . $soapResponse->getContentType(),
$soapResponse->getResponseContent()
);
$soapFault = $this->throwSoapFaultByTracing(
SoapFaultPrefixEnum::PREFIX_PHP . '-' . $nativePhpSoapFault->getCode(),
$nativePhpSoapFault->getMessage(),
$tracingData
);
} else {
throw new SoapFault(
$soapFaultCode,
$soapFaultMessage
$soapFault = $this->throwSoapFaultByTracing(
$nativePhpSoapFault->faultcode,
$nativePhpSoapFault->getMessage(),
new SoapResponseTracingData(
null,
null,
null,
null
)
);
}
return $soapFault;
}
private function checkTracing()
private function getHttpHeadersBySoapVersion(SoapRequest $soapRequest)
{
if ($this->soapClientOptions->getTrace() === false) {
throw new Exception('SoapClientOptions tracing disabled, turn on trace attribute');
if ($soapRequest->getVersion() === SOAP_1_1) {
return [
'Content-Type: ' . $soapRequest->getContentType(),
'SOAPAction: "' . $soapRequest->getAction() . '"',
'Connection: ' . ($this->soapOptions->isConnectionKeepAlive() ? 'Keep-Alive' : 'close'),
];
}
}
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;
return [
'Content-Type: ' . $soapRequest->getContentType() . '; action="' . $soapRequest->getAction() . '"',
'Connection: ' . ($this->soapOptions->isConnectionKeepAlive() ? 'Keep-Alive' : 'close'),
];
}
}

View File

@ -0,0 +1,166 @@
<?php
namespace BeSimple\SoapClient;
use BeSimple\SoapBundle\Soap\SoapAttachment;
use BeSimple\SoapClient\SoapOptions\SoapClientOptions;
use Exception;
trait SoapClientNativeMethodsTrait
{
/** @var SoapClientOptions */
protected $soapClientOptions;
/** @var SoapAttachment[] */
private $soapAttachmentsOnRequestStorage;
/** @var SoapResponse */
private $soapResponseStorage;
/**
* @param string $functionName
* @param array $arguments
* @param array|null $options
* @param SoapAttachment[] $soapAttachments
* @param null $inputHeaders
* @param array|null $outputHeaders
* @return SoapResponse
*/
abstract public function soapCall($functionName, array $arguments, array $soapAttachments = [], array $options = null, $inputHeaders = null, array &$outputHeaders = null);
/**
* @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
*/
abstract protected function performSoapRequest($request, $location, $action, $version, array $soapAttachments = []);
/**
* Avoid using __call directly, it's deprecated even in \SoapClient.
*
* @deprecated
*/
public function __call($function_name, $arguments)
{
throw new Exception(
'The __call method is deprecated. Use __soapCall/soapCall instead.'
);
}
/**
* Using __soapCall returns only response string, use soapCall instead.
*
* @param string $function_name
* @param array $arguments
* @param array|null $options
* @param null $input_headers
* @param array|null $output_headers
* @return string
*/
public function __soapCall($function_name, $arguments, $options = null, $input_headers = null, &$output_headers = null)
{
return $this->soapCall($function_name, $arguments, $options, $input_headers, $output_headers)->getResponseContent();
}
/**
* 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)
{
$soapResponse = $this->performSoapRequest(
$request,
$location,
$action,
$version,
$this->getSoapAttachmentsOnRequestFromStorage()
);
$this->setSoapResponseToStorage($soapResponse);
return $soapResponse->getResponseContent();
}
/** @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.'
);
}
private function checkTracing()
{
if ($this->soapClientOptions->getTrace() === false) {
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

@ -4,7 +4,7 @@ namespace BeSimple\SoapCommon\Fault;
class SoapFaultEnum
{
const SOAP_FAULT_WSDL = 'wsdl';
const SOAP_FAULT_HTTP = 'http';
const SOAP_FAULT_SOAP_CLIENT_ERROR = 'soap-client-error';
const SOAP_FAULT_WSDL = SoapFaultPrefixEnum::PREFIX_DEFAULT.'-'.'wsdl';
const SOAP_FAULT_HTTP = SoapFaultPrefixEnum::PREFIX_DEFAULT.'-'.'http';
const SOAP_FAULT_SOAP_CLIENT_ERROR = SoapFaultPrefixEnum::PREFIX_DEFAULT.'-'.'soap-client-error';
}

View File

@ -0,0 +1,31 @@
<?php
namespace BeSimple\SoapCommon\Fault;
use SimpleXMLElement;
use SoapFault;
class SoapFaultParser
{
/**
* @param string $soapFaultXmlSource
* @return SoapFault
*/
public static function parseSoapFault($soapFaultXmlSource)
{
$simpleXMLElement = new SimpleXMLElement($soapFaultXmlSource);
$faultCode = $simpleXMLElement->xpath('//faultcode');
if ($faultCode === false || count($faultCode) === 0) {
$faultCode = 'Unable to parse faultCode';
}
$faultString = $simpleXMLElement->xpath('//faultstring');
if ($faultString === false || count($faultString) === 0) {
$faultString = 'Unable to parse faultString';
}
return new SoapFault(
(string)$faultCode[0],
(string)$faultString[0]
);
}
}

View File

@ -13,13 +13,13 @@ class SoapFaultSourceGetter
public static function isBeSimpleSoapFault(SoapFault $soapFault)
{
$defaultPrefix = SoapFaultPrefixEnum::PREFIX_DEFAULT;
$nativeSoapFaultPrefix = SoapFaultPrefixEnum::PREFIX_DEFAULT.'-';
if (strpos($soapFault->getCode(), $defaultPrefix) === 0) {
if (strpos($soapFault->faultcode, $nativeSoapFaultPrefix) === 0) {
return false;
return true;
}
return true;
return false;
}
}

View File

@ -21,6 +21,7 @@ class MimeBoundaryAnalyser
}
/**
* @todo: This method is not reliable at all
* @param string $mimeMessageLine
* @return bool
*/

View File

@ -29,25 +29,16 @@ class Parser
/**
* Parse the given Mime-Message and return a \BeSimple\SoapCommon\Mime\MultiPart object.
*
* @param string $mimeMessage Mime message string
* @param string[] $headers array(string=>string) of header elements (e.g. coming from http request)
*
* @return \BeSimple\SoapCommon\Mime\MultiPart
* @param string $mimeMessage Mime message string
* @param string[] $headers array(string=>string) of header elements (e.g. coming from http request)
* @return MultiPart
*/
public static function parseMimeMessage($mimeMessage, array $headers = [])
{
$multiPart = new MultiPart();
$mimeMessageLines = explode("\n", $mimeMessage);
$mimeMessageLineCount = count($mimeMessageLines);
if ($mimeMessageLineCount <= 1) {
throw new Exception(
sprintf(
'Cannot process message of %d characters: got unexpectable low number of lines: %s',
mb_strlen($mimeMessage),
(string)$mimeMessageLineCount
)
);
}
// add given headers, e.g. coming from HTTP headers
if (count($headers) > 0) {
self::setMultiPartHeaders($multiPart, $headers);
@ -56,6 +47,15 @@ class Parser
$hasHttpRequestHeaders = ParsedPartsGetter::HAS_NO_HTTP_REQUEST_HEADERS;
}
if (MimeBoundaryAnalyser::hasMessageBoundary($mimeMessageLines) === true) {
if ($mimeMessageLineCount <= 1) {
throw new Exception(
sprintf(
'Cannot parse MultiPart message of %d characters: got unexpectable low number of lines: %s',
mb_strlen($mimeMessage),
(string)$mimeMessageLineCount
)
);
}
$parsedPartList = ParsedPartsGetter::getPartsFromMimeMessageLines(
$multiPart,
$mimeMessageLines,

View File

@ -121,6 +121,7 @@ class SoapServerOptions
{
$optionsAsArray = [
'keep_alive' => $this->isKeepAlive(),
'exceptions' => $this->isExceptions(),
];
return $optionsAsArray;

View File

@ -0,0 +1,25 @@
<?php
namespace BeSimple\SoapCommon\Fault;
use PHPUnit_Framework_TestCase;
use SoapFault;
class SoapFaultParserTest extends PHPUnit_Framework_TestCase
{
public function testParse()
{
$soapFaultXml = '<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><SOAP-ENV:Fault><faultcode>911</faultcode><faultstring>This is a dummy SoapFault.</faultstring></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>';
$soapFault = SoapFaultParser::parseSoapFault($soapFaultXml);
self::assertInstanceOf(SoapFault::class, $soapFault);
self::assertEquals(
'911',
$soapFault->faultcode
);
self::assertEquals(
'This is a dummy SoapFault.',
$soapFault->getMessage()
);
}
}

View File

@ -0,0 +1,64 @@
<?php
namespace BeSimple\SoapCommon\Fault;
use PHPUnit_Framework_TestCase;
use SoapFault;
class SoapFaultSourceGetterTest extends PHPUnit_Framework_TestCase
{
const FIXTURES_DIR = __DIR__ . '/../../Fixtures';
/**
* @param SoapFault $soapFault
* @dataProvider provideNativeSoapFaults
*/
public function testWithNativeSoapFault(SoapFault $soapFault)
{
self::assertTrue(SoapFaultSourceGetter::isNativeSoapFault($soapFault));
self::assertFalse(SoapFaultSourceGetter::isBeSimpleSoapFault($soapFault));
}
/**
* @param SoapFault $soapFault
* @dataProvider provideBeSimpleSoapFaults
*/
public function testWithBeSimpleSoapFault(SoapFault $soapFault)
{
self::assertFalse(SoapFaultSourceGetter::isNativeSoapFault($soapFault));
self::assertTrue(SoapFaultSourceGetter::isBeSimpleSoapFault($soapFault));
}
public function provideNativeSoapFaults()
{
return [
[$this->getNativeSoapFaultFromClient()],
// @todo: add more test cases for Soap Server \SoapFault
];
}
public function provideBeSimpleSoapFaults()
{
return [
[new SoapFault(SoapFaultEnum::SOAP_FAULT_HTTP, 'HTTP Connection error')],
[new SoapFault(SoapFaultEnum::SOAP_FAULT_SOAP_CLIENT_ERROR, 'SOAP Client error')],
[new SoapFault(SoapFaultEnum::SOAP_FAULT_WSDL, 'WSDL error')],
];
}
/**
* @return SoapFault
*/
private function getNativeSoapFaultFromClient()
{
try {
$soapClient = @new \SoapClient('non-existing-wsdl-throwing-soapfault');
$soapClient->__call('no-function', []);
} catch (SoapFault $e) {
return $e;
}
self::fail('Cannot generate native PHP SoapFault from Client, please review the test');
}
}

View File

@ -0,0 +1,99 @@
<?php
namespace BeSimple\SoapCommon\Mime;
use Exception;
use PHPUnit_Framework_TestCase;
class ParserTest extends PHPUnit_Framework_TestCase
{
const TEST_CASE_SHOULD_FAIL = true;
const TEST_CASE_SHOULD_NOT_FAIL = false;
/**
* @dataProvider provideMimeMessages
* @param string $mimeMessage
* @param string[] $headers
* @param bool $testCaseShouldFail
* @param string|null $failedTestCaseFailMessage
*/
public function testParseMimeMessage(
$mimeMessage,
array $headers,
$testCaseShouldFail,
$failedTestCaseFailMessage = null
) {
if ($testCaseShouldFail === true) {
$this->setExpectedException(Exception::class, $failedTestCaseFailMessage);
}
$mimeMessage = Parser::parseMimeMessage($mimeMessage, $headers);
if ($testCaseShouldFail === false) {
self::assertInstanceOf(MultiPart::class, $mimeMessage);
self::assertInstanceOf(Part::class, $mimeMessage->getMainPart());
}
}
public function provideMimeMessages()
{
return [
'ParseRequest' => [
$this->getMimeMessageFromFile(__DIR__.'/../../../Fixtures/Message/Request/dummyServiceMethod.message.request'),
[],
self::TEST_CASE_SHOULD_NOT_FAIL
],
'ParseRequestOneLiner' => [
$this->getMimeMessageFromFile(__DIR__.'/../../../Fixtures/Message/Request/dummyServiceMethod.oneliner.message.request'),
[],
self::TEST_CASE_SHOULD_NOT_FAIL
],
'ParseSwaResponseWith2FilesAnd1BinaryFile' => [
$this->getMimeMessageFromFile(__DIR__.'/../../../Fixtures/Message/Response/dummyServiceMethodWithOutgoingLargeSwa.response.mimepart.message'),
[
'Content-Type' => 'multipart/related;'.
' type="application/soap+xml"; charset=utf-8;'.
' boundary=Part_13_58e3bc35f3743.58e3bc35f376f;'.
' start="<part-424dbe68-e2da-450f-9a82-cc3e82742503@response.info>"'
],
self::TEST_CASE_SHOULD_NOT_FAIL
],
'ParseSwaResponseWith2FilesAnd1BinaryFileShouldFailWithNoHeaders' => [
$this->getMimeMessageFromFile(__DIR__.'/../../../Fixtures/Message/Response/dummyServiceMethodWithOutgoingLargeSwa.response.mimepart.message'),
[],
self::TEST_CASE_SHOULD_FAIL,
'Unable to get Content-Type boundary'
],
'ParseSwaResponseWith2FilesAnd1BinaryFileShouldFailWithWrongHeaders' => [
$this->getMimeMessageFromFile(__DIR__.'/../../../Fixtures/Message/Response/dummyServiceMethodWithOutgoingLargeSwa.response.mimepart.message'),
[
'Content-Type' => 'multipart/related; type="application/soap+xml"; charset=utf-8; boundary=DOES_NOT_EXIST; start="<non-existing>"'
],
self::TEST_CASE_SHOULD_FAIL,
'cannot parse headers before hitting the first boundary'
],
'ParseSwaRequestWith2Files' => [
$this->getMimeMessageFromFile(__DIR__ . '/../../../Fixtures/Message/Request/dummyServiceMethodWithAttachments.request.mimepart.message'),
[
'Content-Type' => 'multipart/related; type="application/soap+xml"; charset=utf-8; boundary=----=_Part_6_2094841787.1482231370463; start="<rootpart@soapui.org>"'
],
self::TEST_CASE_SHOULD_NOT_FAIL
],
'ParseSwaRequestWith2FilesShouldFailWithNoHeaders' => [
$this->getMimeMessageFromFile(__DIR__ . '/../../../Fixtures/Message/Request/dummyServiceMethodWithAttachments.request.mimepart.message'),
[],
self::TEST_CASE_SHOULD_FAIL,
'Unable to get Content-Type boundary'
],
];
}
private function getMimeMessageFromFile($filePath)
{
if (file_exists($filePath) === false) {
self::fail('Please, update tests data provider - file not found: '.$filePath);
}
return file_get_contents($filePath);
}
}

View File

@ -17,6 +17,7 @@ use Fixtures\DummyServiceMethodWithIncomingLargeSwaRequest;
use Fixtures\DummyServiceMethodWithOutgoingLargeSwaRequest;
use Fixtures\GenerateTestRequest;
use PHPUnit_Framework_TestCase;
use SoapFault;
use SoapHeader;
class SoapServerAndSoapClientCommunicationTest extends PHPUnit_Framework_TestCase
@ -24,7 +25,6 @@ class SoapServerAndSoapClientCommunicationTest extends PHPUnit_Framework_TestCas
const CACHE_DIR = __DIR__ . '/../../cache';
const FIXTURES_DIR = __DIR__ . '/../Fixtures';
const TEST_HTTP_URL = 'http://localhost:8000/tests';
const TEST_LOCAL_WSDL_UK = SoapClientBuilderTest::TEST_LOCAL_WSDL_UK;
const LARGE_SWA_FILE = self::FIXTURES_DIR.'/large-test-file.docx';
private $localWebServerProcess;
@ -125,6 +125,44 @@ class SoapServerAndSoapClientCommunicationTest extends PHPUnit_Framework_TestCas
);
}
public function testSoapCallSwaWithLargeSwaResponseWithSoapFault()
{
$soapClient = $this->getSoapBuilder()->buildWithSoapHeader(
SoapClientOptionsBuilder::createWithEndpointLocation(
self::TEST_HTTP_URL.'/SwaSenderSoapFaultEndpoint.php'
),
SoapOptionsBuilder::createSwaWithClassMap(
self::TEST_HTTP_URL.'/SwaSenderEndpoint.php?wsdl',
new ClassMap([
'GenerateTestRequest' => GenerateTestRequest::class,
]),
SoapOptions::SOAP_CACHE_TYPE_NONE
),
new SoapHeader('http://schema.testcase', 'SoapHeader', [
'user' => 'admin',
])
);
$this->setExpectedException(SoapFault::class);
try {
$soapClient->soapCall('dummyServiceMethodWithOutgoingLargeSwa', []);
} catch (SoapFault $e) {
self::assertEquals(
'911',
$e->faultcode
);
self::assertEquals(
'SOAP HTTP call failed: Curl error "0" with message: occurred while connecting to http://localhost:8000/tests/SwaSenderSoapFaultEndpoint.php with HTTP response code 500 with Message: This is a dummy SoapFault. and Code: 911',
$e->getMessage()
);
throw $e;
}
self::fail('Expected SoapFault was not thrown');
}
public function testSoapCallWithLargeSwaRequest()
{
$soapClient = $this->getSoapBuilder()->buildWithSoapHeader(

View File

@ -0,0 +1 @@
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://schema.testcase"><soapenv:Header><sch:SoapHeader><user>admin</user></sch:SoapHeader></soapenv:Header><soapenv:Body><sch:dummyServiceMethod><request><dummyAttribute>1</dummyAttribute></request></sch:dummyServiceMethod></soapenv:Body></soapenv:Envelope>

View File

@ -0,0 +1,9 @@
<?php
const FIXTURES_DIR = __DIR__.'/Fixtures';
$soapServer = new \SoapServer(FIXTURES_DIR.'/DummyService.wsdl');
$soapServer->fault(
911,
'This is a dummy SoapFault.'
);