128 Commits
v0.4 ... 5.1.0

Author SHA1 Message Date
cc83e1e6db Add support for "overriden" routes 2019-08-06 11:59:02 +02:00
412e7f16c9 Reverted default definition style to RPC. 2019-08-06 11:58:51 +02:00
729a2bc8df Fix dumper in order to generate a valid WSDL 2019-08-06 11:58:35 +02:00
b8bf76aeb9 fixup! Update README, add back some instructions on how to use 2019-08-06 11:07:42 +02:00
a3d691261c Update README, add back some instructions on how to use 2019-08-06 11:04:18 +02:00
6cf62100a4 ext-mcrypt is not necessary and is not included in PHP anymore 2019-08-06 11:03:54 +02:00
2e9dc9a34d Fix issue that each method param overrides the initial input message. 2019-08-06 10:57:16 +02:00
95a91bb8bb Those tests don't have any value as depending on different PHP versions, the headers can slightly change 2019-08-06 10:48:46 +02:00
ec0004ea36 Revert "Fix error handling"
This reverts commit ef81da77ea.
2019-08-06 10:28:55 +02:00
00101ff7fb Restore package name after merging back changes of cocciagialla fork 2019-08-06 10:26:19 +02:00
ef81da77ea Fix error handling 2019-07-03 01:08:36 +02:00
54e9e0ed05 Remove SOAP-ENC 2019-06-27 13:40:00 +02:00
679b44ba13 Use encoded instead of literal 2019-06-27 13:08:26 +02:00
a7a49b793a Add minOccurs and maxOccurs into wsdl dumper 2019-06-26 19:22:09 +02:00
d1f5c1f674 Remove from response the nillable elements. Fix namespace declaration in the xml response 2019-06-26 19:03:56 +02:00
fea1093bed Remove from response the nillable elements. Fix namespace declaration in the xml response 2019-06-26 18:53:59 +02:00
9fc58e4388 Replace SOAP-ENV with soap as soap-envelope ns prefix 2019-06-24 19:05:43 +02:00
a7c610b767 Add hexBinary type 2019-04-15 18:20:57 +02:00
2b8f75b4ae Fix wsdl for array complex type 2019-04-11 10:33:17 +02:00
6718703ba5 Add headers binder for document 2019-04-10 07:31:37 +02:00
f9f47b913d Add base64Binary type 2019-04-10 07:30:54 +02:00
ca4991a9f6 Fix location url from relative to absolute 2019-04-10 07:30:25 +02:00
cdf77dacd8 Fix input naming 2019-04-10 07:29:27 +02:00
7802a0d19f Fix Dumper. Each complext type is now wrapped inside container element tag 2019-04-10 07:28:07 +02:00
c4118855de Fix document literal binder for request and response 2019-04-10 07:22:34 +02:00
2771991fc0 Set document instead of rpc as default binding style 2019-04-10 07:21:51 +02:00
ceb69d97c3 Fix binding name and soapAction attribute on wsdl 2019-04-10 07:20:49 +02:00
19326bccdd Add .idea to ignore 2019-04-10 07:20:07 +02:00
22bde386ab Fix composer json 2019-04-02 15:15:56 +02:00
2d36e5f060 Fix compatibility with SF4 + Other fixes 2019-04-02 14:58:41 +02:00
873dc0ae08 Replace DefinitionDecorator with ChildDefinition 2019-03-14 04:53:26 +01:00
0e24ffe23d Alias dev-master 2019-03-14 04:31:19 +01:00
d0f53bc806 pdate to SF4 2019-03-14 03:24:20 +01:00
0cb00e71b5 Change package name 2019-03-14 03:05:52 +01:00
cc888bf168 Deprecated method change 2019-02-13 10:02:20 +01:00
bb95a6cb45 Response xml format change support with backward compatibility 2019-02-11 12:28:54 +01:00
3f2a99adf6 Array support change
Support of mapping xml attributes
2019-02-11 12:28:54 +01:00
ad93b5ca49 PHP 7 compatibility 2019-02-11 11:55:54 +01:00
cdeab44c2c Like this, DATETIME_FORMAT could be overriden
Partial Fix BeSimple#68
2019-02-11 11:45:50 +01:00
e046be22eb Date semantics must support milliseconds for better interoperability
Fix BeSimple#68
2019-02-11 11:39:35 +01:00
b10b5431e5 Back to original name 2019-02-11 11:33:27 +01:00
408d88f164 Readme typo 2018-04-06 12:07:55 +02:00
0e4034a093 Merge tag '0.3.0' into develop
Compatibility Symfony 3
2018-04-06 12:02:37 +02:00
fd64676312 Merge branch 'release/0.3.0' 2018-04-06 12:02:15 +02:00
4d66d072ec Bump version 0.3.0 2018-04-06 12:01:48 +02:00
735f6a5826 README update 2018-04-06 11:58:10 +02:00
498012697d get path 2018-04-06 11:32:08 +02:00
b96b65cc49 ComtainerAware -> Interface 2018-04-06 11:29:08 +02:00
0748b23630 Symfony 2 fixes 2018-04-06 11:21:50 +02:00
63111261e7 Restart fomr 0.2 2018-04-06 11:18:36 +02:00
2b91f94b8d Ignore composer.phar 2018-04-06 11:06:18 +02:00
1af0828b15 Revert whole code to legacy 2018-04-06 10:58:45 +02:00
0ca2fe1fd5 getCurrentRequest 2018-04-06 10:53:16 +02:00
fd923e032e cache->getPath 2018-04-06 10:50:13 +02:00
e32514abe4 Add Classmap (common) 2018-04-06 10:43:58 +02:00
7322bfefd0 Add Classmap 2018-04-06 10:40:03 +02:00
e232b1d8b9 ContainerAware -> ContainerAwareInterface 2018-04-06 10:20:53 +02:00
1b87b4d212 Fix pattern->path 2018-04-06 10:16:21 +02:00
ae746087e6 Test With Legacy bundle 2018-04-06 10:12:08 +02:00
9312e4af6a Revert "Test"
This reverts commit c3da1d545a.
2018-04-06 10:09:28 +02:00
c3da1d545a Test 2018-04-06 09:58:43 +02:00
ad2bd7bc7a Rename fork 2018-04-04 11:52:49 +02:00
e58927469c Run mock server in each SoapClient test case & unit test fixed 2018-02-06 00:38:35 +01:00
45cce3a41c Integration test fixed 2018-02-06 00:31:06 +01:00
e6beef5a80 Merge pull request #14 from tmirks/enable-symfony-bundle
Get basic bundle configuration working for SoapClient with Symfony 3.x.
2018-02-05 23:53:11 +01:00
69a7005c35 Get basic bundle configuration working for SoapClient with Symfony 3.x. 2018-01-18 11:40:02 -05:00
073028f160 Merge pull request #10 from vhorky/feature/curl-ssl-version
Soap client option allows setting CURLOPT_SSLVERSION
2017-09-21 10:54:15 +02:00
VH
a9f11beb83 Soap client option allows setting CURLOPT_SSLVERSION 2017-09-21 10:45:27 +02:00
7ab8771989 Merge pull request #7 from tuscanicz/develop
Recognizing mime boundary according to RFC
2017-08-23 16:11:19 +02:00
d68c25daad Recognizing mime boundary according to RFC 2017-08-23 15:30:36 +02:00
6e117940a3 ResolveRemoteIncludes fix: RelativePathResolver was not used in XmlDomDocumentImportReplacer 2017-07-24 10:11:13 +02:00
0c47f5a8d4 RelativePathResolver did not work correctly for ../directories 2017-07-21 14:49:42 +02:00
75a0489cce Merge remote-tracking branch 'remotes/origin/develop' 2017-07-21 10:31:28 +02:00
de5d6a2647 SoapClientOptions now contains resolve remove includes option for WSDL downloader 2017-07-21 10:21:50 +02:00
6970b7bbef WsdlDownloader fix: remote includes now work correctly with relative URLs & tests added 2017-07-18 18:52:52 +02:00
b650254d54 SoapClient::trace = SoapClientOptions::SOAP_CLIENT_TRACE_OFF fixed when SoapFault is thrown
Incompatible changes: 1) Default SoapClientOptionsBuilder method now sets tracing to ON and 2) SoapResponse now contains request in all calls so that SoapRequestFactory interface had to be changed.
2017-06-16 13:42:08 +02:00
668f2dd258 PhpUnit - code coverage whitelisted 2017-06-13 13:06:58 +02:00
a8bc834077 Mime/PartHeaders now handle both Content-ID and Content-id according to W3 specs 2017-06-12 15:14:28 +02:00
f74e4b08ce Mime Parser: throws Exception with MimePartMessage contents 2017-06-12 00:28:27 +02:00
dcd5ff5234 Travis now run only PHP 7.x builds, phpstan requirements will fail on 5.6.x or previous 2017-06-07 17:10:10 +02:00
7bd8481a4e Travis CI - test fixes
1. Travis has to run PHP build-in server separately, 2. SoapFault message expectations are now Couldn't/Could not
2017-06-07 17:05:03 +02:00
c82288d641 Travis deployment fixed 2017-06-07 16:23:47 +02:00
bb20882ade Readme updated 2017-06-07 16:18:46 +02:00
2264e329a1 Added phing for running tests & fixed issues in order to pass the tests 2017-06-07 15:50:04 +02:00
b9e36b4900 Fix notice: attribute soapClientOptions defined in Trait and Client class 2017-06-05 10:50:53 +02:00
d495f22413 SoapFaultWithTracingData now provides request / response information from Server SoapFaults 2017-05-30 18:29:51 +02:00
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
564005da93 SoapServer/Client now handle binary files correctly & large tests/fixtures update
Soap Server and Client were breaking binary files during transfer due to invalid Mime Message Parser. Now is it working fine with no errors, but the message parser is about to be rewritten into a better form.
2017-04-04 18:36:18 +02:00
311f9e6d08 Readme update: better examples added 2017-03-15 11:23:21 +01:00
e67d5aa84b Readme updated 2017-03-15 10:48:23 +01:00
d3023b1a5a UnitTests are now located in tests directory & tiny improvements 2017-03-15 10:25:48 +01:00
21d705bbfa SoapClient custom endpoint location & connection keep alive configuration added & tests updated 2017-03-03 11:11:44 +01:00
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
30a9707c59 Merge remote-tracking branch 'remotes/origin/develop' 2017-02-14 16:48:58 +01:00
aee034791e SoapClient large refactoring & tests update 2017-02-14 16:01:39 +01:00
00ddf149b0 Passing SOAP fault with detail 2016-11-29 18:55:55 +01:00
476813e9bb MIME parts now contain only LF (was CRLF) 2016-11-29 18:55:55 +01:00
f57239ad0d MultiPart Boundary generator changed 2016-11-24 15:18:00 +01:00
10caf27da3 MultiPart ContentId is now in different format 2016-11-24 15:00:46 +01:00
51d1abab48 Connection: Keep-alive is now configurable - BUT NOW WORKING
PHP is unable to change Connection type, this configuration is quite missleading for SoapServer, but still working for SoapClient
2016-11-24 12:47:01 +01:00
1224f5f40f Content-Location added for attachment parts 2016-11-24 12:45:17 +01:00
f4a4619fe6 Handling SoapRequests/SoapResponse with attachments fix 2016-11-24 11:04:57 +01:00
052ab20d67 tiny bugfixes 2016-11-11 16:04:44 +01:00
dd7b6904b6 SoapOptions moved 2016-11-09 17:05:43 +01:00
c4d993585f SoapServer now handles get WSDL requests 2016-11-09 13:43:18 +01:00
bd1fbf9cfc SoapServer::handle() has to be compatible with \SoapServer 2016-11-09 12:08:32 +01:00
5fbcfb3e22 AttachmentsHandlerInterface refactored 2016-11-08 18:31:28 +01:00
84c37b1d24 Soap server with attachments refactoring 2016-11-08 15:42:52 +01:00
8d033f9afc SoapResponse is a product of SoapResponseFactory, small refactorings 2016-11-02 16:08:21 +01:00
bf494a42b5 MimeFilters are now stateless 2016-11-02 09:55:12 +01:00
969709cae5 Large refactoring of SoapKernel 2016-11-01 18:13:23 +01:00
155aa029ce SoapRequest is now SoapRequestFactory product 2016-11-01 16:23:21 +01:00
3c0f731086 SoapOptions cache dir introduced + SoapOptionsBuilder - new methods added 2016-11-01 09:24:41 +01:00
374c64538a remove useless SoapClientBuilder 2016-10-31 14:12:35 +01:00
72 changed files with 1405 additions and 1130 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
/vendor/
composer.lock
composer.phar
phpunit.xml
.idea

View File

@ -1,16 +1,16 @@
language: php
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- 7.1
env:
- SYMFONY_VERSION=2.6.*
- SYMFONY_VERSION="dev-master symfony/debug:~2.7@dev symfony/http-kernel:~2.7@dev"
- SYMFONY_VERSION=2.8.*
- SYMFONY_VERSION="dev-master symfony/debug:~2.8@dev symfony/http-kernel:~2.8@dev"
before_script:
- phpenv config-add myphp.ini
- composer self-update
- composer require symfony/framework-bundle:${SYMFONY_VERSION} --no-update
- composer update --no-interaction --prefer-source
@ -22,4 +22,4 @@ script:
matrix:
allow_failures:
- env: SYMFONY_VERSION="dev-master symfony/debug:~2.7@dev symfony/http-kernel:~2.7@dev"
- env: SYMFONY_VERSION="dev-master symfony/debug:~2.8@dev symfony/http-kernel:~2.8@dev"

154
README.md
View File

