147 Commits

Author SHA1 Message Date
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
4d7894b66e Composer info updated 2016-10-27 16:38:53 +02:00
0a157748a8 Large refactoring removing states, abstract magic, vague fluent interfaces 2016-10-27 16:24:44 +02:00
c4a9b58b08 Use request_stack service instead of deprecated request service 2016-01-11 16:28:20 +11:00
fc15bf36ab Call getPath() instead of string casting 2016-01-11 16:28:05 +11:00
8462233a08 Update FlattenException use statements 2016-01-11 16:20:13 +11:00
03227295f6 Use ContainerAwareTrait and ContainerAwareInterface 2016-01-11 16:09:42 +11:00
f285adfa14 Use path attribute rather than pattern 2016-01-11 16:06:22 +11:00
2ecdbe9714 Change package name and description 2016-01-08 11:47:15 +11:00
e5c54164c1 Support Symfony 3.* 2016-01-08 11:42:56 +11:00
27ebf0fbce Merge remote-tracking branch 'origin/0.2' 2015-06-01 16:04:32 +02:00
855e382590 [SoapBundle] Fixed service definition with factory 2015-06-01 16:04:29 +02:00
2974a4f838 Stop support of Symfony <2.6 2015-06-01 16:04:24 +02:00
fb56d51bd8 fixup! [SoapBundle] Set _format route to xml 2015-03-27 16:07:42 +01:00
f2a8a7ebb3 Merge branch '0.2' 2014-08-18 14:20:19 +02:00
7a9119cef1 [SoapBundle] Added configuration to configure client proxy 2014-08-18 13:54:19 +02:00
1a7f60b679 [SoapClient] Added proxy authentication option in SoapClientBuilder
Fixed #47
2014-08-18 11:45:53 +02:00
70db0c42fd [SoapClient] Renamed proxy_user to proxy_login option to keep compatibility with SoapClient of PHP 2014-08-18 10:58:40 +02:00
960c9f557a [SoapClient] Fixed typo for proxy options
Fixed #46
2014-08-18 10:31:20 +02:00
33d641de4e [SoapClient] Fixed tests if proxy exists in environment variables 2014-08-18 10:29:49 +02:00
bbd4c26134 Merge branch '0.2' 2014-08-16 15:48:45 +02:00
29170576c2 [Doc] Added documentation for SoapClient 2014-08-15 21:18:24 +02:00
50ab5a93ad [SoapBundle] Update the cache dir 2014-08-15 19:46:23 +02:00
1290573285 [SoapBundle] Add a hack to load BeSimpleSoap cache with a client 2014-08-15 19:45:21 +02:00
f5675f6ece [SoapBundle] Set cache informations even though it is disabled 2014-08-15 19:43:31 +02:00
15b208d861 Replaced cli_webserver_workaround client option by a check of CONTENT_TYPE AND HTTP_CONTENT_TYPE entry in SERVER superglobal 2014-08-14 21:40:13 +02:00
294a9fe90e Stop support of Symfony <2.3 2014-08-14 15:02:52 +02:00
313840d5a4 Revert "[SoapClient] [Tests] Down required version of symfony/filesystem vendor"
This reverts commit 726ee89936.

Conflicts:
	composer.json
	src/BeSimple/SoapClient/composer.json
2014-08-14 14:57:00 +02:00
8e4d8b0300 Updated BeSimpleSoap vendor requierement to 0.3 2014-08-14 14:56:39 +02:00
621b2080e0 Updated version to 0.3 2014-08-14 14:21:49 +02:00
fe67cff5ff Merge branch 'fixed_tests' 2014-08-14 13:39:12 +02:00
e676eb0432 [composer] Used a stable version of vfsStream 2014-08-14 13:33:32 +02:00
fd402731af Fixed travis configuration 2014-08-14 13:20:09 +02:00
f5d6f5e759 Updated travis 2014-08-14 13:06:18 +02:00
726ee89936 [SoapClient] [Tests] Down required version of symfony/filesystem vendor 2014-08-14 13:06:18 +02:00
36a368e695 [composer] Removed dev minimum-stability 2014-08-14 13:06:18 +02:00
e2de214ea5 [SoapClient] [Tests] Removed uploaded file 2014-08-14 13:06:18 +02:00
3cfeea8371 [SoapClient] [Tests] Fixed typo 2014-08-14 13:06:18 +02:00
c24e8775bf [SoapClient] Add possiblity to disable proxy if present in environment variable 2014-08-14 13:05:50 +02:00
b3c6353af8 [composer] Removed dev minimum-stability 2014-08-14 13:05:50 +02:00
10bba6a3bd [Test] Force replace unziped file for axis server 2014-08-13 22:08:17 +02:00
a122f2a0f6 [SoapBundle] Skipped test for SoapRequest 2014-08-13 22:02:46 +02:00
8d13d2dd5c [Doc] Add missing methods 2014-08-13 06:57:03 +02:00
1c1cd377f1 Merge branch 'ch3ric-fix-doc' 2014-08-13 06:55:07 +02:00
1efa348ae5 Merge branch 'fix-doc' of https://github.com/ch3ric/BeSimpleSoap into ch3ric-fix-doc 2014-08-13 06:55:01 +02:00
ccc4d57501 Merge pull request #42 from mremi/handle-request-options
Handled additional request options
2014-07-10 16:16:27 +02:00
740f146bad Handled additional request options 2014-07-01 15:47:46 +02:00
5e8e669299 Fix Doc 2014-05-25 16:48:26 +02:00
e4cb612aed Created a client classmap even when it is empty 2014-04-30 10:54:07 +02:00
59ea6b1ce0 Merge pull request #33 from borissamardzija/master
Update Curl class to support CURLOPT_CAINFO and CURLOPT_CAPATH options
2014-04-25 13:36:04 +02:00
7bb849e394 Update Curl class to support CURLOPT_CAINFO and CURLOPT_CAPATH options 2014-04-25 11:16:34 +02:00
02722eae8f Merge pull request #32 from mremi/filter-request-headers
Added method to tweak HTTP headers of request
2014-04-24 17:22:00 +02:00
b7581f29b0 Removed reference to return an array of HTTP headers 2014-04-24 09:29:58 +02:00
dc6e1e0889 Merge pull request #31 from mremi/http-auth
Added extra option to configure HTTP authentication
2014-04-24 08:38:03 +02:00
f986400dc2 Merge pull request #30 from mremi/method-visibility
Updated visibility from private to protected
2014-04-24 08:36:23 +02:00
67410805ba Added method to tweak HTTP headers of request 2014-04-22 22:29:11 +02:00
2ae7515294 Added extra option to configure HTTP authentication 2014-04-22 22:05:04 +02:00
cdfc3cd5bd Updated visibility from private to protected 2014-04-22 21:53:14 +02:00
9ac755d86e [SoapBundle] Set _format route to xml 2013-12-13 15:25:20 +01:00
321dcf3058 [SoapBundle] Set wsdl request type only if the "wsdl" parameter in present in URL 2013-12-13 11:24:25 +01:00
3a2b8e32ee [SoapBundle] Enhanced SoapFault management 2013-12-13 08:26:18 +01:00
fd5154a469 Fixed typo 2013-12-10 15:25:09 +01:00
188f282a87 [SoapWsdl] Specify an empty message if the input is empty 2013-12-10 15:25:09 +01:00
f9e230400d [SoapBundle] Fixed RpcLiteralRequestMessageBinderTest 2013-12-08 00:27:33 +01:00
1b4e262c60 [SoapBundle] Simplified conditional statement 2013-12-08 00:25:20 +01:00
60e3714602 [SoapBundle][SoapCommon] Removed unused TypeRepository in Definition\Method classes 2013-12-08 00:23:30 +01:00
1e82d7fdd7 Fix WSDL schema stylesheet including 2013-12-07 19:55:33 +01:00
8788d7595d Cleaned unused method, property, variable or parameter 2013-12-02 16:02:29 +01:00
b45224587a Added use TypeConverterInterface 2013-12-02 15:39:59 +01:00
c8baf14c75 Fixed typo 2013-12-02 15:39:39 +01:00
53849c68e0 Removed unused use statement 2013-12-02 15:35:36 +01:00
ecdf7e1872 Fixed an undefined variable
Fixed commit c830097430
Fixed issue #22
2013-11-20 08:45:12 +01:00
dcfca04db4 Merge remote-tracking branch 'origin/exception'
Conflicts:
	composer.json
	src/BeSimple/SoapBundle/composer.json
2013-11-15 10:37:55 +01:00
73efa627ce Removed old Zend dependencies in composer.json 2013-11-13 16:43:47 +01:00
d6752a41e9 [SoapWsdl] Fixed bad usage name of type included in an other ComplexType
Fixed issue #21
2013-11-13 15:46:50 +01:00
6a6661012e [SoapBundle] Enhanced exception management with Symfony2 2013-11-13 15:02:30 +01:00
10705aa9e2 Merge branch '0.1'
Conflicts:
	src/BeSimple/SoapServer/SoapResponse.php
2013-11-06 16:50:31 +01:00
bd2508f854 Merge branch '0.1' 2013-11-06 09:31:58 +01:00
259c23ee4d Merge pull request #20 from hason/patch-1
Fixed typo
2013-11-01 07:42:46 -07:00
98842b24f8 Fixed typo 2013-11-01 13:40:16 +01:00
48d305e7db Merge branch 'wsse' of https://github.com/aschamberger/BeSimpleSoap into aschamberger-wsse
Conflicts:
	.travis.yml
	src/BeSimple/SoapServer/SoapRequest.php
2013-10-30 17:00:44 +01:00
4a97c2537e Merge branch 'patch-1' of https://github.com/rekky/BeSimpleSoap into rekky-patch-1 2013-10-30 16:51:54 +01:00
b8f37d213e Fixed version besimple/soap-* version in README's 2013-10-30 16:49:47 +01:00
6e967fe811 Merge branch 'readmes' of https://github.com/aschamberger/BeSimpleSoap into aschamberger-readmes 2013-10-30 16:47:25 +01:00
00c8726bb2 Updated composer.json 2013-10-30 16:35:17 +01:00
dc4c9d2191 remove travis status in readme 2013-10-30 15:54:35 +01:00
9ddac35f18 Fix doc comment 2013-10-30 12:31:00 +01:00
ed0846da6f Fix byte length counting if strlen overloading is used 2013-10-30 11:18:22 +04:00
9827e58c56 Merge pull request #19 from rolebi/patch-1
Adding besimple/soap-wsdl to dependencies
2013-10-28 03:40:34 -07:00
e7d86b3e99 Adding besimple/soap-wsdl to dependencies
Fix missing `BeSimple\SoapWsdl\Dumper\Dumper` class in `BeSimple\SoapBundle\WebServiceContext`
2013-10-28 11:09:23 +01:00
54894b2fd9 Fixed ComplexType without alias name
Fix issue #17
2013-10-17 13:51:31 +02:00
95efa3e366 Fixed CS 2013-10-17 13:48:02 +02:00
c830097430 Rewrited Definition of WebService and WSDL dumper 2013-10-15 11:46:12 +02:00
391a3f10b8 Merge branch '0.1' 2013-10-15 11:15:10 +02:00
ebd565cd79 Fixed besimple/soap-* version 2013-09-27 21:35:39 +02:00
81ce2d7571 Merge branch '0.1' 2013-09-27 21:33:11 +02:00
461675042a fix soap header handling 2013-08-30 22:07:44 +02:00
f5a8573a4e Add readme files 2013-08-24 17:38:18 +02:00
1f1d513ea3 remove obsolete file 2013-08-24 11:01:59 +02:00
8e305da9c0 change url for check if axis is ready 2013-08-24 10:53:24 +02:00
dc3fcaf81f remove ini and try axis build 2013-08-24 09:47:34 +02:00
d8d6ca5795 fix problem with cache path 2013-08-24 09:39:56 +02:00
cbd49c3342 add php ini for travis and update travis config 2013-08-24 09:21:13 +02:00
e9388023c1 add php ini for travis and update travis config 2013-08-24 09:05:58 +02:00
76360c193e add php ini for travis and update travis config 2013-08-24 09:03:23 +02:00
155956ca04 change file permissions 2013-08-24 08:44:10 +02:00
3f41d4b7bc change file permissions 2013-08-24 08:41:59 +02:00
4fa7fd759a add phpwebserver to travis 2013-08-24 08:24:31 +02:00
2b12264304 Axis and SoapServer interop tests 2013-08-23 23:25:16 +02:00
f8a80c0361 fix wrong method call 2013-08-23 21:45:09 +02:00
19def707d3 use WsSecurityFilterClientServer from common 2013-08-23 20:59:35 +02:00
b21d758e77 remove now obsolete code 2013-08-23 20:57:37 +02:00
9bc9410172 add BinarySecurityToken resolving to key resolver 2013-08-23 20:57:07 +02:00
1cde22d8bb fix securitykey factory method call 2013-08-23 20:56:37 +02:00
8d62141a3d add workaround for cli webserver option 2013-08-23 20:55:26 +02:00
96f8415b92 added encryption to WS Security 2013-08-23 20:48:13 +02:00
5229091033 further steps with WS-Security (signing) 2013-08-23 20:47:55 +02:00
580e6f4da7 added WsSecurityFilter that supports UsernamePassword for now 2013-08-23 20:47:41 +02:00
6a5109c526 fix SoapFault handling to throw proper soap fault to client 2013-08-23 20:47:20 +02:00
5e6fd5de25 extract common code used in client/server 2013-08-23 20:43:48 +02:00
ee321fca42 fix missing 'cid:' in SwaTypeConverter 2013-08-23 20:43:22 +02:00
b5af83f9d2 add workaround for cli webserver, option implemented in client 2013-08-23 20:30:50 +02:00
9f9bab242d Fixed composer.json 2013-08-16 17:36:03 +02:00
f792f53aa2 Added composer.json 2013-08-16 17:18:40 +02:00
4d9b112bb5 Updated version to 0.2 2013-08-16 17:18:31 +02:00
254 changed files with 11744 additions and 5299 deletions

2
.gitignore vendored
View File

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

View File

@ -4,20 +4,22 @@ php:
- 5.3
- 5.4
- 5.5
- 5.6
env:
- SYMFONY_VERSION=2.0.*
- SYMFONY_VERSION=2.1.*
- SYMFONY_VERSION=2.2.*
- SYMFONY_VERSION=2.3.*
- SYMFONY_VERSION=dev-master
- SYMFONY_VERSION=2.6.*
- SYMFONY_VERSION="dev-master symfony/debug:~2.7@dev symfony/http-kernel:~2.7@dev"
before_script:
- composer require symfony/http-foundation:${SYMFONY_VERSION} --no-interaction --prefer-source
- composer self-update
- composer require symfony/framework-bundle:${SYMFONY_VERSION} --no-update
- composer update --no-interaction --prefer-source
- ./src/BeSimple/SoapClient/Tests/bin/phpwebserver.sh
- ./src/BeSimple/SoapClient/Tests/bin/axis.sh
script:
- phpunit --coverage-text
matrix:
allow_failures:
- env: SYMFONY_VERSION=dev-master
- env: SYMFONY_VERSION="dev-master symfony/debug:~2.7@dev symfony/http-kernel:~2.7@dev"

124
README.md Normal file
View File

