├── .github
└── FUNDING.yml
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── bin
└── soap-client
├── composer.json
├── phpcs.xml.dist
├── phpunit.xml.dist
├── src
├── Builder
│ └── SoapContainerBuilder.php
├── Client.php
├── ClientFactory.php
├── Command
│ └── Generate.php
├── DependencyInjection
│ ├── Configuration.php
│ └── SoapClientExtension.php
├── Exception
│ ├── ClientException.php
│ ├── Fault11Exception.php
│ ├── Fault12Exception.php
│ ├── FaultException.php
│ ├── ServerException.php
│ ├── SoapException.php
│ └── UnexpectedFormatException.php
├── Resources
│ └── config
│ │ └── services.xml
├── Result
│ ├── ResultCreator.php
│ └── ResultCreatorInterface.php
└── StubGeneration
│ ├── ClientStubGenerator.php
│ └── Tag
│ ├── MethodTag.php
│ └── ParamTag.php
└── tests
├── Client
├── BuildClientTest.php
├── Client11RequestResponsesTest.php
├── Client12RequestResponsesTest.php
└── RequestResponsesTest.php
├── DependencyInjection
└── ConfigurationTest.php
├── Fixtures
├── config.yml
└── test.wsdl
├── Stub
└── StubGeneratorTest.php
├── bootstrap.php
└── example.php
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 |
2 | # These are supported funding model platforms
3 |
4 | github: goetas
5 | patreon: goetas
6 | custom: https://www.goetas.com/
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | /vendor
3 | composer.lock
4 | soap
5 | /.phpunit.result.cache
6 | tmp
7 | .phpcs-cache
8 | config.yml
9 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | sudo: false
3 | cache:
4 | directories:
5 | - vendor
6 | - $HOME/.composer/cache
7 | git:
8 | depth: 1
9 |
10 | php:
11 | - 7.2
12 | - 7.3
13 | - 7.4
14 | - 8.0
15 | - 8.1
16 |
17 | matrix:
18 | include:
19 | - php: 7.2
20 | env:
21 | - COMPOSER_FLAGS='--prefer-lowest --prefer-stable'
22 |
23 | before_script:
24 | - phpenv config-rm xdebug.ini
25 | - composer self-update
26 | - composer update $COMPOSER_FLAGS
27 |
28 | script:
29 | - vendor/phpunit/phpunit/phpunit $PHPUNIT_FLAGS
30 | - bin/soap-client generate tests/Fixtures/config.yml soap/src/Container -vvv --dest-class=TestNs/Container/SoapClientContainer
31 | - php tests/example.php
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (C) 2016 Asmir Mustafic
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 | of the Software, and to permit persons to whom the Software is furnished to do
8 | so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # goetas-webservices / soap-client
2 |
3 | [](https://travis-ci.org/goetas-webservices/soap-client)
4 |
5 | PHP implementation of SOAP 1.1 and 1.2 client specifications.
6 |
7 | Strengths:
8 |
9 | - Pure PHP, no dependencies on `ext-soap`
10 | - Extensible (JMS event listeners support)
11 | - PSR-7 HTTP messaging
12 | - PSR-17 HTTP messaging factories
13 | - PSR-18 HTTP Client
14 | - No WSDL/XSD parsing on production
15 | - IDE type hinting support
16 |
17 | Only document/literal style is supported and the webservice should follow
18 | the [WS-I](https://en.wikipedia.org/wiki/WS-I_Basic_Profile) guidelines.
19 |
20 | There are no plans to support the deprecated rpc and encoded styles.
21 | Webservices not following the WS-I specifications might work, but they are officially not supported.
22 |
23 | ## Demo
24 |
25 | [goetas-webservices/soap-client-demo](https://github.com/goetas-webservices/soap-client-demo) is a demo project
26 | that shows how to consume a SOAP api in a generic PHP web application.
27 |
28 |
29 | Installation
30 | -----------
31 |
32 | The recommended way to install goetas-webservices / soap-client is using [Composer](https://getcomposer.org/):
33 |
34 | Add this packages to your `composer.json` file.
35 |
36 | ```
37 | {
38 | "require": {
39 | "goetas-webservices/soap-client": "^0.3",
40 | },
41 | "require-dev": {
42 | "goetas-webservices/wsdl2php": "^0.5.1",
43 | },
44 | }
45 | ```
46 |
47 | # How to
48 |
49 | To improve performance, this library is based on the concept that all the SOAP/WSDL
50 | metadata has to be compiled into PHP compatible metadata (in reality is a big plain PHP array,
51 | so is really fast).
52 |
53 | To do this we have to define a configuration file (in this case called `config.yml`) that
54 | holds some important information.
55 |
56 | Here is an example:
57 |
58 | ```yml
59 | # config.yml
60 |
61 | soap_client:
62 | alternative_endpoints:
63 | MyServiceName:
64 | MySoapPortName: http://localhost:8080/service
65 |
66 | namespaces:
67 | 'http://www.example.org/test/': 'TestNs/MyApp'
68 | destinations_php:
69 | 'TestNs/MyApp': soap/src
70 | destinations_jms:
71 | 'TestNs/MyApp': soap/metadata
72 | aliases:
73 | 'http://www.example.org/test/':
74 | MyCustomXSDType: 'MyCustomMappedPHPType'
75 |
76 | metadata:
77 | 'test.wsdl': ~
78 | 'http://www.webservicex.net/weather.asmx?WSDL': ~
79 | ```
80 |
81 | This file has some important sections:
82 |
83 | ### SOAP Specific
84 | * `alternative_endpoints` (optional) allows you to specify alternative URLs that can be used
85 | when developing your integration.
86 | If this parameter is not present, will be used the URL defined by the WSDL file,
87 | but if is set, will be used the specified URL for the service called
88 | *MyServiceName* and on *MySoapPortName* port.
89 |
90 |
91 | * `unwrap_returns` (optional, default: *false*) allows to define the "wrapped" SOAP services mode.
92 | Instructs the client to "unwrap" all the returns.
93 |
94 | ### WSDL Specific
95 |
96 | * `metadata` specifies where are placed WSDL files that will be used to generate al the required PHP metadata.
97 |
98 |
99 | ### XML/XSD Specific
100 |
101 | * `namespaces` (required) defines the mapping between XML namespaces and PHP namespaces.
102 | (in the example we have the `http://www.example.org/test/` XML namespace mapped to `TestNs\MyApp`)
103 |
104 |
105 | * `destinations_php` (required) specifies the directory where to save the PHP classes that belongs to
106 | `TestNs\MyApp` PHP namespace. (in this example `TestNs\MyApp` classes will ne saved into `soap/src` directory.
107 |
108 |
109 | * `destinations_jms` (required) specifies the directory where to save JMS Serializer metadata files
110 | that belongs to `TestNs\MyApp` PHP namespace.
111 | (in this example `TestNs\MyApp` metadata will ne saved into `soap/metadata` directory.
112 |
113 |
114 | * `aliases` (optional) specifies some mappings that are handled by custom JMS serializer handlers.
115 | Allows to specify to do not generate metadata for some XML types, and assign them directly a PHP class.
116 | For that PHP class is necessary to create a custom JMS serialize/deserialize handler.
117 |
118 |
119 |
120 | ## Metadata generation
121 |
122 | In order to be able to use the SOAP client we have to generate some metadata and PHP classes.
123 |
124 | To do it we can run:
125 |
126 | ```sh
127 | bin/soap-client generate \
128 | tests/config.yml \
129 | --dest-class=GlobalWeather/Container/SoapClientContainer \
130 | soap/src-gw/Container
131 | ```
132 |
133 |
134 | * `bin/soap-client generate` is the command we are running
135 | * `tests/config.yml` is a path to our configuration file
136 | * `--dest-class=GlobalWeather/Container/SoapClientContainer` allows to specify the fully qualified class name of the
137 | container class that will hold all the webservice metadata.
138 | * `soap/src/Container` is the path where to save the container class that holds all the webservice metadata
139 | (you will have to configure the auto loader to load it)
140 |
141 |
142 |
143 | ## Using the client
144 |
145 | Once all the metadata are generated we can use our SOAP client.
146 |
147 | Let's see a minimal example:
148 |
149 | ```php
150 | // composer auto loader
151 | require __DIR__ . '/vendor/autoload.php';
152 |
153 | // instantiate the main container class
154 | // the name was defined by --dest-class=GlobalWeather/Container/SoapClientContainer
155 | // parameter during the generation process
156 | $container = new SoapClientContainer();
157 |
158 | // create a JMS serializer instance
159 | $serializer = SoapContainerBuilder::createSerializerBuilderFromContainer($container)->build();
160 | // get the metadata from the container
161 | $metadata = $container->get('goetas_webservices.soap.metadata_reader');
162 |
163 | $factory = new ClientFactory($metadata, $serializer);
164 |
165 | /**
166 | * @var $client \GlobalWeather\SoapStubs\WeatherSoap
167 | */
168 | // get the soap client
169 | $client = $factory->getClient('http://www.webservicex.net/weather.asmx?WSDL');
170 |
171 | // call the webservice
172 | $result = $client->getWeather(2010, "May", "USA");
173 |
174 | // call the webservice with custom headers
175 | $result = $client->getWeather(2010, "May", "USA", Header::asMustUnderstand(new SomeAuth('me', 'pwd')));
176 |
177 |
178 | ```
179 |
180 |
181 | Please note the `@var $client \GlobalWeather\SoapStubs\WeatherSoap`. The generated metadata have also a "stub" class
182 | that allows modern IDE to give you type hinting for parameters and return data.
183 |
184 | This allows you to develop faster your client.
185 |
186 | ### Using the client with dynamic endpoints
187 |
188 | Suppose that you have same Webservice with different endpoints (ex. for each customer),
189 | so you want to change endpoints dynamically and you don't want to write each new endpoint in your config
190 | and run the generator for each customer.
191 |
192 | With the help of Symfony's `EnvVarProcessorInterface`,
193 | you can use `alternative_endpoints` to set dynamically the webservice endpoints.
194 |
195 | Here is an example:
196 |
197 | ```yml
198 | # config.yml
199 | soap_client:
200 | alternative_endpoints:
201 | MyServiceName:
202 | MySoapPortName: 'env(custom_vars:ENDPOINT_SERVICE1_PORT1)'
203 | ```
204 |
205 | So, `SoapClientContainer` will resolve at runtime the endpoint for the specific service and port and the value will be
206 | taken from the `ENDPOINT_SERVICE1_PORT1` variable.
207 |
208 | Example of simple class that implements `EnvVarProcessorInterface`, responsible for providing a values for
209 | our custom endpoint locations (as `custom_vars:ENDPOINT_SERVICE1_PORT1`).
210 |
211 | ```php
212 | // SimpleEnvVarProcessor.php used for the `env(custom_vars:*)` variables resolution
213 |
214 | use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
215 |
216 | class SimpleEnvVarProcessor implements EnvVarProcessorInterface
217 | {
218 | private $map = [];
219 |
220 | public function __construct(array $map)
221 | {
222 | $this->map = $map;
223 | }
224 |
225 | public function getEnv($prefix, $name, \Closure $getEnv)
226 | {
227 | return $this->map[$name];
228 | }
229 |
230 | public static function getProvidedTypes()
231 | {
232 | return [];
233 | }
234 | }
235 | ```
236 |
237 | At the end, to use the `SoapClientContainer`:
238 |
239 |
240 | ```php
241 | // instantiate our variable processor and set the values for our custom variables
242 | $varProcessor = new SimpleEnvVarProcessor([
243 | 'ENDPOINT_SERVICE1_PORT1' => 'http://localhost:8080/service'
244 | ]);
245 |
246 | // create an empty symfony container and set into it the $varProcessor namined as 'custom_vars'
247 | $varContainer = new \Symfony\Component\DependencyInjection\Container();
248 | $varContainer->set('custom_vars', $varProcessor);
249 |
250 | // create the soap container and use $varContainer "env()" style variables resolution
251 | $container = new SoapClientContainer();
252 | $container->set('container.env_var_processors_locator', $varContainer);
253 |
254 | // now $container can be used as explained in the section "Using the client"
255 | ```
256 |
257 | In this way the endpoint for the `MyServiceName`.`MySoapPortName` will be dynamically resolved to `http://localhost:8080/service`
258 | even if the WSDL stats something else.
259 |
260 | ## Note
261 |
262 | The code in this project is provided under the
263 | [MIT](https://opensource.org/licenses/MIT) license.
264 | For professional support
265 | contact [goetas@gmail.com](mailto:goetas@gmail.com)
266 | or visit [https://www.goetas.com](https://www.goetas.com)
267 |
--------------------------------------------------------------------------------
/bin/soap-client:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | setCatchExceptions(true);
17 | $cli->add(new \GoetasWebservices\SoapServices\SoapClient\Command\Generate());
18 | $cli->run();
19 |
20 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "goetas-webservices/soap-client",
3 | "authors": [
4 | {
5 | "name": "Asmir Mustafic",
6 | "email": "goetas@gmail.com"
7 | }
8 | ],
9 | "license": "MIT",
10 | "require": {
11 | "php": "^7.1|^8.0",
12 |
13 | "psr/http-message": "^1.0",
14 | "psr/http-factory": "^1.0",
15 | "psr/http-client": "^1.0",
16 |
17 | "psr/http-factory-implementation": "^1.0",
18 | "psr/http-message-implementation": "^1.0",
19 | "psr/http-client-implementation": "^1.0",
20 |
21 | "php-http/discovery": "^1.13",
22 |
23 | "symfony/dependency-injection": "^3.3|^4.0|^5.0",
24 | "doctrine/instantiator": "^1.0.3",
25 | "jms/serializer": "^1.6|^2.0|^3.0",
26 |
27 | "goetas-webservices/xsd2php-runtime": "^0.2.11",
28 | "goetas-webservices/soap-common": "^0.2.2",
29 | "goetas-webservices/soap-reader": "^0.3.3",
30 | "goetas-webservices/xsd2php": "^0.4.7",
31 | "laminas/laminas-code": "^4.8",
32 | "doctrine/inflector": "^2.0"
33 | },
34 | "require-dev": {
35 | "phpunit/phpunit": "^7.0|^8.0",
36 | "goetas-webservices/wsdl2php": "^0.5.3",
37 | "laminas/laminas-diactoros": "^2.4",
38 | "guzzlehttp/guzzle": "^7.2",
39 | "doctrine/coding-standard": "^8.1"
40 | },
41 | "autoload": {
42 | "psr-4": {
43 | "GoetasWebservices\\SoapServices\\SoapClient\\": "src"
44 | }
45 | },
46 | "autoload-dev": {
47 | "psr-4": {
48 | "GoetasWebservices\\SoapServices\\SoapClient\\Tests\\": "tests",
49 | "GoetasWebservices\\Xsd\\XsdToPhp\\Tests\\": "vendor/goetas-webservices/xsd2php/tests",
50 | "GoetasWebservices\\WsdlToPhp\\Tests\\": "vendor/goetas-webservices/wsdl2php/tests"
51 | }
52 | },
53 | "extra": {
54 | "branch-alias": {
55 | "dev-master": "0.3-dev"
56 | }
57 | },
58 | "bin": [
59 | "bin/soap-client"
60 | ],
61 | "config": {
62 | "allow-plugins": {
63 | "dealerdirect/phpcodesniffer-composer-installer": false
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/phpcs.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | src/
14 | tests/
15 | /home/goetas/projects/soap-server/soap-metadata
16 | */tmp/*
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | tests/*
78 |
79 |
80 |
81 | tests/*
82 |
83 |
84 |
85 |
86 |
87 |
91 |
95 |
96 |
101 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 | /home/goetas/projects/soap-server/soap-metadata/src/Envelope/*
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | /home/goetas/projects/soap-server/soap-metadata/src/Envelope/*
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 | /home/goetas/projects/soap-server/soap-metadata/src/Envelope/*
139 |
140 |
141 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
20 |
21 | ./tests
22 |
23 |
24 |
25 |
26 |
27 | src
28 | soap-metadata/src
29 | vendor/goetas-webservices/soap-common
30 | ../soap-metadata/src
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/Builder/SoapContainerBuilder.php:
--------------------------------------------------------------------------------
1 | addExtension(new SoapClientExtension());
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Client.php:
--------------------------------------------------------------------------------
1 | serviceDefinition = $serviceDefinition;
90 | $this->serializer = $serializer;
91 |
92 | $this->client = $client;
93 | $this->messageFactory = $messageFactory;
94 | $this->streamFactory = $streamFactory;
95 | $this->argumentsReader = new ArgumentsReader($this->serializer);
96 | $this->resultCreator = new ResultCreator($this->serializer, !empty($serviceDefinition['unwrap']));
97 | }
98 |
99 | public function setDebug(bool $debug): void
100 | {
101 | $this->debug = $debug;
102 | }
103 |
104 | private function extractHeaders(array $args): array
105 | {
106 | $headers = [];
107 | foreach ($args as $k => $arg) {
108 | if ($arg instanceof Header) {
109 | $headers[] = $arg;
110 | unset($args[$k]);
111 | }
112 | }
113 |
114 | return [$args, $headers];
115 | }
116 |
117 | /**
118 | * @param array $args
119 | *
120 | * @return mixed
121 | *
122 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint
123 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint
124 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.UselessReturnAnnotation
125 | */
126 | public function __call(string $functionName, array $args)
127 | {
128 | [$args, $headers] = $this->extractHeaders($args);
129 | $soapOperation = $this->findOperation($functionName, $this->serviceDefinition);
130 | $message = $this->argumentsReader->readArguments($args, $soapOperation['input']);
131 |
132 | $bag = new HeadersOutgoing($headers);
133 | $context = SerializationContext::create()->setAttribute('headers_outgoing', $bag);
134 | $context->setAttribute('soapOperation', $soapOperation);
135 | $context->setAttribute('soapEndpoint', $this->serviceDefinition['endpoint']);
136 |
137 | $xmlMessage = $this->serializer->serialize($message, 'xml', $context);
138 |
139 | $requestMessage = $this->createRequestMessage($xmlMessage, $soapOperation);
140 |
141 | $responseMessage = $this->client->sendRequest($requestMessage);
142 |
143 | if ($this->debug) {
144 | $this->responseMessage = $responseMessage;
145 | }
146 |
147 | $this->assertValidResponse($responseMessage, $requestMessage);
148 |
149 | // fast return if no return is expected
150 | // @todo but we should still check for headers...
151 | if (!count($soapOperation['output']['parts'])) {
152 | return null;
153 | }
154 |
155 | if (200 !== $responseMessage->getStatusCode()) {
156 | throw $this->createFaultException($responseMessage, $requestMessage);
157 | }
158 |
159 | $body = (string) $responseMessage->getBody();
160 | if (false !== strpos($body, ':Fault>')) { // some server returns a fault with 200 OK HTTP
161 | throw $this->createFaultException($responseMessage, $requestMessage);
162 | }
163 |
164 | $bag = new HeadersIncoming();
165 | $context = DeserializationContext::create()->setAttribute('headers_incoming', $bag);
166 | $response = $this->serializer->deserialize($body, $soapOperation['output']['message_fqcn'], 'xml', $context);
167 |
168 | return $this->resultCreator->prepareResult($response, $soapOperation['output']);
169 | }
170 |
171 | public function assertValidResponse(ResponseInterface $responseMessage, RequestInterface $requestMessage, ?\Throwable $previous = null): void
172 | {
173 | if (false === strpos($responseMessage->getHeaderLine('Content-Type'), 'xml')) {
174 | throw new UnexpectedFormatException(
175 | "Unexpected content type '" . $responseMessage->getHeaderLine('Content-Type') . "'",
176 | $responseMessage,
177 | $requestMessage,
178 | $previous
179 | );
180 | }
181 |
182 | $body = (string) $responseMessage->getBody();
183 |
184 | if (false === strpos($body, 'http://schemas.xmlsoap.org/soap/envelope/') && false === strpos($body, 'http://www.w3.org/2003/05/soap-envelope')) {
185 | throw new UnexpectedFormatException(
186 | 'Unexpected content, invalid SOAP message',
187 | $responseMessage,
188 | $requestMessage,
189 | $previous
190 | );
191 | }
192 | }
193 |
194 | private function createFaultException(ResponseInterface $response, RequestInterface $request): FaultException
195 | {
196 | [$faultException, $faultClass] = $this->findFaultClass($response);
197 |
198 | $fault = null;
199 | if (null !== $faultClass) {
200 | $bag = new HeadersIncoming();
201 | $context = DeserializationContext::create()->setAttribute('headers_incoming', $bag);
202 | $fault = $this->serializer->deserialize((string) $response->getBody(), $faultClass, 'xml', $context);
203 | }
204 |
205 | return $faultException::createFromResponse($response, $request, $fault);
206 | }
207 |
208 | /**
209 | * @return string[]
210 | */
211 | protected function findFaultClass(ResponseInterface $response): array
212 | {
213 | if (false === strpos((string) $response->getBody(), ':Fault>')) {
214 | return [FaultException::class, null];
215 | }
216 |
217 | if (false !== strpos($response->getHeaderLine('Content-Type'), 'application/soap+xml')) {
218 | return [Fault12Exception::class, Fault12::class];
219 | }
220 |
221 | if (false !== strpos($response->getHeaderLine('Content-Type'), 'xml')) {
222 | return [Fault11Exception::class, Fault11::class];
223 | }
224 | }
225 |
226 | public function __getLastRequestMessage(): ?RequestInterface
227 | {
228 | return $this->requestMessage;
229 | }
230 |
231 | public function __getLastResponseMessage(): ?ResponseInterface
232 | {
233 | return $this->responseMessage;
234 | }
235 |
236 | /**
237 | * @return string[]
238 | */
239 | protected function buildHeaders(array $operation): array
240 | {
241 | return [
242 | 'Content-Type' => isset($operation['version']) && '1.2' === $operation['version']
243 | ? 'application/soap+xml; charset=utf-8'
244 | : 'text/xml; charset=utf-8',
245 | 'SoapAction' => '"' . $operation['action'] . '"',
246 | ];
247 | }
248 |
249 | protected function findOperation(string $functionName, array $serviceDefinition): array
250 | {
251 | if (isset($serviceDefinition['operations'][$functionName])) {
252 | return $serviceDefinition['operations'][$functionName];
253 | }
254 |
255 | foreach ($serviceDefinition['operations'] as $operation) {
256 | if (strtolower($functionName) === strtolower($operation['method'])) {
257 | return $operation;
258 | }
259 | }
260 |
261 | throw new ClientException(sprintf('Can not find an operation to run %s service call', $functionName));
262 | }
263 |
264 | private function createRequestMessage(string $xmlMessage, array $soapOperation): RequestInterface
265 | {
266 | $headers = $this->buildHeaders($soapOperation);
267 | $requestMessage = $this->messageFactory
268 | ->createRequest('POST', $this->serviceDefinition['endpoint'])
269 | ->withBody($this->streamFactory->createStream($xmlMessage));
270 | foreach ($headers as $name => $val) {
271 | $requestMessage = $requestMessage->withHeader($name, $val);
272 | }
273 |
274 | if ($this->debug) {
275 | $this->requestMessage = $requestMessage;
276 | }
277 |
278 | return $requestMessage;
279 | }
280 | }
281 |
--------------------------------------------------------------------------------
/src/ClientFactory.php:
--------------------------------------------------------------------------------
1 | setMetadataReader($reader);
45 | $this->setSerializer($serializer);
46 | }
47 |
48 | public function setHttpClient(ClientInterface $client): void
49 | {
50 | $this->httpClient = $client;
51 | }
52 |
53 | public function setMessageFactory(RequestFactoryInterface $messageFactory): void
54 | {
55 | $this->messageFactory = $messageFactory;
56 | }
57 |
58 | public function setSerializer(SerializerInterface $serializer): void
59 | {
60 | $this->serializer = $serializer;
61 | }
62 |
63 | public function setMetadataReader(MetadataLoaderInterface $reader): void
64 | {
65 | $this->reader = $reader;
66 | }
67 |
68 | private function getSoapService(string $wsdl, ?string $portName = null, ?string $serviceName = null): array
69 | {
70 | $servicesMetadata = $this->reader->load($wsdl);
71 | $service = MetadataUtils::getService($serviceName, $servicesMetadata);
72 |
73 | return MetadataUtils::getPort($portName, $service);
74 | }
75 |
76 | public function getClient(string $wsdl, ?string $portName = null, ?string $serviceName = null): Client
77 | {
78 | $service = $this->getSoapService($wsdl, $portName, $serviceName);
79 |
80 | $this->httpClient = $this->httpClient ?: Psr18ClientDiscovery::find();
81 | $this->messageFactory = $this->messageFactory ?: Psr17FactoryDiscovery::findRequestFactory();
82 | $this->streamFactory = $this->streamFactory ?: Psr17FactoryDiscovery::findStreamFactory();
83 |
84 | return new Client($service, $this->serializer, $this->messageFactory, $this->streamFactory, $this->httpClient);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/Command/Generate.php:
--------------------------------------------------------------------------------
1 | setName('generate');
21 | $this->setDescription('Create all the necessary PHP classes for a SOAP client');
22 | $this->setDefinition([
23 | new InputArgument('config', InputArgument::REQUIRED, 'Config file location'),
24 | new InputArgument('dest-dir', InputArgument::REQUIRED, 'Container files destination directory'),
25 | new InputOption('dest-class', null, InputOption::VALUE_REQUIRED, 'Container class file destination directory'),
26 | ]);
27 | }
28 |
29 | protected function execute(InputInterface $input, OutputInterface $output): int
30 | {
31 | $logger = new ConsoleLogger($output);
32 | $containerBuilder = new SoapContainerBuilder($input->getArgument('config'), $logger);
33 |
34 | if ($input->getOption('dest-class')) {
35 | $containerBuilder->setContainerClassName($input->getOption('dest-class'));
36 | }
37 |
38 | $debugContainer = $containerBuilder->getDebugContainer();
39 | //$debugContainer->set('logger', $logger);
40 |
41 | $wsdlMetadata = $debugContainer->getParameter('goetas_webservices.soap.config')['metadata'];
42 |
43 | $schemas = [];
44 | $portTypes = [];
45 | $wsdlReader = $debugContainer->get('goetas_webservices.wsdl2php.wsdl_reader');
46 |
47 | foreach (array_keys($wsdlMetadata) as $src) {
48 | $definitions = $wsdlReader->readFile($src);
49 | $schemas[] = $definitions->getSchema();
50 | $portTypes = array_merge($portTypes, $definitions->getAllPortTypes());
51 | }
52 |
53 | $soapReader = $debugContainer->get('goetas_webservices.wsdl2php.soap_reader');
54 | $soapServices = $soapReader->getServices();
55 |
56 | foreach (['php', 'jms'] as $type) {
57 | $converter = $debugContainer->get('goetas_webservices.xsd2php.converter.' . $type);
58 | $wsdlConverter = $debugContainer->get('goetas_webservices.wsdl2php.converter.' . $type);
59 | $items = $wsdlConverter->visitServices($soapServices);
60 | $items = array_merge($items, $converter->convert($schemas));
61 |
62 | $writer = $debugContainer->get('goetas_webservices.xsd2php.writer.' . $type);
63 | $writer->write($items);
64 | }
65 |
66 | $containerBuilder->dumpContainerForProd($input->getArgument('dest-dir'), $debugContainer);
67 |
68 | $stubGenerator = $debugContainer->get('goetas_webservices.soap.stub.client_generator');
69 |
70 | $classDefinitions = $stubGenerator->generate($portTypes);
71 | $classWriter = $debugContainer->get('goetas_webservices.xsd2php.class_writer.php');
72 | $classWriter->write($classDefinitions);
73 |
74 | return 0;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/DependencyInjection/Configuration.php:
--------------------------------------------------------------------------------
1 | getRootNode();
23 | } else {
24 | $rootNode = $treeBuilder->root('soap_client');
25 | }
26 |
27 | $rootNode
28 | ->children()
29 | ->booleanNode('strict_types')->defaultFalse()->end()
30 | ->arrayNode('alternative_endpoints')->fixXmlConfig('alternative_endpoint')
31 | ->prototype('array')
32 | ->prototype('scalar')
33 | ->end()
34 | ->end()
35 | ->end()
36 | ->scalarNode('unwrap_returns')
37 | ->defaultValue(false)
38 | ->end()
39 | ->scalarNode('naming_strategy')
40 | ->defaultValue('short')
41 | ->cannotBeEmpty()
42 | ->end()
43 | ->scalarNode('path_generator')
44 | ->defaultValue('psr4')
45 | ->cannotBeEmpty()
46 | ->end()
47 | ->arrayNode('namespaces')->fixXmlConfig('namespace')
48 | ->cannotBeEmpty()->isRequired()
49 | ->requiresAtLeastOneElement()
50 | ->prototype('scalar')
51 | ->end()
52 | ->end()
53 | ->arrayNode('known_locations')->fixXmlConfig('known_location')
54 | ->prototype('scalar')
55 | ->end()
56 | ->end()
57 | ->arrayNode('destinations_php')->fixXmlConfig('destination_php')
58 | ->cannotBeEmpty()->isRequired()
59 | ->requiresAtLeastOneElement()
60 | ->prototype('scalar')
61 | ->end()
62 | ->end()
63 | ->arrayNode('destinations_jms')->fixXmlConfig('destination_jms')
64 | ->cannotBeEmpty()->isRequired()
65 | ->requiresAtLeastOneElement()
66 | ->prototype('scalar')
67 | ->end()
68 | ->end()
69 | ->arrayNode('aliases')->fixXmlConfig('alias')
70 | ->prototype('array')
71 | ->prototype('scalar')
72 | ->end()
73 | ->end()
74 | ->end()
75 | ->arrayNode('metadata')->fixXmlConfig('metadata')
76 | ->cannotBeEmpty()->isRequired()
77 | ->requiresAtLeastOneElement()
78 | ->prototype('scalar')->end()
79 | ->end()
80 | ->end();
81 |
82 | return $treeBuilder;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/DependencyInjection/SoapClientExtension.php:
--------------------------------------------------------------------------------
1 | processConfiguration(new Configuration(), $configs);
20 | foreach ($configs as $subConfig) {
21 | $config = array_merge($config, $subConfig);
22 | }
23 |
24 | $container->setParameter('goetas_webservices.soap.config', $config);
25 | $container->setParameter('goetas_webservices.soap.unwrap_returns', $config['unwrap_returns']);
26 | $container->setParameter('goetas_webservices.soap.strict_types', $config['strict_types']);
27 |
28 | $xml = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
29 | $xml->load('services.xml');
30 |
31 | $container->setDefinition('logger', new Definition(NullLogger::class));
32 |
33 | $definition = $container->getDefinition('goetas_webservices.xsd2php.path_generator.jms.' . $config['path_generator']);
34 | $container->setDefinition('goetas_webservices.xsd2php.path_generator.jms', clone $definition);
35 |
36 | $definition = $container->getDefinition('goetas_webservices.xsd2php.path_generator.php.' . $config['path_generator']);
37 | $container->setDefinition('goetas_webservices.xsd2php.path_generator.php', clone $definition);
38 |
39 | $pathGenerator = $container->getDefinition('goetas_webservices.xsd2php.path_generator.jms');
40 | $pathGenerator->addMethodCall('setTargets', [$config['destinations_jms']]);
41 |
42 | $pathGenerator = $container->getDefinition('goetas_webservices.xsd2php.path_generator.php');
43 | $pathGenerator->addMethodCall('setTargets', [$config['destinations_php']]);
44 |
45 | foreach (['php', 'jms'] as $type) {
46 | $converter = $container->getDefinition('goetas_webservices.xsd2php.converter.' . $type);
47 | foreach ($config['namespaces'] as $xml => $php) {
48 | $converter->addMethodCall('addNamespace', [$xml, self::sanitizePhp($php)]);
49 | }
50 |
51 | foreach ($config['aliases'] as $xml => $data) {
52 | foreach ($data as $type => $php) {
53 | $converter->addMethodCall('addAliasMapType', [$xml, $type, self::sanitizePhp($php)]);
54 | }
55 | }
56 | }
57 |
58 | $definition = $container->getDefinition('goetas_webservices.xsd2php.naming_convention.' . $config['naming_strategy']);
59 | $container->setDefinition('goetas_webservices.xsd2php.naming_convention', $definition);
60 |
61 | //////////
62 |
63 | $metadataGenerator = $container->getDefinition('goetas_webservices.soap.metadata.generator');
64 | foreach ($config['alternative_endpoints'] as $service => $data) {
65 | foreach ($data as $port => $endPoint) {
66 | $metadataGenerator->addMethodCall('addAlternativeEndpoint', [$service, $port, $endPoint]);
67 | }
68 | }
69 |
70 | //$metadataGenerator->addMethodCall('setBaseNs', [array_intersect_key($config, array_combine($keys, $keys))]);
71 | $metadataGenerator->addMethodCall('setUnwrap', [$config['unwrap_returns']]);
72 | $metadataGenerator->replaceArgument(1, $config['namespaces']);
73 |
74 | $writer = $container->getDefinition('goetas_webservices.soap.stub.client_generator');
75 | $writer->addMethodCall('setUnwrap', [$config['unwrap_returns']]);
76 |
77 | $forProduction = !!$container->getParameter('goetas_webservices.soap.metadata');
78 |
79 | $readerName = 'goetas_webservices.soap.metadata_loader.' . ($forProduction ? 'array' : 'dev');
80 | $alias = $container->setAlias('goetas_webservices.soap.metadata_reader', $readerName);
81 | if ($alias instanceof Alias) {
82 | $alias->setPublic(true);
83 | }
84 | }
85 |
86 | protected static function sanitizePhp(string $ns): string
87 | {
88 | return strtr($ns, '/', '\\');
89 | }
90 |
91 | public function getAlias(): string
92 | {
93 | return 'soap_client';
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/Exception/ClientException.php:
--------------------------------------------------------------------------------
1 | getBody()->getFault()->getString();
21 |
22 | parent::__construct($message, $response, $request, $previous);
23 | $this->fault = $fault;
24 | }
25 |
26 | public function getFault(): Fault
27 | {
28 | return $this->fault;
29 | }
30 |
31 | public static function createFromResponse(ResponseInterface $response, RequestInterface $request, Fault $fault): FaultException
32 | {
33 | return new self($fault, $response, $request);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Exception/Fault12Exception.php:
--------------------------------------------------------------------------------
1 | getBody()->getFault()->getReason());
21 |
22 | parent::__construct($message, $response, $request, $previous);
23 | $this->fault = $fault;
24 | }
25 |
26 | public function getFault(): Fault
27 | {
28 | return $this->fault;
29 | }
30 |
31 | public static function createFromResponse(ResponseInterface $response, RequestInterface $request, Fault $fault): FaultException
32 | {
33 | return new self($fault, $response, $request);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Exception/FaultException.php:
--------------------------------------------------------------------------------
1 | response = $response;
26 | $this->request = $request;
27 | }
28 |
29 | public function getRequest(): RequestInterface
30 | {
31 | return $this->request;
32 | }
33 |
34 | public function getResponse(): ResponseInterface
35 | {
36 | return $this->response;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Exception/SoapException.php:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | FALSE
7 |
8 |
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
22 | %goetas_webservices.soap.metadata%
23 |
24 |
25 |
28 |
29 |
30 |
31 |
32 |
35 |
38 |
39 |
40 |
41 |
42 |
43 |
46 |
47 |
48 | %goetas_webservices.soap.unwrap_returns%
49 |
50 |
51 |
52 |
55 |
56 |
57 |
58 |
59 |
60 |
63 |
66 |
67 |
70 |
71 |
72 | %goetas_webservices.soap.unwrap_returns%
73 |
74 |
75 |
76 |
79 |
80 |
81 |
82 |
83 |
84 |
87 |
88 |
89 |
90 |
91 |
94 | %goetas_webservices.soap.strict_types%
95 |
96 |
97 |
100 |
101 |
102 |
103 |
104 |
107 |
108 |
109 |
110 |
111 |
112 |
115 |
116 |
119 |
120 |
121 |
122 |
123 |
126 |
127 |
128 |
129 |
132 |
133 |
134 |
135 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
--------------------------------------------------------------------------------
/src/Result/ResultCreator.php:
--------------------------------------------------------------------------------
1 | serializer = $serializer;
25 | $this->unwrap = $unwrap;
26 | }
27 |
28 | /**
29 | * @return mixed
30 | */
31 | private function getPropertyValue(PropertyMetadata $propertyMetadata, object $object)
32 | {
33 | $reflectionProperty = new \ReflectionProperty($propertyMetadata->class, $propertyMetadata->name);
34 | $reflectionProperty->setAccessible(true);
35 |
36 | return $reflectionProperty->getValue($object);
37 | }
38 |
39 | /**
40 | * @return mixed
41 | */
42 | public function prepareResult(object $object, array $output)
43 | {
44 | if (!count($output['parts'])) {
45 | return null;
46 | }
47 |
48 | $factory = SerializerUtils::getMetadataFactory($this->serializer);
49 |
50 | $classMetadata = $factory->getMetadataForClass($output['message_fqcn']);
51 | $bodyMetadata = $classMetadata->propertyMetadata['body'];
52 | $bodyClassMetadata = $factory->getMetadataForClass($bodyMetadata->type['name']);
53 | $body = $this->getPropertyValue($bodyMetadata, $object);
54 | $parts = [];
55 | foreach ($bodyClassMetadata->propertyMetadata as $propertyMetadata) {
56 | $parts[$propertyMetadata->name] = $this->getPropertyValue($propertyMetadata, $body);
57 | }
58 |
59 | if (count($output['parts']) > 1) {
60 | return $parts;
61 | } else {
62 | if ($this->unwrap) {
63 | foreach ($bodyClassMetadata->propertyMetadata as $propertyMetadata) {
64 | $propClassMetadata = $factory->getMetadataForClass($propertyMetadata->type['name']);
65 |
66 | if (count($propClassMetadata->propertyMetadata) > 1) {
67 | throw new \Exception('When using wrapped mode, the wrapped object can not have multiple properties');
68 | }
69 |
70 | if (!count($propClassMetadata->propertyMetadata)) {
71 | return null;
72 | }
73 |
74 | $propertyMetadata = reset($propClassMetadata->propertyMetadata);
75 |
76 | return $this->getPropertyValue($propertyMetadata, reset($parts));
77 | }
78 | }
79 |
80 | return reset($parts);
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/Result/ResultCreatorInterface.php:
--------------------------------------------------------------------------------
1 | namingStrategy = $namingStrategy;
42 | $this->phpConverter = $phpConverter;
43 | $this->unwrapReturn = $unwrapReturn;
44 | $this->inflector = InflectorFactory::create()->build();
45 | }
46 |
47 | public function setUnwrap(bool $mode = true): void
48 | {
49 | $this->unwrapReturn = $mode;
50 | }
51 |
52 | /**
53 | * @param PortType[] $ports
54 | */
55 | public function generate(array $ports): array
56 | {
57 | $classes = [];
58 | foreach ($ports as $port) {
59 | $class = new ClassGenerator();
60 | if (false !== $this->visitPortType($class, $port)) {
61 | $classes[] = $class;
62 | }
63 | }
64 |
65 | return $classes;
66 | }
67 |
68 | private function visitPortType(ClassGenerator $class, PortType $portType): ?bool
69 | {
70 | if (!count($portType->getOperations())) {
71 | return false;
72 | }
73 |
74 | $docBlock = new DocBlockGenerator('Class representing ' . $portType->getName());
75 | $docBlock->setWordWrap(false);
76 | if ($portType->getDocumentation()) {
77 | $docBlock->setLongDescription($portType->getDocumentation());
78 | }
79 |
80 | $namespaces = $this->phpConverter->getNamespaces();
81 | $class->setNamespaceName($namespaces[$portType->getDefinition()->getTargetNamespace()] . '\\SoapStubs');
82 | $class->setName($this->inflector->classify($portType->getName()));
83 | $class->setDocblock($docBlock);
84 |
85 | foreach ($portType->getOperations() as $operation) {
86 | $operationTag = $this->visitOperation($operation);
87 | $docBlock->setTag($operationTag);
88 | }
89 |
90 | return null;
91 | }
92 |
93 | private function visitOperation(PortType\Operation $operation): MethodTag
94 | {
95 | $types = $this->getOperationReturnTypes($operation);
96 | $operationTag = new MethodTag(
97 | $this->inflector->camelize($operation->getName()),
98 | $types,
99 | preg_replace("/[\n\r]+/", ' ', $operation->getDocumentation())
100 | );
101 | $params = $this->getOperationParams($operation);
102 | $params[] = $param = new ParamTag('header', ['\\' . Header::class]);
103 | $param->setVaridic();
104 |
105 | $operationTag->setParams($params);
106 |
107 | return $operationTag;
108 | }
109 |
110 | private function getOperationParams(PortType\Operation $operation): array
111 | {
112 | if (!$operation->getInput()) {
113 | return [];
114 | }
115 |
116 | $parts = $operation->getInput()->getMessage()->getParts();
117 | if (!$parts) {
118 | return [];
119 | }
120 |
121 | if (count($parts) > 1) {
122 | $params = [];
123 | foreach ($parts as $part) {
124 | $partName = $this->namingStrategy->getPropertyName($part);
125 | $class = $this->getClassFromPart($part);
126 |
127 | $typeName = $class->getPhpType();
128 | if ($t = $class->isSimpleType()) {
129 | $typeName = $t->getType()->getPhpType();
130 | }
131 |
132 | $params[] = $param = new ParamTag($partName, [$typeName]);
133 | }
134 | } else {
135 | $params = $this->extractSinglePartParameters(reset($parts));
136 | }
137 |
138 | return $params;
139 | }
140 |
141 | private function getOperationReturnTypes(PortType\Operation $operation): array
142 | {
143 | if (!$operation->getOutput() || !$operation->getOutput()->getMessage()->getParts()) {
144 | return ['void'];
145 | }
146 |
147 | $parts = $operation->getOutput()->getMessage()->getParts();
148 | if (count($parts) > 1) {
149 | return ['array'];
150 | }
151 |
152 | /**
153 | * @var $part \GoetasWebservices\XML\WSDLReader\Wsdl\Message\Part
154 | */
155 | $part = reset($parts);
156 |
157 | $class = $this->getClassFromPart($part);
158 | if ($this->unwrapReturn) {
159 | foreach ($class->getProperties() as $property) {
160 | $propertyClass = $property->getType();
161 | if ($t = $propertyClass->isSimpleType()) {
162 | return [$t->getType()->getPhpType()];
163 | }
164 |
165 | return [$propertyClass->getPhpType()];
166 | }
167 | }
168 |
169 | if ($t = $class->isSimpleType()) {
170 | return [$t->getType()->getPhpType()];
171 | }
172 |
173 | return [$class->getPhpType()];
174 | }
175 |
176 | private function getClassFromPart(Part $part): PHPClass
177 | {
178 | if ($part->getType()) {
179 | return $this->phpConverter->visitType($part->getType());
180 | } else {
181 | return $this->phpConverter->visitElementDef($part->getElement());
182 | }
183 | }
184 |
185 | /**
186 | * @return array
187 | */
188 | private function extractSinglePartParameters(Part $part): array
189 | {
190 | $params = [];
191 | $class = $this->getClassFromPart($part);
192 |
193 | foreach ($class->getPropertiesInHierarchy() as $property) {
194 | $t = $property->getType()->getPhpType();
195 | $params[] = $param = new ParamTag($property->getName(), [$t]);
196 | }
197 |
198 | return $params;
199 | }
200 | }
201 |
--------------------------------------------------------------------------------
/src/StubGeneration/Tag/MethodTag.php:
--------------------------------------------------------------------------------
1 | params = $params;
27 | }
28 |
29 | public function generateParams(): string
30 | {
31 | $params = array_map(static function (ParamTag $paramTag) {
32 | if ($paramTag instanceof SoapParamTag) {
33 | return $paramTag->generateForMethod();
34 | }
35 |
36 | return (!empty($paramTag->getTypes()) ? $paramTag->getTypesAsString() : '')
37 | . (!empty($paramTag->getVariableName()) ? ' $' . $paramTag->getVariableName() : '');
38 | }, $this->params);
39 |
40 | return implode(', ', $params);
41 | }
42 |
43 | public function generate(): string
44 | {
45 | $params = $this->generateParams();
46 |
47 | return '@method'
48 | . ($this->isStatic ? ' static' : '')
49 | . (!empty($this->types) ? ' ' . $this->getTypesAsString() : '')
50 | . (!empty($this->methodName) ? ' ' . $this->methodName . '(' . $params . ')' : '')
51 | . (!empty($this->description) ? ' ' . $this->description : '');
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/StubGeneration/Tag/ParamTag.php:
--------------------------------------------------------------------------------
1 | default = $default;
30 | }
31 |
32 | public function setVaridic(): void
33 | {
34 | $this->varidic = true;
35 | }
36 |
37 | public function generate(): string
38 | {
39 | return '@param'
40 | . (!empty($this->types) ? ' ' . $this->getTypesAsString() : '')
41 | . (!empty($this->variableName) ? ' $' . $this->variableName : '')
42 | . (!empty($this->default) ? ' = ' . $this->default : '')
43 | . (!empty($this->description) ? ' ' . $this->description : '');
44 | }
45 |
46 | public function generateForMethod(): string
47 | {
48 | return (!empty($this->types) ? $this->getTypesAsString() : '')
49 | . (!empty($this->variableName) ? ' ' . ($this->varidic ? '...' : '') . '$' . $this->variableName : '')
50 | . (!empty($this->default) ? ' = ' . $this->default : '');
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tests/Client/BuildClientTest.php:
--------------------------------------------------------------------------------
1 | 'Ex'];
28 | $generator = new Generator($namespaces);
29 | $serializer = $generator->buildSerializer();
30 |
31 | $naming = new ShortNamingStrategy();
32 | $metadataGenerator = new MetadataGenerator($naming, $namespaces);
33 |
34 | $dispatcher = new EventDispatcher();
35 | $wsdlReader = new DefinitionsReader(null, $dispatcher);
36 | $soapReader = new SoapReader();
37 | $dispatcher->addSubscriber($soapReader);
38 |
39 | $metadataLoader = new DevMetadataLoader($metadataGenerator, $soapReader, $wsdlReader);
40 | $this->factory = new ClientFactory($metadataLoader, $serializer);
41 | }
42 |
43 | public function testBuildServer(): void
44 | {
45 | $client = $this->factory->getClient(__DIR__ . '/../Fixtures/test.wsdl');
46 | $this->assertNotNull($client);
47 | }
48 |
49 | public function testGetService(): void
50 | {
51 | $client = $this->factory->getClient(__DIR__ . '/../Fixtures/test.wsdl', 'testSOAP');
52 | $this->assertNotNull($client);
53 | }
54 |
55 | public function testGetWrongPort(): void
56 | {
57 | $this->expectException(PortNotFoundException::class);
58 | $this->expectExceptionMessage('The port named XXX can not be found');
59 | $client = $this->factory->getClient(__DIR__ . '/../Fixtures/test.wsdl', 'XXX');
60 | $this->assertNotNull($client);
61 | }
62 |
63 | public function testGetWrongService(): void
64 | {
65 | $this->expectException(PortNotFoundException::class);
66 | $this->expectExceptionMessage('The port named testSOAP can not be found');
67 | $client = $this->factory->getClient(__DIR__ . '/../Fixtures/test.wsdl', 'testSOAP', 'alternativeTest');
68 | $this->assertNotNull($client);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/tests/Client/Client11RequestResponsesTest.php:
--------------------------------------------------------------------------------
1 | factory->getClient(__DIR__ . '/../Fixtures/test.wsdl');
28 | }
29 |
30 | public function testGetLastRequestMessage(): void
31 | {
32 | $httpResponse = new Response(200, ['Content-Type' => 'text/xml'], '
33 |
34 |
35 |
36 |
37 |
38 |
39 | ');
40 |
41 | $this->responseMock->append($httpResponse);
42 |
43 | $this->client->setDebug(true);
44 | $this->assertNull($this->client->__getLastRequestMessage());
45 |
46 | /**
47 | * @var $response \Ex\GetSimpleResponse
48 | */
49 | $response = $this->client->getSimple('foo');
50 | $this->assertInstanceOf('Psr\Http\Message\RequestInterface', $this->client->__getLastRequestMessage());
51 | $this->assertXmlStringEqualsXmlString(
52 | '
53 |
54 |
55 |
56 |
57 |
58 | ',
59 | (string) $this->client->__getLastRequestMessage()->getBody()
60 | );
61 | }
62 |
63 | public function testGetLastResponseMessage(): void
64 | {
65 | $httpResponse = new Response(200, ['Content-Type' => 'text/xml'], '
66 |
67 |
68 |
69 |
70 |
71 |
72 | ');
73 |
74 | $this->responseMock->append($httpResponse);
75 |
76 | $this->client->setDebug(true);
77 | $this->assertNull($this->client->__getLastResponseMessage());
78 |
79 | /**
80 | * @var $response \Ex\GetSimpleResponse
81 | */
82 | $response = $this->client->getSimple('foo');
83 | $this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $this->client->__getLastResponseMessage());
84 | $this->assertSame($httpResponse, $this->client->__getLastResponseMessage());
85 | }
86 |
87 | public function testGetSimple(): void
88 | {
89 | $httpResponse = new Response(200, ['Content-Type' => 'text/xml'], '
90 |
91 |
92 |
93 |
94 |
95 |
96 | ');
97 |
98 | $this->responseMock->append($httpResponse);
99 |
100 | /**
101 | * @var $response \Ex\GetSimpleResponse
102 | */
103 | $response = $this->client->getSimple('foo');
104 | $this->assertInstanceOf('Ex\GetSimpleResponse', $response);
105 | $this->assertEquals('A', $response->getOut());
106 | }
107 |
108 | public function testGetSimpleUnwrapped(): void
109 | {
110 | $httpResponse = new Response(200, ['Content-Type' => 'text/xml'], '
111 |
112 |
113 |
114 |
115 |
116 |
117 | ');
118 | $this->responseMock->append($httpResponse);
119 |
120 | $naming = new ShortNamingStrategy();
121 | $dispatcher = new EventDispatcher();
122 | $wsdlReader = new DefinitionsReader(null, $dispatcher);
123 | $soapReader = new SoapReader();
124 | $dispatcher->addSubscriber($soapReader);
125 |
126 | $metadataGenerator = new MetadataGenerator($naming, self::$namespaces);
127 | $metadataGenerator->setUnwrap(true);
128 | $metadataReader = new DevMetadataLoader($metadataGenerator, $soapReader, $wsdlReader);
129 |
130 | $this->factory->setMetadataReader($metadataReader);
131 |
132 | /**
133 | * @var $response \Ex\GetSimpleResponse
134 | */
135 | $response = $this->getClient()->getSimple('foo');
136 | $this->assertSame('A', $response);
137 | }
138 |
139 | public function testHeaders(): void
140 | {
141 | $httpResponse = new Response(200, ['Content-Type' => 'text/xml'], '
142 |
143 |
144 |
145 |
146 |
147 |
148 | ');
149 |
150 | $this->responseMock->append($httpResponse);
151 | $this->responseMock->append($httpResponse);
152 |
153 | $mp = new GetReturnMultiParam();
154 | $mp->setIn('foo');
155 |
156 | $this->client->getSimple('foo', new Header($mp));
157 | $this->client->getSimple('foo', (new Header($mp))->mustUnderstand());
158 | $this->assertXmlStringEqualsXmlString(
159 | '
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 | ',
171 | (string) $this->requestResponseStack[0]['request']->getBody()
172 | );
173 |
174 | $this->assertXmlStringEqualsXmlString(
175 | '
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 | ',
188 | (string) $this->requestResponseStack[1]['request']->getBody()
189 | );
190 | }
191 |
192 | public function testResponseHeaders(): void
193 | {
194 | $httpResponse = new Response(
195 | 200,
196 | ['Content-Type' => 'text/xml'],
197 | '
200 |
201 |
202 | username
203 | pass
204 |
205 |
206 |
207 |
208 | str
209 |
210 |
211 | '
212 | );
213 |
214 | $this->responseMock->append($httpResponse);
215 |
216 | $res = $this->client->responseHeaderMessages('foo');
217 | $this->assertEquals('str', $res->getOut());
218 | }
219 |
220 | public function testNoOutput(): void
221 | {
222 | $this->responseMock->append(
223 | new Response(200, ['Content-Type' => 'text/xml'], '
224 |
225 |
226 | ')
227 | );
228 |
229 | $response = $this->client->noOutput('foo');
230 | $this->assertNull($response);
231 | }
232 |
233 | public function testNoInput(): void
234 | {
235 | $this->responseMock->append(
236 | new Response(200, ['Content-Type' => 'text/xml'], '
237 |
238 |
239 |
240 |
241 |
242 |
243 | ')
244 | );
245 |
246 | $this->client->noInput('foo');
247 | $this->assertXmlStringEqualsXmlString(
248 | '',
249 | (string) $this->requestResponseStack[0]['request']->getBody()
250 | );
251 | }
252 |
253 | public function testNoBoth(): void
254 | {
255 | $this->responseMock->append(
256 | new Response(
257 | 200,
258 | ['Content-Type' => 'text/xml'],
259 | ''
260 | )
261 | );
262 |
263 | $response = $this->client->noBoth('foo');
264 | $this->assertNull($response);
265 | $this->assertXmlStringEqualsXmlString(
266 | '',
267 | (string) $this->requestResponseStack[0]['request']->getBody()
268 | );
269 | }
270 |
271 | public function testReturnMultiParam(): void
272 | {
273 | $this->responseMock->append(
274 | new Response(
275 | 200,
276 | ['Content-Type' => 'text/xml'],
277 | '
278 |
279 |
280 |
281 |
282 |
283 |
284 | '
285 | )
286 | );
287 |
288 | $mp = new GetReturnMultiParam();
289 | $mp->setIn('foo');
290 | $return = $this->client->getReturnMultiParam($mp);
291 |
292 | $this->assertCount(2, $return);
293 | $this->assertEquals($return['otherParam'], 'str');
294 |
295 | $this->assertInstanceOf(GetReturnMultiParamResponse::class, $return['getReturnMultiParamResponse']);
296 | $this->assertEquals($return['getReturnMultiParamResponse']->getOut(), 'foo');
297 |
298 | $this->assertXmlStringEqualsXmlString(
299 | '
300 |
301 |
302 |
303 |
304 |
305 | ',
306 | (string) $this->requestResponseStack[0]['request']->getBody()
307 | );
308 | }
309 |
310 | public function testMultiParamRequest(): void
311 | {
312 | $this->responseMock->append(
313 | new Response(
314 | 200,
315 | ['Content-Type' => 'text/xml'],
316 | '
317 |
318 |
319 |
320 |
321 |
322 | '
323 | )
324 | );
325 |
326 | $mp = new GetMultiParam();
327 | $mp->setIn('foo');
328 | $this->client->getMultiParam($mp, 'str');
329 |
330 | $this->assertXmlStringEqualsXmlString(
331 | '
332 |
333 |
334 |
335 |
336 |
337 |
338 | ',
339 | (string) $this->requestResponseStack[0]['request']->getBody()
340 | );
341 | }
342 |
343 | /**
344 | * @dataProvider getHttpFaultCodes
345 | */
346 | public function testApplicationError(int $code): void
347 | {
348 | $this->responseMock->append(
349 | new Response($code, ['Content-Type' => 'text/xml'], '
350 |
351 |
352 |
353 | SOAP-ENV:MustUnderstand
354 | SOAP Must Understand Error
355 |
356 |
357 | Business Exception
358 | 100
359 | some message
360 | fault state
361 |
362 |
363 |
364 |
365 | ')
366 | );
367 |
368 | try {
369 | $this->client->getSimple('foo');
370 | $this->assertTrue(false, 'Exception is not thrown');
371 | } catch (Fault11Exception $e) {
372 | $this->assertInstanceOf(Fault::class, $e->getFault());
373 | $this->assertSame('SOAP Must Understand Error', $e->getMessage());
374 | $this->assertInstanceOf(FaultBase::class, $e->getFault()->getBody()->getFault());
375 |
376 | $detail = $e->getFault()->getBody()->getFault()->getRawDetail();
377 | $this->assertInstanceOf(\SimpleXMLElement::class, $detail);
378 | $this->assertXmlStringEqualsXmlString(trim('
379 |
380 | Business Exception
381 | 100
382 | some message
383 | fault state
384 |
385 | '), $detail->asXML());
386 | }
387 | }
388 | }
389 |
--------------------------------------------------------------------------------
/tests/Client/Client12RequestResponsesTest.php:
--------------------------------------------------------------------------------
1 | factory->getClient(__DIR__ . '/../Fixtures/test.wsdl', 'testSOAP12');
30 | }
31 |
32 | public function testGetLastRequestMessage(): void
33 | {
34 | $httpResponse = new Response(200, ['Content-Type' => 'application/soap+xml'], '
35 |
36 |
37 |
38 |
39 |
40 |
41 | ');
42 |
43 | $this->responseMock->append($httpResponse);
44 |
45 | $this->client->setDebug(true);
46 | $this->assertNull($this->client->__getLastRequestMessage());
47 |
48 | /**
49 | * @var $response \Ex\GetSimpleResponse
50 | */
51 | $response = $this->client->getSimple('foo');
52 | $this->assertInstanceOf('Psr\Http\Message\RequestInterface', $this->client->__getLastRequestMessage());
53 | $this->assertXmlStringEqualsXmlString(
54 | '
55 |
56 |
57 |
58 |
59 |
60 | ',
61 | (string) $this->client->__getLastRequestMessage()->getBody()
62 | );
63 | }
64 |
65 | public function testGetLastResponseMessage(): void
66 | {
67 | $httpResponse = new Response(200, ['Content-Type' => 'application/soap+xml'], '
68 |
69 |
70 |
71 |
72 |
73 |
74 | ');
75 |
76 | $this->responseMock->append($httpResponse);
77 |
78 | $this->client->setDebug(true);
79 | $this->assertNull($this->client->__getLastResponseMessage());
80 |
81 | /**
82 | * @var $response \Ex\GetSimpleResponse
83 | */
84 | $response = $this->client->getSimple('foo');
85 | $this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $this->client->__getLastResponseMessage());
86 | $this->assertSame($httpResponse, $this->client->__getLastResponseMessage());
87 | }
88 |
89 | public function testGetSimple(): void
90 | {
91 | $httpResponse = new Response(200, ['Content-Type' => 'application/soap+xml'], '
92 |
93 |
94 |
95 |
96 |
97 |
98 | ');
99 |
100 | $this->responseMock->append($httpResponse);
101 |
102 | /**
103 | * @var $response \Ex\GetSimpleResponse
104 | */
105 | $response = $this->client->getsimple('foo');
106 | $this->assertInstanceOf('Ex\GetSimpleResponse', $response);
107 | $this->assertEquals('A', $response->getOut());
108 | }
109 |
110 | public function testGetSimpleUnwrapped(): void
111 | {
112 | $httpResponse = new Response(200, ['Content-Type' => 'application/soap+xml'], '
113 |
114 |
115 |
116 |
117 |
118 |
119 | ');
120 | $this->responseMock->append($httpResponse);
121 |
122 | $naming = new ShortNamingStrategy();
123 | $dispatcher = new EventDispatcher();
124 | $wsdlReader = new DefinitionsReader(null, $dispatcher);
125 | $soapReader = new SoapReader();
126 | $dispatcher->addSubscriber($soapReader);
127 |
128 | $metadataGenerator = new MetadataGenerator($naming, self::$namespaces);
129 | $metadataGenerator->setUnwrap(true);
130 | $metadataReader = new DevMetadataLoader($metadataGenerator, $soapReader, $wsdlReader);
131 |
132 | $this->factory->setMetadataReader($metadataReader);
133 |
134 | /**
135 | * @var $response \Ex\GetSimpleResponse
136 | */
137 | $response = $this->getClient()->getSimple('foo');
138 | $this->assertSame('A', $response);
139 | }
140 |
141 | public function testHeaders(): void
142 | {
143 | $httpResponse = new Response(200, ['Content-Type' => 'application/soap+xml'], '
144 |
145 |
146 |
147 |
148 |
149 |
150 | ');
151 |
152 | $this->responseMock->append($httpResponse);
153 | $this->responseMock->append($httpResponse);
154 |
155 | $this->client = $this->factory->getClient(__DIR__ . '/../Fixtures/test.wsdl', 'testSOAP12', null, true);
156 |
157 | $mp = new GetReturnMultiParam();
158 | $mp->setIn('foo');
159 |
160 | $this->client->getSimple('foo', new Header($mp));
161 | $this->client->getSimple('foo', (new Header($mp))->mustUnderstand());
162 | $this->assertXmlStringEqualsXmlString(
163 | trim(
164 | '
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 | '
176 | ),
177 | (string) $this->requestResponseStack[0]['request']->getBody()
178 | );
179 |
180 | $this->assertXmlStringEqualsXmlString(
181 | '
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 | ',
194 | (string) $this->requestResponseStack[1]['request']->getBody()
195 | );
196 | }
197 |
198 | public function testResponseHeaders(): void
199 | {
200 | $httpResponse = new Response(
201 | 200,
202 | ['Content-Type' => 'application/soap+xml'],
203 | '
206 |
207 |
208 | username
209 | pass
210 |
211 |
212 |
213 |
214 | str
215 |
216 |
217 | '
218 | );
219 |
220 | $this->responseMock->append($httpResponse);
221 |
222 | $res = $this->client->responseHeaderMessages('foo');
223 | $this->assertEquals('str', $res->getOut());
224 | }
225 |
226 | public function testNoOutput(): void
227 | {
228 | $this->responseMock->append(
229 | new Response(200, ['Content-Type' => 'application/soap+xml'], '
230 |
231 |
232 | ')
233 | );
234 | $this->client = $this->factory->getClient(__DIR__ . '/../Fixtures/test.wsdl', 'testSOAP12', null, true);
235 |
236 | $response = $this->client->noOutput('foo');
237 | $this->assertNull($response);
238 | }
239 |
240 | public function testNoInput(): void
241 | {
242 | $this->responseMock->append(
243 | new Response(200, ['Content-Type' => 'application/soap+xml'], '
244 |
245 |
246 |
247 |
248 |
249 |
250 | ')
251 | );
252 | $this->client = $this->factory->getClient(__DIR__ . '/../Fixtures/test.wsdl', 'testSOAP12', null, true);
253 |
254 | $this->client->noInput('foo');
255 | $this->assertXmlStringEqualsXmlString(
256 | '',
257 | (string) $this->requestResponseStack[0]['request']->getBody()
258 | );
259 | }
260 |
261 | public function testNoBoth(): void
262 | {
263 | $this->responseMock->append(
264 | new Response(
265 | 200,
266 | ['Content-Type' => 'application/soap+xml'],
267 | ''
268 | )
269 | );
270 | $this->client = $this->factory->getClient(__DIR__ . '/../Fixtures/test.wsdl', 'testSOAP12', null, true);
271 |
272 | $response = $this->client->noBoth('foo');
273 | $this->assertNull($response);
274 | $this->assertXmlStringEqualsXmlString(
275 | '',
276 | (string) $this->requestResponseStack[0]['request']->getBody()
277 | );
278 | }
279 |
280 | public function testReturnMultiParam(): void
281 | {
282 | $this->responseMock->append(
283 | new Response(
284 | 200,
285 | ['Content-Type' => 'application/soap+xml'],
286 | '
287 |
288 |
289 |
290 |
291 |
292 |
293 | '
294 | )
295 | );
296 | $this->client = $this->factory->getClient(__DIR__ . '/../Fixtures/test.wsdl', 'testSOAP12', null, true);
297 |
298 | $mp = new GetReturnMultiParam();
299 | $mp->setIn('foo');
300 | $return = $this->client->getReturnMultiParam($mp);
301 |
302 | $this->assertCount(2, $return);
303 | $this->assertEquals($return['otherParam'], 'str');
304 |
305 | $this->assertInstanceOf(GetReturnMultiParamResponse::class, $return['getReturnMultiParamResponse']);
306 | $this->assertEquals($return['getReturnMultiParamResponse']->getOut(), 'foo');
307 |
308 | $this->assertXmlStringEqualsXmlString(
309 | '
310 |
311 |
312 |
313 |
314 |
315 | ',
316 | (string) $this->requestResponseStack[0]['request']->getBody()
317 | );
318 | }
319 |
320 | public function testMultiParamRequest(): void
321 | {
322 | $this->responseMock->append(
323 | new Response(
324 | 200,
325 | ['Content-Type' => 'application/soap+xml'],
326 | '
327 |
328 |
329 |
330 |
331 |
332 | '
333 | )
334 | );
335 | $this->client = $this->factory->getClient(__DIR__ . '/../Fixtures/test.wsdl', 'testSOAP12', null, true);
336 |
337 | $mp = new GetMultiParam();
338 | $mp->setIn('foo');
339 | $this->client->getMultiParam($mp, 'str');
340 |
341 | $this->assertXmlStringEqualsXmlString(
342 | '
343 |
344 |
345 |
346 |
347 |
348 |
349 | ',
350 | (string) $this->requestResponseStack[0]['request']->getBody()
351 | );
352 | }
353 |
354 | /**
355 | * @dataProvider getHttpFaultCodes
356 | */
357 | public function testApplicationError(int $code): void
358 | {
359 | $this->responseMock->append(
360 | new Response($code, ['Content-Type' => 'application/soap+xml'], '
361 |
362 |
363 |
364 |
365 | server
366 |
367 |
368 | String index out of range: 3
369 |
370 |
371 |
372 | Business Exception
373 | 100
374 | some message
375 | fault state
376 |
377 |
378 |
379 |
380 | ')
381 | );
382 |
383 | try {
384 | $this->client->getSimple('foo');
385 | $this->assertTrue(false, 'Exception is not thrown');
386 | } catch (Fault12Exception $e) {
387 | $this->assertInstanceOf(Fault::class, $e->getFault());
388 | $this->assertSame('String index out of range: 3', $e->getMessage());
389 | $this->assertInstanceOf(FaultBase::class, $e->getFault()->getBody()->getFault());
390 |
391 | $detail = $e->getFault()->getBody()->getFault()->getRawDetail();
392 | $this->assertInstanceOf(\SimpleXMLElement::class, $detail);
393 | $this->assertXmlStringEqualsXmlString(trim('
394 |
395 | Business Exception
396 | 100
397 | some message
398 | fault state
399 |
400 | '), $detail->asXML());
401 | }
402 | }
403 |
404 | public function testSerializerContextParametersAreAdded()
405 | {
406 | $this->handlerRegistry->registerHandler(GraphNavigator::DIRECTION_SERIALIZATION, \Ex\SoapEnvelope12\Messages\GetSimpleInput::class, 'xml',
407 | function($visitor, \Ex\SoapEnvelope12\Messages\GetSimpleInput $obj, array $type, Context $context) {
408 |
409 | $this->assertTrue($context->hasAttribute('soapEndpoint'), 'The "soapEndpoint" attribute was not found on the context object');
410 | $this->assertEquals('http://www.example.org/12', $context->getAttribute('soapEndpoint'));
411 |
412 | $this->assertTrue($context->hasAttribute('soapOperation'), 'The "soapOperation" attribute was not found on the context object');
413 | $this->assertTrue(
414 | is_array($context->getAttribute('soapOperation')),
415 | 'The "soapOperation" attribute is not of type array, but '.gettype($context->getAttribute('soapOperation'))
416 | );
417 | $this->assertEquals('http://www.example.org/test/getSimple', $context->getAttribute('soapOperation')['action']);
418 |
419 | throw new SerializerHandlerAssertionsWereExecuted('Stop serialization, test has finished');
420 | }
421 | );
422 |
423 | $client = $this->getClient();
424 |
425 | // Assert that subscribing handler with assertions was executed
426 | $this->expectException(SerializerHandlerAssertionsWereExecuted::class);
427 |
428 | $client->getSimple('foo');
429 | }
430 | }
431 |
432 | class SerializerHandlerAssertionsWereExecuted extends \Exception {};
--------------------------------------------------------------------------------
/tests/Client/RequestResponsesTest.php:
--------------------------------------------------------------------------------
1 | 'Ex'];
36 | /**
37 | * @var Generator
38 | */
39 | protected static $generator;
40 |
41 | /**
42 | * @var MockHandler
43 | */
44 | protected $responseMock;
45 |
46 | /**
47 | * @var array
48 | */
49 | protected $requestResponseStack = [];
50 |
51 | /**
52 | * @var Client
53 | */
54 | protected $client;
55 |
56 | /**
57 | * @var ClientFactory
58 | */
59 | protected $factory;
60 |
61 | /**
62 | * @var HandlerRegistryInterface
63 | */
64 | protected $handlerRegistry;
65 |
66 | public static function setUpBeforeClass(): void
67 | {
68 | self::$generator = new Generator(self::$namespaces, [], __DIR__ . '/tmp');
69 | self::$generator->generate([__DIR__ . '/../Fixtures/test.wsdl']);
70 | self::$generator->registerAutoloader();
71 | }
72 |
73 | public static function tearDownAfterClass(): void
74 | {
75 | self::$generator->unRegisterAutoloader();
76 | //self::$generator->cleanDirectories();
77 | }
78 |
79 | public function setUp(): void
80 | {
81 | $ref = new \ReflectionClass(Fault::class);
82 |
83 | $headerHandler = new HeaderHandler();
84 |
85 | $listeners = static function (EventDispatcherInterface $d) use ($headerHandler): void {
86 | $d->addSubscriber($headerHandler);
87 | };
88 |
89 | $handlers = function (HandlerRegistryInterface $h) use ($headerHandler): void {
90 | $h->registerSubscribingHandler($headerHandler);
91 | $this->handlerRegistry = $h;
92 | };
93 |
94 | $serializer = self::$generator->buildSerializer($handlers, [
95 | 'GoetasWebservices\SoapServices\Metadata\Envelope\SoapEnvelope12' => dirname($ref->getFileName()) . '/../../../Resources/metadata/jms12',
96 | 'GoetasWebservices\SoapServices\Metadata\Envelope\SoapEnvelope' => dirname($ref->getFileName()) . '/../../../Resources/metadata/jms',
97 | ], $listeners);
98 |
99 | $this->responseMock = new MockHandler();
100 | $history = Middleware::history($this->requestResponseStack);
101 |
102 | $handler = HandlerStack::create($this->responseMock);
103 | $handler->push($history);
104 |
105 | $guzzle = new GuzzleHttpClient(['handler' => $handler]);
106 |
107 | $naming = new ShortNamingStrategy();
108 | $dispatcher = new EventDispatcher();
109 | $wsdlReader = new DefinitionsReader(null, $dispatcher);
110 | $soapReader = new SoapReader();
111 | $dispatcher->addSubscriber($soapReader);
112 |
113 | $metadataGenerator = new MetadataGenerator($naming, self::$namespaces);
114 | $metadataLoader = new DevMetadataLoader($metadataGenerator, $soapReader, $wsdlReader);
115 |
116 | $this->factory = new ClientFactory($metadataLoader, $serializer);
117 | $this->factory->setHttpClient($guzzle);
118 | $this->client = $this->getClient();
119 | }
120 |
121 | abstract protected function getClient(): Client;
122 |
123 | public function getErrorResponses(): array
124 | {
125 | return [
126 | [new Response(500, ['Content-Type' => 'text/xml'], '')],
127 | [new Response(500, ['Content-Type' => 'text/html'])],
128 |
129 | [new Response(404, ['Content-Type' => 'text/xml'], '')],
130 | [new Response(404, ['Content-Type' => 'text/html'])],
131 |
132 | [new Response(200, ['Content-Type' => 'text/html'])],
133 |
134 | [new Response(500, ['Content-Type' => 'application/soap+xml'], '')],
135 | [new Response(500, ['Content-Type' => 'text/html'])],
136 |
137 | [new Response(404, ['Content-Type' => 'application/soap+xml'], '')],
138 | [new Response(404, ['Content-Type' => 'text/html'])],
139 |
140 | [new Response(200, ['Content-Type' => 'text/html'])],
141 | ];
142 | }
143 |
144 | public function getHttpFaultCodes(): array
145 | {
146 | return [
147 | [500],
148 | [200],
149 | ];
150 | }
151 |
152 | /**
153 | * @dataProvider getErrorResponses
154 | */
155 | public function testGetSimpleError(ResponseInterface $response): void
156 | {
157 | $this->expectException(UnexpectedFormatException::class);
158 | $this->responseMock->append($response);
159 |
160 | $this->client->getSimple('foo');
161 | }
162 |
163 | public function testNoMethod(): void
164 | {
165 | $this->expectException(ClientException::class);
166 | $this->expectExceptionMessage('Can not find an operation to run abc service call');
167 |
168 | $this->client->abc();
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/tests/DependencyInjection/ConfigurationTest.php:
--------------------------------------------------------------------------------
1 | getDebugContainer();
16 |
17 | $tempDir = sys_get_temp_dir();
18 |
19 | $builder->dumpContainerForProd($tempDir, $debugContainer);
20 | $this->assertFileExists($tempDir . '/SoapContainer.php');
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tests/Fixtures/config.yml:
--------------------------------------------------------------------------------
1 | soap_client:
2 | metadata:
3 | 'tests/Fixtures/test.wsdl': ~
4 | alternative_endpoints:
5 | service:
6 | port: xxx
7 | namespaces:
8 | 'http://www.example.org/test/': 'TestNs'
9 | destinations_php:
10 | 'TestNs': soap/src
11 | destinations_jms:
12 | 'TestNs\SoapEnvelope12': soap/metadata/soap-env-12
13 | 'TestNs\SoapEnvelope': soap/metadata/soap-env-11
14 | 'TestNs\SoapParts': soap/metadata/soap-parts
15 | 'TestNs': soap/metadata
16 | aliases:
17 | 'http://www.example.org/test/':
18 | responseHeaderMessagesResponse: 'HeaderResponse'
19 |
20 |
--------------------------------------------------------------------------------
/tests/Fixtures/test.wsdl:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
--------------------------------------------------------------------------------
/tests/Stub/StubGeneratorTest.php:
--------------------------------------------------------------------------------
1 | getDebugContainer();
16 |
17 | /**
18 | * @var $clientStubGenerator \GoetasWebservices\SoapServices\SoapClient\StubGeneration\ClientStubGenerator
19 | */
20 | $clientStubGenerator = $debugContainer->get('goetas_webservices.soap.stub.client_generator');
21 |
22 | $wsdlMetadata = $debugContainer->getParameter('goetas_webservices.soap.config')['metadata'];
23 | $schemas = [];
24 | $portTypes = [];
25 | $wsdlReader = $debugContainer->get('goetas_webservices.wsdl2php.wsdl_reader');
26 |
27 | foreach (array_keys($wsdlMetadata) as $src) {
28 | $definitions = $wsdlReader->readFile($src);
29 | $schemas[] = $definitions->getSchema();
30 | $portTypes = array_merge($portTypes, $definitions->getAllPortTypes());
31 | }
32 |
33 | $classDefinitions = $clientStubGenerator->generate($portTypes);
34 |
35 | $this->assertCount(2, $classDefinitions);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 | addPsr4('TestNs\\', __DIR__ . '/../soap/src');
22 |
23 | $container = new SoapClientContainer();
24 |
25 | $serializer = SoapContainerBuilder::createSerializerBuilderFromContainer($container)->build();
26 | $metadata = $container->get('goetas_webservices.soap.metadata_reader');
27 |
28 | $responseMock = new MockHandler();
29 | $httpResponse = new Response(200, ['Content-Type' => 'text/xml'], '
30 |
31 |
32 |
33 |
34 |
35 |
36 | ');
37 |
38 | $responseMock->append($httpResponse);
39 |
40 | $requestResponseStack = [];
41 | $history = Middleware::history($requestResponseStack);
42 |
43 | $handler = HandlerStack::create($responseMock);
44 | $handler->push($history);
45 |
46 | $guzzle = new Client(['handler' => $handler]);
47 |
48 | $factory = new ClientFactory($metadata, $serializer);
49 | $factory->setHttpClient($guzzle);
50 |
51 | /**
52 | * @var $client \TestNs\SoapStubs\Test
53 | */
54 | $client = $factory->getClient('tests/Fixtures/test.wsdl');
55 |
56 | $out = $client->getSimple('bar');
57 |
58 | if (!($out instanceof GetSimpleResponse)) {
59 | echo "\$out is not instanceof GetSimpleResponse\n";
60 | exit(-128);
61 | }
62 |
63 | $request = (string) $requestResponseStack[0]['request']->getBody();
64 | if (false === strpos($request, '') || false === strpos($request, ':getSimple ')) {
65 | echo "Wrong request\n";
66 | exit(-128);
67 | }
68 |
69 | exit(0);
70 |
--------------------------------------------------------------------------------