@ -1,56 +1,146 @@
# BeSimpleSoap
# BeSimpleSoap (Symfony 3.4 / 4.x)
Build SOAP and WSDL based web services
This fork provides the BeSimpleSoap bundle, updated to be compatible with Symfony 3.4 and 4.x (as well as with PHP 7.0-7.4).
We forked the official [BeSimpleSoap](https://github.com/BeSimple/BeSimpleSoap) repository in order to sucessfully maintain some of our projects.
We now have integrated changes and fixes from sub-forks (thank you guys!), and we should be up to date now :)
This fork is maintained by people from [Cadoles](https://www.cadoles.com/).
# Contributing
We do welcome pull requests :) please include tests if you can.
Running tests can be done by running `php vendor/bin/phpunit`.
# Installation
If you do not yet have composer, follow instructions on the [Composer website](https://getcomposer.org/download/) to install it.
Then just running:
```
$ composer require cadoles/soap
```
should be enough to get you up and running.
# Components
BeSimpleSoap consists of five components ...
## BeSimpleSoapBundle
The BeSimpleSoapBundle is a Symfony2 bundle to build WSDL and SOAP based web services.
For further information see the [README](https://github.com/BeSimple/BeSimpleSoap/blob/master/src/BeSimple/SoapBundle/README.md).
## BeSimpleSoapClient
The BeSimpleSoapClient is a component that extends the native PHP SoapClient with further features like SwA, MTOM and WS-Security.
For further information see the [README](https://github.com/BeSimple/BeSimpleSoap/blob/master/src/BeSimple/SoapClient/README.md).
## BeSimpleSoapCommon
The BeSimpleSoapCommon component contains functionylity shared by both the server and client implementations.
For further information see the [README](https://github.com/BeSimple/BeSimpleSoap/blob/master/src/BeSimple/SoapCommon/README.md).
**Refactored** BeSimpleSoapClient is a component that extends the native PHP SoapClient with further features like SwA and WS-Security.
## BeSimpleSoapServer
The BeSimpleSoapServer is a component that extends the native PHP SoapServer with further features like SwA, MTOM and WS-Security.
For further information see the [README](https://github.com/BeSimple/BeSimpleSoap/blob/master/src/BeSimple/SoapServer/README.md).
**Refactored** BeSimpleSoapServer is a component that extends the native PHP SoapServer with further features like SwA and WS-Security.
## BeSimpleSoapCommon
**Refactored** BeSimpleSoapCommon component contains functionality shared by both the server and client implementations.
## BeSimpleSoapWsdl
For further information see the [README](https://github.com/BeSimple/BeSimpleSoap/blob/master/src/BeSimple/SoapWsdl/README.md).
**Untouched!**
The component is not affected by refactoring so it should work properly.
For further information see the original [README](https://github.com/BeSimple/BeSimpleSoap/blob/master/src/BeSimple/SoapWsdl/README.md).
# Installation
## BeSimpleSoapBundle
If you do not yet have composer, install it like this:
**Unsupported!**
The BeSimpleSoapBundle is a Symfony2 bundle to build WSDL and SOAP based web services.
For further information see the the original [README](https://github.com/BeSimple/BeSimpleSoap/blob/master/src/BeSimple/SoapBundle/README.md).
*Will not work since the Symfony libraries were removed and usages of other components were not refactored. Feel free to fork this repository and fix it!*
```sh
curl -s http://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin
# How to use
You can investigate the unit tests dir ``tests`` in order to get a clue.
Forget about associative arrays, vague configurations, multiple extension and silent errors!
This may look a bit more complex at the first sight,
but it will guide you to configure and set up your client or server properly.
## Example of soap client call
```php
$soapClientBuilder = new SoapClientBuilder();
$soapClient = $soapClientBuilder->build(
SoapClientOptionsBuilder::createWithDefaults(),
SoapOptionsBuilder::createWithDefaults('http://path/to/wsdlfile.wsdl')
);
$myRequest = new MyRequest();
$myRequest->attribute = 'string value';
$soapResponse = $soapClient->soapCall('myMethod', [$myRequest]);
var_dump($soapResponse); // Contains Response, Attachments
```
Create a `composer.json` file:
### Something wrong?!
```json
Turn on the tracking and catch `SoapFaultWithTracingData` exception to get some sweets :)
```php
try {
$soapResponse = $soapClient->soapCall('myMethod', [$myRequest]);
} catch (SoapFaultWithTracingData $fault) {
var_dump($fault->getSoapResponseTracingData()->getLastRequest());
}
```
In this example, a ``MyRequest`` object has been used to describe request.
Using a ClassMap, you help SoapClient to turn it into XML request.
## Example of soap server handling
Starting a SOAP server is a bit more complex.
I recommend you to inspect SoapServer unit tests for inspiration.
```php
$dummyService = new DummyService();
$classMap = new ClassMap();
foreach ($dummyService->getClassMap() as $type => $className) {
$classMap->add($type, $className);
}
$soapServerBuilder = new SoapServerBuilder();
$soapServerOptions = SoapServerOptionsBuilder::createWithDefaults($dummyService);
$soapOptions = SoapOptionsBuilder::createWithClassMap($dummyService->getWsdlPath(), $classMap);
$soapServer = $soapServerBuilder->build($soapServerOptions, $soapOptions);
$request = $soapServer->createRequest(
$dummyService->getEndpoint(),
'DummyService.dummyServiceMethod',
'text/xml;charset=UTF-8',
'<received><soap><request><here /></request></soap></received>'
);
$response = $soapServer->handleRequest($request);
var_dump($response); // Contains Response, Attachments
```
In this example, a ``DummyService`` service has been used to handle request.
Using a service can help you create coherent SoapServer endpoints.
Service can hold an endpoint URL, WSDL path and a class map as associative array.
You can hold a class map as ``ClassMap`` object directly in the ``DummyService`` instead of array.
In the service you should describe SOAP methods from given WSDL.
In the example, the dummyServiceMethod is called.
The method will receive request object and return response object that are matched according to the class map.
See a simplified implementation of ``dummyServiceMethod`` to get a clue:
```php
/**
* @param DummyServiceRequest $dummyServiceRequest
* @return DummyServiceResponse
*/
public function dummyServiceMethod(DummyServiceRequest $dummyServiceRequest)
{
"require": {
"besimple/soap": "0.2.*@dev"
}
$response = new DummyServiceResponse();
$response->status = true;
return $response;
}
```
Now you are ready to install the library:
```sh
php /usr/local/bin/composer.phar install
```
For further information and getting inspiration for your implementation, see the unit tests in ``tests`` dir.

View File

@ -1,8 +1,9 @@
{
"name": "tuscanicz/soap",
"name": "cadoles/soap",
"type": "library",
"description": "A fork of the abandoned besimple/soap package with added support for Symfony 3.0",
"keywords": ["soap", "soap server", "soap client"],
"description": "Build and consume SOAP and WSDL based web services",
"keywords": ["soap"],
"homepage": "https://github.com/Cadoles/BeSimpleSoap",
"license": "MIT",
"authors": [
{
@ -16,19 +17,15 @@
{
"name": "Andreas Schamberger",
"email": "mail@andreass.net"
},
{
"name": "Petr Bechyně",
"email": "petr.bechyne@vodafone.com"
}
],
"require": {
"php": ">=5.3.0",
"php": ">=7.0",
"ext-soap": "*",
"ext-curl": "*",
"ass/xmlsecurity": "~1.0",
"symfony/framework-bundle": "~2.6|~3.0",
"symfony/twig-bundle": "~2.6|~3.0",
"symfony/framework-bundle": "~3.4|~4.0",
"symfony/twig-bundle": "~3.4|~4.0",
"zendframework/zend-mime": "2.1.*"
},
"replace": {
@ -36,20 +33,21 @@
"besimple/soap-client": "self.version",
"besimple/soap-common": "self.version",
"besimple/soap-server": "self.version",
"besimple/soap-wsdl": "self.version"
"besimple/soap-wsdl": "self.version",
"cocciagialla/soap": "self.version"
},
"require-dev": {
"ext-mcrypt": "*",
"mikey179/vfsStream": "~1.0",
"mikey179/vfsstream": "~1.6.5",
"symfony/filesystem": "~2.3",
"symfony/process": "~2.3"
"symfony/process": "~2.3",
"phpunit/phpunit": "^5.7"
},
"autoload": {
"psr-0": { "BeSimple\\": "src/" }
},
"extra": {
"branch-alias": {
"dev-master": "0.4-dev"
"dev-master": "master-dev"
}
}
}

View File

@ -1,3 +1,4 @@
vendor/
composer.lock
phpunit.xml
.idea/

View File

@ -15,15 +15,18 @@ namespace BeSimple\SoapBundle\Controller;
use BeSimple\SoapBundle\Handler\ExceptionHandler;
use BeSimple\SoapBundle\Soap\SoapRequest;
use BeSimple\SoapBundle\Soap\SoapResponse;
use BeSimple\SoapBundle\WebServiceContext;
use BeSimple\SoapServer\SoapServerBuilder;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* @author Christian Kerl <christian-kerl@web.de>
@ -63,6 +66,7 @@ class SoapWebServiceController implements ContainerAwareInterface
*/
public function callAction($webservice)
{
/** @var WebServiceContext $webServiceContext */
$webServiceContext = $this->getWebServiceContext($webservice);
$this->serviceBinder = $webServiceContext->getServiceBinder();
@ -86,19 +90,26 @@ class SoapWebServiceController implements ContainerAwareInterface
}
/**
* @return Symfony\Component\HttpFoundation\Response
* @return Response
*/
public function definitionAction($webservice)
{
$routeName = $webservice . '_webservice_call';
$result = $this->container->get('router')->getRouteCollection()->get($routeName);
if ($result === null) {
$routeName = '_webservice_call';
}
$response = new Response($this->getWebServiceContext($webservice)->getWsdlFileContent(
$this->container->get('router')->generate(
'_webservice_call',
$routeName,
array('webservice' => $webservice),
true
UrlGeneratorInterface::ABSOLUTE_URL
)
));
$request = $this->container->get('request');
/** @var Request $request */
$request = $this->container->get('request_stack')->getCurrentRequest();
$query = $request->query;
if ($query->has('wsdl') || $query->has('WSDL')) {
$request->setRequestFormat('wsdl');
@ -124,9 +135,9 @@ class SoapWebServiceController implements ContainerAwareInterface
throw new \LogicException(sprintf('The parameter "%s" is required in Request::$query parameter bag to generate the SoapFault.', '_besimple_soap_webservice'), null, $e);
}
$view = 'TwigBundle:Exception:'.($this->container->get('kernel')->isDebug() ? 'exception' : 'error').'.txt.twig';
$view = '@Twig/Exception/'.($this->container->get('kernel')->isDebug() ? 'exception' : 'error').'.txt.twig';
$code = $exception->getStatusCode();
$details = $this->container->get('templating')->render($view, array(
$details = $this->container->get('twig')->render($view, array(
'status_code' => $code,
'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '',
'exception' => $exception,
@ -141,8 +152,8 @@ class SoapWebServiceController implements ContainerAwareInterface
$request->query->remove('_besimple_soap_fault');
}
$server = SoapServerBuilder::createWithDefaults(__DIR__.'/../Handler/wsdl/exception.wsdl')
->withWsdl()
$server = SoapServerBuilder::createWithDefaults()
->withWsdl(__DIR__.'/../Handler/wsdl/exception.wsdl')
->withWsdlCacheNone()
->withHandler($handler)
->build()
@ -240,7 +251,7 @@ class SoapWebServiceController implements ContainerAwareInterface
return $this->soapResponse = $response;
}
private function getWebServiceContext($webservice)
protected function getWebServiceContext($webservice)
{
$context = sprintf('besimple.soap.context.%s', $webservice);

View File

@ -12,7 +12,7 @@ namespace BeSimple\SoapBundle\Converter;
use BeSimple\SoapBundle\Soap\SoapRequest;
use BeSimple\SoapBundle\Soap\SoapResponse;
use BeSimple\SoapBundle\Util\String;
use BeSimple\SoapBundle\Util\BsString;
use BeSimple\SoapCommon\Converter\TypeConverterInterface;
/**
@ -40,7 +40,7 @@ class XopIncludeTypeConverter implements TypeConverterInterface
$ref = $include->getAttribute('href');
if (String::startsWith($ref, 'cid:')) {
if (BsString::startsWith($ref, 'cid:')) {
$cid = urldecode(substr($ref, 4));
return $request->getSoapAttachments()->get($cid)->getContent();

View File

@ -17,10 +17,11 @@ use BeSimple\SoapCommon\Cache;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\HttpKernel\Kernel;
/**
* BeSimpleSoapExtension.
@ -80,10 +81,14 @@ class BeSimpleSoapExtension extends Extension
private function registerClientConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
{
if (3 === Kernel::MAJOR_VERSION) {
$loader->load('client3.xml');
} else {
$loader->load('client.xml');
}
foreach ($config as $client => $options) {
$definition = new DefinitionDecorator('besimple.soap.client.builder');
$definition = new ChildDefinition('besimple.soap.client.builder');
$container->setDefinition(sprintf('besimple.soap.client.builder.%s', $client), $definition);
$definition->replaceArgument(0, $options['wsdl']);
@ -130,7 +135,7 @@ class BeSimpleSoapExtension extends Extension
private function createClientClassmap($client, array $classmap, ContainerBuilder $container)
{
$definition = new DefinitionDecorator('besimple.soap.classmap');
$definition = new ChildDefinition('besimple.soap.classmap');
$container->setDefinition(sprintf('besimple.soap.classmap.%s', $client), $definition);
if (!empty($classmap)) {
@ -144,13 +149,17 @@ class BeSimpleSoapExtension extends Extension
private function createClient($client, ContainerBuilder $container)
{
$definition = new DefinitionDecorator('besimple.soap.client');
$definition = new ChildDefinition('besimple.soap.client');
$container->setDefinition(sprintf('besimple.soap.client.%s', $client), $definition);
if (3 === Kernel::MAJOR_VERSION) {
$definition->setFactory(array(
new Reference(sprintf('besimple.soap.client.builder.%s', $client)),
'build'
));
} else {
$definition->setFactoryService(sprintf('besimple.soap.client.builder.%s', $client));
}
}
private function createWebServiceContext(array $config, ContainerBuilder $container)
@ -159,7 +168,7 @@ class BeSimpleSoapExtension extends Extension
unset($config['binding']);
$contextId = 'besimple.soap.context.'.$config['name'];
$definition = new DefinitionDecorator('besimple.soap.context.'.$bindingSuffix);
$definition = new ChildDefinition('besimple.soap.context.'.$bindingSuffix);
$container->setDefinition($contextId, $definition);
if (isset($config['cache_type'])) {
@ -168,6 +177,7 @@ class BeSimpleSoapExtension extends Extension
$options = $container
->getDefinition('besimple.soap.context.'.$bindingSuffix)
->setPublic(true)
->getArgument(2);
$definition->replaceArgument(2, array_merge($options, $config));

View File

@ -19,9 +19,7 @@
<argument type="service" id="besimple.soap.cache" /> <!-- hack to load besimple cache configuration -->
</service>
<service id="besimple.soap.client" class="%besimple.soap.client.builder.class%" abstract="true">
<factory class="besimple.soap.client.builder" method="build" />
</service>
<service id="besimple.soap.client" factory-service="besimple.soap.client.builder" factory-method="build" class="%besimple.soap.client.builder.class%" abstract="true" />
<service id="besimple.soap.classmap" class="%besimple.soap.classmap.class%" abstract="true" />
</services>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="besimple.soap.client.builder.class">BeSimple\SoapBundle\Soap\SoapClientBuilder</parameter>
<parameter key="besimple.soap.classmap.class">BeSimple\SoapCommon\Classmap</parameter>
</parameters>
<services>
<service id="besimple.soap.client.builder" class="%besimple.soap.client.builder.class%" abstract="true">
<argument /> <!-- wsdl URI -->
<argument type="collection">
<argument key="debug">%kernel.debug%</argument>
</argument>
<argument type="service" id="besimple.soap.classmap" />
<argument type="service" id="besimple.soap.converter.collection" />
<argument type="service" id="besimple.soap.cache" /> <!-- hack to load besimple cache configuration -->
</service>
<service id="besimple.soap.client" class="%besimple.soap.client.builder.class%" abstract="true">
<factory service="besimple.soap.client.builder" method="build" />
</service>
<service id="besimple.soap.classmap" class="%besimple.soap.classmap.class%" abstract="true" />
</services>
</container>

View File

@ -4,15 +4,13 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="_webservice_call" path="/{webservice}">
<route id="_webservice_call" path="/{webservice}" methods="POST">
<default key="_controller">BeSimpleSoapBundle:SoapWebService:Call</default>
<default key="_format">xml</default>
<requirement key="_method">POST</requirement>
</route>
<route id="_webservice_definition" path="/{webservice}">
<route id="_webservice_definition" path="/{webservice}" methods="GET">
<default key="_controller">BeSimpleSoapBundle:SoapWebService:Definition</default>
<default key="_format">xml</default>
<requirement key="_method">GET</requirement>
</route>
</routes>

View File

@ -19,7 +19,7 @@
</parameters>
<services>
<service id="besimple.soap.response" class="%besimple.soap.response.class%" />
<service id="besimple.soap.response" class="%besimple.soap.response.class%" public="true" />
<service id="besimple.soap.response.listener" class="%besimple.soap.response.listener.class%">
<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" />
@ -96,6 +96,14 @@
<argument>dateTime</argument>
<argument>xsd:dateTime</argument>
</call>
<call method="addType">
<argument>base64Binary</argument>
<argument>xsd:base64Binary</argument>
</call>
<call method="addType">
<argument>hexBinary</argument>
<argument>xsd:hexBinary</argument>
</call>
</service>
</services>

View File

@ -0,0 +1,33 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (c) Christian Kerl <christian-kerl@web.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Method;
use BeSimple\SoapCommon\Definition\Type\TypeRepository;
/**
* @author Francis Besset <francis.besset@gmail.com>
*/
class DocumentLiteralWrappedRequestHeaderMessageBinder extends DocumentLiteralWrappedRequestMessageBinder
{
private $header;
public function setHeader($header)
{
$this->header = $header;
}
public function processMessage(Method $messageDefinition, $message, TypeRepository $typeRepository)
{
$headerDefinition = $messageDefinition->getHeaders()->get($this->header);
return [];
}
}

View File

@ -11,13 +11,14 @@
namespace BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Method;
use BeSimple\SoapCommon\Definition\Type\TypeRepository;
/**
* @author Christian Kerl <christian-kerl@web.de>
*/
class DocumentLiteralWrappedRequestMessageBinder implements MessageBinderInterface
{
public function processMessage(Method $messageDefinition, $message)
public function processMessage(Method $messageDefinition, $message, TypeRepository $typeRepository)
{
if(count($message) > 1) {
throw new \InvalidArgumentException();
@ -26,8 +27,8 @@ class DocumentLiteralWrappedRequestMessageBinder implements MessageBinderInterfa
$result = array();
$message = $message[0];
foreach($messageDefinition->getArguments() as $argument) {
$result[$argument->getName()] = $message->{$argument->getName()};
foreach($messageDefinition->getInput()->all() as $argument) {
$result[$argument->getName()] = $message;
}
return $result;

View File

@ -11,17 +11,15 @@
namespace BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Method;
use BeSimple\SoapCommon\Definition\Type\TypeRepository;
/**
* @author Christian Kerl <christian-kerl@web.de>
*/
class DocumentLiteralWrappedResponseMessageBinder implements MessageBinderInterface
{
public function processMessage(Method $messageDefinition, $message)
public function processMessage(Method $messageDefinition, $message, TypeRepository $typeRepository)
{
$result = new \stdClass();
$result->{$messageDefinition->getName().'Result'} = $message;
return $result;
return $message;
}
}

View File

@ -55,7 +55,8 @@ class RpcLiteralRequestMessageBinder implements MessageBinderInterface
$isArray = true;
$array = array();
$type = $this->typeRepository->getType($type->get('item')->getType());
$phpType = substr($type->getPhpType(), 0, strlen($type->getPhpType()) - 2);
$type = $this->typeRepository->getType($phpType);
}
// @TODO Fix array reference
@ -78,6 +79,21 @@ class RpcLiteralRequestMessageBinder implements MessageBinderInterface
$array = $assocArray;
}
}
if (is_array($message)) {
foreach ($message as $complexType) {
$array[] = $this->checkComplexType($phpType, $complexType);
}
// See https://github.com/BeSimple/BeSimpleSoapBundle/issues/29
if (in_array('BeSimple\SoapCommon\Type\AbstractKeyValue', class_parents($phpType))) {
$assocArray = array();
foreach ($array as $keyValue) {
$assocArray[$keyValue->getKey()] = $keyValue->getValue();
}
$array = $assocArray;
}
}
$message = $array;
} else {

View File

@ -32,7 +32,10 @@ class RpcLiteralResponseMessageBinder implements MessageBinderInterface
{
$this->typeRepository = $typeRepository;
return $this->processType($messageDefinition->getOutput()->get('return')->getType(), $message);
$parts = $messageDefinition->getOutput()->all();
$part = array_shift($parts);
return $this->processType($part->getType(), $message);
}
private function processType($phpType, $message)

View File

@ -18,6 +18,7 @@ class ComplexType extends Configuration
private $name;
private $value;
private $isNillable = false;
private $isAttribute = false;
public function getName()
{
@ -34,6 +35,11 @@ class ComplexType extends Configuration
return $this->isNillable;
}
public function getIsNillable()
{
return $this->isNillable;
}
public function setName($name)
{
$this->name = $name;
@ -49,6 +55,31 @@ class ComplexType extends Configuration
$this->isNillable = (bool) $isNillable;
}
public function setIsNillable($isNillable)
{
$this->isNillable = (bool) $isNillable;
}
/**
* @return bool
*/
public function isAttribute()
{
return $this->isAttribute;
}
/**
* @param bool $isAttribute
*
* @return $this
*/
public function setIsAttribute($isAttribute)
{
$this->isAttribute = $isAttribute;
return $this;
}
public function getAliasName()
{
return 'complextype';

View File

@ -20,6 +20,7 @@ class ComplexType
private $name;
private $value;
private $isNillable = false;
private $isAttribute = false;
public function getName()
{
@ -46,6 +47,26 @@ class ComplexType
$this->value = $value;
}
/**
* @return bool
*/
public function isAttribute()
{
return $this->isAttribute;
}
/**
* @param bool $isAttribute
*
* @return $this
*/
public function setIsAttribute($isAttribute)
{
$this->isAttribute = $isAttribute;
return $this;
}
public function setNillable($isNillable)
{
$this->isNillable = (bool) $isNillable;

View File

@ -96,6 +96,7 @@ class AnnotationClassLoader extends Loader
}
$serviceReturn = $annotation->getPhpType();
$serviceXmlReturn = $annotation->getXmlType();
}
}
@ -116,7 +117,11 @@ class AnnotationClassLoader extends Loader
throw new \LogicException(sprintf('@Soap\Result non-existent for "%s".', $method->getName()));
}
$serviceMethod->setOutput($this->loadType($serviceReturn));
if (!isset($serviceXmlReturn) || !$serviceXmlReturn) {
$serviceXmlReturn = 'return';
}
$serviceMethod->setOutput($this->loadType($serviceReturn), $serviceXmlReturn);
$definition->addMethod($serviceMethod);
}
@ -155,7 +160,7 @@ class AnnotationClassLoader extends Loader
$loaded = $complexTypeResolver->load($phpType);
$complexType = new ComplexType($phpType, isset($loaded['alias']) ? $loaded['alias'] : $phpType);
foreach ($loaded['properties'] as $name => $property) {
$complexType->add($name, $this->loadType($property->getValue()), $property->isNillable());
$complexType->add($name, $this->loadType($property->getValue()), $property->isNillable(), $property->isAttribute());
}
$this->typeRepository->addComplexType($complexType);

View File

@ -58,6 +58,7 @@ class AnnotationComplexTypeLoader extends AnnotationClassLoader
$propertyComplexType = new ComplexType();
$propertyComplexType->setValue($complexType->getValue());
$propertyComplexType->setNillable($complexType->isNillable());
$propertyComplexType->setIsAttribute($complexType->isAttribute());
$propertyComplexType->setName($property->getName());
$annotations['properties']->add($propertyComplexType);
}

View File

@ -15,7 +15,7 @@ namespace BeSimple\SoapBundle\Util;
*
* @author Christian Kerl <christian-kerl@web.de>
*/
class String
class BsString
{
/**
* Checks if a string starts with a given string.

View File

@ -79,7 +79,10 @@ class WebServiceContext
}
$dumper = new Dumper($definition, array('stylesheet' => $this->options['wsdl_stylesheet']));
$cache->write($dumper->dump());
$wsdl = $dumper->dump();
$cache->write($wsdl);
}
return $cache->getPath();

View File

@ -22,16 +22,16 @@
"require": {
"php": ">=5.3.0",
"ext-soap": "*",
"besimple/soap-common": "0.3.*",
"besimple/soap-wsdl": "0.3.*",
"besimple/soap-common": "0.2.*",
"besimple/soap-wsdl": "0.2.*",
"ass/xmlsecurity": "~1.0",
"symfony/framework-bundle": "~2.6",
"symfony/twig-bundle": "~2.6",
"symfony/framework-bundle": "~2.0|~3.0",
"symfony/twig-bundle": "~2.0|~3.0",
"zendframework/zend-mime": "2.1.*"
},
"suggest": {
"besimple/soap-client": "0.3.*",
"besimple/soap-server": "0.3.*"
"besimple/soap-client": "0.2.*",
"besimple/soap-server": "0.2.*"
},
"autoload": {
"psr-0": { "BeSimple\\SoapBundle": "" }
@ -39,7 +39,7 @@
"target-dir": "BeSimple/SoapBundle",
"extra": {
"branch-alias": {
"dev-master": "0.3-dev"
"dev-master": "0.2-dev"
}
}
}

View File

@ -50,7 +50,6 @@ class Curl
/**
* Constructor.
*
* @todo: do not use options as Array
* @param array $options Options array from SoapClient constructor
* @param int $followLocationMaxRedirects Redirection limit for Location header
*/

View File

@ -15,8 +15,7 @@ namespace BeSimple\SoapClient;
use BeSimple\SoapCommon\Helper;
use BeSimple\SoapCommon\Converter\MtomTypeConverter;
use BeSimple\SoapCommon\Converter\SwaTypeConverter;
use BeSimple\SoapCommon\SoapOptions\SoapOptions;
use BeSimple\SoapCommon\SoapRequest;
use BeSimple\SoapCommon\SoapMessage;
/**
* Extended SoapClient that uses a a cURL wrapper for all underlying HTTP
@ -28,7 +27,12 @@ use BeSimple\SoapCommon\SoapRequest;
*/
class SoapClient extends \SoapClient
{
protected $soapVersion;
/**
* Soap version.
*
* @var int
*/
protected $soapVersion = SOAP_1_1;
/**
* Tracing enabled?
@ -82,24 +86,37 @@ class SoapClient extends \SoapClient
/**
* Constructor.
*
* @param SoapClientOptions $soapClientOptions
* @param SoapOptions $soapOptions
* @param string $wsdl WSDL file
* @param array(string=>mixed) $options Options array
*/
public function __construct(SoapClientOptions $soapClientOptions, SoapOptions $soapOptions)
public function __construct($wsdl, array $options = array())
{
// tracing enabled: store last request/response header and body
if (isset($options['trace']) && $options['trace'] === true) {
$this->tracingEnabled = true;
}
// store SOAP version
if (isset($options['soap_version'])) {
$this->soapVersion = $options['soap_version'];
}
$this->curl = new Curl($options);
if (isset($options['extra_options'])) {
unset($options['extra_options']);
}
$wsdlFile = $this->loadWsdl($wsdl, $options);
// TODO $wsdlHandler = new WsdlHandler($wsdlFile, $this->soapVersion);
$this->soapKernel = new SoapKernel();
$this->soapVersion = $soapOptions->getSoapVersion();
$this->tracingEnabled = $soapClientOptions->getTrace();
// @todo: refactor SoapClient: do not use $options as array
$options = $this->configureMime($soapOptions->toArray());
// @todo: refactor SoapClient: do not use $options as array
$this->curl = new Curl($soapClientOptions->toArray());
// @todo: refactor SoapClient: do not use $options as array
$wsdlFile = $this->loadWsdl($soapOptions->getWsdlFile(), $soapOptions->toArray());
// set up type converter and mime filter
$this->configureMime($options);
// we want the exceptions option to be set
$options['exceptions'] = true;
// disable obsolete trace option for native SoapClient as we need to do our own tracing anyways
$options['trace'] = false;
// disable WSDL caching as we handle WSDL caching for remote URLs ourself
$options['cache_wsdl'] = WSDL_CACHE_NONE;
parent::__construct($wsdlFile, $options);
}
@ -174,7 +191,6 @@ class SoapClient extends \SoapClient
* Custom request method to be able to modify the SOAP messages.
* $oneWay parameter is not used at the moment.
*
* @todo: refactor SoapClient: refactoring starts from here
* @param string $request Request string
* @param string $location Location
* @param string $action SOAP action
@ -292,9 +308,16 @@ class SoapClient extends \SoapClient
return $this->soapKernel;
}
private function configureMime(array $options)
/**
* Configure filter and type converter for SwA/MTOM.
*
* @param array &$options SOAP constructor options array.
*
* @return void
*/
private function configureMime(array &$options)
{
if (Helper::ATTACHMENTS_TYPE_BASE64 !== $options['attachment_type']) {
if (isset($options['attachment_type']) && Helper::ATTACHMENTS_TYPE_BASE64 !== $options['attachment_type']) {
// register mime filter in SoapKernel
$mimeFilter = new MimeFilter($options['attachment_type']);
$this->soapKernel->registerFilter($mimeFilter);
@ -323,8 +346,6 @@ class SoapClient extends \SoapClient
},
);
}
return $options;
}
/**

View File

@ -12,22 +12,237 @@
namespace BeSimple\SoapClient;
use BeSimple\SoapCommon\SoapOptions\SoapOptions;
use BeSimple\SoapCommon\AbstractSoapBuilder;
use BeSimple\SoapCommon\Helper;
/**
* Provides a SoapClient instance.
* Fluent interface builder for SoapClient instance.
*
* @author Francis Besset <francis.besset@gmail.com>
* @author Christian Kerl <christian-kerl@web.de>
* @author Petr Bechyně <petr.bechyne@vodafone.com>
*/
class SoapClientBuilder
class SoapClientBuilder extends AbstractSoapBuilder
{
public function build(SoapClientOptions $soapClientOptions, SoapOptions $soapOptions)
/**
* Authentication options.
*
* @var array(string=>mixed)
*/
protected $soapOptionAuthentication = array();
/**
* Create new instance with default options.
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/
public static function createWithDefaults()
{
return new SoapClient(
$soapClientOptions,
$soapOptions
return parent::createWithDefaults()
->withUserAgent('BeSimpleSoap');
}
/**
* Finally returns a SoapClient instance.
*
* @return \BeSimple\SoapClient\SoapClient
*/
public function build()
{
$this->validateOptions();
return new SoapClient($this->wsdl, $this->getSoapOptions());
}
/**
* Get final array of SOAP options.
*
* @return array(string=>mixed)
*/
public function getSoapOptions()
{
return parent::getSoapOptions() + $this->soapOptionAuthentication;
}
/**
* Configure option 'trace'.
*
* @param boolean $trace Enable/Disable
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/
public function withTrace($trace = true)
{
$this->soapOptions['trace'] = $trace;
return $this;
}
/**
* Configure option 'exceptions'.
*
* @param boolean $exceptions Enable/Disable
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/
public function withExceptions($exceptions = true)
{
$this->soapOptions['exceptions'] = $exceptions;
return $this;
}
/**
* Configure option 'user_agent'.
*
* @param string $userAgent User agent string
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/
public function withUserAgent($userAgent)
{
$this->soapOptions['user_agent'] = $userAgent;
return $this;
}
/**
* Enable gzip compression.
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/
public function withCompressionGzip()
{
$this->soapOptions['compression'] = SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP;
return $this;
}
/**
* Enable deflate compression.
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/
public function withCompressionDeflate()
{
$this->soapOptions['compression'] = SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_DEFLATE;
return $this;
}
/**
* Configure basic authentication
*
* @param string $username Username
* @param string $password Password
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/
public function withBasicAuthentication($username, $password)
{
$this->soapOptionAuthentication = array(
'authentication' => SOAP_AUTHENTICATION_BASIC,
'login' => $username,
'password' => $password,
);
return $this;
}
/**
* Configure digest authentication.
*
* @param string $certificate Certificate
* @param string $passphrase Passphrase
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/
public function withDigestAuthentication($certificate, $passphrase = null)
{
$this->soapOptionAuthentication = array(
'authentication' => SOAP_AUTHENTICATION_DIGEST,
'local_cert' => $certificate,
);
if ($passphrase) {
$this->soapOptionAuthentication['passphrase'] = $passphrase;
}
return $this;
}
/**
* Configure proxy.
*
* @param string $host Host
* @param int $port Port
* @param string $login Login
* @param string $password Password
* @param int $auth Authentication method
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/
public function withProxy($host, $port, $login = null, $password = null, $auth = null)
{
$this->soapOptions['proxy_host'] = $host;
$this->soapOptions['proxy_port'] = $port;
if ($login) {
$this->soapOptions['proxy_login'] = $login;
$this->soapOptions['proxy_password'] = $password;
if ($auth) {
if (!in_array($auth, array(\CURLAUTH_BASIC, \CURLAUTH_NTLM), true)) {
throw new \InvalidArgumentException('Invalid authentication method: CURLAUTH_BASIC or CURLAUTH_NTLM constants are availables.');
}
$this->soapOptions['proxy_auth'] = $auth;
}
}
return $this;
}
/**
* SOAP attachment type Base64.
*
* @return \BeSimple\SoapServer\SoapServerBuilder
*/
public function withBase64Attachments()
{
$this->options['attachment_type'] = Helper::ATTACHMENTS_TYPE_BASE64;
return $this;
}
/**
* SOAP attachment type SwA.
*
* @return \BeSimple\SoapServer\SoapServerBuilder
*/
public function withSwaAttachments()
{
$this->options['attachment_type'] = Helper::ATTACHMENTS_TYPE_SWA;
return $this;
}
/**
* SOAP attachment type MTOM.
*
* @return \BeSimple\SoapServer\SoapServerBuilder
*/
public function withMtomAttachments()
{
$this->options['attachment_type'] = Helper::ATTACHMENTS_TYPE_MTOM;
return $this;
}
/**
* Validate options.
*/
protected function validateOptions()
{
$this->validateWsdl();
}
}

View File

@ -1,35 +0,0 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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\SoapCommon\SoapOptions\SoapOptions;
/**
* Provides a SoapClient instance.
*
* @author Francis Besset <francis.besset@gmail.com>
* @author Christian Kerl <christian-kerl@web.de>
* @author Petr Bechyně <petr.bechyne@vodafone.com>
*/
class SoapClientOptionsBuilder
{
public static function createWithDefaults()
{
return new SoapClientOptions(
SoapClientOptions::SOAP_CLIENT_TRACE_OFF,
SoapClientOptions::SOAP_CLIENT_EXCEPTIONS_ON,
'BeSimpleSoap',
SoapClientOptions::SOAP_CLIENT_COMPRESSION_NONE
);
}
}

View File

@ -1,107 +0,0 @@
<?php
namespace BeSimple\SoapClient;
use BeSimple\SoapClient\SoapServerAuthentication\SoapServerAuthenticationInterface;
use BeSimple\SoapClient\SoapServerProxy\SoapServerProxy;
class SoapClientOptions
{
const SOAP_CLIENT_TRACE_ON = true;
const SOAP_CLIENT_TRACE_OFF = false;
const SOAP_CLIENT_EXCEPTIONS_ON = true;
const SOAP_CLIENT_EXCEPTIONS_OFF = false;
const SOAP_CLIENT_COMPRESSION_NONE = null;
const SOAP_CLIENT_COMPRESSION_GZIP = SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP;
const SOAP_CLIENT_COMPRESSION_DEFLATE = SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_DEFLATE;
private $trace;
private $exceptions;
private $userAgent;
private $compression;
private $authentication;
private $proxy;
/**
* @param bool $trace = SoapClientOptions::SOAP_CLIENT_TRACE_ON|SoapClientOptions::SOAP_CLIENT_TRACE_OFF
* @param bool $exceptions = SoapClientOptions::SOAP_CLIENT_EXCEPTIONS_ON|SoapClientOptions::SOAP_CLIENT_EXCEPTIONS_OFF
* @param string $userAgent
* @param int $compression = SoapClientOptions::SOAP_CLIENT_COMPRESSION_NONE|SoapClientOptions::SOAP_CLIENT_COMPRESSION_GZIP|SoapClientOptions::SOAP_CLIENT_COMPRESSION_DEFLATE
* @param SoapServerAuthenticationInterface $authentication = null
* @param SoapServerProxy $proxy = null
*/
public function __construct($trace, $exceptions, $userAgent, $compression = null, SoapServerAuthenticationInterface $authentication = null, SoapServerProxy $proxy = null)
{
$this->trace = $trace;
$this->exceptions = $exceptions;
$this->userAgent = $userAgent;
$this->compression = $compression;
$this->authentication = $authentication;
$this->proxy = $proxy;
}
public function getTrace()
{
return $this->trace;
}
public function getExceptions()
{
return $this->exceptions;
}
public function getUserAgent()
{
return $this->userAgent;
}
public function hasCompression()
{
return $this->compression !== self::SOAP_CLIENT_COMPRESSION_NONE;
}
public function getCompression()
{
return $this->compression;
}
public function hasAuthentication()
{
return $this->authentication !== null;
}
public function hasProxy()
{
return $this->proxy !== null;
}
public function getAuthentication()
{
return $this->authentication;
}
public function getProxy()
{
return $this->proxy;
}
public function toArray()
{
$optionsAsArray = [
'trace' => $this->getTrace(),
'exceptions' => $this->getExceptions(),
'user_agent' => $this->getUserAgent(),
];
if ($this->hasCompression()) {
$optionsAsArray['compression'] = $this->getCompression();
}
if ($this->hasAuthentication()) {
$optionsAsArray += $this->getAuthentication()->toArray();
}
if ($this->hasProxy()) {
$optionsAsArray += $this->getProxy()->toArray();
}
return $optionsAsArray;
}
}

View File

@ -1,39 +0,0 @@
<?php
namespace BeSimple\SoapClient\SoapServerAuthentication;
class SoapServerAuthenticationBasic implements SoapServerAuthenticationInterface
{
private $login;
private $password;
public function __construct($login, $password)
{
$this->login = $login;
$this->password = $password;
}
public function getAuthentication()
{
return \SOAP_AUTHENTICATION_BASIC;
}
public function getLogin()
{
return $this->login;
}
public function getPassword()
{
return $this->password;
}
public function toArray()
{
return [
'authentication' => $this->getAuthentication(),
'login' => $this->getLogin(),
'password' => $this->getPassword(),
];
}
}

View File

@ -1,52 +0,0 @@
<?php
namespace BeSimple\SoapClient\SoapServerAuthentication;
class SoapServerAuthenticationDigest implements SoapServerAuthenticationInterface
{
private $localCert;
private $passPhrase;
/**
* @param string $localCert
* @param string $passPhrase = null
*/
public function __construct($localCert, $passPhrase = null)
{
$this->localCert = $localCert;
$this->passPhrase = $passPhrase;
}
public function getLocalCert()
{
return $this->localCert;
}
public function hasPassPhrase()
{
return $this->passPhrase !== null;
}
public function getPassPhrase()
{
return $this->passPhrase;
}
public function getAuthentication()
{
return \SOAP_AUTHENTICATION_DIGEST;
}
public function toArray()
{
$authenticationAsArray = [
'authentication' => $this->getAuthentication(),
'local_cert' => $this->getLocalCert()
];
if ($this->hasPassPhrase()) {
$authenticationAsArray['passphrase'] = $this->getPassPhrase();
}
return $authenticationAsArray;
}
}

View File

@ -1,16 +0,0 @@
<?php
namespace BeSimple\SoapClient\SoapServerAuthentication;
interface SoapServerAuthenticationInterface
{
/**
* @return int
*/
public function getAuthentication();
/**
* @return array
*/
public function toArray();
}

View File

@ -1,84 +0,0 @@
<?php
namespace BeSimple\SoapClient\SoapServerProxy;
class SoapServerProxy
{
const PROXY_AUTHENTICATION_TYPE_NONE = null;
const PROXY_AUTHENTICATION_TYPE_BASIC = \CURLAUTH_BASIC;
const PROXY_AUTHENTICATION_TYPE_NTLM = \CURLAUTH_NTLM;
private $host;
private $port;
private $login;
private $password;
private $authenticationType;
/**
* @param string $host
* @param int $port
* @param string $login = null
* @param string $password = null
* @param int $authenticationType = null|SoapServerProxy::PROXY_AUTHENTICATION_TYPE_BASIC|SoapServerProxy::PROXY_AUTHENTICATION_TYPE_NTLM
*/
public function __construct($host, $port, $login = null, $password = null, $authenticationType = null)
{
$this->host = $host;
$this->port = $port;
$this->login = $login;
$this->password = $password;
$this->authenticationType = $authenticationType;
}
public function getHost()
{
return $this->host;
}
public function getPort()
{
return $this->port;
}
public function hasCredentials()
{
return $this->login !== null;
}
public function getLogin()
{
return $this->login;
}
public function getPassword()
{
return $this->password;
}
public function hasAuthenticationType()
{
return $this->authenticationType !== self::PROXY_AUTHENTICATION_TYPE_NONE;
}
public function getAuthenticationType()
{
return $this->authenticationType;
}
public function toArray()
{
$proxyAsArray = [
'proxy_host' => $this->getHost(),
'proxy_port' => $this->getPort(),
];
if ($this->hasCredentials()) {
$proxyAsArray['proxy_login'] = $this->getLogin();
$proxyAsArray['proxy_password'] = $this->getPassword();
}
if ($this->hasAuthenticationType()) {
$proxyAsArray['proxy_auth'] = $this->getAuthenticationType();
}
return $proxyAsArray;
}
}

View File

@ -0,0 +1,48 @@
<?php
/*
* 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\SoapCommon\SoapRequest as CommonSoapRequest;
use BeSimple\SoapCommon\SoapMessage;
/**
* SoapRequest class for SoapClient. Provides factory function for request object.
*
* @author Andreas Schamberger <mail@andreass.net>
*/
class SoapRequest extends CommonSoapRequest
{
/**
* Factory function for SoapRequest.
*
* @param string $content Content
* @param string $location Location
* @param string $action SOAP action
* @param string $version SOAP version
*
* @return BeSimple\SoapClient\SoapRequest
*/
public static function create($content, $location, $action, $version)
{
$request = new SoapRequest();
// $content is if unmodified from SoapClient not a php string type!
$request->setContent((string) $content);
$request->setLocation($location);
$request->setAction($action);
$request->setVersion($version);
$contentType = SoapMessage::getContentTypeForVersion($version);
$request->setContentType($contentType);
return $request;
}
}

View File

@ -30,7 +30,7 @@ class SoapResponse extends CommonSoapResponse
* @param string $version SOAP version
* @param string $contentType Content type header
*
* @return SoapResponse
* @return BeSimple\SoapClient\SoapResponse
*/
public static function create($content, $location, $action, $version, $contentType)
{

View File

@ -24,7 +24,6 @@ abstract class AbstractWebServerTest extends \PHPUnit_Framework_TestCase
* @var ProcessBuilder
*/
static protected $webserver;
static protected $websererPortLength;
public static function setUpBeforeClass()
{
@ -44,8 +43,6 @@ abstract class AbstractWebServerTest extends \PHPUnit_Framework_TestCase
self::$webserver->start();
usleep(100000);
self::$websererPortLength = strlen(WEBSERVER_PORT);
}
public static function tearDownAfterClass()

View File

@ -45,19 +45,6 @@ class CurlTest extends AbstractWebserverTest
$this->assertEquals('Unable to parse URL', $curl->getErrorMessage());
}
public function testGetRequestHeaders()
{
$curl = new Curl(array(
'proxy_host' => false,
));
$curl->exec(sprintf('http://localhost:%d/curl.txt', WEBSERVER_PORT));
$this->assertEquals(132 + self::$websererPortLength, strlen($curl->getRequestHeaders()));
$curl->exec(sprintf('http://localhost:%s/404.txt', WEBSERVER_PORT));
$this->assertEquals(131 + self::$websererPortLength, strlen($curl->getRequestHeaders()));
}
public function testGetResponse()
{
$curl = new Curl(array(
@ -66,7 +53,6 @@ class CurlTest extends AbstractWebserverTest
$curl->exec(sprintf('http://localhost:%d/curl.txt', WEBSERVER_PORT));
$this->assertSame('OK', $curl->getResponseStatusMessage());
$this->assertEquals(145 + self::$websererPortLength, strlen($curl->getResponse()));
$curl->exec(sprintf('http://localhost:%d/404.txt', WEBSERVER_PORT));
$this->assertSame('Not Found', $curl->getResponseStatusMessage());
@ -95,19 +81,6 @@ class CurlTest extends AbstractWebserverTest
$this->assertEquals('text/html; charset=UTF-8', $curl->getResponseContentType());
}
public function testGetResponseHeaders()
{
$curl = new Curl(array(
'proxy_host' => false,
));
$curl->exec(sprintf('http://localhost:%d/curl.txt', WEBSERVER_PORT));
$this->assertEquals(117 + self::$websererPortLength, strlen($curl->getResponseHeaders()));
$curl->exec(sprintf('http://localhost:%d/404.txt', WEBSERVER_PORT));
$this->assertEquals(124 + self::$websererPortLength, strlen($curl->getResponseHeaders()));
}
public function testGetResponseStatusCode()
{
$curl = new Curl(array(

View File

@ -114,7 +114,7 @@ class SoapClientBuilderTest extends \PHPUnit_Framework_TestCase
public function testCreateWithDefaults()
{
$builder = SoapClientBuilder::createClientWithDefaults();
$builder = SoapClientBuilder::createWithDefaults();
$this->assertInstanceOf('BeSimple\SoapClient\SoapClientBuilder', $builder);

View File

@ -278,7 +278,7 @@ class WsdlDownloaderTest extends AbstractWebserverTest
$content = file_get_contents(self::$fixturesPath.$file);
$content = preg_replace('#'.preg_quote('%location%').'#', sprintf('localhost:%d', WEBSERVER_PORT), $content);
self::$filesystem->dumpFile(self::$fixturesPath.'build_include'.DIRECTORY_SEPARATOR.pathinfo($file, PATHINFO_BASENAME), $content);
file_put_contents(self::$fixturesPath.'build_include'.DIRECTORY_SEPARATOR.pathinfo($file, PATHINFO_BASENAME), $content);
}
}

View File

@ -23,12 +23,12 @@
"php": ">=5.3.0",
"ext-soap": "*",
"ext-curl": "*",
"besimple/soap-common": "0.3.*",
"besimple/soap-common": "0.2.*",
"ass/xmlsecurity": "~1.0"
},
"require-dev": {
"mikey179/vfsStream": "~1.0",
"symfony/filesystem": "~2.3",
"symfony/filesystem": "~2.0",
"symfony/process": "~2.3"
},
"autoload": {
@ -37,7 +37,7 @@
"target-dir": "BeSimple/SoapClient",
"extra": {
"branch-alias": {
"dev-master": "0.3-dev"
"dev-master": "0.2-dev"
}
}
}

View File

@ -0,0 +1,246 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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\SoapCommon;
use BeSimple\SoapCommon\Converter\TypeConverterCollection;
use BeSimple\SoapCommon\Converter\TypeConverterInterface;
/**
* @author Christian Kerl <christian-kerl@web.de>
* @author Francis Besset <francis.besset@gmail.com>
*/
abstract class AbstractSoapBuilder
{
protected $wsdl;
protected $soapOptions = array();
/**
* @return AbstractSoapBuilder
*/
static public function createWithDefaults()
{
$builder = new static();
return $builder
->withSoapVersion12()
->withEncoding('UTF-8')
->withSingleElementArrays()
;
}
public function __construct()
{
$this->soapOptions['features'] = 0;
$this->soapOptions['classmap'] = new Classmap();
$this->soapOptions['typemap'] = new TypeConverterCollection();
}
public function getWsdl()
{
return $this->wsdl;
}
public function getSoapOptions()
{
$options = $this->soapOptions;
$options['classmap'] = $this->soapOptions['classmap']->all();
$options['typemap'] = $this->soapOptions['typemap']->getTypemap();
return $options;
}
/**
* @return AbstractSoapBuilder
*/
public function withWsdl($wsdl)
{
$this->wsdl = $wsdl;
return $this;
}
/**
* @return AbstractSoapBuilder
*/
public function withSoapVersion11()
{
$this->soapOptions['soap_version'] = \SOAP_1_1;
return $this;
}
/**
* @return AbstractSoapBuilder
*/
public function withSoapVersion12()
{
$this->soapOptions['soap_version'] = \SOAP_1_2;
return $this;
}
public function withEncoding($encoding)
{
$this->soapOptions['encoding'] = $encoding;
return $this;
}
public function withWsdlCache($cache)
{
if (!in_array($cache, Cache::getTypes(), true)) {
throw new \InvalidArgumentException();
}
$this->soapOptions['cache_wsdl'] = $cache;
return $this;
}
/**
* @return AbstractSoapBuilder
*/
public function withWsdlCacheNone()
{
$this->soapOptions['cache_wsdl'] = Cache::TYPE_NONE;
return $this;
}
/**
* @return AbstractSoapBuilder
*/
public function withWsdlCacheDisk()
{
$this->soapOptions['cache_wsdl'] = Cache::TYPE_DISK;
return $this;
}
/**
* @return AbstractSoapBuilder
*/
public function withWsdlCacheMemory()
{
$this->soapOptions['cache_wsdl'] = Cache::TYPE_MEMORY;
return $this;
}
/**
* @return AbstractSoapBuilder
*/
public function withWsdlCacheDiskAndMemory()
{
$this->soapOptions['cache_wsdl'] = Cache::TYPE_DISK_MEMORY;
return $this;
}
/**
* Enables the SOAP_SINGLE_ELEMENT_ARRAYS feature.
* If enabled arrays containing only one element will be passed as arrays otherwise the single element is extracted and directly passed.
*
* @return AbstractSoapBuilder
*/
public function withSingleElementArrays()
{
$this->soapOptions['features'] |= \SOAP_SINGLE_ELEMENT_ARRAYS;
return $this;
}
/**
* Enables the SOAP_WAIT_ONE_WAY_CALLS feature.
*
* @return AbstractSoapBuilder
*/
public function withWaitOneWayCalls()
{
$this->soapOptions['features'] |= \SOAP_WAIT_ONE_WAY_CALLS;
return $this;
}
/**
* Enables the SOAP_USE_XSI_ARRAY_TYPE feature.
*
* @return AbstractSoapBuilder
*/
public function withUseXsiArrayType()
{
$this->soapOptions['features'] |= \SOAP_USE_XSI_ARRAY_TYPE;
return $this;
}
public function withTypeConverter(TypeConverterInterface $converter)
{
$this->soapOptions['typemap']->add($converter);
return $this;
}
public function withTypeConverters(TypeConverterCollection $converters, $merge = true)
{
if ($merge) {
$this->soapOptions['typemap']->addCollection($converters);
} else {
$this->soapOptions['typemap']->set($converters->all());
}
return $this;
}
/**
* Adds a class mapping to the classmap.
*
* @param string $xmlType
* @param string $phpType
*
* @return AbstractSoapBuilder
*/
public function withClassMapping($xmlType, $phpType)
{
$this->soapOptions['classmap']->add($xmlType, $phpType);
return $this;
}
/**
* Sets the classmap.
*
* @param array $classmap The classmap.
* @param boolean $merge If true the given classmap is merged into the existing one, otherwise the existing one is overwritten.
*
* @return AbstractSoapBuilder
*/
public function withClassmap(Classmap $classmap, $merge = true)
{
if ($merge) {
$this->soapOptions['classmap']->addClassmap($classmap);
} else {
$this->soapOptions['classmap']->set($classmap->all());
}
return $this;
}
protected function validateWsdl()
{
if (null === $this->wsdl) {
throw new \InvalidArgumentException('The WSDL has to be configured!');
}
}
}

View File

@ -25,23 +25,18 @@ class Cache
const TYPE_MEMORY = WSDL_CACHE_MEMORY;
const TYPE_DISK_MEMORY = WSDL_CACHE_BOTH;
static protected $types = [
static protected $types = array(
self::TYPE_NONE,
self::TYPE_DISK,
self::TYPE_MEMORY,
self::TYPE_DISK_MEMORY,
];
);
static public function getTypes()
{
return self::$types;
}
static public function hasType($cacheType)
{
return in_array($cacheType, self::$types);
}
static public function isEnabled()
{
return self::iniGet('soap.wsdl_cache_enabled');

View File

@ -15,29 +15,27 @@ namespace BeSimple\SoapCommon;
/**
* @author Francis Besset <francis.besset@gmail.com>
*/
class ClassMap
class Classmap
{
protected $classMap;
/**
* @var array
*/
protected $classmap = array();
public function __construct(array $classMap = [])
{
$this->classmap = [];
foreach ($classMap as $type => $className) {
$this->add($type, $className);
}
}
/**
* @return array
*/
public function getAll()
public function all()
{
return $this->classMap;
return $this->classmap;
}
/**
* @param string $type
*
* @return string
*
* @throws \InvalidArgumentException
*/
public function get($type)
@ -46,25 +44,39 @@ class ClassMap
throw new \InvalidArgumentException(sprintf('The type "%s" does not exists', $type));
}
return $this->classMap[$type];
return $this->classmap[$type];
}
/**
* @param string $type
* @param string $className
* @param string $classname
*
* @throws \InvalidArgumentException
*/
public function add($type, $className)
public function add($type, $classname)
{
if ($this->has($type)) {
throw new \InvalidArgumentException(sprintf('The type "%s" already exists', $type));
}
$this->classMap[$type] = $className;
$this->classmap[$type] = $classname;
}
/**
* @param array $classmap
*/
public function set(array $classmap)
{
$this->classmap = array();
foreach ($classmap as $type => $classname) {
$this->add($type, $classname);
}
}
/**
* @param string $type
*
* @return boolean
*/
public function has($type)
@ -72,10 +84,10 @@ class ClassMap
return isset($this->classmap[$type]);
}
public function addClassMap(ClassMap $classMap)
public function addClassmap(Classmap $classmap)
{
foreach ($classMap->getAll() as $type => $className) {
$this->add($type, $className);
foreach ($classmap->all() as $type => $classname) {
$this->add($type, $classname);
}
}
}

View File

@ -19,7 +19,7 @@ class TypeConverterCollection
{
private $converters = array();
public function getAll()
public function all()
{
return array_values($this->converters);
}
@ -58,7 +58,7 @@ class TypeConverterCollection
public function addCollection(TypeConverterCollection $converterCollection)
{
foreach ($converterCollection->getAll() as $converter) {
foreach ($converterCollection->all() as $converter) {
$this->add($converter);
}
}

View File

@ -48,13 +48,13 @@ class Message
return 0 === count($this->parts) ? true : false;
}
public function add($name, $phpType, $nillable = false)
public function add($name, $phpType, $nillable = false, $attribute = false)
{
if ($phpType instanceof TypeInterface) {
$phpType = $phpType->getPhpType();
}
$this->parts[$name] = new Part($name, $phpType, $nillable);
$this->parts[$name] = new Part($name, $phpType, $nillable, $attribute);
return $this;
}

View File

@ -53,7 +53,7 @@ class Method
public function getUse()
{
return \SOAP_LITERAL;
return \SOAP_ENCODED;
}
public function addHeader($name, $type)
@ -66,9 +66,13 @@ class Method
$this->input->add($name, $type);
}
public function setOutput($type)
public function setOutput($type, $name = 'return')
{
$this->output->add('return', $type);
if ('return' !== $name) {
$this->output = new Message($name);
}
$this->output->add($name, $type);
}
public function getHeaders()

View File

@ -20,12 +20,14 @@ class Part
protected $name;
protected $type;
protected $nillable;
protected $attribute;
public function __construct($name, $type, $nillable = false)
public function __construct($name, $type, $nillable = false, $attribute = false)
{
$this->name = $name;
$this->type = $type;
$this->setNillable($nillable);
$this->setAttribute($attribute);
}
public function getName()
@ -33,6 +35,26 @@ class Part
return $this->name;
}
/**
* @return bool
*/
public function isAttribute()
{
return $this->attribute;
}
/**
* @param bool $attribute
*
* @return $this
*/
public function setAttribute($attribute)
{
$this->attribute = $attribute;
return $this;
}
public function getType()
{
return $this->type;

View File

@ -1,70 +0,0 @@
<?php
namespace BeSimple\SoapCommon\SoapOptions\SoapFeatures;
use Exception;
class SoapFeatures
{
const SINGLE_ELEMENT_ARRAYS = \SOAP_SINGLE_ELEMENT_ARRAYS;
const WAIT_ONE_WAY_CALLS = \SOAP_WAIT_ONE_WAY_CALLS;
const USE_XSI_ARRAY_TYPE = \SOAP_USE_XSI_ARRAY_TYPE;
private $featuresSum;
private $singleElementArrays = false;
private $oneWayCallsOn = false;
private $useXsiArrayType = false;
/**
* @param array $features array of SoapFeatures::FEATURE_NAME
* @throws Exception
*/
public function __construct(array $features)
{
$this->resolveFeatures($features);
}
public function isSingleElementArrays()
{
return $this->singleElementArrays;
}
public function isOneWayCallsOn()
{
return $this->oneWayCallsOn;
}
public function isUseXsiArrayType()
{
return $this->useXsiArrayType;
}
public function getFeaturesSum()
{
return $this->featuresSum;
}
private function resolveFeatures(array $features)
{
$featuresSum = 0;
foreach ($features as $feature) {
switch ($feature) {
case self::SINGLE_ELEMENT_ARRAYS:
$this->singleElementArrays = true;
$featuresSum += $feature;
break;
case self::WAIT_ONE_WAY_CALLS:
$this->oneWayCallsOn = true;
$featuresSum += $feature;
break;
case self::USE_XSI_ARRAY_TYPE:
$this->useXsiArrayType = true;
$featuresSum += $feature;
break;
default:
throw new Exception('Unknown SOAP feature: ' . $feature);
}
}
$this->featuresSum = $featuresSum;
}
}

View File

@ -1,124 +0,0 @@
<?php
namespace BeSimple\SoapCommon\SoapOptions;
use BeSimple\SoapCommon\Cache;
use BeSimple\SoapCommon\ClassMap;
use BeSimple\SoapCommon\Converter\TypeConverterCollection;
use BeSimple\SoapCommon\Helper;
use BeSimple\SoapCommon\SoapOptions\SoapFeatures\SoapFeatures;
class SoapOptions
{
const SOAP_VERSION_1_1 = \SOAP_1_1;
const SOAP_VERSION_1_2 = \SOAP_1_2;
const SOAP_ENCODING_UTF8 = 'UTF-8';
const SOAP_SINGLE_ELEMENT_ARRAYS_OFF = 0;
const SOAP_CACHE_TYPE_NONE = Cache::TYPE_NONE;
const SOAP_CACHE_TYPE_DISK = Cache::TYPE_DISK;
const SOAP_CACHE_TYPE_MEMORY = Cache::TYPE_MEMORY;
const SOAP_CACHE_TYPE_DISK_MEMORY = Cache::TYPE_DISK_MEMORY;
const SOAP_ATTACHMENTS_OFF = null;
const SOAP_ATTACHMENTS_TYPE_BASE64 = Helper::ATTACHMENTS_TYPE_BASE64;
const SOAP_ATTACHMENTS_TYPE_MTOM = Helper::ATTACHMENTS_TYPE_MTOM;
const SOAP_ATTACHMENTS_TYPE_SWA = Helper::ATTACHMENTS_TYPE_SWA;
protected $soapVersion;
protected $encoding;
protected $soapFeatures;
protected $wsdlFile;
protected $wsdlCacheType;
protected $classMap;
protected $typeConverterCollection;
protected $attachmentType;
/**
* @param int $soapVersion = SoapOptions::SOAP_VERSION_1_1|SoapOptions::SOAP_VERSION_1_2
* @param string $encoding = SoapOptions::SOAP_ENCODING_UTF8
* @param SoapFeatures $features
* @param string $wsdlFile
* @param string $wsdlCacheType = SoapOptions::SOAP_CACHE_TYPE_NONE|SoapOptions::SOAP_CACHE_TYPE_MEMORY|SoapOptions::SOAP_CACHE_TYPE_DISK|SoapOptions::SOAP_CACHE_TYPE_DISK_MEMORY
* @param ClassMap $classMap
* @param TypeConverterCollection $typeConverterCollection
* @param string $attachmentType = SoapOptions::SOAP_ATTACHMENTS_OFF|SoapOptions::SOAP_ATTACHMENTS_TYPE_SWA|SoapOptions::ATTACHMENTS_TYPE_MTOM|SoapOptions::ATTACHMENTS_TYPE_BASE64
*/
public function __construct(
$soapVersion,
$encoding,
SoapFeatures $features,
$wsdlFile,
$wsdlCacheType,
ClassMap $classMap,
TypeConverterCollection $typeConverterCollection,
$attachmentType = null
) {
$this->soapVersion = $soapVersion;
$this->encoding = $encoding;
$this->soapFeatures = $features;
$this->wsdlFile = $wsdlFile;
$this->wsdlCacheType = $wsdlCacheType;
$this->classMap = $classMap;
$this->typeConverterCollection = $typeConverterCollection;
$this->attachmentType = $attachmentType;
}
public function getSoapVersion()
{
return $this->soapVersion;
}
public function getEncoding()
{
return $this->encoding;
}
public function getWsdlFile()
{
return $this->wsdlFile;
}
public function getWsdlCacheType()
{
return $this->wsdlCacheType;
}
public function hasAttachments()
{
return $this->attachmentType !== self::SOAP_ATTACHMENTS_OFF;
}
public function getAttachmentType()
{
return $this->attachmentType;
}
public function getSoapFeatures()
{
return $this->soapFeatures;
}
public function getClassMap()
{
return $this->classMap;
}
public function getTypeConverterCollection()
{
return $this->typeConverterCollection;
}
public function toArray()
{
$optionsAsArray = [
'soap_version' => $this->getSoapVersion(),
'encoding' => $this->getEncoding(),
'features' => $this->getSoapFeatures(),
'wsdl' => $this->getWsdlFile(),
'cache_wsdl' => $this->getWsdlCacheType(),
'classmap' => $this->getClassMap()->getAll(),
'typemap' => $this->getTypeConverterCollection()->getTypemap(),
];
return $optionsAsArray;
}
}

View File

@ -1,44 +0,0 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (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\SoapCommon;
use BeSimple\SoapCommon\Converter\TypeConverterCollection;
use BeSimple\SoapCommon\SoapOptions\SoapFeatures\SoapFeatures;
use BeSimple\SoapCommon\SoapOptions\SoapOptions;
use InvalidArgumentException;
/**
* @author Petr Bechyně <petr.bechyne@vodafone.com>
*/
class SoapOptionsBuilder
{
static public function createWithDefaults($wsdlFile, $wsdlCacheType = Cache::TYPE_NONE)
{
if (!Cache::hasType($wsdlCacheType)) {
throw new InvalidArgumentException;
}
$soapOptions = new SoapOptions(
SoapOptions::SOAP_VERSION_1_2,
SoapOptions::SOAP_ENCODING_UTF8,
new SoapFeatures([
SoapFeatures::SINGLE_ELEMENT_ARRAYS
]),
$wsdlFile,
$wsdlCacheType,
new ClassMap(),
new TypeConverterCollection()
);
return $soapOptions;
}
}

View File

@ -13,6 +13,7 @@
namespace BeSimple\SoapCommon;
use BeSimple\SoapCommon\SoapMessage;
/**
* SOAP request message.
@ -21,27 +22,5 @@ namespace BeSimple\SoapCommon;
*/
class SoapRequest extends SoapMessage
{
/**
* Factory function for SoapRequest.
*
* @param string $content Content
* @param string $location Location
* @param string $action SOAP action
* @param string $version SOAP version
*
* @return SoapRequest
*/
public static function create($content, $location, $action, $version)
{
$request = new SoapRequest();
// $content is if unmodified from SoapClient not a php string type!
$request->setContent((string) $content);
$request->setLocation($location);
$request->setAction($action);
$request->setVersion($version);
$contentType = SoapMessage::getContentTypeForVersion($version);
$request->setContentType($contentType);
return $request;
}
}

View File

@ -177,7 +177,7 @@ class AbstractSoapBuilderTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(1, count($options['classmap']));
$classmap = new ClassMap();
$classmap = new Classmap();
$classmap->add('bar', __CLASS__);
$builder->withClassmap($classmap);
$options = $builder->getSoapOptions();

View File

@ -1,65 +0,0 @@
<?php
/*
* This file is part of the BeSimpleSoapCommon.
*
* (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\SoapCommon\Tests;
use BeSimple\SoapCommon\ClassMap;
/**
* UnitTest for \BeSimple\SoapCommon\ClassMap.
*
* @author Francis Besset <francis.besset@gmail.com>
*/
class ClassMapTest extends \PHPUnit_Framework_TestCase
{
public function testAll()
{
$classmap = new ClassMap();
$this->assertSame(array(), $classmap->getAll());
}
public function testAdd()
{
$classmap = new ClassMap();
$classmap->add('foobar', 'BeSimple\SoapCommon\ClassMap');
$this->setExpectedException('InvalidArgumentException');
$classmap->add('foobar', 'BeSimple\SoapCommon\ClassMap');
}
public function testGet()
{
$classmap = new ClassMap();
$classmap->add('foobar', 'BeSimple\SoapCommon\ClassMap');
$this->assertSame('BeSimple\SoapCommon\ClassMap', $classmap->get('foobar'));
$this->setExpectedException('InvalidArgumentException');
$classmap->get('bar');
}
public function testAddClassMap()
{
$classmap1 = new ClassMap();
$classmap2 = new ClassMap();
$classmap2->add('foobar', 'BeSimple\SoapCommon\ClassMap');
$classmap1->addClassMap($classmap2);
$this->assertEquals(array('foobar' => 'BeSimple\SoapCommon\ClassMap'), $classmap1->getAll());
$this->setExpectedException('InvalidArgumentException');
$classmap1->addClassMap($classmap2);
}
}

View File

@ -0,0 +1,81 @@
<?php
/*
* This file is part of the BeSimpleSoapCommon.
*
* (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\SoapCommon\Tests;
use BeSimple\SoapCommon\Classmap;
/**
* UnitTest for \BeSimple\SoapCommon\Classmap.
*
* @author Francis Besset <francis.besset@gmail.com>
*/
class ClassmapTest extends \PHPUnit_Framework_TestCase
{
public function testAll()
{
$classmap = new Classmap();
$this->assertSame(array(), $classmap->all());
}
public function testAdd()
{
$classmap = new Classmap();
$classmap->add('foobar', 'BeSimple\SoapCommon\Classmap');
$this->setExpectedException('InvalidArgumentException');
$classmap->add('foobar', 'BeSimple\SoapCommon\Classmap');
}
public function testGet()
{
$classmap = new Classmap();
$classmap->add('foobar', 'BeSimple\SoapCommon\Classmap');
$this->assertSame('BeSimple\SoapCommon\Classmap', $classmap->get('foobar'));
$this->setExpectedException('InvalidArgumentException');
$classmap->get('bar');
}
public function testSet()
{
$classmap = new Classmap();
$classmap->add('foobar', 'BeSimple\SoapCommon\Tests\ClassmapTest');
$classmap->add('foo', 'BeSimple\SoapCommon\Tests\Classmap');
$map = array(
'foobar' => 'BeSimple\SoapCommon\Classmap',
'barfoo' => 'BeSimple\SoapCommon\Tests\ClassmapTest',
);
$classmap->set($map);
$this->assertSame($map, $classmap->all());
}
public function testAddClassmap()
{
$classmap1 = new Classmap();
$classmap2 = new Classmap();
$classmap2->add('foobar', 'BeSimple\SoapCommon\Classmap');
$classmap1->addClassmap($classmap2);
$this->assertEquals(array('foobar' => 'BeSimple\SoapCommon\Classmap'), $classmap1->all());
$this->setExpectedException('InvalidArgumentException');
$classmap1->addClassmap($classmap2);
}
}

View File

@ -30,12 +30,12 @@ class TypeConverterCollectionTest extends \PHPUnit_Framework_TestCase
$dateTimeTypeConverter = new DateTimeTypeConverter();
$converters->add($dateTimeTypeConverter);
$this->assertSame(array($dateTimeTypeConverter), $converters->getAll());
$this->assertSame(array($dateTimeTypeConverter), $converters->all());
$dateTypeConverter = new DateTypeConverter();
$converters->add($dateTypeConverter);
$this->assertSame(array($dateTimeTypeConverter, $dateTypeConverter), $converters->getAll());
$this->assertSame(array($dateTimeTypeConverter, $dateTypeConverter), $converters->all());
}
public function testGetTypemap()
@ -73,7 +73,7 @@ class TypeConverterCollectionTest extends \PHPUnit_Framework_TestCase
$converter = array(new DateTypeConverter);
$converters->set($converter);
$this->assertSame($converter, $converters->getAll());
$this->assertSame($converter, $converters->all());
}
public function testAddCollection()
@ -85,7 +85,7 @@ class TypeConverterCollectionTest extends \PHPUnit_Framework_TestCase
$converters2->add($dateTimeTypeConverter);
$converters1->addCollection($converters2);
$this->assertSame(array($dateTimeTypeConverter), $converters1->getAll());
$this->assertSame(array($dateTimeTypeConverter), $converters1->all());
$this->setExpectedException('InvalidArgumentException');
$converters1->addCollection($converters2);

View File

@ -5,7 +5,7 @@ namespace BeSimple\SoapCommon\Type\KeyValue;
use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap;
use BeSimple\SoapCommon\Type\AbstractKeyValue;
class Boolean extends AbstractKeyValue
class BsBoolean extends AbstractKeyValue
{
/**
* @Soap\ComplexType("boolean")

View File

@ -5,7 +5,7 @@ namespace BeSimple\SoapCommon\Type\KeyValue;
use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap;
use BeSimple\SoapCommon\Type\AbstractKeyValue;
class Float extends AbstractKeyValue
class BsFloat extends AbstractKeyValue
{
/**
* @Soap\ComplexType("float")

View File

@ -5,7 +5,7 @@ namespace BeSimple\SoapCommon\Type\KeyValue;
use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap;
use BeSimple\SoapCommon\Type\AbstractKeyValue;
class Int extends AbstractKeyValue
class BsInt extends AbstractKeyValue
{
/**
* @Soap\ComplexType("int")

View File

@ -5,7 +5,7 @@ namespace BeSimple\SoapCommon\Type\KeyValue;
use BeSimple\SoapBundle\ServiceDefinition\Annotation as Soap;
use BeSimple\SoapCommon\Type\AbstractKeyValue;
class String extends AbstractKeyValue
class BsString extends AbstractKeyValue
{
/**
* @Soap\ComplexType("string")

View File

@ -30,7 +30,7 @@ abstract class WsSecurityFilterClientServer
/**
* The date format to be used with {@link \DateTime}
*/
const DATETIME_FORMAT = 'Y-m-d\TH:i:s.000\Z';
const DATETIME_FORMAT = 'Y-m-d\TH:i:s.u\Z';
/**
* (X509 3.2.1) Reference to a Subject Key Identifier

View File

@ -25,8 +25,7 @@
"ass/xmlsecurity": "~1.0"
},
"require-dev": {
"ext-mcrypt": "*",
"mikey179/vfsStream": "~1.0"
"mikey179/vfsstream": "~1.0"
},
"autoload": {
"psr-0": { "BeSimple\\SoapCommon": "" }
@ -34,7 +33,7 @@
"target-dir": "BeSimple/SoapCommon",
"extra": {
"branch-alias": {
"dev-master": "0.3-dev"
"dev-master": "0.2-dev"
}
}
}

View File

@ -1,137 +0,0 @@
<?php
namespace BeSimple\SoapServer\SoapOptions;
class SoapServerOptions
{
const SOAP_SERVER_PERSISTENCE_NONE = 0;
const SOAP_SERVER_PERSISTENCE_REQUEST = \SOAP_PERSISTENCE_REQUEST;
const SOAP_SERVER_PERSISTENCE_SESSION = \SOAP_PERSISTENCE_SESSION;
const SOAP_SERVER_KEEP_ALIVE_ON = true;
const SOAP_SERVER_KEEP_ALIVE_OFF = false;
const SOAP_SERVER_ERROR_REPORTING_ON = true;
const SOAP_SERVER_ERROR_REPORTING_OFF = false;
const SOAP_SERVER_EXCEPTIONS_ON = true;
const SOAP_SERVER_EXCEPTIONS_OFF = false;
private $handlerClass;
private $handlerObject;
private $keepAlive;
private $errorReporting;
private $persistence;
/**
* @param mixed $handlerClassOrObject
* @param bool $keepAlive = SoapServerOptions::SOAP_SERVER_KEEP_ALIVE_ON|SoapServerOptions::SOAP_SERVER_KEEP_ALIVE_OFF
* @param bool $errorReporting = SoapServerOptions::SOAP_SERVER_ERROR_REPORTING_ON|SoapServerOptions::SOAP_SERVER_ERROR_REPORTING_OFF
* @param bool $exceptions = SoapServerOptions::SOAP_SERVER_EXCEPTIONS_ON|SoapServerOptions::SOAP_SERVER_EXCEPTIONS_OFF
* @param int $persistence = SoapServerOptions::SOAP_SERVER_PERSISTENCE_NONE|SoapServerOptions::SOAP_SERVER_PERSISTENCE_REQUEST|SoapServerOptions::SOAP_SERVER_PERSISTENCE_SESSION
*/
public function __construct(
$handlerClassOrObject,
$keepAlive,
$errorReporting,
$exceptions,
$persistence
) {
$this->handlerClass = $this->resolveHandlerClass($handlerClassOrObject);
$this->handlerObject = $this->resolveHandlerObject($handlerClassOrObject);
$this->keepAlive = $keepAlive;
$this->errorReporting = $errorReporting;
$this->exceptions = $exceptions;
$this->persistence = $persistence;
}
public function hasHandlerClass()
{
return $this->handlerClass !== null;
}
public function getHandlerClass()
{
return $this->handlerClass;
}
public function hasHandlerObject()
{
return $this->handlerObject !== null;
}
public function getHandlerObject()
{
return $this->handlerObject;
}
public function hasPersistence()
{
return $this->persistence !== SoapServerOptions::SOAP_SERVER_PERSISTENCE_NONE;
}
public function getPersistence()
{
return $this->persistence;
}
public function isErrorReporting()
{
return $this->errorReporting;
}
public function isExceptions()
{
return $this->exceptions;
}
public function isKeepAlive()
{
return $this->keepAlive;
}
public function toArray()
{
$optionsAsArray = [
'keep_alive' => $this->isKeepAlive(),
];
return $optionsAsArray;
}
/**
* @param mixed $handler
* @return mixed|null
*/
private function resolveHandlerObject($handler)
{
if (is_string($handler) && class_exists($handler)) {
return null;
} elseif (is_object($handler)) {
return $handler;
} else {
throw new \InvalidArgumentException('The handler has to be a class name or an object');
}
}
/**
* @param mixed $handler
* @return mixed|null
*/
private function resolveHandlerClass($handler)
{
if (is_string($handler) && class_exists($handler)) {
return $handler;
} elseif (is_object($handler)) {
return null;
} else {
throw new \InvalidArgumentException('The handler has to be a class name or an object');
}
}
}

View File

@ -12,15 +12,15 @@
namespace BeSimple\SoapServer;
use BeSimple\SoapCommon\SoapRequest as CommonSoapRequest;
use BeSimple\SoapCommon\SoapMessage;
use BeSimple\SoapCommon\SoapRequest;
/**
* SoapRequest class for SoapClient. Provides factory function for request object.
*
* @author Andreas Schamberger <mail@andreass.net>
*/
class SoapRequestFactory extends SoapRequest
class SoapRequest extends CommonSoapRequest
{
/**
* Factory function for SoapRequest.
@ -28,14 +28,14 @@ class SoapRequestFactory extends SoapRequest
* @param string $content Content
* @param string $version SOAP version
*
* @return SoapRequest
* @return BeSimple\SoapClient\SoapRequest
*/
public static function create($content, $version)
{
$request = new SoapRequest();
// $content is if unmodified from SoapClient not a php string type!
$request->setContent($content);
$request->setContent((string) (null === $content ? file_get_contents("php://input") : $content));
$request->setLocation(self::getCurrentUrl());
$request->setAction(isset($_SERVER[SoapMessage::SOAP_ACTION_HEADER]) ? $_SERVER[SoapMessage::SOAP_ACTION_HEADER] : null);
$request->setVersion($version);

View File

@ -29,11 +29,10 @@ class SoapResponse extends CommonSoapResponse
* @param string $location Location
* @param string $action SOAP action
* @param string $version SOAP version
* @param array $attachments SOAP attachments
*
* @return SoapResponse
* @return BeSimple\SoapClient\SoapResponse
*/
public static function create($content, $location, $action, $version, $attachments = [])
public static function create($content, $location, $action, $version)
{
$response = new SoapResponse();
$response->setContent($content);
@ -49,11 +48,12 @@ class SoapResponse extends CommonSoapResponse
/**
* Send SOAP response to client.
*/
public function getResponseContent()
public function send()
{
// set Content-Type header
header('Content-Type: '.$this->getContentType());
return $this->getContent();
// send content to client
echo $this->getContent();
}
}

View File

@ -12,67 +12,73 @@
namespace BeSimple\SoapServer;
use BeSimple\SoapCommon\SoapOptions\SoapOptions;
use BeSimple\SoapCommon\SoapRequest;
use BeSimple\SoapServer\SoapOptions\SoapServerOptions;
use BeSimple\SoapCommon\Helper;
use BeSimple\SoapCommon\Converter\MtomTypeConverter;
use BeSimple\SoapCommon\Converter\SwaTypeConverter;
use Exception;
/**
* Extended SoapServer that allows adding filters for SwA, MTOM, ... .
*
* @author Andreas Schamberger <mail@andreass.net>
* @author Christian Kerl <christian-kerl@web.de>
* @author Petr Bechyně <petr.bechyne@vodafone.com>
*/
class SoapServer extends \SoapServer
{
const SOAP_SERVER_REQUEST_FAILED = false;
/**
* Soap version.
*
* @var int
*/
protected $soapVersion = SOAP_1_1;
protected $soapVersion;
protected $soapKernel;
/**
* Soap kernel.
*
* @var \BeSimple\SoapServer\SoapKernel
*/
protected $soapKernel = null;
/**
* Constructor.
*
* @param SoapServerOptions $soapServerOptions
* @param SoapOptions $soapOptions
* @param string $wsdl WSDL file
* @param array(string=>mixed) $options Options array
*/
public function __construct(SoapServerOptions $soapServerOptions, SoapOptions $soapOptions)
public function __construct($wsdl, array $options = array())
{
if ($soapOptions->hasAttachments()) {
$soapOptions = $this->configureMime($soapOptions);
// store SOAP version
if (isset($options['soap_version'])) {
$this->soapVersion = $options['soap_version'];
}
// create soap kernel instance
$this->soapKernel = new SoapKernel();
$this->soapVersion = $soapOptions->getSoapVersion();
parent::__construct(
$soapOptions->getWsdlFile(),
$soapServerOptions->toArray() + $soapOptions->toArray()
);
// set up type converter and mime filter
$this->configureMime($options);
// we want the exceptions option to be set
$options['exceptions'] = true;
parent::__construct($wsdl, $options);
}
/**
* Custom handle method to be able to modify the SOAP messages.
*
* @param string $request Request string
* @return string
*/
public function handle($request = null)
{
$soapRequest = SoapRequestFactory::create($request, $this->soapVersion);
// wrap request data in SoapRequest object
$soapRequest = SoapRequest::create($request, $this->soapVersion);
// handle actual SOAP request
try {
$soapResponse = $this->handleSoapRequest($soapRequest);
$soapResponse = $this->handle2($soapRequest);
} catch (\SoapFault $fault) {
// issue an error to the client
$this->fault($fault->faultcode, $fault->faultstring);
return self::SOAP_SERVER_REQUEST_FAILED;
}
return $soapResponse->getResponseContent();
// send SOAP response to client
$soapResponse->send();
}
/**
@ -84,15 +90,44 @@ class SoapServer extends \SoapServer
*
* @return SoapResponse
*/
private function handleSoapRequest(SoapRequest $soapRequest)
public function handle2(SoapRequest $soapRequest)
{
// run SoapKernel on SoapRequest
$this->soapKernel->filterRequest($soapRequest);
// call parent \SoapServer->handle() and buffer output
ob_start();
parent::handle($soapRequest->getContent());
$response = ob_get_clean();
$dom = \DOMDocument::loadXML($response);
/** @var \DOMElement $envelop */
$envelop = $dom->childNodes->item(0);
$ns1 = $envelop->getAttribute('xmlns:ns1');
$envelop->removeAttributeNS($ns1, 'ns1');
$envelop->prefix = 'soap';
$envelop->setAttribute('soap:encodingStyle', 'http://schemas.xmlsoap.org/soap/encoding/');
$envelop->setAttribute('xmlns:soapenc', 'http://schemas.xmlsoap.org/soap/encoding/');
$envelop->setAttribute('xmlns:xsd', 'http://www.w3.org/2001/XMLSchema');
$envelop->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
/** @var \DOMElement $body */
$body = $envelop->childNodes->item(0);
$body->prefix = 'soap';
/** @var \DOMElement $responseRoot */
$responseRoot = $body->childNodes->item(0);
$responseRoot->setAttribute('xmlns', $ns1);
$envelop->removeAttributeNS('http://schemas.xmlsoap.org/soap/envelope/', 'SOAP-ENV');
$envelop->removeAttributeNS('http://schemas.xmlsoap.org/soap/encoding/', 'SOAP-ENC');
$response = $dom->saveXML();
$response = preg_replace('/xsi:type="ns1:\w*"/', '', $response);
// Remove headers added by SoapServer::handle() method
header_remove('Content-Length');
header_remove('Content-Type');
@ -121,25 +156,43 @@ class SoapServer extends \SoapServer
return $this->soapKernel;
}
private function configureMime(SoapOptions $soapOptions)
/**
* Configure filter and type converter for SwA/MTOM.
*
* @param array &$options SOAP constructor options array.
*
* @return void
*/
private function configureMime(array &$options)
{
if ($soapOptions->getAttachmentType() !== SoapOptions::SOAP_ATTACHMENTS_TYPE_BASE64) {
$mimeFilter = new MimeFilter($soapOptions->getAttachmentType());
if (isset($options['attachment_type']) && Helper::ATTACHMENTS_TYPE_BASE64 !== $options['attachment_type']) {
// register mime filter in SoapKernel
$mimeFilter = new MimeFilter($options['attachment_type']);
$this->soapKernel->registerFilter($mimeFilter);
if ($soapOptions->getAttachmentType() === SoapOptions::SOAP_ATTACHMENTS_TYPE_SWA) {
// configure type converter
if (Helper::ATTACHMENTS_TYPE_SWA === $options['attachment_type']) {
$converter = new SwaTypeConverter();
$converter->setKernel($this->soapKernel);
$soapOptions->getTypeConverterCollection()->add($converter);
} elseif ($soapOptions->getAttachmentType() === SoapOptions::SOAP_ATTACHMENTS_TYPE_MTOM) {
$this->soapKernel->registerFilter(new XmlMimeFilter($soapOptions->getAttachmentType()));
} elseif (Helper::ATTACHMENTS_TYPE_MTOM === $options['attachment_type']) {
$xmlMimeFilter = new XmlMimeFilter($options['attachment_type']);
$this->soapKernel->registerFilter($xmlMimeFilter);
$converter = new MtomTypeConverter();
$converter->setKernel($this->soapKernel);
$soapOptions->getTypeConverterCollection()->add($converter);
} else {
throw new Exception('Unresolved SOAP_ATTACHMENTS_TYPE: ' . $soapOptions->getAttachmentType());
}
// configure typemap
if (!isset($options['typemap'])) {
$options['typemap'] = array();
}
$options['typemap'][] = array(
'type_name' => $converter->getTypeName(),
'type_ns' => $converter->getTypeNamespace(),
'from_xml' => function($input) use ($converter) {
return $converter->convertXmlToPhp($input);
},
'to_xml' => function($input) use ($converter) {
return $converter->convertPhpToXml($input);
},
);
}
}
return $soapOptions;
}
}

View File

@ -12,38 +12,189 @@
namespace BeSimple\SoapServer;
use BeSimple\SoapCommon\SoapOptions\SoapOptions;
use BeSimple\SoapServer\SoapOptions\SoapServerOptions;
use BeSimple\SoapCommon\AbstractSoapBuilder;
use BeSimple\SoapCommon\Helper;
/**
* SoapServerBuilder provides a SoapServer instance from SoapServerOptions and SoapOptions.
* SoapServerBuilder provides a fluent interface to configure and create a SoapServer instance.
*
* @author Petr Bechyně <petr.bechyne@vodafone.com>
* @author Christian Kerl <christian-kerl@web.de>
*/
class SoapServerBuilder
class SoapServerBuilder extends AbstractSoapBuilder
{
/**
* Builds a SoapServer instance.
*
* @param SoapServerOptions $soapServerOptions
* @param SoapOptions $soapOptions
*
* @return SoapServer
*/
public function build(SoapServerOptions $soapServerOptions, SoapOptions $soapOptions)
{
use_soap_error_handler($soapServerOptions->isErrorReporting());
protected $persistence;
protected $errorReporting;
$server = new SoapServer($soapServerOptions, $soapOptions);
if ($soapServerOptions->hasPersistence()) {
$server->setPersistence($soapServerOptions->getPersistence());
protected $handlerClass;
protected $handlerObject;
/**
* Create new instance with default options.
*
* @return \BeSimple\SoapServer\SoapServerBuilder
*/
static public function createWithDefaults()
{
return parent::createWithDefaults()
->withErrorReporting(false);
}
if ($soapServerOptions->hasHandlerClass()) {
$server->setClass($soapServerOptions->getHandlerClass());
} else if ($soapServerOptions->hasHandlerObject()) {
$server->setObject($soapServerOptions->getHandlerObject());
/**
* Initializes all options with the defaults used in the native SoapServer.
*/
public function __construct()
{
parent::__construct();
// TODO: this is not the default, but safer
$this->withErrorReporting(false);
}
/**
* Finally returns a SoapClient instance.
*
* @return \BeSimple\SoapServer\SoapServer
*/
public function build()
{
$this->validateOptions();
use_soap_error_handler($this->errorReporting);
$server = new SoapServer($this->wsdl, $this->getSoapOptions());
if (null !== $this->persistence) {
$server->setPersistence($this->persistence);
}
if (null !== $this->handlerClass) {
$server->setClass($this->handlerClass);
} elseif (null !== $this->handlerObject) {
$server->setObject($this->handlerObject);
}
return $server;
}
/**
* Cofigures the SOAP actor.
*
* @param string $actor Actor name
*
* @return \BeSimple\SoapServer\SoapServerBuilder
*/
public function withActor($actor)
{
$this->options['actor'] = $actor;
return $this;
}
/**
* Enables persistence.
*
* @return \BeSimple\SoapServer\SoapServerBuilder
*/
public function withPersistanceRequest()
{
$this->persistence = SOAP_PERSISTENCE_REQUEST;
return $this;
}
/**
* Enables the HTTP session. The handler object is persisted between multiple requests in a session.
*
* @return \BeSimple\SoapServer\SoapServerBuilder
*/
public function withPersistenceSession()
{
$this->persistence = SOAP_PERSISTENCE_SESSION;
return $this;
}
/**
* Enables reporting of internal errors to clients. This should only be enabled in development environments.
*
* @param boolean $enable Enable error reporting
*
* @return \BeSimple\SoapServer\SoapServerBuilder
*/
public function withErrorReporting($enable = true)
{
$this->errorReporting = $enable;
return $this;
}
/**
* SOAP attachment type Base64.
*
* @return \BeSimple\SoapServer\SoapServerBuilder
*/
public function withBase64Attachments()
{
$this->options['attachment_type'] = Helper::ATTACHMENTS_TYPE_BASE64;
return $this;
}
/**
* SOAP attachment type SwA.
*
* @return \BeSimple\SoapServer\SoapServerBuilder
*/
public function withSwaAttachments()
{
$this->options['attachment_type'] = Helper::ATTACHMENTS_TYPE_SWA;
return $this;
}
/**
* SOAP attachment type MTOM.
*
* @return \BeSimple\SoapServer\SoapServerBuilder
*/
public function withMtomAttachments()
{
$this->options['attachment_type'] = Helper::ATTACHMENTS_TYPE_MTOM;
return $this;
}
/**
* Configures the handler class or object.
*
* @param mixed $handler Can be either a class name or an object.
*
* @return \BeSimple\SoapServer\SoapServerBuilder
*/
public function withHandler($handler)
{
if (is_string($handler) && class_exists($handler)) {
$this->handlerClass = $handler;
$this->handlerObject = null;
} elseif (is_object($handler)) {
$this->handlerClass = null;
$this->handlerObject = $handler;
} else {
throw new \InvalidArgumentException('The handler has to be a class name or an object');
}
return $this;
}
/**
* Validate options.
*/
protected function validateOptions()
{
$this->validateWsdl();
if (null === $this->handlerClass && null === $this->handlerObject) {
throw new \InvalidArgumentException('The handler has to be configured!');
}
}
}

View File

@ -1,19 +0,0 @@
<?php
namespace BeSimple\SoapServer;
use BeSimple\SoapServer\SoapOptions\SoapServerOptions;
class SoapServerOptionsBuilder
{
static public function createWithDefaults($handlerClassOrObject)
{
return new SoapServerOptions(
$handlerClassOrObject,
SoapServerOptions::SOAP_SERVER_KEEP_ALIVE_OFF,
SoapServerOptions::SOAP_SERVER_ERROR_REPORTING_OFF,
SoapServerOptions::SOAP_SERVER_EXCEPTIONS_ON,
SoapServerOptions::SOAP_SERVER_PERSISTENCE_NONE
);
}
}

View File

@ -86,7 +86,7 @@ class WsSecurityFilter extends WsSecurityFilterClientServer implements SoapReque
$expires = $xpath->query($query, $security)->item(0);
if (null !== $expires) {
$expiresDatetime = \DateTime::createFromFormat(self::DATETIME_FORMAT, $expires->textContent, new \DateTimeZone('UTC'));
$expiresDatetime = \DateTime::createFromFormat(static::DATETIME_FORMAT, $expires->textContent, new \DateTimeZone('UTC'));
$currentDatetime = new \DateTime('now', new \DateTimeZone('UTC'));
if ($currentDatetime > $expiresDatetime) {
@ -170,7 +170,7 @@ class WsSecurityFilter extends WsSecurityFilterClientServer implements SoapReque
// init timestamp
$dt = new \DateTime('now', new \DateTimeZone('UTC'));
$createdTimestamp = $dt->format(self::DATETIME_FORMAT);
$createdTimestamp = $dt->format(static::DATETIME_FORMAT);
// create security header
$security = $filterHelper->createElement(Helper::NS_WSS, 'Security');
@ -182,7 +182,7 @@ class WsSecurityFilter extends WsSecurityFilterClientServer implements SoapReque
$timestamp->appendChild($created);
if (null !== $this->expires) {
$dt->modify('+' . $this->expires . ' seconds');
$expiresTimestamp = $dt->format(self::DATETIME_FORMAT);
$expiresTimestamp = $dt->format(static::DATETIME_FORMAT);
$expires = $filterHelper->createElement(Helper::NS_WSU, 'Expires', $expiresTimestamp);
$timestamp->appendChild($expires);
}

View File

@ -22,7 +22,7 @@
"require": {
"php": ">=5.3.0",
"ext-soap": "*",
"besimple/soap-common": "0.3.*",
"besimple/soap-common": "0.2.*",
"ass/xmlsecurity": "~1.0"
},
"autoload": {
@ -31,7 +31,7 @@
"target-dir": "BeSimple/SoapServer",
"extra": {
"branch-alias": {
"dev-master": "0.3-dev"
"dev-master": "0.2-dev"
}
}
}

View File

@ -61,7 +61,7 @@ abstract class AbstractVersion implements VersionInterface
{
if (!$this->bindingNode) {
$this->bindingNode = $this->document->createElement('binding');
$this->bindingNode->setAttribute('name', $this->name.'Binding');
$this->bindingNode->setAttribute('name', $this->name.'_SOAP');
$this->bindingNode->setAttribute('type', $this->portTypeName);
$this->addSoapBinding();
@ -75,7 +75,7 @@ abstract class AbstractVersion implements VersionInterface
if (!$this->servicePortNode) {
$this->servicePortNode = $this->document->createElement('port');
$this->servicePortNode->setAttribute('name', $this->name.'Port');
$this->servicePortNode->setAttribute('binding', $this->typeNs.':'.$this->name.'Binding');
$this->servicePortNode->setAttribute('binding', $this->typeNs.':'.$this->name.'_SOAP');
$this->addSoapAddress();
}

View File

@ -203,9 +203,9 @@ class Dumper
$partElement->setAttribute('name', $part->getName());
if ($type instanceof ComplexType) {
$partElement->setAttribute('type', static::TYPES_NS.':'.$type->getXmlType());
$partElement->setAttribute('element', static::TYPES_NS.':'.$type->getXmlType());
} else {
$partElement->setAttribute('type', $type);
$partElement->setAttribute('element', $type);
}
$messageElement->appendChild($partElement);
@ -217,11 +217,13 @@ class Dumper
protected function addComplexTypes()
{
$types = $this->document->createElement('types');
$types = $this->document->createElement(static::WSDL_NS . ':types');
$this->domDefinitions->appendChild($types);
$this->domSchema = $this->document->createElement(static::XSD_NS.':schema');
$this->domSchema->setAttribute('targetNamespace', $this->definition->getNamespace());
$this->domSchema->setAttribute('elementFormDefault', 'unqualified');
$this->domSchema->setAttribute(static::XML_NS.':'.static::XSD_NS, static::XSD_NS_URI);
$types->appendChild($this->domSchema);
foreach ($this->definition->getTypeRepository()->getComplexTypes() as $type) {
@ -236,13 +238,18 @@ class Dumper
$complexType = $this->document->createElement(static::XSD_NS.':complexType');
$complexType->setAttribute('name', $type->getXmlType());
$all = $this->document->createElement(static::XSD_NS.':'.($type instanceof ArrayOfType ? 'sequence' : 'all'));
$all = $this->document->createElement(static::XSD_NS.':'.'sequence');
$complexType->appendChild($all);
foreach ($type->all() as $child) {
$isArray = false;
$childType = $this->definition->getTypeRepository()->getType($child->getType());
if ($child->isAttribute()) {
$element = $this->document->createElement(static::XSD_NS.':attribute');
} else {
$element = $this->document->createElement(static::XSD_NS.':element');
}
$element->setAttribute('name', $child->getName());
if ($childType instanceof ComplexType) {
@ -257,16 +264,24 @@ class Dumper
}
if ($child->isNillable()) {
$element->setAttribute('nillable', 'true');
$element->setAttribute('minOccurs', 0);
} else {
$element->setAttribute('minOccurs', 1);
}
if ($type instanceof ArrayOfType) {
if ($type instanceof ArrayOfType || $isArray) {
$element->setAttribute('minOccurs', 0);
$element->setAttribute('maxOccurs', 'unbounded');
} else {
$element->setAttribute('maxOccurs', 1);
}
if ($child->isAttribute()) {
$complexType->appendChild($element);
} else {
$all->appendChild($element);
}
}
$this->domSchema->appendChild($complexType);
}

View File

@ -24,7 +24,7 @@
"target-dir": "BeSimple/SoapWsdl",
"extra": {
"branch-alias": {
"dev-master": "0.3-dev"
"dev-master": "0.2-dev"
}
}
}