@ -0,0 +1,124 @@
# BeSimpleSoap
Build SOAP and WSDL based web services.
This fork from 2017 is a refactored version that fixes a lot of errors and provides
better APi, more robust, stable and modern codebase.
See [How to use](#how-to-use) that will help you to understand the magic.
# Components
BeSimpleSoap consists of five components ...
## BeSimpleSoapClient
**Refactored** BeSimpleSoapClient is a component that extends the native PHP SoapClient with further features like SwA and WS-Security.
## BeSimpleSoapServer
**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
Currently **unsupported!** For further information see the [README](https://github.com/BeSimple/BeSimpleSoap/blob/master/src/BeSimple/SoapWsdl/README.md).
## BeSimpleSoapBundle
Currently **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).
*May not work properly since the Symfony libraries were removed.*
# Installation
If you do not yet have composer, install it like this:
```sh
curl -s http://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin
```
Create a `composer.json` file:
```json
{
"require": {
"besimple/soap": "0.2.*@dev"
}
}
```
Now you are ready to install the library:
```sh
php /usr/local/bin/composer.phar install
```
# How to use
You can investigate the unit tests in order to get a clue.
Forget about associative arrays, multiple extension and silent errors!
```
// unit tests for soap client
BeSimple\SoapClient\Tests\SoapClientBuilderTest
// unit tests for soap server
BeSimple\SoapServer\Tests\SoapServerBuilderTest
```
## Small 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
```
### Something wrong?!
Turn on the tracking and catch `SoapFaultWithTracingData` exception to get some sweets :)
```php
try {
$soapResponse = $soapClient->soapCall('GetUKLocationByCounty', [$getUKLocationByCountyRequest]);
} catch (SoapFaultWithTracingData $fault) {
var_dump($fault->getSoapResponseTracingData()->getLastRequest());
}
```
## Small example of soap server handling
Starting a SOAP server is a bit more complex.
I would suggest you to inspect SoapServer unit tests to get complete image.
But don't be scared too much, you just need to create a DummyService that will
handle your client SOAP calls.
```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',
'<your><soap><request><here /></request></soap></your>'
);
$response = $soapServer->handleRequest($request);
var_dump($response); // Contains Response, Attachments
```

2
cache/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -1,9 +1,8 @@
{
"name": "besimple/soap",
"name": "tuscanicz/soap",
"type": "library",
"description": "Build and consume SOAP and WSDL based web services",
"keywords": ["soap"],
"homepage": "http://besim.pl",
"description": "A fork of the abandoned besimple/soap package with added support for Symfony 3.0",
"keywords": ["soap", "soap server", "soap client"],
"license": "MIT",
"authors": [
{
@ -17,37 +16,37 @@
{
"name": "Andreas Schamberger",
"email": "mail@andreass.net"
},
{
"name": "Petr Bechyně",
"email": "mail@petrbechyne.com"
}
],
"require": {
"php": ">=5.3.0",
"php": ">=5.3.0|>=7.0",
"ext-soap": "*",
"ext-curl": "*",
"ass/xmlsecurity": "dev-master",
"symfony/framework-bundle": "~2.0",
"zendframework/zend-mail": "2.1.*",
"zendframework/zend-mime": "2.1.*",
"zendframework/zend-soap": "2.1.*"
"ass/xmlsecurity": "~1.0",
"zendframework/zend-mime": "2.1.*"
},
"replace": {
"besimple/soap-bundle": "self.version",
"besimple/soap-client": "self.version",
"besimple/soap-common": "self.version",
"besimple/soap-server": "self.version"
"besimple/soap-server": "self.version",
"besimple/soap-wsdl": "self.version"
},
"require-dev": {
"ext-mcrypt": "*",
"mikey179/vfsStream": "dev-master",
"symfony/filesystem": "~2.3",
"symfony/process": "~2.3"
"mikey179/vfsStream": "~1.0",
"phpunit/phpunit": "~4.0"
},
"autoload": {
"psr-0": { "BeSimple\\": "src/" }
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "0.1-dev"
"dev-master": "0.4-dev"
}
}
}

1349
composer.lock generated Normal file
View File

@ -0,0 +1,1349 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "7601f1ec280c086361e1deaf021f8ca1",
"content-hash": "59372f34124af17d5c35938dfffc6075",
"packages": [
{
"name": "ass/xmlsecurity",
"version": "v1.1.1",
"source": {
"type": "git",
"url": "https://github.com/aschamberger/XmlSecurity.git",
"reference": "c8976519ebbf6e4d953cd781d09df44b7f65fbb8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aschamberger/XmlSecurity/zipball/c8976519ebbf6e4d953cd781d09df44b7f65fbb8",
"reference": "c8976519ebbf6e4d953cd781d09df44b7f65fbb8",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"lib-openssl": ">=0.9.0",
"php": ">=5.3.0"
},
"require-dev": {
"satooshi/php-coveralls": "dev-master"
},
"suggest": {
"ext-mcrypt": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-0": {
"ass\\XmlSecurity": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Robert Richards",
"email": "rrichards@cdatazone.org"
},
{
"name": "Andreas Schamberger",
"email": "mail@andreass.net"
}
],
"description": "The XmlSecurity library is written in PHP for working with XML Encryption and Signatures",
"homepage": "https://github.com/aschamberger/XmlSecurity",
"keywords": [
"encryption",
"security",
"signature",
"xml"
],
"time": "2015-05-31 10:10:35"
},
{
"name": "zendframework/zend-mime",
"version": "2.1.6",
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-mime.git",
"reference": "066d6eecff586a7fb10e8907c032beaf1a9d6104"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-mime/zipball/066d6eecff586a7fb10e8907c032beaf1a9d6104",
"reference": "066d6eecff586a7fb10e8907c032beaf1a9d6104",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"zendframework/zend-stdlib": "self.version"
},
"require-dev": {
"fabpot/php-cs-fixer": "1.7.*",
"phpunit/phpunit": "~4.0",
"satooshi/php-coveralls": "dev-master",
"zendframework/zend-mail": "self.version"
},
"suggest": {
"zendframework/zend-mail": "Zend\\Mail component"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.1-dev",
"dev-develop": "2.2-dev"
}
},
"autoload": {
"psr-4": {
"Zend\\Mime\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"homepage": "https://github.com/zendframework/zend-mime",
"keywords": [
"mime",
"zf2"
],
"time": "2013-04-17 13:32:54"
},
{
"name": "zendframework/zend-stdlib",
"version": "2.1.6",
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-stdlib.git",
"reference": "0027339961ad3d49f91ee092e23f7269c18cb470"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/0027339961ad3d49f91ee092e23f7269c18cb470",
"reference": "0027339961ad3d49f91ee092e23f7269c18cb470",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"fabpot/php-cs-fixer": "1.7.*",
"phpunit/phpunit": "~4.0",
"satooshi/php-coveralls": "dev-master",
"zendframework/zend-eventmanager": "self.version",
"zendframework/zend-filter": "self.version",
"zendframework/zend-serializer": "self.version",
"zendframework/zend-servicemanager": "self.version"
},
"suggest": {
"pecl-weakref": "Implementation of weak references for Stdlib\\CallbackHandler",
"zendframework/zend-eventmanager": "To support aggregate hydrator usage",
"zendframework/zend-filter": "To support naming strategy hydrator usage",
"zendframework/zend-serializer": "Zend\\Serializer component",
"zendframework/zend-servicemanager": "To support hydrator plugin manager usage"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.1-dev",
"dev-develop": "2.2-dev"
}
},
"autoload": {
"psr-4": {
"Zend\\Stdlib\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"homepage": "https://github.com/zendframework/zend-stdlib",
"keywords": [
"stdlib",
"zf2"
],
"time": "2013-04-17 13:32:54"
}
],
"packages-dev": [
{
"name": "doctrine/instantiator",
"version": "1.0.5",
"source": {
"type": "git",
"url": "https://github.com/doctrine/instantiator.git",
"reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
"reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
"shasum": ""
},
"require": {
"php": ">=5.3,<8.0-DEV"
},
"require-dev": {
"athletic/athletic": "~0.1.8",
"ext-pdo": "*",
"ext-phar": "*",
"phpunit/phpunit": "~4.0",
"squizlabs/php_codesniffer": "~2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com",
"homepage": "http://ocramius.github.com/"
}
],
"description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
"homepage": "https://github.com/doctrine/instantiator",
"keywords": [
"constructor",
"instantiate"
],
"time": "2015-06-14 21:17:01"
},
{
"name": "mikey179/vfsStream",
"version": "v1.6.4",
"source": {
"type": "git",
"url": "https://github.com/mikey179/vfsStream.git",
"reference": "0247f57b2245e8ad2e689d7cee754b45fbabd592"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mikey179/vfsStream/zipball/0247f57b2245e8ad2e689d7cee754b45fbabd592",
"reference": "0247f57b2245e8ad2e689d7cee754b45fbabd592",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "~4.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.6.x-dev"
}
},
"autoload": {
"psr-0": {
"org\\bovigo\\vfs\\": "src/main/php"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Frank Kleine",
"homepage": "http://frankkleine.de/",
"role": "Developer"
}
],
"description": "Virtual file system to mock the real file system in unit tests.",
"homepage": "http://vfs.bovigo.org/",
"time": "2016-07-18 14:02:57"
},
{
"name": "phpdocumentor/reflection-common",
"version": "1.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionCommon.git",
"reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c",
"reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c",
"shasum": ""
},
"require": {
"php": ">=5.5"
},
"require-dev": {
"phpunit/phpunit": "^4.6"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"phpDocumentor\\Reflection\\": [
"src"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jaap van Otterdijk",
"email": "opensource@ijaap.nl"
}
],
"description": "Common reflection classes used by phpdocumentor to reflect the code structure",
"homepage": "http://www.phpdoc.org",
"keywords": [
"FQSEN",
"phpDocumentor",
"phpdoc",
"reflection",
"static analysis"
],
"time": "2015-12-27 11:43:31"
},
{
"name": "phpdocumentor/reflection-docblock",
"version": "3.1.1",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e",
"reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e",
"shasum": ""
},
"require": {
"php": ">=5.5",
"phpdocumentor/reflection-common": "^1.0@dev",
"phpdocumentor/type-resolver": "^0.2.0",
"webmozart/assert": "^1.0"
},
"require-dev": {
"mockery/mockery": "^0.9.4",
"phpunit/phpunit": "^4.4"
},
"type": "library",
"autoload": {
"psr-4": {
"phpDocumentor\\Reflection\\": [
"src/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mike van Riel",
"email": "me@mikevanriel.com"
}
],
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"time": "2016-09-30 07:12:33"
},
{
"name": "phpdocumentor/type-resolver",
"version": "0.2.1",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb",
"reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb",
"shasum": ""
},
"require": {
"php": ">=5.5",
"phpdocumentor/reflection-common": "^1.0"
},
"require-dev": {
"mockery/mockery": "^0.9.4",
"phpunit/phpunit": "^5.2||^4.8.24"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"phpDocumentor\\Reflection\\": [
"src/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mike van Riel",
"email": "me@mikevanriel.com"
}
],
"time": "2016-11-25 06:54:22"
},
{
"name": "phpspec/prophecy",
"version": "v1.6.2",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "6c52c2722f8460122f96f86346600e1077ce22cb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/6c52c2722f8460122f96f86346600e1077ce22cb",
"reference": "6c52c2722f8460122f96f86346600e1077ce22cb",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"php": "^5.3|^7.0",
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2",
"sebastian/comparator": "^1.1",
"sebastian/recursion-context": "^1.0|^2.0"
},
"require-dev": {
"phpspec/phpspec": "^2.0",
"phpunit/phpunit": "^4.8 || ^5.6.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.6.x-dev"
}
},
"autoload": {
"psr-0": {
"Prophecy\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Konstantin Kudryashov",
"email": "ever.zet@gmail.com",
"homepage": "http://everzet.com"
},
{
"name": "Marcello Duarte",
"email": "marcello.duarte@gmail.com"
}
],
"description": "Highly opinionated mocking framework for PHP 5.3+",
"homepage": "https://github.com/phpspec/prophecy",
"keywords": [
"Double",
"Dummy",
"fake",
"mock",
"spy",
"stub"
],
"time": "2016-11-21 14:58:47"
},
{
"name": "phpunit/php-code-coverage",
"version": "2.2.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979",
"reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"phpunit/php-file-iterator": "~1.3",
"phpunit/php-text-template": "~1.2",
"phpunit/php-token-stream": "~1.3",
"sebastian/environment": "^1.3.2",
"sebastian/version": "~1.0"
},
"require-dev": {
"ext-xdebug": ">=2.1.4",
"phpunit/phpunit": "~4"
},
"suggest": {
"ext-dom": "*",
"ext-xdebug": ">=2.2.1",
"ext-xmlwriter": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.2.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
"homepage": "https://github.com/sebastianbergmann/php-code-coverage",
"keywords": [
"coverage",
"testing",
"xunit"
],
"time": "2015-10-06 15:47:00"
},
{
"name": "phpunit/php-file-iterator",
"version": "1.4.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
"reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5",
"reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "FilterIterator implementation that filters files based on a list of suffixes.",
"homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
"keywords": [
"filesystem",
"iterator"
],
"time": "2016-10-03 07:40:28"
},
{
"name": "phpunit/php-text-template",
"version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-text-template.git",
"reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
"reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Simple template engine.",
"homepage": "https://github.com/sebastianbergmann/php-text-template/",
"keywords": [
"template"
],
"time": "2015-06-21 13:50:34"
},
{
"name": "phpunit/php-timer",
"version": "1.0.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-timer.git",
"reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260",
"reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4|~5"
},
"type": "library",
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Utility class for timing",
"homepage": "https://github.com/sebastianbergmann/php-timer/",
"keywords": [
"timer"
],
"time": "2016-05-12 18:03:57"
},
{
"name": "phpunit/php-token-stream",
"version": "1.4.9",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
"reference": "3b402f65a4cc90abf6e1104e388b896ce209631b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3b402f65a4cc90abf6e1104e388b896ce209631b",
"reference": "3b402f65a4cc90abf6e1104e388b896ce209631b",
"shasum": ""
},
"require": {
"ext-tokenizer": "*",
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Wrapper around PHP's tokenizer extension.",
"homepage": "https://github.com/sebastianbergmann/php-token-stream/",
"keywords": [
"tokenizer"
],
"time": "2016-11-15 14:06:22"
},
{
"name": "phpunit/phpunit",
"version": "4.8.34",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "7eb45205d27edd94bd2b3614085ea158bd1e2bca"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7eb45205d27edd94bd2b3614085ea158bd1e2bca",
"reference": "7eb45205d27edd94bd2b3614085ea158bd1e2bca",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-json": "*",
"ext-pcre": "*",
"ext-reflection": "*",
"ext-spl": "*",
"php": ">=5.3.3",
"phpspec/prophecy": "^1.3.1",
"phpunit/php-code-coverage": "~2.1",
"phpunit/php-file-iterator": "~1.4",
"phpunit/php-text-template": "~1.2",
"phpunit/php-timer": "^1.0.6",
"phpunit/phpunit-mock-objects": "~2.3",
"sebastian/comparator": "~1.2.2",
"sebastian/diff": "~1.2",
"sebastian/environment": "~1.3",
"sebastian/exporter": "~1.2",
"sebastian/global-state": "~1.0",
"sebastian/version": "~1.0",
"symfony/yaml": "~2.1|~3.0"
},
"suggest": {
"phpunit/php-invoker": "~1.1"
},
"bin": [
"phpunit"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.8.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "The PHP Unit Testing framework.",
"homepage": "https://phpunit.de/",
"keywords": [
"phpunit",
"testing",
"xunit"
],
"time": "2017-01-26 16:15:36"
},
{
"name": "phpunit/phpunit-mock-objects",
"version": "2.3.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
"reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983",
"reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"php": ">=5.3.3",
"phpunit/php-text-template": "~1.2",
"sebastian/exporter": "~1.2"
},
"require-dev": {
"phpunit/phpunit": "~4.4"
},
"suggest": {
"ext-soap": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Mock Object library for PHPUnit",
"homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
"keywords": [
"mock",
"xunit"
],
"time": "2015-10-02 06:51:40"
},
{
"name": "sebastian/comparator",
"version": "1.2.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be",
"reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"sebastian/diff": "~1.2",
"sebastian/exporter": "~1.2 || ~2.0"
},
"require-dev": {
"phpunit/phpunit": "~4.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Volker Dusch",
"email": "github@wallbash.com"
},
{
"name": "Bernhard Schussek",
"email": "bschussek@2bepublished.at"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Provides the functionality to compare PHP values for equality",
"homepage": "http://www.github.com/sebastianbergmann/comparator",
"keywords": [
"comparator",
"compare",
"equality"
],
"time": "2017-01-29 09:50:25"
},
{
"name": "sebastian/diff",
"version": "1.4.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "13edfd8706462032c2f52b4b862974dd46b71c9e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e",
"reference": "13edfd8706462032c2f52b4b862974dd46b71c9e",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.8"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Kore Nordmann",
"email": "mail@kore-nordmann.de"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Diff implementation",
"homepage": "https://github.com/sebastianbergmann/diff",
"keywords": [
"diff"
],
"time": "2015-12-08 07:14:41"
},
{
"name": "sebastian/environment",
"version": "1.3.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
"reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea",
"reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea",
"shasum": ""
},
"require": {
"php": "^5.3.3 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8 || ^5.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.3.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Provides functionality to handle HHVM/PHP environments",
"homepage": "http://www.github.com/sebastianbergmann/environment",
"keywords": [
"Xdebug",
"environment",
"hhvm"
],
"time": "2016-08-18 05:49:44"
},
{
"name": "sebastian/exporter",
"version": "1.2.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "42c4c2eec485ee3e159ec9884f95b431287edde4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4",
"reference": "42c4c2eec485ee3e159ec9884f95b431287edde4",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"sebastian/recursion-context": "~1.0"
},
"require-dev": {
"ext-mbstring": "*",
"phpunit/phpunit": "~4.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.3.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Volker Dusch",
"email": "github@wallbash.com"
},
{
"name": "Bernhard Schussek",
"email": "bschussek@2bepublished.at"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
"name": "Adam Harvey",
"email": "aharvey@php.net"
}
],
"description": "Provides the functionality to export PHP variables for visualization",
"homepage": "http://www.github.com/sebastianbergmann/exporter",
"keywords": [
"export",
"exporter"
],
"time": "2016-06-17 09:04:28"
},
{
"name": "sebastian/global-state",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
"reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4",
"reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.2"
},
"suggest": {
"ext-uopz": "*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Snapshotting of global state",
"homepage": "http://www.github.com/sebastianbergmann/global-state",
"keywords": [
"global state"
],
"time": "2015-10-12 03:26:01"
},
{
"name": "sebastian/recursion-context",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
"reference": "913401df809e99e4f47b27cdd781f4a258d58791"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791",
"reference": "913401df809e99e4f47b27cdd781f4a258d58791",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
"name": "Adam Harvey",
"email": "aharvey@php.net"
}
],
"description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
"time": "2015-11-11 19:50:13"
},
{
"name": "sebastian/version",
"version": "1.0.6",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/version.git",
"reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
"reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
"shasum": ""
},
"type": "library",
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version",
"time": "2015-06-21 13:59:46"
},
{
"name": "symfony/yaml",
"version": "v3.2.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "50eadbd7926e31842893c957eca362b21592a97d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/50eadbd7926e31842893c957eca362b21592a97d",
"reference": "50eadbd7926e31842893c957eca362b21592a97d",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"require-dev": {
"symfony/console": "~2.8|~3.0"
},
"suggest": {
"symfony/console": "For validating YAML files using the lint command"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Yaml\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2017-01-03 13:51:32"
},
{
"name": "webmozart/assert",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/webmozart/assert.git",
"reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f",
"reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f",
"shasum": ""
},
"require": {
"php": "^5.3.3 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "^4.6",
"sebastian/version": "^1.0.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.3-dev"
}
},
"autoload": {
"psr-4": {
"Webmozart\\Assert\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Bernhard Schussek",
"email": "bschussek@gmail.com"
}
],
"description": "Assertions to validate method input/output with nice error messages.",
"keywords": [
"assert",
"check",
"validate"
],
"time": "2016-11-23 20:04:58"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=5.3.0|>=7.0",
"ext-soap": "*",
"ext-curl": "*"
},
"platform-dev": {
"ext-mcrypt": "*"
}
}

View File

@ -9,6 +9,7 @@
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
stderr="true"
bootstrap="vendor/autoload.php"
>
@ -18,17 +19,8 @@
<testsuites>
<testsuite name="BeSimpleSoap Test Suite">
<directory>./src/BeSimple/*/Tests/</directory>
<file>src/BeSimple/SoapClient/Tests/SoapClientBuilderTest.php</file>
<file>src/BeSimple/SoapServer/Tests/SoapServerBuilderTest.php</file>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./src/BeSimple/</directory>
<exclude>
<directory>./src/BeSimple/*/Tests</directory>
<directory>./src/BeSimple/*/Resources</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

View File

@ -13,28 +13,44 @@
namespace BeSimple\SoapBundle;
use BeSimple\SoapCommon\Cache as BaseCache;
use BeSimple\SoapCommon\SoapOptions\SoapOptions;
use Exception;
/**
* @author Francis Besset <francis.besset@gmail.com>
*/
class Cache
{
public function __construct($cacheDisabled, $type, $directory, $lifetime = null, $limit = null)
public function __construct(SoapOptions $soapOptions)
{
$isEnabled = (Boolean) $cacheDisabled ? BaseCache::DISABLED : BaseCache::ENABLED;
if ($soapOptions->isWsdlCached()) {
$isEnabled = (bool)$soapOptions->isWsdlCached() ? BaseCache::ENABLED : BaseCache::DISABLED;
BaseCache::setEnabled($isEnabled);
BaseCache::setEnabled($isEnabled);
BaseCache::setType($soapOptions->getWsdlCacheType());
BaseCache::setDirectory($soapOptions->getWsdlCacheDir());
} else {
BaseCache::setEnabled(BaseCache::DISABLED);
BaseCache::setType(SoapOptions::SOAP_CACHE_TYPE_NONE);
BaseCache::setDirectory(null);
}
}
if (BaseCache::ENABLED == BaseCache::isEnabled()) {
BaseCache::setType($type);
BaseCache::setDirectory($directory);
if (null !== $lifetime) {
BaseCache::setLifetime($lifetime);
public function validateSettings(SoapOptions $soapOptions)
{
if ($soapOptions->isWsdlCached()) {
if (BaseCache::isEnabled() !== true) {
throw new Exception('WSDL cache could not be set');
}
if (null !== $limit) {
BaseCache::setLimit($limit);
if ($soapOptions->getWsdlCacheType() !== (int)BaseCache::getType()) {
throw new Exception('WSDL cache type could not be set, ini settings is: '.BaseCache::getType());
}
if ($soapOptions->getWsdlCacheDir() !== BaseCache::getDirectory()) {
throw new Exception('WSDL cache dir could not be set, real dir is: '.BaseCache::getDirectory());
}
} else {
if (BaseCache::isEnabled() !== false) {
throw new Exception('WSDL cache could not be turned off');
}
}
}

View File

@ -12,21 +12,27 @@
namespace BeSimple\SoapBundle\Controller;
use BeSimple\SoapBundle\Handler\ExceptionHandler;
use BeSimple\SoapBundle\Soap\SoapRequest;
use BeSimple\SoapBundle\Soap\SoapResponse;
use BeSimple\SoapServer\Exception as SoapException;
use Symfony\Component\DependencyInjection\ContainerAware;
use BeSimple\SoapServer\SoapServerBuilder;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
/**
* @author Christian Kerl <christian-kerl@web.de>
* @author Francis Besset <francis.besset@gmail.com>
*/
class SoapWebServiceController extends ContainerAware
class SoapWebServiceController implements ContainerAwareInterface
{
use ContainerAwareTrait;
/**
* @var \SoapServer
*/
@ -61,9 +67,10 @@ class SoapWebServiceController extends ContainerAware
$this->serviceBinder = $webServiceContext->getServiceBinder();
$this->soapRequest = SoapRequest::createFromHttpRequest($this->container->get('request'));
$this->soapRequest = SoapRequest::createFromHttpRequest($this->container->get('request_stack')->getCurrentRequest());
$this->soapServer = $webServiceContext
->getServerBuilder()
->withSoapVersion11()
->withHandler($this)
->build()
;
@ -91,14 +98,69 @@ class SoapWebServiceController extends ContainerAware
)
));
$query = $this->container->get('request')->query;
if (!$query->has('wsdl') && !$query->has('WSDL')) {
$this->container->get('request')->setRequestFormat('xml');
$request = $this->container->get('request');
$query = $request->query;
if ($query->has('wsdl') || $query->has('WSDL')) {
$request->setRequestFormat('wsdl');
}
return $response;
}
/**
* Converts an Exception to a SoapFault Response.
*
* @param Request $request The request
* @param FlattenException $exception A FlattenException instance
* @param DebugLoggerInterface $logger A DebugLoggerInterface instance
*
* @return Response
*
* @throws \LogicException When the request query parameter "_besimple_soap_webservice" does not exist
*/
public function exceptionAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null)
{
if (!$webservice = $request->query->get('_besimple_soap_webservice')) {
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';
$code = $exception->getStatusCode();
$details = $this->container->get('templating')->render($view, array(
'status_code' => $code,
'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '',
'exception' => $exception,
'logger' => $logger,
));
$handler = new ExceptionHandler($exception, $details);
if ($soapFault = $request->query->get('_besimple_soap_fault')) {
$handler->setSoapFault($soapFault);
// Remove parameter from query because cannot be Serialized in Logger
$request->query->remove('_besimple_soap_fault');
}
$server = SoapServerBuilder::createWithDefaults(__DIR__.'/../Handler/wsdl/exception.wsdl')
->withWsdl()
->withWsdlCacheNone()
->withHandler($handler)
->build()
;
ob_start();
$server->handle(
'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://besim.pl/soap/exception/1.0/">'.
'<soapenv:Header/>'.
'<soapenv:Body>'.
'<ns:exception />'.
'</soapenv:Body>'.
'</soapenv:Envelope>'
);
return new Response(ob_get_clean());
}
/**
* This method gets called once for every SOAP header the \SoapServer received
* and afterwards once for the called SOAP operation.
@ -124,13 +186,7 @@ class SoapWebServiceController extends ContainerAware
);
// forward to controller
try {
$response = $this->container->get('http_kernel')->handle($this->soapRequest, HttpKernelInterface::SUB_REQUEST, false);
} catch (\Exception $e) {
$this->soapResponse = new Response(null, 500);
throw $e instanceof \SoapFault || $this->container->getParameter('kernel.debug') ? $e : new SoapException\ReceiverSoapFault($e->getMessage());
}
$response = $this->container->get('http_kernel')->handle($this->soapRequest, HttpKernelInterface::SUB_REQUEST, false);
$this->setResponse($response);

View File

@ -12,8 +12,6 @@ namespace BeSimple\SoapBundle\Converter;
use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
use BeSimple\SoapBundle\Util\Assert;
use BeSimple\SoapBundle\Util\QName;
use BeSimple\SoapBundle\Util\String;
/**
* @author Christian Kerl <christian-kerl@web.de>
@ -50,8 +48,6 @@ class TypeRepository
public function fixTypeInformation(ServiceDefinition $definition)
{
$typeMap = $this->defaultTypeMap;
foreach($definition->getAllTypes() as $type) {
$phpType = $type->getPhpType();
$xmlType = $type->getXmlType();

View File

@ -13,6 +13,7 @@ namespace BeSimple\SoapBundle\Converter;
use BeSimple\SoapBundle\Soap\SoapRequest;
use BeSimple\SoapBundle\Soap\SoapResponse;
use BeSimple\SoapBundle\Util\String;
use BeSimple\SoapCommon\Converter\TypeConverterInterface;
/**
* @author Christian Kerl <christian-kerl@web.de>

View File

@ -63,6 +63,8 @@ class BeSimpleSoapExtension extends Extension
$serviceConfig['name'] = $name;
$this->createWebServiceContext($serviceConfig, $container);
}
$container->setParameter('besimple.soap.exception_listener.controller', $config['exception_controller']);
}
private function registerCacheConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
@ -82,7 +84,7 @@ class BeSimpleSoapExtension extends Extension
foreach ($config as $client => $options) {
$definition = new DefinitionDecorator('besimple.soap.client.builder');
$context = $container->setDefinition(sprintf('besimple.soap.client.builder.%s', $client), $definition);
$container->setDefinition(sprintf('besimple.soap.client.builder.%s', $client), $definition);
$definition->replaceArgument(0, $options['wsdl']);
@ -96,18 +98,31 @@ class BeSimpleSoapExtension extends Extension
}
}
$proxy = $options['proxy'];
if (false !== $proxy['host']) {
if (null !== $proxy['auth']) {
if ('basic' === $proxy['auth']) {
$proxy['auth'] = \CURLAUTH_BASIC;
} elseif ('ntlm' === $proxy['auth']) {
$proxy['auth'] = \CURLAUTH_NTLM;
}
}
$definition->addMethodCall('withProxy', array(
$proxy['host'], $proxy['port'],
$proxy['login'], $proxy['password'],
$proxy['auth']
));
}
if (isset($defOptions['cache_type'])) {
$defOptions['cache_type'] = $this->getCacheType($defOptions['cache_type']);
}
$definition->replaceArgument(1, $defOptions);
if (!empty($options['classmap'])) {
$classmap = $this->createClientClassmap($client, $options['classmap'], $container);
$definition->replaceArgument(2, new Reference($classmap));
} else {
$definition->replaceArgument(2, null);
}
$classmap = $this->createClientClassmap($client, $options['classmap'], $container);
$definition->replaceArgument(2, new Reference($classmap));
$this->createClient($client, $container);
}
@ -116,11 +131,13 @@ class BeSimpleSoapExtension extends Extension
private function createClientClassmap($client, array $classmap, ContainerBuilder $container)
{
$definition = new DefinitionDecorator('besimple.soap.classmap');
$context = $container->setDefinition(sprintf('besimple.soap.classmap.%s', $client), $definition);
$container->setDefinition(sprintf('besimple.soap.classmap.%s', $client), $definition);
$definition->setMethodCalls(array(
array('set', array($classmap)),
));
if (!empty($classmap)) {
$definition->setMethodCalls(array(
array('set', array($classmap)),
));
}
return sprintf('besimple.soap.classmap.%s', $client);
}
@ -128,9 +145,12 @@ class BeSimpleSoapExtension extends Extension
private function createClient($client, ContainerBuilder $container)
{
$definition = new DefinitionDecorator('besimple.soap.client');
$context = $container->setDefinition(sprintf('besimple.soap.client.%s', $client), $definition);
$container->setDefinition(sprintf('besimple.soap.client.%s', $client), $definition);
$definition->setFactoryService(sprintf('besimple.soap.client.builder.%s', $client));
$definition->setFactory(array(
new Reference(sprintf('besimple.soap.client.builder.%s', $client)),
'build'
));
}
private function createWebServiceContext(array $config, ContainerBuilder $container)
@ -140,7 +160,7 @@ class BeSimpleSoapExtension extends Extension
$contextId = 'besimple.soap.context.'.$config['name'];
$definition = new DefinitionDecorator('besimple.soap.context.'.$bindingSuffix);
$context = $container->setDefinition($contextId, $definition);
$container->setDefinition($contextId, $definition);
if (isset($config['cache_type'])) {
$config['cache_type'] = $this->getCacheType($config['cache_type']);
@ -148,9 +168,9 @@ class BeSimpleSoapExtension extends Extension
$options = $container
->getDefinition('besimple.soap.context.'.$bindingSuffix)
->getArgument(5);
->getArgument(2);
$definition->replaceArgument(5, array_merge($options, $config));
$definition->replaceArgument(2, array_merge($options, $config));
}
private function getCacheType($type)

View File

@ -24,6 +24,7 @@ use Symfony\Component\Config\Definition\Builder\TreeBuilder;
class Configuration
{
private $cacheTypes = array('none', 'disk', 'memory', 'disk_memory');
private $proxyAuth = array('basic', 'ntlm');
/**
* Generates the configuration tree.
@ -40,6 +41,12 @@ class Configuration
$this->addServicesSection($rootNode);
$this->addWsdlDumperSection($rootNode);
$rootNode
->children()
->scalarNode('exception_controller')->defaultValue('BeSimpleSoapBundle:SoapWebService:exception')->end()
->end()
;
return $treeBuilder->buildTree();
}
@ -79,12 +86,33 @@ class Configuration
->scalarNode('cache_type')
->validate()
->ifNotInArray($this->cacheTypes)
->thenInvalid(sprintf('The cache type has to be either %s', implode(', ', $this->cacheTypes)))
->thenInvalid(sprintf('The cache type has to be either: %s', implode(', ', $this->cacheTypes)))
->end()
->end()
->arrayNode('classmap')
->useAttributeAsKey('name')->prototype('scalar')->end()
->end()
->arrayNode('proxy')
->info('proxy configuration')
->addDefaultsIfNotSet()
->beforeNormalization()
->ifTrue(function ($v) { return !is_array($v); })
->then(function ($v) { return array('host' => null === $v ? false : $v); })
->end()
->children()
->scalarNode('host')->defaultFalse()->end()
->scalarNode('port')->defaultValue(3128)->end()
->scalarNode('login')->defaultNull()->end()
->scalarNode('password')->defaultNull()->end()
->scalarNode('auth')
->defaultNull()
->validate()
->ifNotInArray($this->proxyAuth)
->thenInvalid(sprintf('The proxy auth has to be either: %s', implode(', ', $this->proxyAuth)))
->end()
->end()
->end()
->end()
->end()
->end()
->end()

View File

@ -2,7 +2,6 @@
namespace BeSimple\SoapBundle\EventListener;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
class RequestFormatListener

View File

@ -0,0 +1,87 @@
<?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\SoapBundle\EventListener;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\EventListener\ExceptionListener;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* @author Francis Besset <francis.besset@gmail.com>
*/
class SoapExceptionListener extends ExceptionListener
{
/**
* @var ContainerInterface
*/
protected $container;
/**
* To avoid conflict between , the logger param is not typed:
* The parent class needs and instance of `Psr\Log\LoggerInterface` from Symfony 2.2,
* before logger is an instance of `Symfony\Component\HttpKernel\Log\LoggerInterface`.
*
* @param ContainerInterface $container A ContainerInterface instance
* @param string $controller The controller name to call
* @param LoggerInterface $logger A logger instance
*/
public function __construct(ContainerInterface $container, $controller, $logger)
{
parent::__construct($controller, $logger);
$this->container = $container;
}
public function onKernelException(GetResponseForExceptionEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
$request = $event->getRequest();
if (!in_array($request->getRequestFormat(), array('soap', 'xml'))) {
return;
} elseif ('xml' === $request->getRequestFormat() && '_webservice_call' !== $request->attributes->get('_route')) {
return;
}
$attributes = $request->attributes;
if (!$webservice = $attributes->get('webservice')) {
return;
}
if (!$this->container->has(sprintf('besimple.soap.context.%s', $webservice))) {
return;
}
// hack to retrieve the current WebService name in the controller
$request->query->set('_besimple_soap_webservice', $webservice);
$exception = $event->getException();
if ($exception instanceof \SoapFault) {
$request->query->set('_besimple_soap_fault', $exception);
}
parent::onKernelException($event);
}
public static function getSubscribedEvents()
{
return array(
// Must be called before ExceptionListener of HttpKernel component
KernelEvents::EXCEPTION => array('onKernelException', -64),
);
}
}

View File

@ -0,0 +1,53 @@
<?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\SoapBundle\Handler;
use BeSimple\SoapServer\Exception\ReceiverSoapFault;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\HttpFoundation\Response;
/**
* @author Francis Besset <francis.besset@gmail.com>
*/
class ExceptionHandler
{
protected $exception;
protected $details;
protected $soapFault;
public function __construct(FlattenException $exception, $details = null)
{
$this->exception = $exception;
$this->details = $details;
}
public function setSoapFault(\SoapFault $soapFault)
{
$this->soapFault = $soapFault;
}
public function __call($method, $arguments)
{
if (isset($this->soapFault)) {
throw $this->soapFault;
}
$code = $this->exception->getStatusCode();
throw new ReceiverSoapFault(
isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '',
null,
$this->details
);
}
}

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://besim.pl/soap/exception/1.0/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" name="Exception" targetNamespace="http://besim.pl/soap/exception/1.0/">
<portType name="ExceptionPortType">
<operation name="exception">
<input message="tns:empty"/>
<output message="tns:empty"/>
</operation>
</portType>
<message name="empty" />
<binding name="ExceptionBinding" type="tns:ExceptionPortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/>
<operation name="exception">
<soap:operation soapAction="http://besim.pl/soap/exception/1.0/exception"/>
<input>
<soap:body use="literal" namespace="http://besim.pl/soap/exception/1.0/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</input>
<output>
<soap:body use="literal" namespace="http://besim.pl/soap/exception/1.0/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</output>
</operation>
</binding>
<service name="ExceptionService">
<port name="ExceptionPort" binding="tns:ExceptionBinding">
<soap:address location="http://localhost/soap/Exception"/>
</port>
</service>
</definitions>

View File

@ -16,9 +16,12 @@
</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" factory-service="besimple.soap.client.builder" factory-method="build" class="%besimple.soap.client.builder.class%" abstract="true" />
<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.classmap" class="%besimple.soap.classmap.class%" abstract="true" />
</services>

View File

@ -29,10 +29,13 @@
<service id="besimple.soap.definition.loader.annot_class" class="%besimple.soap.definition.loader.annot_class.class%" public="false">
<tag name="besimple.soap.definition.loader" />
<argument type="service" id="annotation_reader" />
<argument type="service" id="besimple.soap.type.repository" />
</service>
<service id="besimple.soap.definition.loader.annot_complextype" class="%besimple.soap.definition.loader.annot_complextype.class%" public="false">
<tag name="besimple.soap.definition.loader" />
<argument type="service" id="annotation_reader" />
<argument type="service" id="besimple.soap.type.repository" />
</service>
</services>

View File

@ -4,15 +4,15 @@
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" pattern="/{webservice}">
<route id="_webservice_call" path="/{webservice}">
<default key="_controller">BeSimpleSoapBundle:SoapWebService:Call</default>
<default key="_format">soap</default>
<default key="_format">xml</default>
<requirement key="_method">POST</requirement>
</route>
<route id="_webservice_definition" pattern="/{webservice}">
<route id="_webservice_definition" path="/{webservice}">
<default key="_controller">BeSimpleSoapBundle:SoapWebService:Definition</default>
<default key="_format">wsdl</default>
<default key="_format">xml</default>
<requirement key="_method">GET</requirement>
</route>
</routes>

View File

@ -12,7 +12,7 @@
<service id="besimple.soap.cache" class="%besimple.soap.cache.class%">
<argument>%kernel.debug%</argument>
<argument>%besimple.soap.cache.type%</argument>
<argument>%besimple.soap.cache.dir%/php</argument>
<argument>%besimple.soap.cache.dir%/cache</argument>
<argument>%besimple.soap.cache.lifetime%</argument>
<argument>%besimple.soap.cache.limit%</argument>
</service>

View File

@ -6,6 +6,7 @@
<parameters>
<parameter key="besimple.soap.response.class">BeSimple\SoapBundle\Soap\SoapResponse</parameter>
<parameter key="besimple.soap.response.listener.class">BeSimple\SoapBundle\EventListener\SoapResponseListener</parameter>
<parameter key="besimple.soap.exception_listener.class">BeSimple\SoapBundle\EventListener\SoapExceptionListener</parameter>
<parameter key="besimple.soap.context.class">BeSimple\SoapBundle\WebServiceContext</parameter>
<parameter key="besimple.soap.binder.request_header.rpcliteral.class">BeSimple\SoapBundle\ServiceBinding\RpcLiteralRequestHeaderMessageBinder</parameter>
<parameter key="besimple.soap.binder.request.rpcliteral.class">BeSimple\SoapBundle\ServiceBinding\RpcLiteralRequestMessageBinder</parameter>
@ -13,8 +14,7 @@
<parameter key="besimple.soap.binder.request.documentwrapped.class">BeSimple\SoapBundle\ServiceBinding\DocumentLiteralWrappedRequestMessageBinder</parameter>
<parameter key="besimple.soap.binder.request_header.documentwrapped.class">BeSimple\SoapBundle\ServiceBinding\DocumentLiteralWrappedRequestHeaderMessageBinder</parameter>
<parameter key="besimple.soap.binder.response.documentwrapped.class">BeSimple\SoapBundle\ServiceBinding\DocumentLiteralWrappedResponseMessageBinder</parameter>
<parameter key="besimple.soap.definition.dumper.wsdl.rpcliteral.class">BeSimple\SoapBundle\ServiceDefinition\Dumper\WsdlDumper</parameter>
<parameter key="besimple.soap.type.repository.class">BeSimple\SoapBundle\Converter\TypeRepository</parameter>
<parameter key="besimple.soap.type.repository.class">BeSimple\SoapCommon\Definition\Type\TypeRepository</parameter>
<parameter key="besimple.soap.server.classmap.class">BeSimple\SoapServer\Classmap</parameter>
</parameters>
@ -26,11 +26,16 @@
<argument type="service" id="besimple.soap.response" />
</service>
<service id="besimple.soap.exception_listener" class="%besimple.soap.exception_listener.class%">
<tag name="kernel.event_subscriber" />
<tag name="monolog.logger" channel="request" />
<argument type="service" id="service_container" />
<argument>%besimple.soap.exception_listener.controller%</argument>
<argument type="service" id="logger" on-invalid="null" />
</service>
<service id="besimple.soap.context.rpcliteral" class="%besimple.soap.context.class%" abstract="true">
<argument type="service" id="besimple.soap.definition.loader" />
<argument type="service" id="besimple.soap.definition.dumper.wsdl.rpcliteral" />
<argument type="service" id="besimple.soap.server.classmap" />
<argument type="service" id="besimple.soap.type.repository" />
<argument type="service" id="besimple.soap.converter.collection" />
<argument type="collection">
<argument key="cache_dir">%besimple.soap.cache.dir%</argument>
@ -39,15 +44,13 @@
<argument key="binder_request_header_class">%besimple.soap.binder.request_header.rpcliteral.class%</argument>
<argument key="binder_request_class">%besimple.soap.binder.request.rpcliteral.class%</argument>
<argument key="binder_response_class">%besimple.soap.binder.response.rpcliteral.class%</argument>
<argument key="wsdl_stylesheet">%besimple.soap.definition.dumper.options.stylesheet%</argument>
</argument>
<argument type="service" id="besimple.soap.cache" />
</service>
<service id="besimple.soap.context.documentwrapped" class="%besimple.soap.context.class%" abstract="true">
<argument type="service" id="besimple.soap.definition.loader" />
<argument type="service" id="besimple.soap.definition.dumper.wsdl.documentwrapped" />
<argument type="service" id="besimple.soap.server.classmap" />
<argument type="service" id="besimple.soap.type.repository" />
<argument type="service" id="besimple.soap.converter.collection" />
<argument type="collection">
<argument key="cache_dir">%besimple.soap.cache.dir%</argument>
@ -56,46 +59,40 @@
<argument key="binder_request_header_class">%besimple.soap.binder.request_header.documentwrapped.class%</argument>
<argument key="binder_request_class">%besimple.soap.binder.request.documentwrapped.class%</argument>
<argument key="binder_response_class">%besimple.soap.binder.response.documentwrapped.class%</argument>
<argument key="wsdl_stylesheet">%besimple.soap.definition.dumper.options.stylesheet%</argument>
</argument>
<argument type="service" id="besimple.soap.cache" />
</service>
<service id="besimple.soap.definition.dumper.wsdl.rpcliteral" class="%besimple.soap.definition.dumper.wsdl.rpcliteral.class%">
<argument type="service" id="besimple.soap.definition.loader.annot_complextype" />
<argument type="service" id="besimple.soap.type.repository" />
<argument type="collection">
<argument key="stylesheet">%besimple.soap.definition.dumper.options.stylesheet%</argument>
</argument>
</service>
<service id="besimple.soap.server.classmap" class="%besimple.soap.server.classmap.class%" public="false" />
<service id="besimple.soap.type.repository" class="%besimple.soap.type.repository.class%">
<argument type="service" id="besimple.soap.server.classmap" />
<call method="addXmlNamespace">
<argument>xsd</argument>
<argument>http://www.w3.org/2001/XMLSchema</argument>
</call>
<call method="addDefaultTypeMapping">
<call method="addType">
<argument>string</argument>
<argument>xsd:string</argument>
</call>
<call method="addDefaultTypeMapping">
<call method="addType">
<argument>boolean</argument>
<argument>xsd:boolean</argument>
</call>
<call method="addDefaultTypeMapping">
<call method="addType">
<argument>int</argument>
<argument>xsd:int</argument>
</call>
<call method="addDefaultTypeMapping">
<call method="addType">
<argument>float</argument>
<argument>xsd:float</argument>
</call>
<call method="addDefaultTypeMapping">
<call method="addType">
<argument>date</argument>
<argument>xsd:date</argument>
</call>
<call method="addDefaultTypeMapping">
<call method="addType">
<argument>dateTime</argument>
<argument>xsd:dateTime</argument>
</call>

View File

@ -33,4 +33,8 @@ SoapServer
SoapClient
----------
Coming soon.
.. toctree::
:maxdepth: 1
:numbered:
soapclient/configuration

View File

@ -0,0 +1,158 @@
Configuration
=============
Client configuration
--------------------
Configure your first client in your config file:
.. code-block:: yaml
# app/config/config.yml
be_simple_soap:
clients:
DemoApi:
# required
wsdl: http://localhost/app_dev.php/ws/DemoApi?wsdl
# classmap (optional)
classmap:
type_name: "Full\Class\Name"
# proxy (optional)
proxy:
host: proxy.domain.name # required to enable proxy configuration
port: 3128
login: ~
password: ~
auth: ~ # can be 'basic' or 'ntlm'
Using client
------------
.. code-block:: php
namespace Acme\DemoBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DemoController extends Controller
{
public function helloAction($name)
{
// The client service name is `besimple.soap.client.demoapi`:
// `besimple.soap.client.`: is the base name of your client
// `demoapi`: is the name specified in your config file converted to lowercase
$client = $this->container->get('besimple.soap.client.demoapi');
// call `hello` method on WebService with the string parameter `$name`
$helloResult = $client->hello($name);
return $this->render('AcmeDemoBundle:Demo:hello.html.twig', array(
'hello' => $helloResult,
));
}
}
Classmap
--------
Configuration
~~~~~~~~~~~~~
.. code-block:: yaml
# app/config/config.yml
be_simple_soap:
clients:
DemoApi:
# ...
classmap:
User: Acme\DemoBundle\Api\UserApi
# add other type_name: classname
UserApi class
~~~~~~~~~~~~~
.. code-block:: php
namespace Acme\DemoBundle\Api;
class UserApi
{
private $username;
private $firstname;
private $lastname;
public function __construct($username)
{
$this->username = $username;
}
public function getFirstname()
{
return $this->firstname;
}
public function getLastname()
{
return $this->lastname;
}
}
Usage
~~~~~
.. code-block:: php
namespace Acme\DemoBundle\Controller;
use Acme\DemoBundle\Api\UserApi;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DemoController extends Controller
{
public function userAction($username)
{
// The client service name is `besimple.soap.client.demoapi`:
// `besimple.soap.client.`: is the base name of your client
// `demoapi`: is the name specified in your config file converted to lowercase
$client = $this->container->get('besimple.soap.client.demoapi');
// call `getUser` method on WebService with an instance of UserApi
// if the `getUserByUsername` method return a `User` type then `$userResult` is an instance of UserApi
$userResult = $client->getUserByUsername($username);
return $this->render('AcmeDemoBundle:Demo:user.html.twig', array(
'user' => $userResult,
));
}
}
Without classmap configuration the `$userResult` is an instance of `stdClass`:
.. code-block:: text
object(stdClass)#5561 (3) {
["username"]=>
string(6) "FooBar"
["firstname"]=>
string(3) "Foo"
["lastname"]=>
string(3) "Bar"
}
With classmap configuration the `$userResult` is an instance of `Acme\DemoBundle\Api\UserApi`:
.. code-block:: text
object(Acme\DemoBundle\Api\UserApi)#208 (3) {
["username":"Acme\DemoBundle\Api\UserApi":private]=>
string(6) "FooBar"
["firstname":"Acme\DemoBundle\Api\UserApi":private]=>
string(3) "Foo"
["lastname":"Acme\DemoBundle\Api\UserApi":private]=>
string(3) "Bar"
}

View File

@ -47,9 +47,9 @@ Controller
/**
* @Soap\Method("sendAssocArray")
* @Soap\Param("assocArray", phpType = "BeSimple\SoapCommon\Type\KeyValue\String[]")
* @Soap\Return(phpType = "BeSimple\SoapCommon\Type\KeyValue\String[]")
* @Soap\Result(phpType = "BeSimple\SoapCommon\Type\KeyValue\String[]")
*/
public function assocArrayOfStringAction(array $assocArray)
public function sendAssocArrayOfStringAction(array $assocArray)
{
// The $assocArray it's a real associative array
// var_dump($assocArray);die;
@ -97,3 +97,4 @@ How to create my Associative Array?
'user2' => new User('user2', 'user2@user.com'),
);
}
}

View File

@ -86,6 +86,16 @@ You can expose only the properties (public, protected or private) of a complex t
*/
private $newsletter;
/**
* @Soap\ComplexType("date")
*/
private $createdAt:
/**
* @Soap\ComplexType("datetime")
*/
private $updatedAt;
public function getId()
{
return $this->id;
@ -128,7 +138,27 @@ You can expose only the properties (public, protected or private) of a complex t
public function setNewsletter($newsletter)
{
$this->newletter = (Boolean) $newsletter
$this->newletter = (Boolean) $newsletter;
}
public function getCreatedAt()
{
return $this->createdAt;
}
public function setCreatedAt(\DateTime $createdAt)
{
$this->createdAt = $createdAt;
}
public function getUpdatedAt()
{
return this->updatedAt;
}
public function setUpdatedAt(\DateTime $updatedAt)
{
$this->updatedAt = $updatedAt;
}
}

View File

@ -11,6 +11,7 @@
namespace BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Method;
use BeSimple\SoapCommon\Definition\Type\TypeRepository;
/**
* @author Christian Kerl <christian-kerl@web.de>
@ -23,5 +24,5 @@ interface MessageBinderInterface
*
* @return mixed
*/
function processMessage(Method $messageDefinition, $message, array $definitionComplexTypes = array());
function processMessage(Method $messageDefinition, $message, TypeRepository $typeRepository);
}

View File

@ -11,6 +11,7 @@
namespace BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Method;
use BeSimple\SoapCommon\Definition\Type\TypeRepository;
/**
* @author Francis Besset <francis.besset@gmail.com>
@ -24,10 +25,11 @@ class RpcLiteralRequestHeaderMessageBinder extends RpcLiteralRequestMessageBinde
$this->header = $header;
}
public function processMessage(Method $messageDefinition, $message, array $definitionComplexTypes = array())
public function processMessage(Method $messageDefinition, $message, TypeRepository $typeRepository)
{
$this->typeRepository = $typeRepository;
$headerDefinition = $messageDefinition->getHeaders()->get($this->header);
return $this->processType($headerDefinition->getType()->getPhpType(), $message, $definitionComplexTypes);
return $this->processType($headerDefinition->getType(), $message);
}
}

View File

@ -13,8 +13,9 @@
namespace BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Method;
use BeSimple\SoapBundle\ServiceDefinition\Strategy\MethodComplexType;
use BeSimple\SoapBundle\ServiceDefinition\Strategy\PropertyComplexType;
use BeSimple\SoapCommon\Definition\Type\ArrayOfType;
use BeSimple\SoapCommon\Definition\Type\ComplexType;
use BeSimple\SoapCommon\Definition\Type\TypeRepository;
use BeSimple\SoapCommon\Util\MessageBinder;
/**
@ -23,19 +24,20 @@ use BeSimple\SoapCommon\Util\MessageBinder;
*/
class RpcLiteralRequestMessageBinder implements MessageBinderInterface
{
private $messageRefs = array();
private $definitionComplexTypes;
protected $typeRepository;
public function processMessage(Method $messageDefinition, $message, array $definitionComplexTypes = array())
private $messageRefs = array();
public function processMessage(Method $messageDefinition, $message, TypeRepository $typeRepository)
{
$this->definitionComplexTypes = $definitionComplexTypes;
$this->typeRepository = $typeRepository;
$result = array();
$i = 0;
foreach ($messageDefinition->getArguments() as $argument) {
foreach ($messageDefinition->getInput()->all() as $argument) {
if (isset($message[$i])) {
$result[$argument->getName()] = $this->processType($argument->getType()->getPhpType(), $message[$i]);
$result[$argument->getName()] = $this->processType($argument->getType(), $message[$i]);
}
$i++;
@ -48,14 +50,18 @@ class RpcLiteralRequestMessageBinder implements MessageBinderInterface
{
$isArray = false;
if (preg_match('/^([^\[]+)\[\]$/', $phpType, $match)) {
$type = $this->typeRepository->getType($phpType);
if ($type instanceof ArrayOfType) {
$isArray = true;
$array = array();
$phpType = $match[1];
$array = array();
$type = $this->typeRepository->getType($type->get('item')->getType());
}
// @TODO Fix array reference
if (isset($this->definitionComplexTypes[$phpType])) {
if ($type instanceof ComplexType) {
$phpType = $type->getPhpType();
if ($isArray) {
if (isset($message->item)) {
foreach ($message->item as $complexType) {
@ -98,17 +104,16 @@ class RpcLiteralRequestMessageBinder implements MessageBinderInterface
$this->messageRefs[$hash] = $message;
$messageBinder = new MessageBinder($message);
foreach ($this->definitionComplexTypes[$phpType]['properties'] as $type) {
foreach ($this->typeRepository->getType($phpType)->all() as $type) {
$property = $type->getName();
$value = $messageBinder->readProperty($property);
if (null !== $value) {
$value = $this->processType($type->getValue(), $value);
$value = $this->processType($type->getType(), $value);
$messageBinder->writeProperty($property, $value);
}
if (!$type->isNillable() && null === $value) {
} elseif (!$type->isNillable()) {
// @TODO use xmlType instead of phpType
throw new \SoapFault('SOAP_ERROR_COMPLEX_TYPE', sprintf('"%s:%s" cannot be null.', ucfirst($phpType), $type->getName()));
}
}

View File

@ -13,8 +13,9 @@
namespace BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Method;
use BeSimple\SoapBundle\ServiceDefinition\Strategy\PropertyComplexType;
use BeSimple\SoapBundle\ServiceDefinition\Strategy\MethodComplexType;
use BeSimple\SoapCommon\Definition\Type\ArrayOfType;
use BeSimple\SoapCommon\Definition\Type\ComplexType;
use BeSimple\SoapCommon\Definition\Type\TypeRepository;
use BeSimple\SoapCommon\Util\MessageBinder;
/**
@ -23,26 +24,31 @@ use BeSimple\SoapCommon\Util\MessageBinder;
*/
class RpcLiteralResponseMessageBinder implements MessageBinderInterface
{
protected $typeRepository;
private $messageRefs = array();
private $definitionComplexTypes;
public function processMessage(Method $messageDefinition, $message, array $definitionComplexTypes = array())
public function processMessage(Method $messageDefinition, $message, TypeRepository $typeRepository)
{
$this->definitionComplexTypes = $definitionComplexTypes;
$this->typeRepository = $typeRepository;
return $this->processType($messageDefinition->getReturn()->getPhpType(), $message);
return $this->processType($messageDefinition->getOutput()->get('return')->getType(), $message);
}
private function processType($phpType, $message)
{
$isArray = false;
if (preg_match('/^([^\[]+)\[\]$/', $phpType, $match)) {
$type = $this->typeRepository->getType($phpType);
if ($type instanceof ArrayOfType) {
$isArray = true;
$phpType = $match[1];
$type = $this->typeRepository->getType($type->get('item')->getType());
}
if (isset($this->definitionComplexTypes[$phpType])) {
if ($type instanceof ComplexType) {
$phpType = $type->getPhpType();
if ($isArray) {
$array = array();
@ -79,16 +85,16 @@ class RpcLiteralResponseMessageBinder implements MessageBinderInterface
$this->messageRefs[$hash] = $message;
if (!$message instanceof $phpType) {
throw new \InvalidArgumentException(sprintf('The instance class must be "%s", "%s" given.', get_class($message), $phpType));
throw new \InvalidArgumentException(sprintf('The instance class must be "%s", "%s" given.', $phpType, get_class($message)));
}
$messageBinder = new MessageBinder($message);
foreach ($this->definitionComplexTypes[$phpType]['properties'] as $type) {
foreach ($this->typeRepository->getType($phpType)->all() as $type) {
$property = $type->getName();
$value = $messageBinder->readProperty($property);
if (null !== $value) {
$value = $this->processType($type->getValue(), $value);
$value = $this->processType($type->getType(), $value);
$messageBinder->writeProperty($property, $value);
}

View File

@ -10,8 +10,7 @@
namespace BeSimple\SoapBundle\ServiceBinding;
use BeSimple\SoapBundle\ServiceDefinition\Header;
use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
use BeSimple\SoapBundle\ServiceDefinition\Definition;
use BeSimple\SoapBundle\Soap\SoapHeader;
/**
@ -40,12 +39,12 @@ class ServiceBinder
private $responseMessageBinder;
/**
* @param ServiceDefinition $definition
* @param Definition $definition
* @param MessageBinderInterface $requestHeaderMessageBinder
* @param MessageBinderInterface $requestMessageBinder
* @param MessageBinderInterface $responseMessageBinder
*/
public function __construct(ServiceDefinition $definition, MessageBinderInterface $requestHeaderMessageBinder, MessageBinderInterface $requestMessageBinder, MessageBinderInterface $responseMessageBinder) {
public function __construct(Definition $definition, MessageBinderInterface $requestHeaderMessageBinder, MessageBinderInterface $requestMessageBinder, MessageBinderInterface $responseMessageBinder) {
$this->definition = $definition;
$this->requestHeaderMessageBinder = $requestHeaderMessageBinder;
@ -62,7 +61,7 @@ class ServiceBinder
*/
public function isServiceHeader($method, $header)
{
return $this->definition->getMethods()->get($method)->getHeaders()->has($header);
return $this->definition->getMethod($method)->getHeader($header);
}
/**
@ -72,7 +71,7 @@ class ServiceBinder
*/
public function isServiceMethod($method)
{
return $this->definition->getMethods()->has($method);
return null !== $this->definition->getMethod($method);
}
/**
@ -84,11 +83,11 @@ class ServiceBinder
*/
public function processServiceHeader($method, $header, $data)
{
$methodDefinition = $this->definition->getMethods()->get($method);
$headerDefinition = $methodDefinition->getHeaders()->get($header);
$methodDefinition = $this->definition->getMethod($method);
$headerDefinition = $methodDefinition->getHeader($header);
$this->requestHeaderMessageBinder->setHeader($header);
$data = $this->requestHeaderMessageBinder->processMessage($methodDefinition, $data, $this->definition->getDefinitionComplexTypes());
$data = $this->requestHeaderMessageBinder->processMessage($methodDefinition, $data, $this->definition->getTypeRepository());
return new SoapHeader($this->definition->getNamespace(), $headerDefinition->getName(), $data);
}
@ -101,11 +100,11 @@ class ServiceBinder
*/
public function processServiceMethodArguments($method, $arguments)
{
$methodDefinition = $this->definition->getMethods()->get($method);
$methodDefinition = $this->definition->getMethod($method);
return array_merge(
array('_controller' => $methodDefinition->getController()),
$this->requestMessageBinder->processMessage($methodDefinition, $arguments, $this->definition->getDefinitionComplexTypes())
$this->requestMessageBinder->processMessage($methodDefinition, $arguments, $this->definition->getTypeRepository())
);
}
@ -117,8 +116,8 @@ class ServiceBinder
*/
public function processServiceMethodReturnValue($name, $return)
{
$methodDefinition = $this->definition->getMethods()->get($name);
$methodDefinition = $this->definition->getMethod($name);
return $this->responseMessageBinder->processMessage($methodDefinition, $return, $this->definition->getDefinitionComplexTypes());
return $this->responseMessageBinder->processMessage($methodDefinition, $return, $this->definition->getTypeRepository());
}
}

View File

@ -1,43 +0,0 @@
<?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\ServiceDefinition;
class Argument
{
private $name;
private $type;
public function __construct($name = null, Type $type = null)
{
$this->setName($name);
$this->setType($type);
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
public function getType()
{
return $this->type;
}
public function setType(Type $type)
{
$this->type = $type;
}
}

View File

@ -1,8 +1,10 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
* This file is part of the BeSimpleSoap.
*
* (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.

View File

@ -0,0 +1,44 @@
<?php
/*
* This file is part of the BeSimpleSoap.
*
* (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\SoapBundle\ServiceDefinition;
use BeSimple\SoapCommon\Definition\Definition as BaseDefinition;
use BeSimple\SoapCommon\Definition\Type\TypeRepository;
/**
* @author Christian Kerl <christian-kerl@web.de>
* @author Francis Besset <francis.besset@gmail.com>
*/
class Definition extends BaseDefinition
{
public function __construct(TypeRepository $typeRepository)
{
$this->typeRepository = $typeRepository;
$this->setOptions(array());
}
public function setName($name)
{
$this->name = $name;
return $this;
}
public function setNamespace($namespace)
{
$this->namespace = $namespace;
return $this;
}
}

View File

@ -1,18 +0,0 @@
<?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\ServiceDefinition\Dumper;
use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
interface DumperInterface
{
function dumpServiceDefinition(ServiceDefinition $definition, $endpoint);
}

View File

@ -1,80 +0,0 @@
<?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\ServiceDefinition\Dumper;
use BeSimple\SoapBundle\Converter\TypeRepository;
use BeSimple\SoapBundle\ServiceDefinition\Type;
use Zend\Soap\Wsdl as BaseWsdl;
/**
* @author Francis Besset <francis.besset@gmail.com>
*/
class Wsdl extends BaseWsdl
{
private $typeRepository;
public function __construct(TypeRepository $typeRepository, $name, $uri, $strategy = true)
{
$this->typeRepository = $typeRepository;
parent::__construct($name, $uri, $strategy);
}
public function getType($type)
{
if ($type instanceof Type) {
return $type->getXmlType();
}
if ('\\' === $type[0]) {
$type = substr($type, 1);
}
if (!$xmlType = $this->typeRepository->getXmlTypeMapping($type)) {
$xmlType = $this->addComplexType($type);
}
return $xmlType;
}
/**
* Translate PHP type into WSDL QName
*
* @param string $type
* @return string QName
*/
public function translateType($type)
{
if (isset($this->classMap[$type])) {
return $this->classMap[$type];
}
return str_replace('\\', '.', $type);
}
public function addBindingOperationHeader(\DOMElement $bindingOperation, array $headers, array $baseBinding)
{
foreach ($headers as $header) {
$inputNode = $bindingOperation->getElementsByTagName('input')->item(0);
$headerNode = $this->toDomDocument()->createElement('soap:header');
$headerNode->setAttribute('part', $header);
foreach ($baseBinding as $name => $value) {
$headerNode->setAttribute($name, $value);
}
$inputNode->appendChild($headerNode);
}
return $bindingOperation;
}
}

View File

@ -1,177 +0,0 @@
<?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\ServiceDefinition\Dumper;
use BeSimple\SoapBundle\Converter\TypeRepository;
use BeSimple\SoapBundle\ServiceDefinition\Method;
use BeSimple\SoapBundle\ServiceDefinition\Type;
use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
use BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationComplexTypeLoader;
use BeSimple\SoapBundle\Util\Assert;
use BeSimple\SoapBundle\Util\QName;
/**
* @author Christian Kerl <christian-kerl@web.de>
*/
class WsdlDumper implements DumperInterface
{
private $loader;
private $typeRepository;
private $options;
private $wsdl;
private $definition;
public function __construct(AnnotationComplexTypeLoader $loader, TypeRepository $typeRepository, array $options)
{
$this->loader = $loader;
$this->typeRepository = $typeRepository;
$this->options = $options;
}
public function dumpServiceDefinition(ServiceDefinition $definition, $endpoint)
{
Assert::thatArgumentNotNull('definition', $definition);
$this->definition = $definition;
$this->wsdl = new Wsdl($this->typeRepository, $definition->getName(), $definition->getNamespace(), new WsdlTypeStrategy($this->loader, $definition));
$port = $this->wsdl->addPortType($this->getPortTypeName());
$binding = $this->wsdl->addBinding($this->getBindingName(), $this->qualify($this->getPortTypeName()));
$this->wsdl->addSoapBinding($binding, 'rpc');
$this->wsdl->addService($this->getServiceName(), $this->getPortName(), $this->qualify($this->getBindingName()), $endpoint);
foreach ($definition->getMethods() as $method) {
$requestHeaderParts =
$requestParts =
$responseParts = array();
foreach ($method->getHeaders() as $header) {
$requestHeaderParts[$header->getName()] = $this->wsdl->getType($header->getType()->getPhpType());
}
foreach ($method->getArguments() as $argument) {
$requestParts[$argument->getName()] = $this->wsdl->getType($argument->getType()->getPhpType());
}
if ($method->getReturn() !== null) {
$responseParts['return'] = $this->wsdl->getType($method->getReturn()->getPhpType());
}
if (!empty($requestHeaderParts)) {
$this->wsdl->addMessage($this->getRequestHeaderMessageName($method), $requestHeaderParts);
}
$this->wsdl->addMessage($this->getRequestMessageName($method), $requestParts);
$this->wsdl->addMessage($this->getResponseMessageName($method), $responseParts);
$portOperation = $this->wsdl->addPortOperation(
$port,
$method->getName(),
$this->qualify($this->getRequestMessageName($method)),
$this->qualify($this->getResponseMessageName($method))
);
$baseBinding =
$inputBinding =
$outputBinding = array(
'use' => 'literal',
'namespace' => $definition->getNamespace(),
'encodingStyle' => 'http://schemas.xmlsoap.org/soap/encoding/',
);
if (!empty($requestParts)) {
$portOperation->setAttribute('parameterOrder', implode(' ', array_keys($requestParts)));
$inputBinding['parts'] = implode(' ', array_keys($requestParts));
}
if (!empty($responseParts)) {
$outputBinding['parts'] = implode(' ', array_keys($responseParts));
}
$bindingOperation = $this->wsdl->addBindingOperation(
$binding,
$method->getName(),
$inputBinding,
$outputBinding
);
$bindingOperation = $this->wsdl->addBindingOperationHeader(
$bindingOperation,
array_keys($requestHeaderParts),
array_merge(array('message' => $this->qualify($this->getRequestHeaderMessageName($method))), $baseBinding)
);
$this->wsdl->addSoapOperation($bindingOperation, $this->getSoapOperationName($method));
}
$this->definition = null;
$dom = $this->wsdl->toDomDocument();
$dom->formatOutput = true;
if ($this->options['stylesheet']) {
$stylesheet = $dom->createProcessingInstruction('xml-stylesheet', sprintf('type="text/xsl" href="%s"', $this->options['stylesheet']));
$dom->insertBefore($stylesheet, $dom->documentElement);
}
return $this->wsdl->toXml();
}
protected function qualify($name, $namespace = null)
{
if($namespace === null) {
$namespace = $this->definition->getNamespace();
}
return $this->wsdl->toDomDocument()->lookupPrefix($namespace).':'.$name;
}
protected function getPortName()
{
return $this->definition->getName().'Port';
}
protected function getPortTypeName()
{
return $this->definition->getName().'PortType';
}
protected function getBindingName()
{
return $this->definition->getName().'Binding';
}
protected function getServiceName()
{
return $this->definition->getName().'Service';
}
protected function getRequestHeaderMessageName(Method $method)
{
return $method->getName().'Header';
}
protected function getRequestMessageName(Method $method)
{
return $method->getName().'Request';
}
protected function getResponseMessageName(Method $method)
{
return $method->getName().'Response';
}
protected function getSoapOperationName(Method $method)
{
return $this->definition->getNamespace().$method->getName();
}
}

View File

@ -1,95 +0,0 @@
<?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\ServiceDefinition\Dumper;
use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
use BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationComplexTypeLoader;
use BeSimple\SoapBundle\ServiceDefinition\Strategy\ComplexType;
use BeSimple\SoapBundle\Util\String;
use Zend\Soap\Exception;
use Zend\Soap\Wsdl as BaseWsdl;
use Zend\Soap\Wsdl\ComplexTypeStrategy\ComplexTypeStrategyInterface;
use Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence;
class WsdlTypeStrategy implements ComplexTypeStrategyInterface
{
/**
* Context WSDL file
*
* @var \Zend\Soap\Wsdl|null
*/
private $context;
private $loader;
private $definition;
private $typeStrategy;
private $arrayStrategy;
public function __construct(AnnotationComplexTypeLoader $loader, ServiceDefinition $definition)
{
$this->loader = $loader;
$this->definition = $definition;
}
/**
* Method accepts the current WSDL context file.
*
* @param \Zend\Soap\Wsdl $context
*/
public function setContext(BaseWsdl $context)
{
$this->context = $context;
return $this;
}
/**
* Create a complex type based on a strategy
*
* @param string $type
*
* @return string XSD type
*
* @throws \Zend\Soap\WsdlException
*/
public function addComplexType($type)
{
if (!$this->context) {
throw new \LogicException(sprintf('Cannot add complex type "%s", no context is set for this composite strategy.', $type));
}
$strategy = String::endsWith($type, '[]') ? $this->getArrayStrategy() : $this->getTypeStrategy();
return $strategy->addComplexType($type);
}
private function getArrayStrategy()
{
if (!$this->arrayStrategy) {
$this->arrayStrategy = new ArrayOfTypeSequence();
$this->arrayStrategy->setContext($this->context);
}
return $this->arrayStrategy;
}
private function getTypeStrategy()
{
if (!$this->typeStrategy) {
$this->typeStrategy = new ComplexType($this->loader, $this->definition);
$this->typeStrategy->setContext($this->context);
}
return $this->typeStrategy;
}
}

View File

@ -1,43 +0,0 @@
<?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\ServiceDefinition;
class Header
{
private $name;
private $type;
public function __construct($name = null, Type $type = null)
{
$this->setName($name);
$this->setType($type);
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
public function getType()
{
return $this->type;
}
public function setType($type)
{
$this->type = $type;
}
}

View File

@ -1,7 +1,7 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
* This file is part of the BeSimpleSoap.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
@ -14,11 +14,10 @@ namespace BeSimple\SoapBundle\ServiceDefinition\Loader;
use BeSimple\SoapBundle\ServiceDefinition as Definition;
use BeSimple\SoapBundle\ServiceDefinition\Annotation;
use BeSimple\SoapCommon\Definition\Type\ComplexType;
use BeSimple\SoapCommon\Definition\Type\TypeRepository;
use Doctrine\Common\Annotations\Reader;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Config\Loader\LoaderResolverInterface;
/**
* AnnotationClassLoader loads ServiceDefinition from a PHP class and its methods.
@ -26,19 +25,23 @@ use Symfony\Component\Config\Loader\LoaderResolverInterface;
* Based on \Symfony\Component\Routing\Loader\AnnotationClassLoader
*
* @author Christian Kerl <christian-kerl@web.de>
* @author Francis Besset <francis.besset@gmail.com>
*/
class AnnotationClassLoader extends Loader
{
protected $reader;
protected $typeRepository;
/**
* Constructor.
*
* @param \Doctrine\Common\Annotations\Reader $reader
*/
public function __construct(Reader $reader)
public function __construct(Reader $reader, TypeRepository $typeRepository)
{
$this->reader = $reader;
$this->typeRepository = $typeRepository;
}
/**
@ -58,39 +61,26 @@ class AnnotationClassLoader extends Loader
}
$class = new \ReflectionClass($class);
$definition = new Definition\ServiceDefinition();
$definition = new Definition\Definition($this->typeRepository);
$serviceMethodHeaders = array();
$sharedHeaders = array();
foreach ($this->reader->getClassAnnotations($class) as $annotation) {
if ($annotation instanceof Annotation\Header) {
$serviceMethodHeaders[$annotation->getValue()] = $annotation;
$sharedHeaders[$annotation->getValue()] = $this->loadType($annotation->getPhpType());
}
}
foreach ($class->getMethods() as $method) {
$serviceArguments =
$serviceHeaders = array();
$serviceHeaders = $sharedHeaders;
$serviceArguments = array();
$serviceMethod =
$serviceReturn = null;
foreach ($serviceMethodHeaders as $annotation) {
$serviceHeaders[$annotation->getValue()] = new Definition\Header(
$annotation->getValue(),
$this->getArgumentType($method, $annotation)
);
}
foreach ($this->reader->getMethodAnnotations($method) as $annotation) {
if ($annotation instanceof Annotation\Header) {
$serviceHeaders[$annotation->getValue()] = new Definition\Header(
$annotation->getValue(),
$this->getArgumentType($method, $annotation)
);
$serviceHeaders[$annotation->getValue()] = $this->loadType($annotation->getPhpType());
} elseif ($annotation instanceof Annotation\Param) {
$serviceArguments[] = new Definition\Argument(
$annotation->getValue(),
$this->getArgumentType($method, $annotation)
);
$serviceArguments[$annotation->getValue()] = $this->loadType($annotation->getPhpType());
} elseif ($annotation instanceof Annotation\Method) {
if ($serviceMethod) {
throw new \LogicException(sprintf('@Soap\Method defined twice for "%s".', $method->getName()));
@ -105,7 +95,7 @@ class AnnotationClassLoader extends Loader
throw new \LogicException(sprintf('@Soap\Result defined twice for "%s".', $method->getName()));
}
$serviceReturn = new Definition\Type($annotation->getPhpType(), $annotation->getXmlType());
$serviceReturn = $annotation->getPhpType();
}
}
@ -114,16 +104,21 @@ class AnnotationClassLoader extends Loader
}
if ($serviceMethod) {
$serviceMethod->setArguments($serviceArguments);
$serviceMethod->setHeaders($serviceHeaders);
foreach ($serviceHeaders as $name => $type) {
$serviceMethod->addHeader($name, $type);
}
foreach ($serviceArguments as $name => $type) {
$serviceMethod->addInput($name, $type);
}
if (!$serviceReturn) {
throw new \LogicException(sprintf('@Soap\Result non-existent for "%s".', $method->getName()));
}
$serviceMethod->setReturn($serviceReturn);
$serviceMethod->setOutput($this->loadType($serviceReturn));
$definition->getMethods()->add($serviceMethod);
$definition->addMethod($serviceMethod);
}
}
@ -145,28 +140,28 @@ class AnnotationClassLoader extends Loader
}
}
/**
* @param \ReflectionMethod $method
* @param \BeSimple\SoapBundle\ServiceDefinition\Annotation\Param $annotation
*
* @return \BeSimple\SoapBundle\ServiceDefinition\Type
*/
private function getArgumentType(\ReflectionMethod $method, Annotation\Param $annotation)
private function loadType($phpType)
{
$phpType = $annotation->getPhpType();
$xmlType = $annotation->getXmlType();
if (null === $phpType) {
foreach ($method->getParameters() as $param) {
if ($param->name === $annotation->getName()) {
$phpType = $param->getClass()->name;
break;
}
}
if (false !== $arrayOf = $this->typeRepository->getArrayOf($phpType)) {
$this->loadType($arrayOf);
}
return new Definition\Type($phpType, $xmlType);
if (!$this->typeRepository->hasType($phpType)) {
$complexTypeResolver = $this->resolve($phpType, 'annotation_complextype');
if (!$complexTypeResolver) {
throw new \Exception();
}
$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());
}
$this->typeRepository->addComplexType($complexType);
}
return $phpType;
}
/**

View File

@ -1,7 +1,7 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
* This file is part of the BeSimpleSoap.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
@ -65,4 +65,17 @@ class AnnotationComplexTypeLoader extends AnnotationClassLoader
return $annotations;
}
/**
* Returns true if this class supports the given resource.
*
* @param mixed $resource A resource
* @param string $type The resource type
*
* @return Boolean True if this class supports the given resource, false otherwise
*/
public function supports($resource, $type = null)
{
return is_string($resource) && class_exists($resource) && 'annotation_complextype' === $type;
}
}

View File

@ -1,7 +1,7 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
* This file is part of the BeSimpleSoap.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
@ -16,7 +16,6 @@ use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\FileLoader;
use Symfony\Component\Config\Resource\FileResource;
/**
* AnnotationFileLoader loads ServiceDefinition from annotations set
@ -62,7 +61,7 @@ class AnnotationFileLoader extends FileLoader
$path = $this->locator->locate($file);
if ($class = $this->findClass($path)) {
return $definition = $this->loader->load($class, $type);
return $this->loader->load($class, $type);
}
return null;

View File

@ -1,145 +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\SoapBundle\ServiceDefinition\Loader;
use BeSimple\SoapBundle\ServiceDefinition\Argument;
use BeSimple\SoapBundle\ServiceDefinition\Header;
use BeSimple\SoapBundle\ServiceDefinition\Method;
use BeSimple\SoapBundle\ServiceDefinition\Type;
use BeSimple\SoapBundle\ServiceDefinition\ServiceDefinition;
use Symfony\Component\Config\Loader\FileLoader;
class XmlFileLoader extends FileLoader
{
public function supports($resource, $type = null)
{
return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION);
}
public function load($file, $type = null)
{
$path = $this->locator->locate($file);
$xml = $this->parseFile($path);
$definition = new ServiceDefinition();
$definition->setName((string) $xml['name']);
$definition->setNamespace((string) $xml['namespace']);
foreach($xml->header as $header) {
$definition->getHeaders()->add($this->parseHeader($header));
}
foreach($xml->method as $method) {
$definition->getMethods()->add($this->parseMethod($method));
}
return $definition;
}
/**
* @param \SimpleXMLElement $node
*
* @return \BeSimple\SoapBundle\ServiceDefinition\Header
*/
protected function parseHeader(\SimpleXMLElement $node)
{
return new Header((string)$node['name'], $this->parseType($node->type));
}
/**
* @param \SimpleXMLElement $node
*
* @return \BeSimple\SoapBundle\ServiceDefinition\Method
*/
protected function parseMethod(\SimpleXMLElement $node)
{
$method = new Method((string)$node['name'], (string)$node['controller']);
foreach($node->argument as $argument) {
$method->getArguments()->add($this->parseArgument($argument));
}
$method->setReturn($this->parseType($node->return->type));
return $method;
}
/**
* @param \SimpleXMLElement $node
*
* @return \BeSimple\SoapBundle\ServiceDefinition\Argument
*/
protected function parseArgument(\SimpleXMLElement $node)
{
$argument = new Argument((string)$node['name'], $this->parseType($node->type));
return $argument;
}
/**
* @param \SimpleXMLElement $node
*
* @return \BeSimple\SoapBundle\ServiceDefinition\Type
*/
protected function parseType(\SimpleXMLElement $node)
{
$namespaces = $node->getDocNamespaces(true);
$qname = explode(':', $node['xml-type'], 2);
$xmlType = sprintf('{%s}%s', $namespaces[$qname[0]], $qname[1]);
return new Type((string)$node['php-type'], $xmlType, (string)$node['converter']);
}
/**
* @param string $file
*
* @return \SimpleXMLElement
*/
protected function parseFile($file)
{
$dom = new \DOMDocument();
libxml_use_internal_errors(true);
if (!$dom->load($file, LIBXML_COMPACT)) {
throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors()));
}
if (!$dom->schemaValidate(__DIR__.'/schema/servicedefinition-1.0.xsd')) {
throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors()));
}
$dom->validateOnParse = true;
$dom->normalizeDocument();
libxml_use_internal_errors(false);
return simplexml_import_dom($dom);
}
protected function getXmlErrors()
{
$errors = array();
foreach (libxml_get_errors() as $error) {
$errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
$error->code,
trim($error->message),
$error->file ? $error->file : 'n/a',
$error->line,
$error->column
);
}
libxml_clear_errors();
libxml_use_internal_errors(false);
return $errors;
}
}

View File

@ -1,8 +1,10 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
* This file is part of the BeSimpleSoap.
*
* (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.
@ -10,36 +12,22 @@
namespace BeSimple\SoapBundle\ServiceDefinition;
use BeSimple\SoapBundle\Util\Collection;
use BeSimple\SoapCommon\Definition\Method as BaseMethod;
use BeSimple\SoapCommon\Definition\Type\TypeRepository;
class Method
/**
* @author Christian Kerl <christian-kerl@web.de>
* @author Francis Besset <francis.besset@gmail.com>
*/
class Method extends BaseMethod
{
private $name;
private $controller;
private $arguments;
private $headers;
private $return;
public function __construct($name = null, $controller = null, array $headers = array(), array $arguments = array(), Type $return = null)
public function __construct($name, $controller)
{
$this->setName($name);
$this->setController($controller);
$this->setHeaders($headers);
$this->setArguments($arguments);
parent::__construct($name);
if ($return) {
$this->setReturn($return);
}
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
$this->controller = $controller;
}
public function getController()
@ -47,40 +35,8 @@ class Method
return $this->controller;
}
public function setController($controller)
public function getVersions()
{
$this->controller = $controller;
}
public function getHeaders()
{
return $this->headers;
}
public function setHeaders(array $headers)
{
$this->headers = new Collection('getName', 'BeSimple\SoapBundle\ServiceDefinition\Header');
$this->headers->addAll($headers);
}
public function getArguments()
{
return $this->arguments;
}
public function setArguments(array $arguments)
{
$this->arguments = new Collection('getName', 'BeSimple\SoapBundle\ServiceDefinition\Argument');
$this->arguments->addAll($arguments);
}
public function getReturn()
{
return $this->return;
}
public function setReturn(Type $return)
{
$this->return = $return;
return array(\SOAP_1_1);
}
}

View File

@ -1,151 +0,0 @@
<?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\ServiceDefinition;
use BeSimple\SoapBundle\Util\Collection;
use BeSimple\SoapCommon\Classmap;
class ServiceDefinition
{
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $namespace;
/**
* @var \BeSimple\SoapBundle\Util\Collection
*/
private $methods;
/**
* @var \BeSimple\SoapCommon\Classmap
*/
private $classmap;
private $complexTypes = array();
public function __construct($name = null, $namespace = null, array $methods = array(), Classmap $classmap = null)
{
$this->setName($name);
$this->setNamespace($namespace);
$this->methods = new Collection('getName', 'BeSimple\SoapBundle\ServiceDefinition\Method');
$this->setMethods($methods);
$this->classmap = $classmap;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* @return string
*/
public function getNamespace()
{
return $this->namespace;
}
/**
* @param string $namespace
*/
public function setNamespace($namespace)
{
$this->namespace = $namespace;
}
/**
* @return \BeSimple\SoapBundle\Util\Collection
*/
public function getMethods()
{
return $this->methods;
}
/**
* @param array $methods
*/
public function setMethods(array $methods)
{
$this->methods->addAll($methods);
}
/**
* @return array
*/
public function getAllTypes()
{
$types = array();
foreach ($this->methods as $method) {
foreach ($method->getArguments() as $argument) {
$types[] = $argument->getType();
}
foreach ($method->getHeaders() as $header) {
$types[] = $header->getType();
}
$types[] = $method->getReturn();
}
return $types;
}
public function getClassmap()
{
return $this->classmap ?: array();
}
public function setClassmap(Classmap $classmap)
{
$this->classmap = $classmap;
}
public function hasDefinitionComplexType($type)
{
return isset($this->complexTypes[$type]);
}
public function addDefinitionComplexType($type, array $definition)
{
if ($this->hasDefinitionComplexType($type)) {
return false;
}
$this->complexTypes[$type] = $definition;
return true;
}
public function getDefinitionComplexTypes()
{
return $this->complexTypes;
}
}

View File

@ -1,89 +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\SoapBundle\ServiceDefinition\Strategy;
use BeSimple\SoapBundle\ServiceDefinition\Loader\AnnotationComplexTypeLoader;
use Zend\Soap\Wsdl;
use Zend\Soap\Wsdl\ComplexTypeStrategy\AbstractComplexTypeStrategy;
/**
* @author Francis Besset <francis.besset@gmail.com>
*/
class ComplexType extends AbstractComplexTypeStrategy
{
private $loader;
private $definition;
public function __construct(AnnotationComplexTypeLoader $loader, $definition)
{
$this->loader = $loader;
$this->definition = $definition;
}
/**
* Add a complex type by recursivly using all the class properties fetched via Reflection.
*
* @param string $type Name of the class to be specified
* @return string XSD Type for the given PHP type
*/
public function addComplexType($classname)
{
$classmap = $this->definition->getClassmap();
if ($classmap->hasByClassname($classname)) {
return 'tns:'.$classmap->getByClassname($classname);
}
if (!$this->loader->supports($classname)) {
throw new \InvalidArgumentException(sprintf('Cannot add ComplexType "%s" because it is not an object or the class could not be found.', $classname));
}
$definitionComplexType = $this->loader->load($classname);
$classnameAlias = isset($definitionComplexType['alias']) ? $definitionComplexType['alias'] : $classname;
$type = $this->getContext()->translateType($classnameAlias);
$xmlType = 'tns:'.$type;
// Register type here to avoid recursion
$classmap->add($type, $classname);
$this->addXmlDefinition($definitionComplexType, $classname, $type);
return $xmlType;
}
private function addXmlDefinition(array $definitionComplexType, $classname, $type)
{
$dom = $this->getContext()->toDomDocument();
$complexType = $dom->createElement('xsd:complexType');
$complexType->setAttribute('name', $type);
$all = $dom->createElement('xsd:all');
$elements = array();
foreach ($definitionComplexType['properties'] as $property) {
$element = $dom->createElement('xsd:element');
$element->setAttribute('name', $property->getName());
$element->setAttribute('type', $this->getContext()->getType($property->getValue()));
if ($property->isNillable()) {
$element->setAttribute('nillable', 'true');
}
$all->appendChild($element);
}
$complexType->appendChild($all);
$this->getContext()->getSchema()->appendChild($complexType);
$this->definition->addDefinitionComplexType($classname, $definitionComplexType);
}
}

View File

@ -1,60 +0,0 @@
<?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\ServiceDefinition;
class Type
{
private $phpType;
private $xmlType;
private $converter;
public function __construct($phpType = null, $xmlType = null, $converter = null)
{
$this->setPhpType($phpType);
$this->setXmlType($xmlType);
$this->setConverter($converter);
}
public function getPhpType()
{
return $this->phpType;
}
public function setPhpType($phpType)
{
$phpType = $phpType;
if ($phpType[0] == '\\') {
$phpType = substr($phpType, 1);
}
$this->phpType = $phpType;
}
public function getXmlType()
{
return $this->xmlType;
}
public function setXmlType($xmlType)
{
$this->xmlType = $xmlType;
}
public function getConverter()
{
return $this->converter;
}
public function setConverter($converter)
{
$this->converter = $converter;
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace BeSimple\SoapBundle\Soap;
class SoapAttachmentList
{
private $soapAttachments;
/**
* @param SoapAttachment[] $soapAttachments
*/
public function __construct(array $soapAttachments = [])
{
$this->soapAttachments = $soapAttachments;
}
public function hasSoapAttachments()
{
return $this->soapAttachments !== null && count($this->soapAttachments) > 0;
}
public function getSoapAttachments()
{
return $this->soapAttachments;
}
public function getSoapAttachmentIds()
{
$ids = [];
foreach ($this->getSoapAttachments() as $soapAttachment) {
$ids[] = $soapAttachment->getId();
}
return $ids;
}
}

View File

@ -1,77 +0,0 @@
<?php
namespace BeSimple\SoapBundle\Soap;
use BeSimple\SoapCommon\Classmap;
use BeSimple\SoapCommon\Converter\TypeConverterCollection;
use BeSimple\SoapClient\SoapClientBuilder as BaseSoapClientBuilder;
class SoapClientBuilder extends BaseSoapClientBuilder
{
protected $soapClient;
public function __construct($wsdl, array $options, Classmap $classmap = null, TypeConverterCollection $converters = null)
{
parent::__construct();
$this->checkOptions($options);
$this
->withWsdl($wsdl)
->withTrace($options['debug'])
;
if (isset($options['user_agent'])) {
$this->withUserAgent($options['user_agent']);
}
if (isset($options['cache_type'])) {
$this->withWsdlCache($options['cache_type']);
}
if ($classmap) {
$this->withClassmap($classmap);
}
if ($converters) {
$this->withTypeConverters($converters);
}
}
public function build()
{
if (!$this->soapClient) {
$this->soapClient = parent::build();
}
return $this->soapClient;
}
protected function checkOptions(array $options)
{
$checkOptions = array(
'debug' => false,
'cache_type' => null,
'exceptions' => true,
'user_agent' => 'BeSimpleSoap',
);
// check option names and live merge, if errors are encountered Exception will be thrown
$invalid = array();
$isInvalid = false;
foreach ($options as $key => $value) {
if (!array_key_exists($key, $checkOptions)) {
$isInvalid = true;
$invalid[] = $key;
}
}
if ($isInvalid) {
throw new \InvalidArgumentException(sprintf(
'The "%s" class does not support the following options: "%s".',
get_class($this),
implode('\', \'', $invalid)
));
}
}
}

View File

@ -11,10 +11,7 @@
namespace BeSimple\SoapBundle\Soap;
use BeSimple\SoapBundle\Util\Collection;
use Symfony\Component\HttpFoundation\Request;
use Zend\Mime\Mime;
use Zend\Mime\Message;
/**

View File

@ -16,66 +16,70 @@ use BeSimple\SoapBundle\ServiceBinding\RpcLiteralRequestMessageBinder;
use BeSimple\SoapBundle\ServiceDefinition as Definition;
use BeSimple\SoapBundle\Tests\fixtures\ServiceBinding as Fixtures;
use BeSimple\SoapBundle\Util\Collection;
use BeSimple\SoapCommon\Definition\Type\ComplexType;
use BeSimple\SoapCommon\Definition\Type\TypeRepository;
class RpcLiteralRequestMessageBinderTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider messageProvider
*/
public function testProcessMessage(Definition\Method $method, $message, $assert)
public function testProcessMessage(Definition\Method $method, array $message, array $assert)
{
$messageBinder = new RpcLiteralRequestMessageBinder();
$result = $messageBinder->processMessage($method, $message);
$result = $messageBinder->processMessage($method, $message, $this->getTypeRepository());
$this->assertSame($assert, $result);
}
public function testProcessMessageWithComplexType()
{
$typeRepository = $this->addComplexTypes($this->getTypeRepository());
$messageBinder = new RpcLiteralRequestMessageBinder();
$method = new Definition\Method('complextype_argument', null);
$method->addInput('foo', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo');
$foo = new Fixtures\Foo('foobar', 19395);
$result = $messageBinder->processMessage(
new Definition\Method('complextype_argument', null, array(), array(
new Definition\Argument('foo', new Definition\Type('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo')),
)),
$method,
array($foo),
$this->getDefinitionComplexTypes()
$typeRepository
);
$this->assertEquals(array('foo' => $foo), $result);
$foo1 = new Fixtures\Foo('foobar', 29291);
$foo2 = new Fixtures\Foo('barfoo', 39392);
$foos = new \stdClass();
$foos->item = array($foo1, $foo2);
$method = new Definition\Method('complextype_argument', null);
$method->addInput('foos', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo[]');
$result = $messageBinder->processMessage(
new Definition\Method('complextype_argument', null, array(), array(
new Definition\Argument('foos', new Definition\Type('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo[]')),
)),
$method,
array($foos),
$this->getDefinitionComplexTypes()
$typeRepository
);
$this->assertEquals(array('foos' => array($foo1, $foo2)), $result);
}
/**
* @expectedException SoapFault
*/
public function testProcessMessageSoapFault()
{
$messageBinder = new RpcLiteralRequestMessageBinder();
$method = new Definition\Method('complextype_argument', null);
$method->addInput('foo', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo');
$foo = new Fixtures\Foo('foo', null);
$result = $messageBinder->processMessage(
new Definition\Method('complextype_argument', null, array(), array(
new Definition\Argument('foo', new Definition\Type('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo')),
)),
$this->setExpectedException('SoapFault');
$messageBinder->processMessage(
$method,
array($foo),
$this->getDefinitionComplexTypes()
$this->addComplexTypes($this->getTypeRepository())
);
}
@ -83,16 +87,17 @@ class RpcLiteralRequestMessageBinderTest extends \PHPUnit_Framework_TestCase
{
$messageBinder = new RpcLiteralRequestMessageBinder();
$method = new Definition\Method('complextype_argument', null);
$method->addInput('foos', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo[]');
$foo = new Fixtures\Foo('foo', 2499104);
$foos = new \stdClass();
$foos->item = array($foo, $foo);
$result = $messageBinder->processMessage(
new Definition\Method('complextype_argument', null, array(), array(
new Definition\Argument('foos', new Definition\Type('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo[]')),
)),
$method,
array($foos),
$this->getDefinitionComplexTypes()
$this->addComplexTypes($this->getTypeRepository())
);
$this->assertEquals(array('foos' => array($foo, $foo)), $result);
@ -102,16 +107,17 @@ class RpcLiteralRequestMessageBinderTest extends \PHPUnit_Framework_TestCase
{
$messageBinder = new RpcLiteralRequestMessageBinder();
$method = new Definition\Method('complextype_argument', null);
$method->addInput('fooBar', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooBar');
$foo = new Fixtures\Foo('foo', 38845);
$bar = new Fixtures\Bar('bar', null);
$fooBar = new Fixtures\FooBar($foo, $bar);
$result = $messageBinder->processMessage(
new Definition\Method('complextype_argument', null, array(), array(
new Definition\Argument('fooBar', new Definition\Type('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooBar')),
)),
$method,
array($fooBar),
$this->getDefinitionComplexTypes()
$this->addComplexTypes($this->getTypeRepository())
);
$this->assertEquals(array('fooBar' => $fooBar), $result);
@ -121,17 +127,18 @@ class RpcLiteralRequestMessageBinderTest extends \PHPUnit_Framework_TestCase
{
$messageBinder = new RpcLiteralRequestMessageBinder();
$method = new Definition\Method('complextype_with_array', null);
$method->addInput('simple_arrays', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\SimpleArrays');
$array = array(1, 2, 3, 4);
$stdClass = new \stdClass();
$stdClass->item = $array;
$simpleArrays = new Fixtures\SimpleArrays(null, new \stdClass(), $stdClass);
$result = $messageBinder->processMessage(
new Definition\Method('complextype_with_array', null, array(), array(
new Definition\Argument('simple_arrays', new Definition\Type('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\SimpleArrays')),
)),
$method,
array($simpleArrays),
$this->getDefinitionComplexTypes()
$this->addComplexTypes($this->getTypeRepository())
);
$result = $result['simple_arrays'];
@ -144,12 +151,13 @@ class RpcLiteralRequestMessageBinderTest extends \PHPUnit_Framework_TestCase
{
$messageBinder = new RpcLiteralRequestMessageBinder();
$method = new Definition\Method('empty_array_complex_type', null);
$method->addInput('foo', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo[]');
$result = $messageBinder->processMessage(
new Definition\Method('empty_array_complex_type', null, array(), array(
new Definition\Argument('foo', new Definition\Type('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo[]')),
)),
$method,
array(new \stdClass()),
$this->getDefinitionComplexTypes()
$this->addComplexTypes($this->getTypeRepository())
);
$this->assertEquals(array('foo' => array()), $result);
@ -159,16 +167,17 @@ class RpcLiteralRequestMessageBinderTest extends \PHPUnit_Framework_TestCase
{
$messageBinder = new RpcLiteralRequestMessageBinder();
$method = new Definition\Method('prevent_infinite_recursion', null);
$method->addInput('foo_recursive', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooRecursive');
$foo = new Fixtures\FooRecursive('foo', '');
$bar = new Fixtures\BarRecursive($foo, 10394);
$foo->bar = $bar;
$result = $messageBinder->processMessage(
new Definition\Method('prevent_infinite_recursion', null, array(), array(
new Definition\Argument('foo_recursive', new Definition\Type('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooRecursive')),
)),
$method,
array($foo),
$this->getDefinitionComplexTypes()
$this->addComplexTypes($this->getTypeRepository())
);
$this->assertEquals(array('foo_recursive' => $foo), $result);
@ -179,43 +188,43 @@ class RpcLiteralRequestMessageBinderTest extends \PHPUnit_Framework_TestCase
$messages = array();
$messages[] = array(
new Definition\Method('no_argument'),
new Definition\Method('no_argument', null),
array(),
array(),
);
$method = new Definition\Method('string_argument', null);
$method->addInput('foo', 'string');
$messages[] = array(
new Definition\Method('string_argument', null, array(), array(
new Definition\Argument('foo', new Definition\Type('string')),
)),
$method,
array('bar'),
array('foo' => 'bar'),
);
$method = new Definition\Method('string_int_arguments', null);
$method->addInput('foo', 'string');
$method->addInput('bar', 'int');
$messages[] = array(
new Definition\Method('string_int_arguments', null, array(), array(
new Definition\Argument('foo', new Definition\Type('string')),
new Definition\Argument('bar', new Definition\Type('int')),
)),
$method,
array('test', 20),
array('foo' => 'test', 'bar' => 20),
);
$method = new Definition\Method('array_string_arguments', null);
$method->addInput('foo', 'string[]');
$method->addInput('bar', 'int');
$strings = new \stdClass();
$strings->item = array('foo', 'bar', 'barfoo');
$messages[] = array(
new Definition\Method('array_string_arguments', null, array(), array(
new Definition\Argument('foo', new Definition\Type('string[]')),
new Definition\Argument('bar', new Definition\Type('int')),
)),
$method,
array($strings, 4),
array('foo' => array('foo', 'bar', 'barfoo'), 'bar' => 4),
);
$method = new Definition\Method('empty_array', null);
$method->addInput('foo', 'string[]');
$messages[] = array(
new Definition\Method('empty_array', null, array(), array(
new Definition\Argument('foo', new Definition\Type('string[]')),
)),
$method,
array(new \stdClass()),
array('foo' => array()),
);
@ -223,40 +232,38 @@ class RpcLiteralRequestMessageBinderTest extends \PHPUnit_Framework_TestCase
return $messages;
}
private function getDefinitionComplexTypes()
private function addComplexTypes(TypeRepository $typeRepository)
{
$definitionComplexTypes = array();
$foo = new ComplexType('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo', 'Foo');
$foo->add('foo', 'string');
$foo->add('bar', 'int');
$typeRepository->addComplexType($foo);
$definitionComplexTypes['BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo'] = $this->createComplexTypeCollection(array(
array('foo', 'string'),
array('bar', 'int'),
));
$bar = new ComplexType('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Bar', 'Bar');
$bar->add('foo', 'string');
$bar->add('bar', 'int', true);
$typeRepository->addComplexType($bar);
$definitionComplexTypes['BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Bar'] = $this->createComplexTypeCollection(array(
array('foo', 'string'),
array('bar', 'int', true),
));
$fooBar = new ComplexType('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooBar', 'FooBar');
$fooBar->add('foo', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo');
$fooBar->add('bar', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Bar');
$typeRepository->addComplexType($fooBar);
$definitionComplexTypes['BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooBar'] = $this->createComplexTypeCollection(array(
array('foo', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Foo'),
array('bar', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\Bar'),
));
$simpleArrays = new ComplexType('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\SimpleArrays', 'SimpleArrays');
$simpleArrays->add('array1', 'string[]', true);
$simpleArrays->add('array2', 'string[]');
$simpleArrays->add('array3', 'string[]');
$typeRepository->addComplexType($simpleArrays);
$definitionComplexTypes['BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\SimpleArrays'] = $this->createComplexTypeCollection(array(
array('array1', 'string[]', true),
array('array2', 'string[]'),
array('array3', 'string[]'),
));
$fooRecursive = new ComplexType('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooRecursive', 'FooRecursive');
$fooRecursive->add('bar', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\BarRecursive');
$typeRepository->addComplexType($fooRecursive);
$definitionComplexTypes['BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooRecursive'] = $this->createComplexTypeCollection(array(
array('bar', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\BarRecursive'),
));
$barRecursive = new ComplexType('BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\BarRecursive', 'BarRecursive');
$barRecursive->add('foo', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooRecursive');
$typeRepository->addComplexType($barRecursive);
$definitionComplexTypes['BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\BarRecursive'] = $this->createComplexTypeCollection(array(
array('foo', 'BeSimple\SoapBundle\Tests\fixtures\ServiceBinding\FooRecursive'),
));
return $definitionComplexTypes;
return $typeRepository;
}
private function createComplexTypeCollection(array $properties)
@ -277,4 +284,18 @@ class RpcLiteralRequestMessageBinderTest extends \PHPUnit_Framework_TestCase
return array('properties' => $collection);
}
private function getTypeRepository()
{
$typeRepository = new TypeRepository();
$typeRepository->addXmlNamespace('xsd', 'http://www.w3.org/2001/XMLSchema');
$typeRepository->addType('string', 'xsd:string');
$typeRepository->addType('boolean', 'xsd:boolean');
$typeRepository->addType('int', 'xsd:int');
$typeRepository->addType('float', 'xsd:float');
$typeRepository->addType('date', 'xsd:date');
$typeRepository->addType('dateTime', 'xsd:dateTime');
return $typeRepository;
}
}

View File

@ -23,6 +23,8 @@ class SoapRequestTest extends \PHPUnit_Framework_TestCase
{
public function testMtomMessage()
{
$this->markTestSkipped('Skip because I\'m not sure that SoapRequest is used in a HTTP Request process.');
$content = $this->loadRequestContentFixture('mtom/simple.txt');
$request = new SoapRequest(array(), array(), array(), array(), array(), array(), $content);

View File

@ -3,6 +3,7 @@
* 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.
@ -10,15 +11,12 @@
namespace BeSimple\SoapBundle;
use BeSimple\SoapBundle\Converter\TypeRepository;
use BeSimple\SoapBundle\ServiceBinding\MessageBinderInterface;
use BeSimple\SoapBundle\ServiceBinding\ServiceBinder;
use BeSimple\SoapBundle\ServiceDefinition\Dumper\DumperInterface;
use BeSimple\SoapCommon\Classmap;
use BeSimple\SoapCommon\Converter\TypeConverterCollection;
use BeSimple\SoapCommon\SoapOptionsBuilder;
use BeSimple\SoapServer\SoapServerOptionsBuilder;
use BeSimple\SoapWsdl\Dumper\Dumper;
use BeSimple\SoapServer\SoapServerBuilder;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\Loader\LoaderInterface;
@ -26,43 +24,29 @@ use Symfony\Component\Config\Loader\LoaderInterface;
* WebServiceContext.
*
* @author Christian Kerl <christian-kerl@web.de>
* @author Francis Besset <francis.besset@gmail.com>
*/
class WebServiceContext
{
private $classmap;
private $typeRepository;
private $converterRepository;
private $wsdlFileDumper;
private $options;
private $serviceDefinition;
private $serviceBinder;
private $serverBuilder;
public function __construct(LoaderInterface $loader, DumperInterface $dumper, Classmap $classmap, TypeRepository $typeRepository, TypeConverterCollection $converters, array $options)
public function __construct(LoaderInterface $loader, TypeConverterCollection $converters, array $options)
{
$this->loader = $loader;
$this->wsdlFileDumper = $dumper;
$this->classmap = $classmap;
$this->typeRepository = $typeRepository;
$this->converters = $converters;
// Issue #6: keep the debug because the cache is invalid
$options['debug'] = true;
$this->options = $options;
$this->loader = $loader;
$this->converters = $converters;
$this->options = $options;
}
public function getServiceDefinition()
{
if (null === $this->serviceDefinition) {
$cacheDefinition = new ConfigCache(sprintf('%s/%s.definition.php', $this->options['cache_dir'], $this->options['name']), $this->options['debug']);
if ($cacheDefinition->isFresh()) {
$this->serviceDefinition = include (string) $cacheDefinition;
$cache = new ConfigCache(sprintf('%s/%s.definition.php', $this->options['cache_dir'], $this->options['name']), $this->options['debug']);
if ($cache->isFresh()) {
$this->serviceDefinition = include $cache->getPath();
} else {
if (!$this->loader->supports($this->options['resource'], $this->options['resource_type'])) {
throw new \LogicException(sprintf('Cannot load "%s" (%s)', $this->options['resource'], $this->options['resource_type']));
@ -72,10 +56,7 @@ class WebServiceContext
$this->serviceDefinition->setName($this->options['name']);
$this->serviceDefinition->setNamespace($this->options['namespace']);
$this->serviceDefinition->setClassmap($this->classmap);
$this->classmap = null;
$this->typeRepository->fixTypeInformation($this->serviceDefinition);
$cache->write('<?php return unserialize('.var_export(serialize($this->serviceDefinition), true).');');
}
}
@ -89,19 +70,21 @@ class WebServiceContext
public function getWsdlFile($endpoint = null)
{
$file = sprintf('%s/%s.%s.wsdl', $this->options['cache_dir'], $this->options['name'], md5($endpoint));
$cacheWsdl = new ConfigCache($file, $this->options['debug']);
$file = sprintf ('%s/%s.%s.wsdl', $this->options['cache_dir'], $this->options['name'], md5($endpoint));
$cache = new ConfigCache($file, $this->options['debug']);
if(!$cacheWsdl->isFresh()) {
$serviceDefinition = $this->getServiceDefinition();
if(!$cache->isFresh()) {
$definition = $this->getServiceDefinition();
$cacheWsdl->write($this->wsdlFileDumper->dumpServiceDefinition($serviceDefinition, $endpoint));
if ($endpoint) {
$definition->setOption('location', $endpoint);
}
$cacheDefinition = new ConfigCache(sprintf('%s/%s.definition.php', $this->options['cache_dir'], $this->options['name']), $this->options['debug']);
$cacheDefinition->write('<?php return unserialize('.var_export(serialize($serviceDefinition), true).');');
$dumper = new Dumper($definition, array('stylesheet' => $this->options['wsdl_stylesheet']));
$cache->write($dumper->dump());
}
return (string) $cacheWsdl;
return $cache->getPath();
}
public function getServiceBinder()
@ -121,15 +104,14 @@ class WebServiceContext
public function getServerBuilder()
{
if (null === $this->serverBuilder) {
$this->serverBuilder = SoapServerBuilder::createWithDefaults()
->withWsdl($this->getWsdlFile())
->withClassmap($this->getServiceDefinition()->getClassmap())
->withTypeConverters($this->converters)
;
if (null !== $this->options['cache_type']) {
$this->serverBuilder->withWsdlCache($this->options['cache_type']);
}
$soapServerBuilder = new SoapServerBuilder();
$this->serverBuilder = $soapServerBuilder->build(
SoapServerOptionsBuilder::createWithDefaults(),
SoapOptionsBuilder::createWithClassMap(
$this->getWsdlFile(),
$this->getServiceDefinition()->getTypeRepository()->getClassmap()
)
);
}
return $this->serverBuilder;

View File

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

View File

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

View File

@ -0,0 +1,234 @@
<?php
namespace BeSimple\SoapClient\Curl;
use BeSimple\SoapClient\Curl\Http\HttpAuthenticationBasicOptions;
use BeSimple\SoapClient\Curl\Http\HttpAuthenticationDigestOptions;
use BeSimple\SoapClient\Curl\Http\SslCertificateOptions;
use Exception;
class Curl
{
const CURL_SUCCESS = true;
const CURL_FAILED = false;
private $curlSession;
private $options;
/**
* @param CurlOptions $options
*/
public function __construct(CurlOptions $options)
{
$this->curlSession = $this->acquireNewCurlSession($options);
$this->options = $options;
}
public function __destruct()
{
$this->closeCurlSession($this->curlSession);
}
/**
* @param string $location HTTP location
* @param string $request Request body
* @param array $requestHeaders Request header strings
* @return CurlResponse
*/
public function executeCurlWithCachedSession($location, $request = null, $requestHeaders = [])
{
return $this->executeCurlSession($this->curlSession, $this->options, $location, $request, $requestHeaders);
}
/**
* @param CurlOptions $options
* @param string $location HTTP location
* @param string $request Request body
* @param array $requestHeaders Request header strings
* @return CurlResponse
*/
public function executeCurl(CurlOptions $options, $location, $request = null, $requestHeaders = [])
{
$curlSession = $this->acquireNewCurlSession($options);
$curlResponse = $this->executeCurlSession($curlSession, $options, $location, $request, $requestHeaders);
$this->closeCurlSession($curlSession);
return $curlResponse;
}
private function acquireNewCurlSession(CurlOptions $options)
{
$curlSession = curl_init();
curl_setopt_array($curlSession, [
CURLOPT_ENCODING => '',
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_FAILONERROR => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_HEADER => true,
CURLOPT_USERAGENT => $options->getUserAgent(),
CURLINFO_HEADER_OUT => true,
CURLOPT_CONNECTTIMEOUT => $options->getConnectionTimeout()
]);
return $curlSession;
}
private function closeCurlSession($curlSession)
{
curl_close($curlSession);
}
/**
* @param mixed $curlSession Result of curl_init() handle
* @param CurlOptions $options
* @param string $location HTTP location
* @param string $request Request body
* @param array $requestHeaders Request header strings
* @return CurlResponse
*/
private function executeCurlSession($curlSession, CurlOptions $options, $location, $request = null, $requestHeaders = [])
{
curl_setopt($curlSession, CURLOPT_URL, $location);
curl_setopt($curlSession, CURLOPT_HEADER, true);
curl_setopt($curlSession, CURLOPT_RETURNTRANSFER, true);
if (!is_null($request)) {
curl_setopt($curlSession, CURLOPT_POST, true);
curl_setopt($curlSession, CURLOPT_POSTFIELDS, $request);
} else {
curl_setopt($curlSession, CURLOPT_POST, false);
}
if (count($requestHeaders) > 0) {
curl_setopt($curlSession, CURLOPT_HTTPHEADER, $requestHeaders);
}
if (!$options->getSoapCompression() & SOAP_COMPRESSION_ACCEPT) {
curl_setopt($curlSession, CURLOPT_ENCODING, 'identity');
}
if ($options->hasProxy()) {
$proxyHost = $options->getProxy()->getHost() . $options->getProxy()->getPort();
curl_setopt($curlSession, CURLOPT_PROXY, $proxyHost);
if ($options->getProxy()->hasCredentials()) {
curl_setopt($curlSession, CURLOPT_PROXYUSERPWD, $options->getProxy()->getLogin() . ':' . $options->getProxy()->getPassword());
if ($options->getProxy()->hasAuthenticationType()) {
curl_setopt($curlSession, CURLOPT_PROXYAUTH, $options->getProxy()->getAuthenticationType());
}
}
}
if ($options->hasHttpAuthentication()) {
if ($options->hasHttpAuthenticationBasic()) {
/** @var HttpAuthenticationBasicOptions $httpAuthenticationBasic */
$httpAuthenticationBasic = $options->getHttpAuthentication();
curl_setopt($curlSession, CURLOPT_HTTPAUTH, $httpAuthenticationBasic->getAuthenticationType());
curl_setopt($curlSession, CURLOPT_USERPWD, $httpAuthenticationBasic->getUsername() . ':' . $httpAuthenticationBasic->getPassword());
} else if ($options->hasHttpAuthenticationDigest()) {
/** @var HttpAuthenticationDigestOptions $httpAuthenticationDigest */
$httpAuthenticationDigest = $options->getHttpAuthentication();
curl_setopt($curlSession, CURLOPT_HTTPAUTH, $httpAuthenticationDigest->getAuthenticationType());
} else {
throw new Exception('Unresolved authentication type: '.get_class($options->getHttpAuthentication()));
}
}
if ($options->hasSslCertificateOptions()) {
$sslCertificateOptions = $options->getSslCertificateOptions();
curl_setopt($curlSession, CURLOPT_SSLCERT, $sslCertificateOptions->getCertificateLocalPath());
if ($sslCertificateOptions->hasCertificatePassPhrase()) {
curl_setopt($curlSession, CURLOPT_SSLCERTPASSWD, $sslCertificateOptions->getCertificatePassPhrase());
}
if ($sslCertificateOptions->hasCertificateAuthorityInfo()) {
curl_setopt($curlSession, CURLOPT_CAINFO, $sslCertificateOptions->getCertificateAuthorityInfo());
}
if ($sslCertificateOptions->hasCertificateAuthorityPath()) {
curl_setopt($curlSession, CURLOPT_CAPATH, $sslCertificateOptions->hasCertificateAuthorityPath());
}
}
$executeSoapCallResponse = $this->executeHttpCall($curlSession, $options);
$httpRequestHeadersAsString = curl_getinfo($curlSession, CURLINFO_HEADER_OUT);
$headerSize = curl_getinfo($curlSession, CURLINFO_HEADER_SIZE);
$httpResponseCode = curl_getinfo($curlSession, CURLINFO_HTTP_CODE);
$httpResponseContentType = curl_getinfo($curlSession, CURLINFO_CONTENT_TYPE);;
preg_match('/HTTP\/(1\.[0-1]+) ([0-9]{3}) (.*)/', $executeSoapCallResponse, $httpResponseMessages);
$httpResponseMessage = trim(array_pop($httpResponseMessages));
$curlErrorMessage = sprintf(
'Curl error "%s" with message: %s occurred while connecting to %s',
curl_errno($curlSession),
curl_error($curlSession),
$location
);
if ($executeSoapCallResponse === false) {
return new CurlResponse(
$httpRequestHeadersAsString,
$httpResponseCode,
$httpResponseMessage,
$httpResponseContentType,
self::CURL_FAILED,
$curlErrorMessage
);
}
return new CurlResponse(
$httpRequestHeadersAsString,
$httpResponseCode,
$httpResponseMessage,
$httpResponseContentType,
self::CURL_SUCCESS,
null,
substr($executeSoapCallResponse, 0, $headerSize),
substr($executeSoapCallResponse, $headerSize)
);
}
/**
* Custom curl_exec wrapper that allows to follow redirects when specific
* http response code is set. SOAP only allows 307.
*
* @param mixed $curlSession Result of curl_init() handle
* @param CurlOptions $options
* @param int $executedRedirects
* @return string|null
* @throws Exception
*/
private function executeHttpCall($curlSession, CurlOptions $options, $executedRedirects = 0)
{
if ($executedRedirects > $options->getFollowLocationMaxRedirects()) {
throw new Exception('Cannot executeHttpCall - too many redirects: ' . $executedRedirects);
}
$curlExecResponse = curl_exec($curlSession);
$httpResponseCode = curl_getinfo($curlSession, CURLINFO_HTTP_CODE);
if ($httpResponseCode === 307) {
$newUrl = $this->getRedirectUrlFromResponseHeaders($curlSession, $curlExecResponse);
curl_setopt($curlSession, CURLOPT_URL, $newUrl);
return $this->executeHttpCall($curlSession, $options, ++$executedRedirects);
}
return $curlExecResponse;
}
private function getRedirectUrlFromResponseHeaders($curlSession, $curlExecResponse)
{
$curlExecResponseHeaders = substr($curlExecResponse, 0, curl_getinfo($curlSession, CURLINFO_HEADER_SIZE));
$matches = [];
preg_match('/Location:(.*?)\n/', $curlExecResponseHeaders, $matches);
$url = trim(array_pop($matches));
if (($url = @parse_url($url)) !== false) {
$lastUrl = parse_url(curl_getinfo($curlSession, CURLINFO_EFFECTIVE_URL));
if (!isset($url['scheme'])) {
$url['scheme'] = $lastUrl['scheme'];
}
if (!isset($url['host'])) {
$url['host'] = $lastUrl['host'];
}
if (!isset($url['path'])) {
$url['path'] = $lastUrl['path'];
}
return $url['scheme'] . '://' . $url['host'] . $url['path'] . ($url['query'] ? '?' . $url['query'] : '');
}
throw new Exception('Cannot parse WSDL url redirect: ' . $url);
}
}

View File

@ -0,0 +1,126 @@
<?php
namespace BeSimple\SoapClient\Curl;
use BeSimple\SoapClient\Curl\Http\HttpAuthenticationDigestOptions;
use BeSimple\SoapClient\Curl\Http\HttpAuthenticationInterface;
use BeSimple\SoapClient\Curl\Http\HttpAuthenticationBasicOptions;
use BeSimple\SoapClient\Curl\Http\SslCertificateOptions;
use BeSimple\SoapClient\SoapServerProxy\SoapServerProxy;
class CurlOptions
{
const DEFAULT_USER_AGENT = 'BeSimpleSoap';
const SOAP_COMPRESSION_NONE = null;
const SOAP_COMPRESSION_GZIP = SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP;
const SOAP_COMPRESSION_DEFLATE = SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_DEFLATE;
private $userAgent;
private $followLocationMaxRedirects;
private $soapCompression;
private $connectionTimeout;
private $proxy;
private $httpAuthentication;
private $sslCertificateOptions;
/**
* @param string $userAgent
* @param int $followLocationMaxRedirects
* @param CurlOptions::SOAP_COMPRESSION_NONE|CurlOptions::SOAP_COMPRESSION_GZIP|CurlOptions::SOAP_COMPRESSION_DEFLATE $soapCompression
* @param int $connectionTimeout
* @param SoapServerProxy|null $proxy
* @param HttpAuthenticationInterface|null $httpAuthentication
* @param SslCertificateOptions|null $sslCertificateOptions
*/
public function __construct(
$userAgent,
$followLocationMaxRedirects,
$soapCompression,
$connectionTimeout,
SoapServerProxy $proxy = null,
HttpAuthenticationInterface $httpAuthentication = null,
SslCertificateOptions $sslCertificateOptions = null
) {
$this->userAgent = $userAgent;
$this->followLocationMaxRedirects = $followLocationMaxRedirects;
$this->soapCompression = $soapCompression;
$this->connectionTimeout = $connectionTimeout;
$this->proxy = $proxy;
$this->httpAuthentication = $httpAuthentication;
$this->sslCertificateOptions = $sslCertificateOptions;
}
public function getUserAgent()
{
return $this->userAgent;
}
public function getFollowLocationMaxRedirects()
{
return $this->followLocationMaxRedirects;
}
public function getSoapCompression()
{
return $this->soapCompression;
}
public function getConnectionTimeout()
{
return $this->connectionTimeout;
}
public function getProxy()
{
return $this->proxy;
}
public function getHttpAuthentication()
{
return $this->httpAuthentication;
}
public function getSslCertificateOptions()
{
return $this->sslCertificateOptions;
}
public function hasProxy()
{
return $this->proxy !== null;
}
public function hasHttpAuthentication()
{
return $this->httpAuthentication !== null;
}
public function hasSslCertificateOptions()
{
return $this->sslCertificateOptions !== null;
}
public function hasHttpAuthenticationBasic()
{
if ($this->hasHttpAuthentication()) {
if ($this->getHttpAuthentication() instanceof HttpAuthenticationBasicOptions) {
return true;
}
}
return false;
}
public function hasHttpAuthenticationDigest()
{
if ($this->hasHttpAuthentication()) {
if ($this->getHttpAuthentication() instanceof HttpAuthenticationDigestOptions) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,79 @@
<?php
namespace BeSimple\SoapClient\Curl;
use BeSimple\SoapClient\Curl\Http\HttpAuthenticationDigestOptions;
use BeSimple\SoapClient\Curl\Http\SslCertificateOptions;
use BeSimple\SoapClient\SoapOptions\SoapClientOptions;
use BeSimple\SoapClient\SoapServerAuthentication\SoapServerAuthenticationBasic;
use BeSimple\SoapClient\SoapServerAuthentication\SoapServerAuthenticationDigest;
use BeSimple\SoapClient\Curl\Http\HttpAuthenticationBasicOptions;
use Exception;
class CurlOptionsBuilder
{
const DEFAULT_MAX_REDIRECTS = 10;
const DEFAULT_CONNECTION_TIMEOUT = 10;
public static function buildDefault()
{
return new CurlOptions(
CurlOptions::DEFAULT_USER_AGENT,
self::DEFAULT_MAX_REDIRECTS,
CurlOptions::SOAP_COMPRESSION_NONE,
self::DEFAULT_CONNECTION_TIMEOUT
);
}
public static function buildForSoapClient(SoapClientOptions $soapClientOptions)
{
return new CurlOptions(
$soapClientOptions->getUserAgent(),
self::DEFAULT_MAX_REDIRECTS,
$soapClientOptions->getCompression(),
self::DEFAULT_CONNECTION_TIMEOUT,
$soapClientOptions->getProxy(),
self::getHttpAuthOptions($soapClientOptions),
self::getSslCertificateOptions($soapClientOptions)
);
}
private static function getHttpAuthOptions(SoapClientOptions $soapClientOptions)
{
if ($soapClientOptions->hasAuthentication()) {
if ($soapClientOptions->hasAuthenticationBasic()) {
/** @var SoapServerAuthenticationBasic $basicAuthentication */
$basicAuthentication = $soapClientOptions->getAuthentication();
return new HttpAuthenticationBasicOptions(
$basicAuthentication->getLogin(),
$basicAuthentication->getPassword()
);
} else if ($soapClientOptions->hasAuthenticationDigest()) {
return new HttpAuthenticationDigestOptions();
} else {
throw new Exception('Unresolved authentication type: '.get_class($soapClientOptions->getAuthentication()));
}
}
return null;
}
private static function getSslCertificateOptions(SoapClientOptions $soapClientOptions)
{
if ($soapClientOptions->hasAuthenticationDigest()) {
/** @var SoapServerAuthenticationDigest $digestAuthentication */
$digestAuthentication = $soapClientOptions->getAuthentication();
return new SslCertificateOptions(
$digestAuthentication->getLocalCert(),
$digestAuthentication->getPassPhrase()
);
}
return null;
}
}

View File

@ -0,0 +1,100 @@
<?php
namespace BeSimple\SoapClient\Curl;
class CurlResponse
{
private $httpRequestHeaders;
private $httpResponseStatusCode;
private $httpResponseStatusMessage;
private $httpResponseContentType;
private $curlStatus;
private $curlErrorMessage;
private $responseHeader;
private $responseBody;
public function __construct(
$httpRequestHeaders,
$httpResponseStatusCode,
$httpResponseStatusMessage,
$httpResponseContentType,
$curlStatus,
$curlErrorMessage = null,
$responseHeader = null,
$responseBody = null
) {
$this->httpRequestHeaders = $httpRequestHeaders;
$this->httpResponseStatusCode = $httpResponseStatusCode;
$this->httpResponseStatusMessage = $httpResponseStatusMessage;
$this->httpResponseContentType = $httpResponseContentType;
$this->curlStatus = $curlStatus;
$this->curlErrorMessage = $curlErrorMessage;
$this->responseHeader = $responseHeader;
$this->responseBody = $responseBody;
}
public function getHttpRequestHeaders()
{
return $this->httpRequestHeaders;
}
public function getHttpResponseStatusCode()
{
return $this->httpResponseStatusCode;
}
public function getHttpResponseStatusMessage()
{
return $this->httpResponseStatusMessage;
}
public function getHttpResponseContentType()
{
return $this->httpResponseContentType;
}
public function getCurlStatus()
{
return $this->curlStatus;
}
public function curlStatusSuccess()
{
return $this->curlStatus === Curl::CURL_SUCCESS;
}
public function curlStatusFailed()
{
return $this->curlStatus === Curl::CURL_FAILED;
}
public function hasCurlErrorMessage()
{
return $this->curlErrorMessage !== null;
}
public function getCurlErrorMessage()
{
return $this->curlErrorMessage;
}
public function hasResponseHeader()
{
return $this->responseHeader !== null;
}
public function getResponseHeader()
{
return $this->responseHeader;
}
public function hasResponseBody()
{
return $this->responseBody !== null;
}
public function getResponseBody()
{
return $this->responseBody;
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace BeSimple\SoapClient\Curl\Http;
class HttpAuthenticationBasicOptions implements HttpAuthenticationInterface
{
private $username;
private $password;
/**
* @param string $username
* @param string $password
*/
public function __construct($username, $password)
{
$this->username = $username;
$this->password = $password;
}
public function getUsername()
{
return $this->username;
}
public function getPassword()
{
return $this->password;
}
public function getAuthenticationType()
{
return HttpAuthenticationInterface::AUTHENTICATION_TYPE_BASIC;
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace BeSimple\SoapClient\Curl\Http;
class HttpAuthenticationDigestOptions implements HttpAuthenticationInterface
{
public function getAuthenticationType()
{
return HttpAuthenticationInterface::AUTHENTICATION_TYPE_DIGEST;
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace BeSimple\SoapClient\Curl\Http;
interface HttpAuthenticationInterface
{
const AUTHENTICATION_TYPE_ANY = CURLAUTH_ANY;
const AUTHENTICATION_TYPE_BASIC = CURLAUTH_BASIC;
const AUTHENTICATION_TYPE_DIGEST = CURLAUTH_DIGEST;
/**
* @return string choice from self::AUTHENTICATION_TYPE_ANY|self::AUTHENTICATION_TYPE_BASIC|self::AUTHENTICATION_TYPE_DIGEST
*/
public function getAuthenticationType();
}

View File

@ -0,0 +1,64 @@
<?php
namespace BeSimple\SoapClient\Curl\Http;
class SslCertificateOptions
{
private $certificateLocalPath;
private $certificatePassPhrase;
private $certificateAuthorityInfo;
private $certificateAuthorityPath;
/**
* @param string $certificateLocalPath
* @param string $certificatePassPhrase
* @param string $certificateAuthorityInfo
* @param string $certificateAuthorityPath
*/
public function __construct(
$certificateLocalPath,
$certificatePassPhrase = null,
$certificateAuthorityInfo = null,
$certificateAuthorityPath = null
) {
$this->certificateLocalPath = $certificateLocalPath;
$this->certificatePassPhrase = $certificatePassPhrase;
$this->certificateAuthorityInfo = $certificateAuthorityInfo;
$this->certificateAuthorityPath = $certificateAuthorityPath;
}
public function getCertificateLocalPath()
{
return $this->certificateLocalPath;
}
public function getCertificatePassPhrase()
{
return $this->certificatePassPhrase;
}
public function getCertificateAuthorityInfo()
{
return $this->certificateAuthorityInfo;
}
public function getCertificateAuthorityPath()
{
return $this->certificateAuthorityPath;
}
public function hasCertificatePassPhrase()
{
return $this->certificatePassPhrase !== null;
}
public function hasCertificateAuthorityInfo()
{
return $this->certificateAuthorityInfo !== null;
}
public function hasCertificateAuthorityPath()
{
return $this->certificateAuthorityPath !== null;
}
}

View File

@ -16,9 +16,10 @@ use BeSimple\SoapCommon\Helper;
use BeSimple\SoapCommon\Mime\MultiPart as MimeMultiPart;
use BeSimple\SoapCommon\Mime\Parser as MimeParser;
use BeSimple\SoapCommon\Mime\Part as MimePart;
use BeSimple\SoapCommon\Mime\Part;
use BeSimple\SoapCommon\SoapRequest;
use BeSimple\SoapCommon\SoapRequestFilter;
use BeSimple\SoapCommon\SoapResponse;
use BeSimple\SoapCommon\SoapResponse as CommonSoapResponse;
use BeSimple\SoapCommon\SoapResponseFilter;
/**
@ -28,111 +29,61 @@ use BeSimple\SoapCommon\SoapResponseFilter;
*/
class MimeFilter implements SoapRequestFilter, SoapResponseFilter
{
/**
* Attachment type.
*
* @var int Helper::ATTACHMENTS_TYPE_SWA | Helper::ATTACHMENTS_TYPE_MTOM
*/
protected $attachmentType = Helper::ATTACHMENTS_TYPE_SWA;
/**
* Constructor.
*
* @param int $attachmentType Helper::ATTACHMENTS_TYPE_SWA | Helper::ATTACHMENTS_TYPE_MTOM
*/
public function __construct($attachmentType)
public function filterRequest(SoapRequest $request, $attachmentType)
{
$this->attachmentType = $attachmentType;
}
/**
* Reset all properties to default values.
*/
public function resetFilter()
{
$this->attachmentType = Helper::ATTACHMENTS_TYPE_SWA;
}
/**
* Modify the given request XML.
*
* @param \BeSimple\SoapCommon\SoapRequest $request SOAP request
*
* @return void
*/
public function filterRequest(SoapRequest $request)
{
// get attachments from request object
$attachmentsToSend = $request->getAttachments();
// build mime message if we have attachments
if (count($attachmentsToSend) > 0) {
$multipart = new MimeMultiPart();
$multipart = new MimeMultiPart('Part_' . rand(10, 15) . '_' . uniqid() . '.' . uniqid());
$soapPart = new MimePart($request->getContent(), 'text/xml', 'utf-8', MimePart::ENCODING_EIGHT_BIT);
$soapVersion = $request->getVersion();
// change content type headers for MTOM with SOAP 1.1
if ($soapVersion == SOAP_1_1 && $this->attachmentType & Helper::ATTACHMENTS_TYPE_MTOM) {
if ($soapVersion == SOAP_1_1 && $attachmentType & Helper::ATTACHMENTS_TYPE_MTOM) {
$multipart->setHeader('Content-Type', 'type', 'application/xop+xml');
$multipart->setHeader('Content-Type', 'start-info', 'text/xml');
$soapPart->setHeader('Content-Type', 'application/xop+xml');
$soapPart->setHeader('Content-Type', 'type', 'text/xml');
}
// change content type headers for SOAP 1.2
elseif ($soapVersion == SOAP_1_2) {
} elseif ($soapVersion == SOAP_1_2) {
$multipart->setHeader('Content-Type', 'type', 'application/soap+xml');
$soapPart->setHeader('Content-Type', 'application/soap+xml');
}
$multipart->addPart($soapPart, true);
foreach ($attachmentsToSend as $cid => $attachment) {
$multipart->addPart($attachment, false);
}
$request->setContent($multipart->getMimeMessage());
// TODO
$headers = $multipart->getHeadersForHttp();
list($name, $contentType) = explode(': ', $headers[0]);
list(, $contentType) = explode(': ', $headers[0]);
$request->setContentType($contentType);
}
return $request;
}
/**
* Modify the given response XML.
*
* @param \BeSimple\SoapCommon\SoapResponse $response SOAP response
*
* @return void
*/
public function filterResponse(SoapResponse $response)
public function filterResponse(CommonSoapResponse $response, $attachmentType)
{
// array to store attachments
$attachmentsRecieved = array();
$multiPartMessage = MimeParser::parseMimeMessage(
$response->getContent(),
['Content-Type' => trim($response->getContentType())]
);
$soapPart = $multiPartMessage->getMainPart();
$attachments = $multiPartMessage->getAttachments();
// check content type if it is a multipart mime message
$responseContentType = $response->getContentType();
if (false !== stripos($responseContentType, 'multipart/related')) {
// parse mime message
$headers = array(
'Content-Type' => trim($responseContentType),
);
$multipart = MimeParser::parseMimeMessage($response->getContent(), $headers);
// get soap payload and update SoapResponse object
$soapPart = $multipart->getPart();
// convert href -> myhref for external references as PHP throws exception in this case
// http://svn.php.net/viewvc/php/php-src/branches/PHP_5_4/ext/soap/php_encoding.c?view=markup#l3436
$content = preg_replace('/href=(?!#)/', 'myhref=', $soapPart->getContent());
$response->setContent($content);
$response->setContentType($soapPart->getHeader('Content-Type'));
// store attachments
$attachments = $multipart->getParts(false);
foreach ($attachments as $cid => $attachment) {
$attachmentsRecieved[$cid] = $attachment;
}
$response->setContent($this->sanitizePhpExceptionOnHrefs($soapPart));
$response->setContentType($soapPart->getHeader('Content-Type'));
if (count($attachments) > 0) {
$response->setAttachments($attachments);
}
// add attachments to response object
if (count($attachmentsRecieved) > 0) {
$response->setAttachments($attachmentsRecieved);
}
return $response;
}
private function sanitizePhpExceptionOnHrefs(Part $soapPart)
{
// convert href -> myhref for external references as PHP throws exception in this case
// http://svn.php.net/viewvc/php/php-src/branches/PHP_5_4/ext/soap/php_encoding.c?view=markup#l3436
return preg_replace('/href=(?!#)/', 'myhref=', $soapPart->getContent());
}
}

View File

@ -0,0 +1,34 @@
# BeSimpleSoapClient
The BeSimpleSoapClient is a component that extends the native PHP SoapClient with further features like SwA, MTOM and WS-Security.
# Features (only subsets of the linked specs implemented)
* SwA: SOAP Messages with Attachments [Spec](http://www.w3.org/TR/SOAP-attachments)
* MTOM: SOAP Message Transmission Optimization Mechanism [Spec](http://www.w3.org/TR/soap12-mtom/)
* WS-Security [Spec1](http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0.pdf), [Spec2](http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf)
* WS-Adressing [Spec](http://www.w3.org/2002/ws/addr/)
# Installation
If you do not yet have composer, install it like this:
```sh
curl -s http://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin
```
Create a `composer.json` file:
```json
{
"require": {
"besimple/soap-client": "0.2.*@dev"
}
}
```
Now you are ready to install the library:
```sh
php /usr/local/bin/composer.phar install
```

View File

@ -12,9 +12,20 @@
namespace BeSimple\SoapClient;
use BeSimple\SoapCommon\Helper;
use BeSimple\SoapCommon\Converter\MtomTypeConverter;
use BeSimple\SoapCommon\Converter\SwaTypeConverter;
use BeSimple\SoapBundle\Soap\SoapAttachment;
use BeSimple\SoapBundle\Soap\SoapAttachmentList;
use BeSimple\SoapClient\Curl\Curl;
use BeSimple\SoapClient\Curl\CurlOptionsBuilder;
use BeSimple\SoapClient\Curl\CurlResponse;
use BeSimple\SoapClient\SoapOptions\SoapClientOptions;
use BeSimple\SoapCommon\Fault\SoapFaultEnum;
use BeSimple\SoapCommon\Mime\PartFactory;
use BeSimple\SoapCommon\SoapKernel;
use BeSimple\SoapCommon\SoapOptions\SoapOptions;
use BeSimple\SoapCommon\SoapRequest;
use BeSimple\SoapCommon\SoapRequestFactory;
use Exception;
use SoapFault;
/**
* Extended SoapClient that uses a a cURL wrapper for all underlying HTTP
@ -23,148 +34,91 @@ use BeSimple\SoapCommon\Converter\SwaTypeConverter;
* allows caching of all remote referenced items.
*
* @author Andreas Schamberger <mail@andreass.net>
* @author Petr Bechyně <mail@petrbechyne.com>
*/
class SoapClient extends \SoapClient
{
/**
* Soap version.
*
* @var int
*/
protected $soapVersion = SOAP_1_1;
protected $soapClientOptions;
protected $soapOptions;
private $curl;
/**
* Tracing enabled?
*
* @var boolean
*/
protected $tracingEnabled = false;
/**
* cURL instance.
*
* @var \BeSimple\SoapClient\Curl
*/
protected $curl = null;
/**
* Last request headers.
*
* @var string
*/
private $lastRequestHeaders = '';
/**
* Last request.
*
* @var string
*/
private $lastRequest = '';
/**
* Last response headers.
*
* @var string
*/
private $lastResponseHeaders = '';
/**
* Last response.
*
* @var string
*/
private $lastResponse = '';
/**
* Soap kernel.
*
* @var \BeSimple\SoapClient\SoapKernel
*/
protected $soapKernel = null;
/**
* Constructor.
*
* @param string $wsdl WSDL file
* @param array(string=>mixed) $options Options array
*/
public function __construct($wsdl, array $options = array())
public function __construct(SoapClientOptions $soapClientOptions, SoapOptions $soapOptions)
{
// tracing enabled: store last request/response header and body
if (isset($options['trace']) && $options['trace'] === true) {
$this->tracingEnabled = true;
$this->soapClientOptions = $soapClientOptions;
$this->soapOptions = $soapOptions;
$this->curl = new Curl(
CurlOptionsBuilder::buildForSoapClient($soapClientOptions)
);
try {
$wsdlPath = $this->loadWsdl(
$this->curl,
$soapOptions->getWsdlFile(),
$soapOptions->getWsdlCacheType()
);
} catch (Exception $e) {
throw new SoapFault(
SoapFaultEnum::SOAP_FAULT_SOAP_CLIENT_ERROR,
'Unable to load WsdlPath ('.$soapOptions->getWsdlFile().') with message: '.$e->getMessage().' in file: '.$e->getFile().' (line: '.$e->getLine().')'
);
}
// store SOAP version
if (isset($options['soap_version'])) {
$this->soapVersion = $options['soap_version'];
}
$this->curl = new Curl($options);
$wsdlFile = $this->loadWsdl($wsdl, $options);
// TODO $wsdlHandler = new WsdlHandler($wsdlFile, $this->soapVersion);
$this->soapKernel = new SoapKernel();
// 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);
@parent::__construct($wsdlPath, $soapClientOptions->toArray() + $soapOptions->toArray());
}
/**
* Avoid using __call directly, it's deprecated even in \SoapClient.
*
* @deprecated
*/
public function __call($function_name, $arguments)
{
throw new Exception(
'The __call method is deprecated. Use __soapCall/soapCall instead.'
);
}
/**
* Perform HTTP request with cURL.
*
* @param SoapRequest $soapRequest SoapRequest object
* Using __soapCall returns only response string, use soapCall instead.
*
* @param string $function_name
* @param array $arguments
* @param array|null $options
* @param null $input_headers
* @param array|null $output_headers
* @return string
*/
public function __soapCall($function_name, array $arguments, array $options = null, $input_headers = null, array &$output_headers = null)
{
return $this->soapCall($function_name, $arguments, $options, $input_headers, $output_headers)->getContent();
}
/**
* @param string $functionName
* @param array $arguments
* @param array|null $options
* @param SoapAttachment[] $soapAttachments
* @param null $inputHeaders
* @param array|null $outputHeaders
* @return SoapResponse
*/
private function __doHttpRequest(SoapRequest $soapRequest)
public function soapCall($functionName, array $arguments, array $soapAttachments = [], array $options = null, $inputHeaders = null, array &$outputHeaders = null)
{
// HTTP headers
$headers = array(
'Content-Type:' . $soapRequest->getContentType(),
'SOAPAction: "' . $soapRequest->getAction() . '"',
);
// execute HTTP request with cURL
$responseSuccessfull = $this->curl->exec(
$soapRequest->getLocation(),
$soapRequest->getContent(),
$headers
);
// tracing enabled: store last request header and body
if ($this->tracingEnabled === true) {
$this->lastRequestHeaders = $this->curl->getRequestHeaders();
$this->lastRequest = $soapRequest->getContent();
}
// in case of an error while making the http request throw a soapFault
if ($responseSuccessfull === false) {
// get error message from curl
$faultstring = $this->curl->getErrorMessage();
throw new \SoapFault('HTTP', $faultstring);
}
// tracing enabled: store last response header and body
if ($this->tracingEnabled === true) {
$this->lastResponseHeaders = $this->curl->getResponseHeaders();
$this->lastResponse = $this->curl->getResponseBody();
}
// wrap response data in SoapResponse object
$soapResponse = SoapResponse::create(
$this->curl->getResponseBody(),
$soapRequest->getLocation(),
$soapRequest->getAction(),
$soapRequest->getVersion(),
$this->curl->getResponseContentType()
);
ob_start();
parent::__soapCall($functionName, $arguments, $options, $inputHeaders, $outputHeaders);
$nativeSoapClientRequest = $this->mapNativeDataJsonToDto(ob_get_clean());
return $soapResponse;
return $this->performSoapRequest(
$nativeSoapClientRequest->request,
$nativeSoapClientRequest->location,
$nativeSoapClientRequest->action,
$nativeSoapClientRequest->version,
$soapAttachments
);
}
/**
* Custom request method to be able to modify the SOAP messages.
* $oneWay parameter is not used at the moment.
* This is not performing any HTTP requests, but it is getting data from SoapClient that are needed for this Client
*
* @param string $request Request string
* @param string $location Location
@ -176,158 +130,279 @@ class SoapClient extends \SoapClient
*/
public function __doRequest($request, $location, $action, $version, $oneWay = 0)
{
// wrap request data in SoapRequest object
$soapRequest = SoapRequest::create($request, $location, $action, $version);
$soapClientNativeDataTransferObject = new SoapClientNativeDataTransferObject(
$request,
$location,
$action,
$version
);
echo json_encode($soapClientNativeDataTransferObject);
// do actual SOAP request
$soapResponse = $this->__doRequest2($soapRequest);
// return SOAP response to ext/soap
return $soapResponse->getContent();
return $request;
}
/**
* Runs the currently registered request filters on the request, performs
* the HTTP request and runs the response filters.
* Custom request method to be able to modify the SOAP messages.
* $oneWay parameter is not used at the moment.
*
* @param SoapRequest $soapRequest SOAP request object
* @param mixed $request Request object
* @param string $location Location
* @param string $action SOAP action
* @param int $version SOAP version
* @param SoapAttachment[] $soapAttachments SOAP attachments array
*
* @return SoapResponse
*/
protected function __doRequest2(SoapRequest $soapRequest)
public function performSoapRequest($request, $location, $action, $version, array $soapAttachments = [])
{
// run SoapKernel on SoapRequest
$this->soapKernel->filterRequest($soapRequest);
$soapRequest = $this->createSoapRequest($location, $action, $version, $request, $soapAttachments);
// perform HTTP request with cURL
$soapResponse = $this->__doHttpRequest($soapRequest);
// run SoapKernel on SoapResponse
$this->soapKernel->filterResponse($soapResponse);
return $soapResponse;
return $this->performHttpSoapRequest($soapRequest);
}
/**
* Get last request HTTP headers.
*
* @return string
*/
/** @deprecated */
public function __getLastRequestHeaders()
{
return $this->lastRequestHeaders;
$this->checkTracing();
throw new Exception(
'The __getLastRequestHeaders method is now deprecated. Use callSoapRequest instead and get the tracing information from SoapResponseTracingData.'
);
}
/**
* Get last request HTTP body.
*
* @return string
*/
/** @deprecated */
public function __getLastRequest()
{
return $this->lastRequest;
$this->checkTracing();
throw new Exception(
'The __getLastRequest method is now deprecated. Use callSoapRequest instead and get the tracing information from SoapResponseTracingData.'
);
}
/**
* Get last response HTTP headers.
*
* @return string
*/
/** @deprecated */
public function __getLastResponseHeaders()
{
return $this->lastResponseHeaders;
$this->checkTracing();
throw new Exception(
'The __getLastResponseHeaders method is now deprecated. Use callSoapRequest instead and get the tracing information from SoapResponseTracingData.'
);
}
/**
* Get last response HTTP body.
*
* @return string
*/
/** @deprecated */
public function __getLastResponse()
{
return $this->lastResponse;
$this->checkTracing();
throw new Exception(
'The __getLastResponse method is now deprecated. Use callSoapRequest instead and get the tracing information from SoapResponseTracingData.'
);
}
/**
* Get SoapKernel instance.
* @param string $location Location
* @param string $action SOAP action
* @param int $version SOAP version
* @param string $request SOAP request body
* @param SoapAttachment[] $soapAttachments array of SOAP attachments
*
* @return \BeSimple\SoapClient\SoapKernel
* @return SoapRequest
*/
public function getSoapKernel()
private function createSoapRequest($location, $action, $version, $request, array $soapAttachments = [])
{
return $this->soapKernel;
$soapAttachmentList = new SoapAttachmentList($soapAttachments);
$soapRequest = SoapRequestFactory::create($location, $action, $version, $request);
if (count($soapAttachments) > 0) {
if ($this->soapOptions->hasAttachments() === true) {
$soapRequest->setAttachments(PartFactory::createAttachmentParts($soapAttachments));
$soapRequest = SoapKernel::filterRequest(
$soapRequest,
$this->getAttachmentFilters(),
$this->soapOptions->getAttachmentType()
);
} else {
throw new Exception(
'Non SWA SoapClient cannot handle SOAP action '.$action.' with attachments: '.implode(', ', $soapAttachmentList->getSoapAttachmentIds())
);
}
}
return $soapRequest;
}
/**
* Configure filter and type converter for SwA/MTOM.
*
* @param array &$options SOAP constructor options array.
*
* @return void
*/
private function configureMime(array &$options)
* Perform HTTP request with cURL.
*
* @param SoapRequest $soapRequest SoapRequest object
* @return SoapResponse
* @throws SoapFault
*/
private function performHttpSoapRequest(SoapRequest $soapRequest)
{
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);
// configure type converter
if (Helper::ATTACHMENTS_TYPE_SWA === $options['attachment_type']) {
$converter = new SwaTypeConverter();
$converter->setKernel($this->soapKernel);
} 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);
if ($soapRequest->getVersion() === SOAP_1_1) {
$headers = [
'Content-Type:' . $soapRequest->getContentType(),
'SOAPAction: "' . $soapRequest->getAction() . '"',
];
} else {
$headers = [
'Content-Type:' . $soapRequest->getContentType() . '; action="' . $soapRequest->getAction() . '"',
];
}
$curlResponse = $this->curl->executeCurlWithCachedSession(
$soapRequest->getLocation(),
$soapRequest->getContent(),
$headers
);
$soapResponseTracingData = new SoapResponseTracingData(
$curlResponse->getHttpRequestHeaders(),
$soapRequest->getContent(),
$curlResponse->getResponseHeader(),
$curlResponse->getResponseBody()
);
if ($curlResponse->curlStatusSuccess()) {
$soapResponse = $this->returnSoapResponseByTracing(
$this->soapClientOptions->getTrace(),
$soapRequest,
$curlResponse,
$soapResponseTracingData
);
if ($this->soapOptions->hasAttachments()) {
return SoapKernel::filterResponse(
$soapResponse,
$this->getAttachmentFilters(),
$this->soapOptions->getAttachmentType()
);
} else {
return $soapResponse;
}
// 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);
},
} else if ($curlResponse->curlStatusFailed()) {
return $this->throwSoapFaultByTracing(
$this->soapClientOptions->getTrace(),
SoapFaultEnum::SOAP_FAULT_HTTP.'-'.$curlResponse->getHttpResponseStatusCode(),
$curlResponse->getCurlErrorMessage(),
$soapResponseTracingData
);
} else {
return $this->throwSoapFaultByTracing(
$this->soapClientOptions->getTrace(),
SoapFaultEnum::SOAP_FAULT_SOAP_CLIENT_ERROR,
'Cannot process curl response with unresolved status: ' . $curlResponse->getCurlStatus(),
$soapResponseTracingData
);
}
}
/**
* Downloads WSDL files with cURL. Uses all SoapClient options for
* authentication. Uses the WSDL_CACHE_* constants and the 'soap.wsdl_*'
* ini settings. Does only file caching as SoapClient only supports a file
* name parameter.
*
* @param string $wsdl WSDL file
* @param array(string=>mixed) $options Options array
* @param Curl $curl
* @param string $wsdlPath
* @param int $wsdlCacheType
* @param bool $resolveRemoteIncludes
*
* @return string
*/
private function loadWsdl($wsdl, array $options)
private function loadWsdl(Curl $curl, $wsdlPath, $wsdlCacheType, $resolveRemoteIncludes = true)
{
// option to resolve wsdl/xsd includes
$resolveRemoteIncludes = true;
if (isset($options['resolve_wsdl_remote_includes'])) {
$resolveRemoteIncludes = $options['resolve_wsdl_remote_includes'];
}
// option to enable cache
$wsdlCache = WSDL_CACHE_DISK;
if (isset($options['cache_wsdl'])) {
$wsdlCache = $options['cache_wsdl'];
}
$wsdlDownloader = new WsdlDownloader($this->curl, $resolveRemoteIncludes, $wsdlCache);
$wsdlDownloader = new WsdlDownloader();
try {
$cacheFileName = $wsdlDownloader->download($wsdl);
} catch (\RuntimeException $e) {
throw new \SoapFault('WSDL', "SOAP-ERROR: Parsing WSDL: Couldn't load from '" . $wsdl . "' : failed to load external entity \"" . $wsdl . "\"");
$loadedWsdlFilePath = $wsdlDownloader->getWsdlPath($curl, $wsdlPath, $wsdlCacheType, $resolveRemoteIncludes);
} catch (Exception $e) {
throw new SoapFault(
SoapFaultEnum::SOAP_FAULT_WSDL,
'Unable to load WsdlPath ('.$wsdlPath.') with message: '.$e->getMessage().' in file: '.$e->getFile().' (line: '.$e->getLine().')'
);
}
return $cacheFileName;
return $loadedWsdlFilePath;
}
private function getAttachmentFilters()
{
$filters = [];
if ($this->soapOptions->getAttachmentType() !== SoapOptions::SOAP_ATTACHMENTS_TYPE_BASE64) {
$filters[] = new MimeFilter();
}
if ($this->soapOptions->getAttachmentType() === SoapOptions::SOAP_ATTACHMENTS_TYPE_MTOM) {
$filters[] = new XmlMimeFilter();
}
return $filters;
}
private function mapNativeDataJsonToDto($nativeDataJson)
{
$nativeData = json_decode($nativeDataJson);
return new SoapClientNativeDataTransferObject(
$nativeData->request,
$nativeData->location,
$nativeData->action,
$nativeData->version
);
}
private function returnSoapResponseByTracing(
$isTracingEnabled,
SoapRequest $soapRequest,
CurlResponse $curlResponse,
SoapResponseTracingData $soapResponseTracingData,
array $soapAttachments = []
) {
if ($isTracingEnabled === true) {
return SoapResponseFactory::createWithTracingData(
$curlResponse->getResponseBody(),
$soapRequest->getLocation(),
$soapRequest->getAction(),
$soapRequest->getVersion(),
$curlResponse->getHttpResponseContentType(),
$soapResponseTracingData,
$soapAttachments
);
} else {
return SoapResponseFactory::create(
$curlResponse->getResponseBody(),
$soapRequest->getLocation(),
$soapRequest->getAction(),
$soapRequest->getVersion(),
$curlResponse->getHttpResponseContentType(),
$soapAttachments
);
}
}
private function throwSoapFaultByTracing($isTracingEnabled, $soapFaultCode, $soapFaultMessage, SoapResponseTracingData $soapResponseTracingData)
{
if ($isTracingEnabled === true) {
throw new SoapFaultWithTracingData(
$soapFaultCode,
$soapFaultMessage,
$soapResponseTracingData
);
} else {
throw new SoapFault(
$soapFaultCode,
$soapFaultMessage
);
}
}
private function checkTracing()
{
if ($this->soapClientOptions->getTrace() === false) {
throw new Exception('SoapClientOptions tracing disabled, turn on trace attribute');
}
}
}

View File

@ -12,228 +12,44 @@
namespace BeSimple\SoapClient;
use BeSimple\SoapCommon\AbstractSoapBuilder;
use BeSimple\SoapCommon\Helper;
use BeSimple\SoapBundle\Cache;
use BeSimple\SoapClient\SoapOptions\SoapClientOptions;
use BeSimple\SoapCommon\SoapOptions\SoapOptions;
use Exception;
use SoapHeader;
/**
* Fluent interface builder for SoapClient instance.
* Provides a SoapClient instance.
*
* @author Francis Besset <francis.besset@gmail.com>
* @author Christian Kerl <christian-kerl@web.de>
* @author Petr Bechyně <mail@petrbechyne.com>
*/
class SoapClientBuilder extends AbstractSoapBuilder
class SoapClientBuilder
{
/**
* Authentication options.
*
* @var array(string=>mixed)
*/
protected $soapOptionAuthentication = array();
/**
* Create new instance with default options.
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/
public static function createWithDefaults()
public function build(SoapClientOptions $soapClientOptions, SoapOptions $soapOptions)
{
return parent::createWithDefaults()
->withUserAgent('BeSimpleSoap');
}
$cache = new Cache($soapOptions);
$cache->validateSettings($soapOptions);
/**
* 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 new SoapClient(
$soapClientOptions,
$soapOptions
);
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;
public function buildWithSoapHeader(
SoapClientOptions $soapClientOptions,
SoapOptions $soapOptions,
SoapHeader $soapHeader
) {
$soapClient = $this->build($soapClientOptions, $soapOptions);
if ($soapClient->__setSoapHeaders($soapHeader) === false) {
throw new Exception(
'Could not set SoapHeader: '.var_export($soapHeader, true)
);
}
return $this;
}
/**
* Configure proxy.
*
* @param string $host Host
* @param int $port Port
* @param string $username Username
* @param string $password Password
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/
public function withProxy($host, $port, $username = null, $password = null)
{
$this->soapOptions['proxy_host'] = $host;
$this->soapOptions['proxy_port'] = $port;
if ($username) {
$this->soapOptions['proxy_login'] = $username;
$this->soapOptions['proxy_password'] = $password;
}
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();
return $soapClient;
}
}

View File

@ -0,0 +1,7 @@
<?php
namespace BeSimple\SoapClient;
class SoapClientMessageWithAttachments
{
}

View File

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

View File

@ -0,0 +1,58 @@
<?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\SoapClient\Curl\CurlOptions;
use BeSimple\SoapClient\SoapOptions\SoapClientOptions;
use BeSimple\SoapClient\SoapServerAuthentication\SoapServerAuthenticationInterface;
/**
* Provides a SoapClient instance.
*
* @author Francis Besset <francis.besset@gmail.com>
* @author Christian Kerl <christian-kerl@web.de>
* @author Petr Bechyně <mail@petrbechyne.com>
*/
class SoapClientOptionsBuilder
{
public static function createWithDefaults()
{
return new SoapClientOptions(
SoapClientOptions::SOAP_CLIENT_TRACE_OFF,
SoapClientOptions::SOAP_CLIENT_EXCEPTIONS_ON,
CurlOptions::DEFAULT_USER_AGENT,
SoapClientOptions::SOAP_CLIENT_COMPRESSION_NONE
);
}
public static function createWithTracing()
{
return new SoapClientOptions(
SoapClientOptions::SOAP_CLIENT_TRACE_ON,
SoapClientOptions::SOAP_CLIENT_EXCEPTIONS_ON,
CurlOptions::DEFAULT_USER_AGENT,
SoapClientOptions::SOAP_CLIENT_COMPRESSION_NONE
);
}
public static function createWithAuthentication(SoapServerAuthenticationInterface $authentication)
{
return new SoapClientOptions(
SoapClientOptions::SOAP_CLIENT_TRACE_ON,
SoapClientOptions::SOAP_CLIENT_EXCEPTIONS_ON,
CurlOptions::DEFAULT_USER_AGENT,
SoapClientOptions::SOAP_CLIENT_COMPRESSION_NONE,
$authentication
);
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace BeSimple\SoapClient;
use SoapFault;
class SoapFaultWithTracingData extends SoapFault
{
private $soapResponseTracingData;
public function __construct($code = 0, $message = "", SoapResponseTracingData $soapResponseTracingData)
{
$this->soapResponseTracingData = $soapResponseTracingData;
parent::__construct($code, $message);
}
public function getSoapResponseTracingData()
{
return $this->soapResponseTracingData;
}
}

View File

@ -1,47 +0,0 @@
<?php
/*
* This file is part of the BeSimpleSoapCommon.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
* (c) Andreas Schamberger <mail@andreass.net>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapClient;
use BeSimple\SoapCommon\SoapKernel as CommonSoapKernel;
use BeSimple\SoapCommon\SoapRequest as CommonSoapRequest;
use BeSimple\SoapCommon\SoapResponse as CommonSoapResponse;
/**
* SoapKernel for Client.
*
* @author Andreas Schamberger <mail@andreass.net>
*/
class SoapKernel extends CommonSoapKernel
{
/**
* {@inheritDoc}
*/
public function filterRequest(CommonSoapRequest $request)
{
$request->setAttachments($this->attachments);
$this->attachments = array();
parent::filterRequest($request);
}
/**
* {@inheritDoc}
*/
public function filterResponse(CommonSoapResponse $response)
{
parent::filterResponse($response);
$this->attachments = $response->getAttachments();
}
}

View File

@ -0,0 +1,120 @@
<?php
namespace BeSimple\SoapClient\SoapOptions;
use BeSimple\SoapClient\Curl\CurlOptions;
use BeSimple\SoapClient\SoapServerAuthentication\SoapServerAuthenticationBasic;
use BeSimple\SoapClient\SoapServerAuthentication\SoapServerAuthenticationDigest;
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 = CurlOptions::SOAP_COMPRESSION_NONE;
const SOAP_CLIENT_COMPRESSION_GZIP = CurlOptions::SOAP_COMPRESSION_GZIP;
const SOAP_CLIENT_COMPRESSION_DEFLATE = CurlOptions::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 hasAuthenticationBasic()
{
return $this->hasAuthentication() && $this->getAuthentication() instanceof SoapServerAuthenticationBasic;
}
public function hasAuthenticationDigest()
{
return $this->hasAuthentication() && $this->getAuthentication() instanceof SoapServerAuthenticationDigest;
}
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,48 +0,0 @@
<?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

@ -1,47 +1,13 @@
<?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\SoapResponse as CommonSoapResponse;
use BeSimple\SoapCommon\SoapMessage;
/**
* SoapResponse class for SoapClient. Provides factory function for response object.
*
* @author Andreas Schamberger <mail@andreass.net>
*/
class SoapResponse extends CommonSoapResponse
{
/**
* Factory function for SoapResponse.
*
* @param string $content Content
* @param string $location Location
* @param string $action SOAP action
* @param string $version SOAP version
* @param string $contentType Content type header
*
* @return BeSimple\SoapClient\SoapResponse
*/
public static function create($content, $location, $action, $version, $contentType)
public function getResponseContent()
{
$response = new SoapResponse();
$response->setContent($content);
$response->setLocation($location);
$response->setAction($action);
$response->setVersion($version);
$response->setContentType($contentType);
return $response;
return $this->getContent();
}
}

View File

@ -0,0 +1,98 @@
<?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\SoapBundle\Soap\SoapAttachment;
use BeSimple\SoapCommon\Mime\PartFactory;
/**
* SoapResponseFactory for SoapClient. Provides factory function for SoapResponse object.
*
* @author Andreas Schamberger <mail@andreass.net>
* @author Petr Bechyně <mail@petrbechyne.com>
*/
class SoapResponseFactory
{
/**
* Factory method for SoapClient\SoapResponse.
*
* @param string $content Content
* @param string $location Location
* @param string $action SOAP action
* @param string $version SOAP version
* @param string $contentType Content type header
* @param SoapAttachment[] $attachments SOAP attachments
* @return SoapResponse
*/
public static function create(
$content,
$location,
$action,
$version,
$contentType,
array $attachments = []
) {
$response = new SoapResponse();
$response->setContent($content);
$response->setLocation($location);
$response->setAction($action);
$response->setVersion($version);
$response->setContentType($contentType);
if (count($attachments) > 0) {
$response->setAttachments(
self::createAttachmentParts($attachments)
);
}
return $response;
}
/**
* Factory method for SoapClient\SoapResponse with SoapResponseTracingData.
*
* @param string $content Content
* @param string $location Location
* @param string $action SOAP action
* @param string $version SOAP version
* @param string $contentType Content type header
* @param SoapResponseTracingData $tracingData Data value object suitable for tracing SOAP traffic
* @param SoapAttachment[] $attachments SOAP attachments
* @return SoapResponse
*/
public static function createWithTracingData(
$content,
$location,
$action,
$version,
$contentType,
SoapResponseTracingData $tracingData,
array $attachments = []
) {
$response = new SoapResponse();
$response->setContent($content);
$response->setLocation($location);
$response->setAction($action);
$response->setVersion($version);
$response->setContentType($contentType);
if ($tracingData !== null) {
$response->setTracingData($tracingData);
}
if (count($attachments) > 0) {
$response->setAttachments(
PartFactory::createAttachmentParts($attachments)
);
}
return $response;
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace BeSimple\SoapClient;
class SoapResponseTracingData
{
private $lastRequestHeaders;
private $lastRequest;
private $lastResponseHeaders;
private $lastResponse;
public function __construct($lastRequestHeaders, $lastRequest, $lastResponseHeaders, $lastResponse)
{
$this->lastRequestHeaders = $lastRequestHeaders;
$this->lastRequest = $lastRequest;
$this->lastResponseHeaders = $lastResponseHeaders;
$this->lastResponse = $lastResponse;
}
public function getLastRequestHeaders()
{
return $this->lastRequestHeaders;
}
public function getLastRequest()
{
return $this->lastRequest;
}
public function getLastResponseHeaders()
{
return $this->lastResponseHeaders;
}
public function getLastResponse()
{
return $this->lastResponse;
}
}

View File

@ -0,0 +1,39 @@
<?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

@ -0,0 +1,52 @@
<?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

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

View File

@ -0,0 +1,84 @@
<?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,7 @@
<?php
namespace BeSimple\SoapClient\Tests\AxisInterop\Fixtures;
class AttachmentRequest extends AttachmentType
{
}

View File

@ -0,0 +1,9 @@
<?php
namespace BeSimple\SoapClient\Tests\AxisInterop\Fixtures;
class AttachmentType
{
public $fileName;
public $binaryData;
}

View File

@ -0,0 +1,11 @@
<?php
namespace BeSimple\SoapClient\Tests\AxisInterop\Fixtures;
class BookInformation
{
public $type;
public $isbn;
public $author;
public $title;
}

View File

@ -0,0 +1,11 @@
<?php
namespace BeSimple\SoapClient\Tests\AxisInterop\Fixtures;
class addBook
{
public $type;
public $isbn;
public $author;
public $title;
}

View File

@ -0,0 +1,8 @@
<?php
namespace BeSimple\SoapClient\Tests\AxisInterop\Fixtures;
class addBookResponse
{
public $addBookReturn;
}

View File

@ -0,0 +1,9 @@
<?php
namespace BeSimple\SoapClient\Tests\AxisInterop\Fixtures;
class base64Binary
{
public $_;
public $contentType;
}

View File

@ -0,0 +1,8 @@
<?php
namespace BeSimple\SoapClient\Tests\AxisInterop\Fixtures;
class downloadFile
{
public $name;
}

View File

@ -0,0 +1,8 @@
<?php
namespace BeSimple\SoapClient\Tests\AxisInterop\Fixtures;
class downloadFileResponse
{
public $data;
}

View File

@ -0,0 +1,8 @@
<?php
namespace BeSimple\SoapClient\Tests\AxisInterop\Fixtures;
class getBook
{
public $isbn;
}

View File

@ -0,0 +1,8 @@
<?php
namespace BeSimple\SoapClient\Tests\AxisInterop\Fixtures;
class getBookResponse
{
public $getBookReturn;
}

View File

@ -0,0 +1,8 @@
<?php
namespace BeSimple\SoapClient\Tests\AxisInterop\Fixtures;
class getBooksByType
{
public $type;
}

View File

@ -0,0 +1,8 @@
<?php
namespace BeSimple\SoapClient\Tests\AxisInterop\Fixtures;
class getBooksByTypeResponse
{
public $getBooksByTypeReturn;
}

View File

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Some files were not shown because too many files have changed in this diff Show More