Actualización

This commit is contained in:
Xes
2025-04-10 12:24:57 +02:00
parent 8969cc929d
commit 45420b6f0d
39760 changed files with 4303286 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
# Changelog
All notable changes to this project will be documented in this file, in reverse chronological order by release.
## 2.6.0 - 2016-04-21
### Added
- [#1](https://github.com/zendframework/zend-soap/pull/1) adds
support for the `SoapClient` options `keep_alive` and `ssl_method`.
- [#20](https://github.com/zendframework/zend-soap/pull/20) adds support for
the `SoapServer` `send_errors` constructor option.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- Nothing.
## 2.5.2 - 2016-04-21
### Added
- Adds GitHub Pages documentation at https://zendframework.github.io/zend-soap/
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#7](https://github.com/zendframework/zend-soap/pull/7) fixes
behavior when the request contains empty content.
- [#21](https://github.com/zendframework/zend-soap/pull/21) updates the
dependencies to allow usage with zend-stdlib v3 releases.

View File

@@ -0,0 +1,43 @@
# Contributor Code of Conduct
The Zend Framework project adheres to [The Code Manifesto](http://codemanifesto.com)
as its guidelines for contributor interactions.
## The Code Manifesto
We want to work in an ecosystem that empowers developers to reach their
potential — one that encourages growth and effective collaboration. A space that
is safe for all.
A space such as this benefits everyone that participates in it. It encourages
new developers to enter our field. It is through discussion and collaboration
that we grow, and through growth that we improve.
In the effort to create such a place, we hold to these values:
1. **Discrimination limits us.** This includes discrimination on the basis of
race, gender, sexual orientation, gender identity, age, nationality, technology
and any other arbitrary exclusion of a group of people.
2. **Boundaries honor us.** Your comfort levels are not everyones comfort
levels. Remember that, and if brought to your attention, heed it.
3. **We are our biggest assets.** None of us were born masters of our trade.
Each of us has been helped along the way. Return that favor, when and where
you can.
4. **We are resources for the future.** As an extension of #3, share what you
know. Make yourself a resource to help those that come after you.
5. **Respect defines us.** Treat others as you wish to be treated. Make your
discussions, criticisms and debates from a position of respectfulness. Ask
yourself, is it true? Is it necessary? Is it constructive? Anything less is
unacceptable.
6. **Reactions require grace.** Angry responses are valid, but abusive language
and vindictive actions are toxic. When something happens that offends you,
handle it assertively, but be respectful. Escalate reasonably, and try to
allow the offender an opportunity to explain themselves, and possibly correct
the issue.
7. **Opinions are just that: opinions.** Each and every one of us, due to our
background and upbringing, have varying opinions. The fact of the matter, is
that is perfectly acceptable. Remember this: if you respect your own
opinions, you should respect the opinions of others.
8. **To err is human.** You might not intend it, but mistakes do happen and
contribute to build experience. Tolerate honest mistakes, and don't hesitate
to apologize if you make one yourself.

View File

@@ -0,0 +1,234 @@
# CONTRIBUTING
## RESOURCES
If you wish to contribute to Zend Framework, please be sure to
read/subscribe to the following resources:
- [Coding Standards](https://github.com/zendframework/zf2/wiki/Coding-Standards)
- [Contributor's Guide](http://framework.zend.com/participate/contributor-guide)
- ZF Contributor's mailing list:
Archives: http://zend-framework-community.634137.n4.nabble.com/ZF-Contributor-f680267.html
Subscribe: zf-contributors-subscribe@lists.zend.com
- ZF Contributor's IRC channel:
#zftalk.dev on Freenode.net
If you are working on new features or refactoring [create a proposal](https://github.com/zendframework/zend-soap/issues/new).
## Reporting Potential Security Issues
If you have encountered a potential security vulnerability, please **DO NOT** report it on the public
issue tracker: send it to us at [zf-security@zend.com](mailto:zf-security@zend.com) instead.
We will work with you to verify the vulnerability and patch it as soon as possible.
When reporting issues, please provide the following information:
- Component(s) affected
- A description indicating how to reproduce the issue
- A summary of the security vulnerability and impact
We request that you contact us via the email address above and give the project
contributors a chance to resolve the vulnerability and issue a new release prior
to any public exposure; this helps protect users and provides them with a chance
to upgrade and/or update in order to protect their applications.
For sensitive email communications, please use [our PGP key](http://framework.zend.com/zf-security-pgp-key.asc).
## RUNNING TESTS
> ### Note: testing versions prior to 2.4
>
> This component originates with Zend Framework 2. During the lifetime of ZF2,
> testing infrastructure migrated from PHPUnit 3 to PHPUnit 4. In most cases, no
> changes were necessary. However, due to the migration, tests may not run on
> versions < 2.4. As such, you may need to change the PHPUnit dependency if
> attempting a fix on such a version.
To run tests:
- Clone the repository:
```console
$ git clone git@github.com:zendframework/zend-soap.git
$ cd
```
- Install dependencies via composer:
```console
$ curl -sS https://getcomposer.org/installer | php --
$ ./composer.phar install
```
If you don't have `curl` installed, you can also download `composer.phar` from https://getcomposer.org/
- Run the tests via `phpunit` and the provided PHPUnit config, like in this example:
```console
$ ./vendor/bin/phpunit
```
You can turn on conditional tests with the phpunit.xml file.
To do so:
- Copy `phpunit.xml.dist` file to `phpunit.xml`
- Edit `phpunit.xml` to enable any specific functionality you
want to test, as well as to provide test values to utilize.
## Running Coding Standards Checks
This component uses [php-cs-fixer](http://cs.sensiolabs.org/) for coding
standards checks, and provides configuration for our selected checks.
`php-cs-fixer` is installed by default via Composer.
To run checks only:
```console
$ ./vendor/bin/php-cs-fixer fix . -v --diff --dry-run --config-file=.php_cs
```
To have `php-cs-fixer` attempt to fix problems for you, omit the `--dry-run`
flag:
```console
$ ./vendor/bin/php-cs-fixer fix . -v --diff --config-file=.php_cs
```
If you allow php-cs-fixer to fix CS issues, please re-run the tests to ensure
they pass, and make sure you add and commit the changes after verification.
## Recommended Workflow for Contributions
Your first step is to establish a public repository from which we can
pull your work into the master repository. We recommend using
[GitHub](https://github.com), as that is where the component is already hosted.
1. Setup a [GitHub account](http://github.com/), if you haven't yet
2. Fork the repository (http://github.com/zendframework/zend-soap)
3. Clone the canonical repository locally and enter it.
```console
$ git clone git://github.com:zendframework/zend-soap.git
$ cd zend-soap
```
4. Add a remote to your fork; substitute your GitHub username in the command
below.
```console
$ git remote add {username} git@github.com:{username}/zend-soap.git
$ git fetch {username}
```
### Keeping Up-to-Date
Periodically, you should update your fork or personal repository to
match the canonical ZF repository. Assuming you have setup your local repository
per the instructions above, you can do the following:
```console
$ git checkout master
$ git fetch origin
$ git rebase origin/master
# OPTIONALLY, to keep your remote up-to-date -
$ git push {username} master:master
```
If you're tracking other branches -- for example, the "develop" branch, where
new feature development occurs -- you'll want to do the same operations for that
branch; simply substitute "develop" for "master".
### Working on a patch
We recommend you do each new feature or bugfix in a new branch. This simplifies
the task of code review as well as the task of merging your changes into the
canonical repository.
A typical workflow will then consist of the following:
1. Create a new local branch based off either your master or develop branch.
2. Switch to your new local branch. (This step can be combined with the
previous step with the use of `git checkout -b`.)
3. Do some work, commit, repeat as necessary.
4. Push the local branch to your remote repository.
5. Send a pull request.
The mechanics of this process are actually quite trivial. Below, we will
create a branch for fixing an issue in the tracker.
```console
$ git checkout -b hotfix/9295
Switched to a new branch 'hotfix/9295'
```
... do some work ...
```console
$ git commit
```
... write your log message ...
```console
$ git push {username} hotfix/9295:hotfix/9295
Counting objects: 38, done.
Delta compression using up to 2 threads.
Compression objects: 100% (18/18), done.
Writing objects: 100% (20/20), 8.19KiB, done.
Total 20 (delta 12), reused 0 (delta 0)
To ssh://git@github.com/{username}/zend-soap.git
b5583aa..4f51698 HEAD -> master
```
To send a pull request, you have two options.
If using GitHub, you can do the pull request from there. Navigate to
your repository, select the branch you just created, and then select the
"Pull Request" button in the upper right. Select the user/organization
"zendframework" as the recipient.
If using your own repository - or even if using GitHub - you can use `git
format-patch` to create a patchset for us to apply; in fact, this is
**recommended** for security-related patches. If you use `format-patch`, please
send the patches as attachments to:
- zf-devteam@zend.com for patches without security implications
- zf-security@zend.com for security patches
#### What branch to issue the pull request against?
Which branch should you issue a pull request against?
- For fixes against the stable release, issue the pull request against the
"master" branch.
- For new features, or fixes that introduce new elements to the public API (such
as new public methods or properties), issue the pull request against the
"develop" branch.
### Branch Cleanup
As you might imagine, if you are a frequent contributor, you'll start to
get a ton of branches both locally and on your remote.
Once you know that your changes have been accepted to the master
repository, we suggest doing some cleanup of these branches.
- Local branch cleanup
```console
$ git branch -d <branchname>
```
- Remote branch removal
```console
$ git push {username} :<branchname>
```
## Conduct
Please see our [CONDUCT.md](CONDUCT.md) to understand expected behavior when interacting with others in the project.

View File

@@ -0,0 +1,28 @@
Copyright (c) 2005-2015, Zend Technologies USA, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of Zend Technologies USA, Inc. nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,11 @@
# zend-soap
[![Build Status](https://secure.travis-ci.org/zendframework/zend-soap.svg?branch=master)](https://secure.travis-ci.org/zendframework/zend-soap)
[![Coverage Status](https://coveralls.io/repos/zendframework/zend-soap/badge.svg?branch=master)](https://coveralls.io/r/zendframework/zend-soap?branch=master)
`Zend\Soap` is a component to manage the [SOAP](http://en.wikipedia.org/wiki/SOAP)
protocol in order to design client or server PHP application.
- File issues at https://github.com/zendframework/zend-soap/issues
- Documentation is at https://zendframework.github.io/zend-soap/

View File

@@ -0,0 +1,58 @@
{
"name": "zendframework/zend-soap",
"description": " ",
"license": "BSD-3-Clause",
"keywords": [
"zf2",
"soap"
],
"homepage": "https://github.com/zendframework/zend-soap",
"autoload": {
"psr-4": {
"Zend\\Soap\\": "src/"
}
},
"require": {
"php": "^5.5 || ^7.0",
"zendframework/zend-server": "^2.6.1",
"zendframework/zend-stdlib": "^2.7 || ^3.0",
"zendframework/zend-uri": "^2.5.2"
},
"require-dev": {
"zendframework/zend-config": "^2.6",
"zendframework/zend-http": "^2.5.4",
"phpunit/PHPUnit": "^4.8",
"squizlabs/php_codesniffer": "^2.3.1"
},
"suggest": {
"zendframework/zend-http": "Zend\\Http component"
},
"minimum-stability": "dev",
"prefer-stable": true,
"extra": {
"branch-alias": {
"dev-master": "2.6-dev",
"dev-develop": "2.7-dev"
}
},
"autoload-dev": {
"psr-4": {
"ZendTest\\Soap\\": "test/"
},
"files": [
"test/TestAsset/commontypes.php",
"test/TestAsset/call_user_func.php"
]
},
"scripts": {
"check": [
"@cs-check",
"@test"
],
"upload-coverage": "coveralls -v",
"cs-check": "phpcs",
"cs-fix": "phpcbf",
"test": "phpunit",
"test-coverage": "phpunit --coverage-clover clover.xml"
}
}

View File

@@ -0,0 +1,224 @@
# AutoDiscovery
SOAP functionality implemented within this component is intended to make all
steps required for SOAP communications more simple. SOAP is a language
independent protocol, however, which means it may be used for more than just
PHP-to-PHP communications, adding some complexity to its implementation.
There are three configurations for SOAP applications supported by zend-soap:
- SOAP server PHP application &lt;---&gt; SOAP client PHP application
- SOAP server non-PHP application &lt;---&gt; SOAP client PHP application
- SOAP server PHP application &lt;---&gt; SOAP client non-PHP application
In each situation, the SOAP server must expose the functionality it provides so
the client knows how to interact with it. This is done via a
[WSDL](http://www.w3.org/TR/wsdl) (Web Services Description Language) document.
The WSDL language is quite complex, making preparation of WSDL documents
difficult; this task is complicated when the API for your service changes, as
any changes then need to be synced back to the WSDL.
These problems may be solved via WSDL autodiscovery, which zend-soap provides
via its `Zend\Soap\AutoDiscover` class.
Autodiscovery in zend-soap follows the same patterns as you use for creating a
zend-soap `Server`, but uses the classes and functions attached to it to extract
the information required to generate a WSDL document.
As a refresher, zend-soap allows using either of the following to define a
server:
- PHP classes.
- PHP functions.
Each are also supported by the autodiscovery functionality. Additionally,
`AutoDiscover` supports datatype mappins from PHP to [XSD types](http://www.w3.org/TR/xmlschema-2/).
The following is a basic example demonstrating the autodiscovery functionality.
It uses similar functionality as when using [Zend\Soap\Server](server.md), but
instead of using `handle()` to handle an incoming SOAP request, it provides a
`generate()` method, which returns a [Zend\Soap\Wsdl](wsdl.md) instance. This
can then be used to return an XML representation to the client.
```php
class MySoapServerClass
{
/* ... */
}
$autodiscover = new Zend\Soap\AutoDiscover();
$autodiscover
->setClass('MySoapServerClass')
->setUri('http://localhost/server.php')
->setServiceName('MySoapService');
$wsdl = $autodiscover->generate();
// Emit the XML:
echo $wsdl->toXml();
// Or dump it to a file; this is a good way to cache the WSDL
$wsdl->dump("/path/to/file.wsdl");
// Or create a DOMDocument, which you can then manipulate:
$dom = $wsdl->toDomDocument();
```
> ### AutoDiscover !== Server
>
> `AutoDiscover` *is not a `Server` instance*; it cannot and does not act as a
> SOAP server on its own, but instead provides the WSDL used by clients that
> will interact with your SOAP server.
>
> SOAP interactions are always performed over HTTP POST requests, while
> retrieval of WSDL is performed using HTTP GET. As such, you *can* server both
> from the same script, provided you detect the incoming method and respond
> accordingly:
>
> ```php
> if ($_SERVER['REQUEST_METHOD'] == 'GET') {
> if (! isset($_GET['wsdl'])) {
> header('HTTP/1.1 400 Client Error');
> return;
> }
>
> $autodiscover = new Zend\Soap\AutoDiscover();
> $autodiscover->setClass('HelloWorldService')
> ->setUri('http://example.com/soap.php');
> header('Content-Type: application/wsdl+xml');
> echo $autodiscover->toXml();
> return;
> }
>
> if ($_SERVER['REQUEST_METHOD'] != 'POST') {
> header('HTTP/1.1 400 Client Error');
> return;
> }
>
> // pointing to the current file here
> $soap = new Zend\Soap\Server("http://example.com/soap.php?wsdl");
> $soap->setClass('HelloWorldService');
> $soap->handle();
> ```
## Class autodiscovery
If a class is used to provide SOAP server functionality, then the same class
should be provided to `Zend\Soap\AutoDiscover` for WSDL generation:
```php
$autodiscover = new Zend\Soap\AutoDiscover();
$autodiscover
->setClass('My_SoapServer_Class')
->setUri('http://localhost/server.php')
->setServiceName('MySoapService');
$wsdl = $autodiscover->generate();
```
The following rules are used during WSDL generation:
- The generated WSDL describes an RPC/Encoded style web service. If you want to
describe a document/literal server, use the `setBindingStyle()` and
`setOperationBodyStyle()` methods.
- The PHP class name is used as the web service name unless `setServiceName()`
is used explicitly to set the name. When only functions are used, the service
name has to be set explicitly or an exception will be thrown during WSDL
document generation.
- You can set the endpoint of the actual SOAP Server via the `setUri()` method.
This is a required option, and also used as the target namespace for all
service related names (including described complex types).
Complex types are generated using the following rules:
- Class methods are joined into one [Port Type](http://www.w3.org/TR/wsdl#_porttypes),
with port names using the format `<$serviceName>Port`.
- Each class method/function is registered as a corresponding port operation.
- Only the "longest" available method prototype is used for WSDL generation.
- WSDL autodiscovery utilizes PHP docblocks provided by the developer to determine the
parameter and return types. In fact, for scalar types, this is the only way to
determine the parameter types, and for return types, this is the only way to
determine them. This means that *providing correct and fully detailed
docblocks is not only best practice, but required for autodiscovery*.
## Function autodiscovery
If a set of functions are used to provide your SOAP server functionality, then
the same set should be provided to `Zend\Soap\AutoDiscovery` for WSDL
generation:
```php
$autodiscover = new Zend\Soap\AutoDiscover();
$autodiscover->addFunction('function1');
$autodiscover->addFunction('function2');
$autodiscover->addFunction('function3');
$wsdl = $autodiscover->generate();
```
The same rules apply to generation as described in the class autodiscovery section above.
## Autodiscovering Datatypes
Input/output datatypes are converted into network service types using the
following mapping:
- PHP strings &lt;-&gt; `xsd:string`.
- PHP integers &lt;-&gt; `xsd:int`.
- PHP floats and doubles &lt;-&gt; `xsd:float`.
- PHP booleans &lt;-&gt; `xsd:boolean`.
- PHP arrays &lt;-&gt; `soap-enc:Array`.
- PHP object &lt;-&gt; `xsd:struct`.
- PHP class &lt;-&gt; based on complex type strategy (See the [WSDL section on adding complex types](wsdl.md#adding-complex-type-information)).
- type\[\] or object\[\] (ie. int\[\]) &lt;-&gt; based on complex type strategy
- PHP void &lt;-&gt; empty type.
- If type is not matched to any of these types by some reason, then `xsd:anyType` is used.
Where:
- `xsd:` refers to the [http://www.w3.org/2001/XMLSchema](http://www.w3.org/2001/XMLSchema)
namespace
- `soap-enc:` refers to the [http://schemas.xmlsoap.org/soap/encoding/](http://schemas.xmlsoap.org/soap/encoding/)
namespace
- `tns:` is the "target namespace" for the service.
> ### Complex type discovery
>
> `Zend\Soap\AutoDiscover` will be created with the
> `Zend\Soap\Wsdl\ComplexTypeStrategy\DefaultComplexType` class as its detection
> algorithm for complex types. The first parameter of the `AutoDiscover`
> constructor takes any complex type strategy implementing
> `Zend\Soap\Wsdl\ComplexTypeStrategy\ComplexTypeStrategyInterface` (or a string
> class name of an implementation). name of the class. See the
> [Zend\Soap\Wsdl documentation on adding complex types](wsdl.md#adding-complex-type-information)
> for more information.
## WSDL Binding Styles
WSDL offers different transport mechanisms and styles. This affects the
`soap:binding` and `soap:body` tags within the `Binding` section of the WSDL
document. Different clients have different requirements as to what options
really work. Therefore you can set the styles before you call either the
`setClass()` or `addFunction()` method on the `AutoDiscover` class.
```php
$autodiscover = new Zend\Soap\AutoDiscover();
// Defaults are
// - 'use' => 'encoded'
// - 'encodingStyle' => 'http://schemas.xmlsoap.org/soap/encoding/'
$autodiscover->setOperationBodyStyle([
'use' => 'literal',
'namespace' => 'http://framework.zend.com',
]);
// Defaults are:
// - 'style' => 'rpc'
// - 'transport' => 'http://schemas.xmlsoap.org/soap/http'
$autodiscover->setBindingStyle([
'style' => 'document',
'transport' => 'http://framework.zend.com',
]);
$autodiscover->addFunction('myfunc1');
$wsdl = $autodiscover->generate();
```

View File

@@ -0,0 +1,132 @@
# Zend\\Soap\\Client
The `Zend\Soap\Client` class simplifies SOAP client development for PHP
programmers, and may be used in either WSDL or non-WSDL mode.
Under WSDL mode, `Zend\Soap\Client` uses a WSDL document to define transport
layer options.
The WSDL description is usually provided by the web service the client will
access. If the WSDL description is not made available, you may want to use
`Zend\Soap\Client` in non-WSDL mode. Under this mode, all SOAP protocol options
have to be set explicitly on the `Zend\Soap\Client` class.
## Instantiation
The `Zend\Soap\Client` constructor takes two parameters:
- `$wsdl` - the URI of a WSDL file.
- `$options` - options for modifying the behavior of the client instance.
Both of these parameters may be set later using the `setWsdl($wsdl)` and
`setOptions($options)` methods respectively.
> ### Non-WSDL mode requirements
>
> If you use `Zend\Soap\Client` component in non-WSDL mode, you **must** set the
> 'location' and 'uri' options.
The following options are recognized:
- `soap_version` (`soapVersion`) - soap version to use (`SOAP_1_1` or
`SOAP_1_2`).
- `classmap` (`classMap`) - maps WSDL types to PHP classes; option must be an
array where keys are the WSDL types, and values are the PHP class to which
to map.
- `encoding` - internal character encoding (UTF-8 is always used as an external
encoding).
- `wsdl` - specifying this option sets the client in WSDL mode. Can be set
after-the-fact using `setWsdl($wsdl)`.
- `uri` - target namespace for the SOAP service (required for non-WSDL-mode;
no-op when in WSDL mode).
- `location` - the URL to request (required for non-WSDL-mode; no-op when in
WSDL mode).
- `style` - request style (non-WSDL mode only); one of `SOAP_RPC` or
`SOAP_DOCUMENT`.
- `use` - method to use when encoding messages (non-WSDL mode only);
either `SOAP_ENCODED` or `SOAP_LITERAL`.
- `login` and `password` - login and password for HTTP authentication.
- `proxy_host`, `proxy_port`, `proxy_login`, and `proxy_password` - use when
specifying a service behind a proxy server.
- `local_cert` and `passphrase` - HTTPS client certificate authentication
options.
- `compression` - compression options; combination of
`SOAP_COMPRESSION_ACCEPT`, `SOAP_COMPRESSION_GZIP` and/or
`SOAP_COMPRESSION_DEFLATE` options.
The following demonstrate usage of compression options:
```php
// Accept response compression
$client = new Zend\Soap\Client(
'some.wsdl',
['compression' => SOAP_COMPRESSION_ACCEPT]
);
// Compress requests using gzip with compression level 5
$client = new Zend\Soap\Client(
'some.wsdl',
['compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP | 5]
);
// Compress requests using deflate compression
$client = new Zend\Soap\Client(
"some.wsdl",
['compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_DEFLATE]
);
```
## Performing SOAP Requests
After we've created a `Zend\Soap\Client` instance, we can perform SOAP requests.
Each web service method is mapped to a virtual `Zend\Soap\Client` instance
method which takes parameters with common PHP types.
As an example, given the following server:
```php
class MyClass
{
/**
* This method takes ...
*
* @param integer $inputParam
* @return string
*/
public function method1($inputParam)
{
/* ... */
}
/**
* This method takes ...
*
* @param integer $inputParam1
* @param string $inputParam2
* @return float
*/
public function method2($inputParam1, $inputParam2)
{
/* ... */
}
/* ... */
}
$server = new Zend\Soap\Server(null, $options);
$server->setClass('MyClass');
$server->handle();
```
We can write a client as follows:
```php
$client = new Zend\Soap\Client("MyService.wsdl");
// $result1 is a string
$result1 = $client->method1(10);
// $result2 is a float
$result2 = $client->method2(22, 'some string');
```

View File

@@ -0,0 +1,10 @@
<div class="container">
<div class="jumbotron">
<h1>zend-soap</h1>
<p>Create, serve, and access SOAP applications, and parse and generate WSDL.</p>
<pre><code class="language-bash">$ composer require zendframework/zend-soap</code></pre>
</div>
</div>

View File

@@ -0,0 +1,11 @@
# zend-soap
[![Build Status](https://secure.travis-ci.org/zendframework/zend-soap.svg?branch=master)](https://secure.travis-ci.org/zendframework/zend-soap)
[![Coverage Status](https://coveralls.io/repos/zendframework/zend-soap/badge.svg?branch=master)](https://coveralls.io/r/zendframework/zend-soap?branch=master)
`Zend\Soap` is a component to manage the [SOAP](http://en.wikipedia.org/wiki/SOAP)
protocol in order to design client or server PHP application.
- File issues at https://github.com/zendframework/zend-soap/issues
- Documentation is at https://zendframework.github.io/zend-soap/

View File

@@ -0,0 +1,261 @@
# Zend\\Soap\\Server
`Zend\Soap\Server` provides a wrapper around PHP's
[SoapServer](http://php.net/SoapServer) implementation with convenience
functionality for generating WSDL and registering internal handlers.
It may be used in WSDL or non-WSDL mode, and can map functionality to either PHP
classes or functions in order to define your web service API.
When in WSDL mode, it uses a prepared WSDL document to define server object
behavior and transport layer options.
WSDL documents may be auto-generated with functionality provided by the
[Zend\Soap\AutoDiscover](auto-discovery.md) component, or constructed manually
using the [Zend\Soap\Wsdl](wsdl.md) class or any other XML generation tool.
If the non-WSDL mode is used, then all protocol options must be provided via the
options mechanism.
## Zend\Soap\Server instantiation
Instantiation of `Server` instances varies based on whether or not you are using
WSDL mode.
### Instantiation for WSDL mode
When in WSDL mode, the constructor expects two optional paramters:
- `$wsdl`: the URI of a WSDL file. This may be set after-the-fact using
`$server->setWsdl($wsdl)`.
- `$options`: options to use when creating the instance. These may be set later
using `$server->setOptions($options)`.
The following options are recognized in the WSDL mode:
- `soap_version` (`soapVersion`) - soap version to use (`SOAP_1_1` or `SOAP_1_2`).
- `actor` - the actor URI for the server.
- `classmap` (`classMap`) which can be used to map some WSDL types to PHP
classes. The option must be an array with WSDL types as keys, and names of PHP
classes as values.
- `encoding` - internal character encoding (UTF-8 is always used as an external encoding).
- `wsdl` - equivalent to calling `setWsdl($wsdlValue)`.
### Instantiation for non-WSDL mode
The first constructor parameter **must** be set to `NULL` if you plan to use
`Zend\Soap\Server` functionality in non-WSDL mode.
You also have to set the `uri` option in this case (see below).
The second constructor parameter, `$options`, is an array of options for
configuring the behavior of the server; these may also be provided later using
`$server->setOptions($options)`. Options recognized in non-WSDL mode include:
- `soap_version` (`soapVersion`) - soap version to use (`SOAP_1_1` or `SOAP_1_2`).
- `actor` - the actor URI for the server.
- `classmap` (`classMap`) - an associative array used to map WSDL types to PHP
classes. The option must be an associative array using WSDL types as the
keys, and PHP class names as values.
- `encoding` - internal character encoding (UTF-8 is always used as an external
encoding).
- `uri` (required) - URI namespace for SOAP server.
## Defining your SOAP API
There are two ways to define your SOAP API in order to expose PHP functionality.
The first one is to attach a class to the `Zend\Soap\Server` object that
completely describes your API:
```php
class MyClass
{
/**
* This method takes ...
*
* @param integer $inputParam
* @return string
*/
public function method1($inputParam)
{
// ...
}
/**
* This method takes ...
*
* @param integer $inputParam1
* @param string $inputParam2
* @return float
*/
public function method2($inputParam1, $inputParam2)
{
// ...
}
/* ... */
}
$server = new Zend\Soap\Server(null, $options);
// Bind class to Soap Server:
$server->setClass(MyClass::class);
// Or bind an instance:
$server->setObject(new MyClass());
// Handle a request:
$server->handle();
```
> ### Docblocks are required
>
> You should completely describe each method using a method docblock if you plan
> to use autodiscover functionality to prepare your WSDL.
The second method for defining your API is to use one or more functions, passing
them to one or more of the `addFunction()` or `loadFunctions()` methods:
```php
/**
* This function ...
*
* @param integer $inputParam
* @return string
*/
function function1($inputParam)
{
// ...
}
/**
* This function ...
*
* @param integer $inputParam1
* @param string $inputParam2
* @return float
*/
function function2($inputParam1, $inputParam2)
{
// ...
}
$server = new Zend\Soap\Server(null, $options);
$server->addFunction('function1');
$server->addFunction('function2');
$server->handle();
```
## Request and response handling
`Zend\Soap\Server` component performs request/response processing automatically,
but allows you to intercept each in order to perform pre- or post-processing.
### Request pre- and post-processing
The `Zend\Soap\Server::handle()` method handles a request from the standard
input stream ('php://input'). It may be overridden either by supplying a request
instance to the `handle()` method, or by setting the request via the
`setRequest()` method:
```php
$server = new Zend\Soap\Server(/* ... */);
// Set request using optional $request parameter to the handle() method:
$server->handle($request);
// Set request using setRequest() method:
$server->setRequest();
$server->handle();
```
A request object may be represented using any of the following, and handled as
follows:
- `DOMDocument` (casts to XML)
- `DOMNode` (owner document is retrieved and cast to XML)
- `SimpleXMLElement` (casts to XML)
- `stdClass` (`__toString()` is called and verified to be valid XML)
- `string` (verified to be valid XML)
The last request processed may be retrieved using the `getLastRequest()` method,
which returns the XML string:
```php
$server = new Zend\Soap\Server(/* ... */);
$server->handle();
$request = $server->getLastRequest();
```
### Response post-processing
The `Zend\Soap\Server::handle()` method automatically emits the generated
response to the output stream. It may be blocked using `setReturnResponse()`
with `true` or `false` as a parameter. When set to `true`, `handle()` will
return the generated response instead of emitting it.
The returned response will be either an XML string representing the response, or
a `SoapFault` exception instance.
> #### Do not return SoapFaults
>
> SoapFault instances, when cast to a string, will contain the full exception
> stack trace. For security purposes, you do not want to return that
> information. As such, check your return type before emitting the response
> manually.
```php
$server = new Zend\Soap\Server(/* ... */);
// Get a response as a return value of handle(),
// instead of emitting it to standard output:
$server->setReturnResponse(true);
$response = $server->handle();
if ($response instanceof SoapFault) {
/* ... */
} else {
/* ... */
}
```
The last response emitted may also be retrieved for post-processing using
`getLastResponse()`:
```php
$server = new Zend\Soap\Server(/* ... */);
$server->handle();
$response = $server->getLastResponse();
if ($response instanceof SoapFault) {
/* ... */
} else {
/* ... */
}
```
## Document/Literal WSDL Handling
The document/literal binding-style/encoding pattern is used to make SOAP
messages as human-readable as possible and allow abstraction between very
incompatible languages. The .NET framework uses this pattern for SOAP service
generation by default. The central concept of this approach to SOAP is the
introduction of a Request and an Response object for every function/method of
the SOAP service. The parameters of the function are properties on the request
object, and the response object contains a single parameter that is built in the
style `<methodName>Result`
zend-soap supports this pattern in both the AutoDiscover and Server
components. You can write your service object without knowledge of the pattern.
Use docblock comments to hint the parameter and return types as usual. The
`Zend\Soap\Server\DocumentLiteralWrapper` wraps around your service object and
converts request and response into normal method calls on your service.
See the class doc block of the `DocumentLiteralWrapper` for a detailed example
and discussion.

View File

@@ -0,0 +1,312 @@
# WSDL Parsing and Generation
The `Zend\Soap\Wsdl` class is used by `Zend\Soap\Server` internally to operate
with WSDL documents. In most cases, you will not interact with it directly.
Nevertheless, you could also use functionality provided by this class for your
own needs. `Zend\Soap\Wsdl` contains both a parser and a generator for WSDL
documents.
## Instantiation
The `Zend\Soap\Wsdl` constructor takes three parameters:
- `$name` - name of the web service being described.
- `$uri` - URI where the WSDL will be available (could also be a reference to
the file in the filesystem.)
- `$strategy` - optional flag used to identify the strategy for complex types
(objects) detection. To read more on complex type detection strategies go to
the section on [adding complex types](#adding-complex-type-information).
- `$classMap` - Optional array of class name translations from PHP Type (key) to
WSDL type (value).
## addMessage() method
The `addMessage($name, $parts)` method adds a new message description to the
WSDL document (`/definitions/message` element).
Each message corresponds to methods in terms of `Zend\Soap\Server` and
`Zend\Soap\Client` functionality.
The `$name` parameter represents the message name.
The `$parts` parameter is an array of message parts which describes SOAP call
parameters, represented as an associative array of 'part name' (SOAP call
parameter name) =&gt; 'part type' pairs.
Type mapping management is performed using one of the `addTypes()` and
`addComplexType()` methods (see below).
> ### Message Typing
>
> Messages parts can use either the `element` or `type` attribute for typing (see
> [the W3C WSDL specification](http://www.w3.org/TR/wsdl#_messages)).
>
> The `element` attribute must refer to a corresponding element in the data type
> definition. A `type` attribute refers to a corresponding complexType entry.
>
> All standard XSD types have both `element` and `complexType` definitions (see
> the [SOAP encoding specification](http://schemas.xmlsoap.org/soap/encoding/)
> for details).
>
> All non-standard types, which may be added using the
> `Zend\Soap\Wsdl::addComplexType()` method, are described using the
> `complexType` node of the `/definitions/types/schema/` section of the WSDL
> document.
>
> The `addMessage()` method always uses the `type` attribute to describe types.
## addPortType() method
The `addPortType($name)` method adds a new port type to the WSDL document
(`/definitions/portType`) with the specified port type name.
In terms of the `Zend\Soap\Server` implementation, it joins a set of web service
methods into a single operation.
See [the W3C portTypes documentation](http://www.w3.org/TR/wsdl#_porttypes) for
more details.
## addPortOperation() method
The `addPortOperation($portType, $name, $input = false, $output = false, $fault
= false)` method adds new port operation to the specified port type of the WSDL
document (`/definitions/portType/operation`).
In terms of the `Zend\Soap\Server` implementation, Each port operation
corresponds to a class method (if the web service is based on a class) or
function (if the web service is based on a set of methods).
It also adds corresponding port operation messages depending on the specified
`$input`, `$output` and `$fault` parameters.
> ### Generated messages
>
> `Zend\Soap\Server` generates two messages for each port operation when
> describing operations it provides:
>
> - input message with name `<$methodName>Request`.
> - output message with name `<$methodName>Response`.
See the [W3C WSDL request/response documentation](http://www.w3.org/TR/wsdl#_request-response)
for more details.
## addBinding() method
The `addBinding($name, $portType)` method adds new binding to the WSDL document
(`/definitions/binding`).
A `binding` WSDL document node defines the message format and protocol details
for operations and messages defined by a particular portType (see the [W3C WSDL
binding documentation](http://www.w3.org/TR/wsdl#_bindings)).
The method creates a binding node and returns it; you may then fill the returned
node with data.
`Zend\Soap\Server` uses the name `<$serviceName>Binding` for the 'binding'
element in the WSDL document.
## addBindingOperation() method
The `addBindingOperation($binding, $name, $input = false, $output = false,
$fault = false)` method adds an operation to a binding element
(`/definitions/binding/operation`) with the specified name.
It takes an `XML_Tree_Node` object returned by `addBinding()` as an input
(`$binding` parameter) to add an 'operation' element with input/output/false
entries depending on the specified parameters
The `Zend\Soap\Server` implementation adds a corresponding binding entry for each web service method with
input and output entries, defining the `soap:body` element as `<soap:body use="encoded"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">`.
See the [W3C WSDL bindings documentation](http://www.w3.org/TR/wsdl#_bindings)
for more details.
## addSoapBinding() method
The `addSoapBinding($binding, $style = 'document', $transport =
'http://schemas.xmlsoap.org/soap/http')` method adds a SOAP binding
(`soap:binding`) entry to the binding element (which is already linked to some
port type) using the specified style and transport (the `Zend\Soap\Server`
implementation uses the RPC style over HTTP).
A `/definitions/binding/soap:binding` element is used to signify that the
binding is bound to the SOAP protocol format.
See the [W3C bindings documentation](http://www.w3.org/TR/wsdl#_bindings) for
more details.
## addSoapOperation() method
The `addSoapOperation($binding, $soap_action)` method adds a SOAP operation
(`soap:operation`) entry to the binding element with the specified action. The
`style` attribute of the `soap:operation` element is not used since the
programming model (RPC-oriented or document-oriented) may be using the
`addSoapBinding()` method already.
The `soapAction` attribute of `/definitions/binding/soap:operation` element
specifies the value of the SOAP action header for this operation. This attribute
is required for SOAP over HTTP and **must not** be specified for other
transports.
The `Zend\Soap\Server` implementation uses the format
`<$serviceUri>#<$methodName>` for the SOAP operation action name.
See the [W3C soap:operation documentation](http://www.w3.org/TR/wsdl#_soap:operation)
for more details.
## addService() method
The `addService($name, $port_name, $binding, $location)` method adds a
`/definitions/service` element to the WSDL document with the specified service
name, port name, binding, and location.
WSDL 1.1 allows several port types (sets of operations) per service; however,
zend-soap does not support this ability.
The `Zend\Soap\Server` implementation uses:
- `<$name>Service` as the service name.
- `<$name>Port` as the port type name.
- `tns:<$name>Binding` [1] as the binding name. (`tns:namespace` is defined as
the script URI; generally this is `'http://' . $_SERVER['HTTP_HOST'] .
$_SERVER['SCRIPT_NAME']`)
- the script URI (`'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME']`)
as the service URI for the service definition.
where `$name` is either:
- a class name, for servers representing a PHP class,
- a script name, for servers representing a collection of PHP functions.
See the [W3C WSDL services documentation](http://www.w3.org/TR/wsdl#_services)
for more details.
## Type mapping
The zend-soap WSDL implementation uses the following type mappings between PHP
and SOAP types:
- PHP strings &lt;-&gt; `xsd:string`.
- PHP integers &lt;-&gt; `xsd:int`.
- PHP floats and doubles &lt;-&gt; `xsd:float`.
- PHP booleans &lt;-&gt; `xsd:boolean`.
- PHP arrays &lt;-&gt; `soap-enc:Array`.
- PHP object &lt;-&gt; `xsd:struct`.
- PHP class &lt;-&gt; based on complex type strategy (See
[the section on adding complex types](#adding-complex-type-information)).
- PHP void &lt;-&gt; empty type.
- If a type is not matched to any of the above, then `xsd:anyType` is used.
Where:
- `xsd:` refers to the [http://www.w3.org/2001/XMLSchema](http://www.w3.org/2001/XMLSchema) namespace
- `soap-enc:` refers to the [http://schemas.xmlsoap.org/soap/encoding/](http://schemas.xmlsoap.org/soap/encoding/)
namespace
- `tns:` is the "target namespace" for the service.
> ### Complex types
>
> By default, `Zend\Soap\Wsdl` will be created with the
> `Zend\Soap\Wsdl\ComplexTypeStrategy\DefaultComplexType` class as its detection
> algorithm for complex types. The first parameter of the `AutoDiscover`
> constructor takes any complex type strategy implementing
> `Zend\Soap\Wsdl\ComplexTypeStrategy\ComplexTypeStrategyInterface`, or a string
> class name of a class implementing the interface. For backwards compatibility
> with the `$extractComplexType` setting, boolean variables are parsed the
> following way:
>
> - If `TRUE`, `Zend\Soap\Wsdl\ComplexTypeStrategy\DefaultComplexType` is used.
> - If `FALSE`, `Zend\Soap\Wsdl\ComplexTypeStrategy\AnyType` is used.
### Retrieving type information
The `getType($type)` method may be used to retrieve the mapping for a specified
PHP type:
```php
$wsdl = new Zend\Soap\Wsdl('My_Web_Service', $myWebServiceUri);
$soapIntType = $wsdl->getType('int');
class MyClass
{
/* ... */
}
$soapMyClassType = $wsdl->getType('MyClass');
```
### Adding complex type information
The `addComplexType($type)` method is used to add complex types (PHP classes) to
a WSDL document.
The method is automatically used by the `getType()` method to add corresponding
complex types of method parameters or return types.
The detection and generation algorithm it uses is based on the currently active
detection strategy for complex types. You can set the detection strategy either
by specifying the class name as a string or providing an instance of a
`Zend\Soap\Wsdl\ComplexTypeStrategy` implementation as the third parameter to
the constructor, or by calling the `setComplexTypeStrategy($strategy)` function
of `Zend\Soap\Wsdl`.
The following detection strategies currently exist:
- `Zend\Soap\Wsdl\ComplexTypeStrategy\DefaultComplexType`: Enabled by default
(when no third constructor parameter is set). Iterates over the public
attributes of a class type and registers them as subtypes of the complex
object type.
- `Zend\Soap\Wsdl\ComplexTypeStrategy\AnyType`: Casts all complex types into the
simple XSD type `xsd:anyType`. Warning: this shortcut for complex type
detection can probably only be handled successfully by weakly typed languages
such as PHP.
- `Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence`: This strategy allows
specifying arrays of the given type, which can be any PHP scalar type (`int`,
`string`, `bool`, `float`), as well as objects or arrays of objects.
- `Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeComplex`: This strategy allows
detecting very complex arrays of objects. Objects types are detected based on
the `Zend\Soap\Wsdl\Strategy\DefaultComplexType`, and an array is wrapped
around that definition.
- `Zend\Soap\Wsdl\ComplexTypeStrategy\Composite`: This strategy can combine all
strategies by connecting PHP complex types (classes/objects) to the desired
strategy via the `connectTypeToStrategy($type, $strategy)` method. A complete
typemap can be given to the constructor as an array with `$type` / `$strategy`
pairs. The second parameter specifies the default strategy that will be used
if an unknown type is requested for adding, and defaults to the
`Zend\Soap\Wsdl\Strategy\DefaultComplexType` strategy.
The `addComplexType()` method creates a
`/definitions/types/xsd:schema/xsd:complexType` element for each described
complex type, using the specified PHP class name.
Class properties **MUST** have a docblock section with the described PHP type in
order to be included in the WSDL description.
`addComplexType()` checks if the type is already described within types section
of the WSDL document, and prevents duplication of types. Additionally, it has
recursion detection.
See the [W3C WSDL types documentation](http://www.w3.org/TR/wsdl#_types) for
more details.
## addDocumentation() method
The `addDocumentation($input_node, $documentation)` method adds human readable
documentation using the optional `wsdl:document` element.
The `/definitions/binding/soap:binding` element is used to signify that the
binding is bound to the SOAP protocol format.
See the [W3C WSDL documentation section](http://www.w3.org/TR/wsdl#_documentation)
for more details.
## Retrieve the final WSDL document
Several methods exist for retrieving the full WSDL definition document:
- `toXML()` will generate an XML string.
- `toDomDocument()` will generate a PHP `DOMDocument` instance.
- `dump($filename = false)` will dump the XML to the specified filename, or, if
no filename is provided, return the XML string.

View File

@@ -0,0 +1,13 @@
docs_dir: doc/book
site_dir: doc/html
pages:
- index.md
- Reference:
- Servers: server.md
- Clients: client.md
- "WSDL Parsing and Generation": wsdl.md
- "WSDL AutoDiscovery": auto-discovery.md
site_name: zend-soap
site_description: zend-soap
repo_url: 'https://github.com/zendframework/zend-soap'
copyright: 'Copyright (c) 2016 <a href="http://www.zend.com/">Zend Technologies USA Inc.</a>'

View File

@@ -0,0 +1,23 @@
<?xml version="1.0"?>
<ruleset name="Zend Framework coding standard">
<description>Zend Framework coding standard</description>
<!-- display progress -->
<arg value="p"/>
<arg name="colors"/>
<!-- inherit rules from: -->
<rule ref="PSR2"/>
<rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace">
<properties>
<property name="ignoreBlankLines" value="false"/>
</properties>
</rule>
<!-- Paths to check -->
<file>src</file>
<file>test</file>
<exclude-pattern>test/_files/*</exclude-pattern>
<exclude-pattern>test/TestAsset/*</exclude-pattern>
</ruleset>

View File

@@ -0,0 +1,621 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap;
use Zend\Server\Reflection;
use Zend\Soap\AutoDiscover\DiscoveryStrategy\DiscoveryStrategyInterface as DiscoveryStrategy;
use Zend\Soap\AutoDiscover\DiscoveryStrategy\ReflectionDiscovery;
use Zend\Soap\Wsdl\ComplexTypeStrategy\ComplexTypeStrategyInterface as ComplexTypeStrategy;
use Zend\Uri;
class AutoDiscover
{
/**
* @var string
*/
protected $serviceName;
/**
* @var Reflection
*/
protected $reflection = null;
/**
* Service function names
* @var array
*/
protected $functions = [];
/**
* Service class name
* @var string
*/
protected $class;
/**
* @var bool
*/
protected $strategy;
/**
* Url where the WSDL file will be available at.
* @var WSDL Uri
*/
protected $uri;
/**
* soap:body operation style options
* @var array
*/
protected $operationBodyStyle = [
'use' => 'encoded',
'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/"
];
/**
* soap:operation style
* @var array
*/
protected $bindingStyle = [
'style' => 'rpc',
'transport' => 'http://schemas.xmlsoap.org/soap/http'
];
/**
* Name of the class to handle the WSDL creation.
* @var string
*/
protected $wsdlClass = 'Zend\Soap\Wsdl';
/**
* Class Map of PHP to WSDL types.
* @var array
*/
protected $classMap = [];
/**
* Discovery strategy for types and other method details.
* @var DiscoveryStrategy
*/
protected $discoveryStrategy;
/**
* Constructor
*
* @param null|ComplexTypeStrategy $strategy
* @param null|string|Uri\Uri $endpointUri
* @param null|string $wsdlClass
* @param null|array $classMap
*/
public function __construct(
ComplexTypeStrategy $strategy = null,
$endpointUri = null,
$wsdlClass = null,
array $classMap = []
) {
$this->reflection = new Reflection();
$this->setDiscoveryStrategy(new ReflectionDiscovery());
if (null !== $strategy) {
$this->setComplexTypeStrategy($strategy);
}
if (null !== $endpointUri) {
$this->setUri($endpointUri);
}
if (null !== $wsdlClass) {
$this->setWsdlClass($wsdlClass);
}
$this->setClassMap($classMap);
}
/**
* Set the discovery strategy for method type and other information.
*
* @param DiscoveryStrategy $discoveryStrategy
* @return self
*/
public function setDiscoveryStrategy(DiscoveryStrategy $discoveryStrategy)
{
$this->discoveryStrategy = $discoveryStrategy;
return $this;
}
/**
* Get the discovery strategy.
*
* @return DiscoveryStrategy
*/
public function getDiscoveryStrategy()
{
return $this->discoveryStrategy;
}
/**
* Get the class map of php to wsdl mappings.
*
* @return array
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* Set the class map of php to wsdl mappings.
*
* @param array $classMap
* @return self
* @throws Exception\InvalidArgumentException
*/
public function setClassMap($classMap)
{
if (!is_array($classMap)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects an array; received "%s"',
__METHOD__,
(is_object($classMap) ? get_class($classMap) : gettype($classMap))
));
}
$this->classMap = $classMap;
return $this;
}
/**
* Set service name
*
* @param string $serviceName
* @return self
* @throws Exception\InvalidArgumentException
*/
public function setServiceName($serviceName)
{
$matches = [];
// first character must be letter or underscore {@see http://www.w3.org/TR/wsdl#_document-n}
$i = preg_match('/^[a-z\_]/ims', $serviceName, $matches);
if ($i != 1) {
throw new Exception\InvalidArgumentException('Service Name must start with letter or _');
}
$this->serviceName = $serviceName;
return $this;
}
/**
* Get service name
*
* @return string
* @throws Exception\RuntimeException
*/
public function getServiceName()
{
if (!$this->serviceName) {
if ($this->class) {
return $this->reflection->reflectClass($this->class)->getShortName();
} else {
throw new Exception\RuntimeException('No service name given. Call AutoDiscover::setServiceName().');
}
}
return $this->serviceName;
}
/**
* Set the location at which the WSDL file will be available.
*
* @param Uri\Uri|string $uri
* @return self
* @throws Exception\InvalidArgumentException
*/
public function setUri($uri)
{
if (!is_string($uri) && !($uri instanceof Uri\Uri)) {
throw new Exception\InvalidArgumentException(
'Argument to \Zend\Soap\AutoDiscover::setUri should be string or \Zend\Uri\Uri instance.'
);
}
$uri = trim($uri);
$uri = htmlspecialchars($uri, ENT_QUOTES, 'UTF-8', false);
if (empty($uri)) {
throw new Exception\InvalidArgumentException('Uri contains invalid characters or is empty');
}
$this->uri = $uri;
return $this;
}
/**
* Return the current Uri that the SOAP WSDL Service will be located at.
*
* @return Uri\Uri
* @throws Exception\RuntimeException
*/
public function getUri()
{
if ($this->uri === null) {
throw new Exception\RuntimeException(
'Missing uri. You have to explicitly configure the Endpoint Uri by calling AutoDiscover::setUri().'
);
}
if (is_string($this->uri)) {
$this->uri = Uri\UriFactory::factory($this->uri);
}
return $this->uri;
}
/**
* Set the name of the WSDL handling class.
*
* @param string $wsdlClass
* @return self
* @throws Exception\InvalidArgumentException
*/
public function setWsdlClass($wsdlClass)
{
if (!is_string($wsdlClass) && !is_subclass_of($wsdlClass, '\Zend\Soap\Wsdl')) {
throw new Exception\InvalidArgumentException(
'No \Zend\Soap\Wsdl subclass given to Zend\Soap\AutoDiscover::setWsdlClass as string.'
);
}
$this->wsdlClass = $wsdlClass;
return $this;
}
/**
* Return the name of the WSDL handling class.
*
* @return string
*/
public function getWsdlClass()
{
return $this->wsdlClass;
}
/**
* Set options for all the binding operations soap:body elements.
*
* By default the options are set to 'use' => 'encoded' and
* 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/".
*
* @param array $operationStyle
* @return self
* @throws Exception\InvalidArgumentException
*/
public function setOperationBodyStyle(array $operationStyle = [])
{
if (!isset($operationStyle['use'])) {
throw new Exception\InvalidArgumentException('Key "use" is required in Operation soap:body style.');
}
$this->operationBodyStyle = $operationStyle;
return $this;
}
/**
* Set Binding soap:binding style.
*
* By default 'style' is 'rpc' and 'transport' is 'http://schemas.xmlsoap.org/soap/http'.
*
* @param array $bindingStyle
* @return self
*/
public function setBindingStyle(array $bindingStyle = [])
{
if (isset($bindingStyle['style'])) {
$this->bindingStyle['style'] = $bindingStyle['style'];
}
if (isset($bindingStyle['transport'])) {
$this->bindingStyle['transport'] = $bindingStyle['transport'];
}
return $this;
}
/**
* Set the strategy that handles functions and classes that are added AFTER this call.
*
* @param ComplexTypeStrategy $strategy
* @return self
*/
public function setComplexTypeStrategy(ComplexTypeStrategy $strategy)
{
$this->strategy = $strategy;
return $this;
}
/**
* Set the Class the SOAP server will use
*
* @param string $class Class Name
* @return self
*/
public function setClass($class)
{
$this->class = $class;
return $this;
}
/**
* Add a Single or Multiple Functions to the WSDL
*
* @param string $function Function Name
* @return self
* @throws Exception\InvalidArgumentException
*/
public function addFunction($function)
{
if (is_array($function)) {
foreach ($function as $row) {
$this->addFunction($row);
}
} elseif (is_string($function)) {
if (function_exists($function)) {
$this->functions[] = $function;
} else {
throw new Exception\InvalidArgumentException(
'Argument to Zend\Soap\AutoDiscover::addFunction should be a valid function name.'
);
}
} else {
throw new Exception\InvalidArgumentException(
'Argument to Zend\Soap\AutoDiscover::addFunction should be string or array of strings.'
);
}
return $this;
}
/**
* Generate the WSDL for a service class.
*
* @return Wsdl
*/
protected function generateClass()
{
return $this->generateWsdl($this->reflection->reflectClass($this->class)->getMethods());
}
/**
* Generate the WSDL for a set of functions.
*
* @return Wsdl
*/
protected function generateFunctions()
{
$methods = [];
foreach (array_unique($this->functions) as $func) {
$methods[] = $this->reflection->reflectFunction($func);
}
return $this->generateWsdl($methods);
}
/**
* Generate the WSDL for a set of reflection method instances.
*
* @param array $reflectionMethods
* @return Wsdl
*/
protected function generateWsdl(array $reflectionMethods)
{
$uri = $this->getUri();
$serviceName = $this->getServiceName();
$wsdl = new $this->wsdlClass($serviceName, $uri, $this->strategy, $this->classMap);
// The wsdl:types element must precede all other elements (WS-I Basic Profile 1.1 R2023)
$wsdl->addSchemaTypeSection();
$port = $wsdl->addPortType($serviceName . 'Port');
$binding = $wsdl->addBinding($serviceName . 'Binding', Wsdl::TYPES_NS . ':' . $serviceName . 'Port');
$wsdl->addSoapBinding($binding, $this->bindingStyle['style'], $this->bindingStyle['transport']);
$wsdl->addService(
$serviceName . 'Service',
$serviceName . 'Port',
Wsdl::TYPES_NS . ':' . $serviceName . 'Binding',
$uri
);
foreach ($reflectionMethods as $method) {
$this->addFunctionToWsdl($method, $wsdl, $port, $binding);
}
return $wsdl;
}
/**
* Add a function to the WSDL document.
*
* @param $function Reflection\AbstractFunction function to add
* @param $wsdl Wsdl WSDL document
* @param $port \DOMElement wsdl:portType
* @param $binding \DOMElement wsdl:binding
* @throws Exception\InvalidArgumentException
*/
protected function addFunctionToWsdl($function, $wsdl, $port, $binding)
{
$uri = $this->getUri();
// We only support one prototype: the one with the maximum number of arguments
$prototype = null;
$maxNumArgumentsOfPrototype = -1;
foreach ($function->getPrototypes() as $tmpPrototype) {
$numParams = count($tmpPrototype->getParameters());
if ($numParams > $maxNumArgumentsOfPrototype) {
$maxNumArgumentsOfPrototype = $numParams;
$prototype = $tmpPrototype;
}
}
if ($prototype === null) {
throw new Exception\InvalidArgumentException(sprintf(
'No prototypes could be found for the "%s" function',
$function->getName()
));
}
$functionName = $wsdl->translateType($function->getName());
// Add the input message (parameters)
$args = [];
if ($this->bindingStyle['style'] == 'document') {
// Document style: wrap all parameters in a sequence element
$sequence = [];
foreach ($prototype->getParameters() as $param) {
$sequenceElement = [
'name' => $param->getName(),
'type' => $wsdl->getType($this->discoveryStrategy->getFunctionParameterType($param))
];
if ($param->isOptional()) {
$sequenceElement['nillable'] = 'true';
}
$sequence[] = $sequenceElement;
}
$element = [
'name' => $functionName,
'sequence' => $sequence
];
// Add the wrapper element part, which must be named 'parameters'
$args['parameters'] = ['element' => $wsdl->addElement($element)];
} else {
// RPC style: add each parameter as a typed part
foreach ($prototype->getParameters() as $param) {
$args[$param->getName()] = [
'type' => $wsdl->getType($this->discoveryStrategy->getFunctionParameterType($param))
];
}
}
$wsdl->addMessage($functionName . 'In', $args);
$isOneWayMessage = $this->discoveryStrategy->isFunctionOneWay($function, $prototype);
if ($isOneWayMessage == false) {
// Add the output message (return value)
$args = [];
if ($this->bindingStyle['style'] == 'document') {
// Document style: wrap the return value in a sequence element
$sequence = [];
if ($prototype->getReturnType() != "void") {
$sequence[] = [
'name' => $functionName . 'Result',
'type' => $wsdl->getType($this->discoveryStrategy->getFunctionReturnType($function, $prototype))
];
}
$element = [
'name' => $functionName . 'Response',
'sequence' => $sequence
];
// Add the wrapper element part, which must be named 'parameters'
$args['parameters'] = ['element' => $wsdl->addElement($element)];
} elseif ($prototype->getReturnType() != "void") {
// RPC style: add the return value as a typed part
$args['return'] = [
'type' => $wsdl->getType($this->discoveryStrategy->getFunctionReturnType($function, $prototype))
];
}
$wsdl->addMessage($functionName . 'Out', $args);
}
// Add the portType operation
if ($isOneWayMessage == false) {
$portOperation = $wsdl->addPortOperation(
$port,
$functionName,
Wsdl::TYPES_NS . ':' . $functionName . 'In',
Wsdl::TYPES_NS . ':' . $functionName . 'Out'
);
} else {
$portOperation = $wsdl->addPortOperation(
$port,
$functionName,
Wsdl::TYPES_NS . ':' . $functionName . 'In',
false
);
}
$desc = $this->discoveryStrategy->getFunctionDocumentation($function);
if (strlen($desc) > 0) {
$wsdl->addDocumentation($portOperation, $desc);
}
// When using the RPC style, make sure the operation style includes a 'namespace'
// attribute (WS-I Basic Profile 1.1 R2717)
$operationBodyStyle = $this->operationBodyStyle;
if ($this->bindingStyle['style'] == 'rpc' && !isset($operationBodyStyle['namespace'])) {
$operationBodyStyle['namespace'] = '' . $uri;
}
// Add the binding operation
if ($isOneWayMessage == false) {
$operation = $wsdl->addBindingOperation($binding, $functionName, $operationBodyStyle, $operationBodyStyle);
} else {
$operation = $wsdl->addBindingOperation($binding, $functionName, $operationBodyStyle);
}
$wsdl->addSoapOperation($operation, $uri . '#' . $functionName);
}
/**
* Generate the WSDL file from the configured input.
*
* @return Wsdl
* @throws Exception\RuntimeException
*/
public function generate()
{
if ($this->class && $this->functions) {
throw new Exception\RuntimeException('Can either dump functions or a class as a service, not both.');
}
if ($this->class) {
$wsdl = $this->generateClass();
} else {
$wsdl = $this->generateFunctions();
}
return $wsdl;
}
/**
* Proxy to WSDL dump function
*
* @param string $filename
* @return bool
* @throws Exception\RuntimeException
*/
public function dump($filename)
{
return $this->generate()->dump($filename);
}
/**
* Proxy to WSDL toXml() function
*
* @return string
* @throws Exception\RuntimeException
*/
public function toXml()
{
return $this->generate()->toXml();
}
/**
* Handle WSDL document.
*/
public function handle()
{
header('Content-Type: text/xml');
echo $this->toXml();
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\AutoDiscover\DiscoveryStrategy;
use Zend\Server\Reflection\AbstractFunction;
use Zend\Server\Reflection\Prototype;
use Zend\Server\Reflection\ReflectionParameter;
/**
* Describes how types, return values and method details are detected during
* AutoDiscovery of a WSDL.
*/
interface DiscoveryStrategyInterface
{
/**
* Get the function parameters php type.
*
* Default implementation assumes the default param doc-block tag.
*
* @param ReflectionParameter $param
* @return string
*/
public function getFunctionParameterType(ReflectionParameter $param);
/**
* Get the functions return php type.
*
* Default implementation assumes the value of the return doc-block tag.
*
* @param AbstractFunction $function
* @param Prototype $prototype
* @return string
*/
public function getFunctionReturnType(AbstractFunction $function, Prototype $prototype);
/**
* Detect if the function is a one-way or two-way operation.
*
* Default implementation assumes one-way, when return value is "void".
*
* @param AbstractFunction $function
* @param Prototype $prototype
* @return bool
*/
public function isFunctionOneWay(AbstractFunction $function, Prototype $prototype);
/**
* Detect the functions documentation.
*
* Default implementation uses docblock description.
*
* @param AbstractFunction $function
* @return string
*/
public function getFunctionDocumentation(AbstractFunction $function);
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\AutoDiscover\DiscoveryStrategy;
use Zend\Server\Reflection\AbstractFunction;
use Zend\Server\Reflection\Prototype;
use Zend\Server\Reflection\ReflectionParameter;
/**
* Describes how types, return values and method details are detected during
* AutoDiscovery of a WSDL.
*/
class ReflectionDiscovery implements DiscoveryStrategyInterface
{
/**
* Returns description from phpdoc block
*
* @param AbstractFunction $function
* @return string
*/
public function getFunctionDocumentation(AbstractFunction $function)
{
return $function->getDescription();
}
/**
* Return parameter type
*
* @param ReflectionParameter $param
* @return string
*/
public function getFunctionParameterType(ReflectionParameter $param)
{
return $param->getType();
}
/**
* Return function return type
*
* @param AbstractFunction $function
* @param Prototype $prototype
* @return string
*/
public function getFunctionReturnType(AbstractFunction $function, Prototype $prototype)
{
return $prototype->getReturnType();
}
/**
* Return true if function is one way (return nothing)
*
* @param AbstractFunction $function
* @param Prototype $prototype
* @return bool
*/
public function isFunctionOneWay(AbstractFunction $function, Prototype $prototype)
{
return $prototype->getReturnType() == 'void';
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,57 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\Client;
use SoapClient;
class Common extends SoapClient
{
/**
* doRequest() pre-processing method
*
* @var callable
*/
protected $doRequestCallback;
/**
* Common Soap Client constructor
*
* @param callable $doRequestCallback
* @param string $wsdl
* @param array $options
*/
public function __construct($doRequestCallback, $wsdl, $options)
{
$this->doRequestCallback = $doRequestCallback;
parent::__construct($wsdl, $options);
}
/**
* Performs SOAP request over HTTP.
* Overridden to implement different transport layers, perform additional
* XML processing or other purpose.
*
* @param string $request
* @param string $location
* @param string $action
* @param int $version
* @param int $oneWay
* @return mixed
*/
public function __doRequest($request, $location, $action, $version, $oneWay = null)
{
// ltrim is a workaround for https://bugs.php.net/bug.php?id=63780
if ($oneWay === null) {
return call_user_func($this->doRequestCallback, $this, ltrim($request), $location, $action, $version);
}
return call_user_func($this->doRequestCallback, $this, ltrim($request), $location, $action, $version, $oneWay);
}
}

View File

@@ -0,0 +1,259 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\Client;
use Zend\Http\Client\Adapter\Curl as CurlClient;
use Zend\Http\Response as HttpResponse;
use Zend\Soap\Client as SOAPClient;
use Zend\Soap\Client\Common as CommonClient;
use Zend\Soap\Exception;
use Zend\Uri\Http as HttpUri;
/**
* .NET SOAP client
*
* Class is intended to be used with .NET Web Services.
*/
class DotNet extends SOAPClient
{
/**
* Curl HTTP client adapter.
* @var CurlClient
*/
protected $curlClient = null;
/**
* The last request headers.
* @var string
*/
protected $lastRequestHeaders = '';
/**
* The last response headers.
* @var string
*/
protected $lastResponseHeaders = '';
/**
* SOAP client options.
* @var array
*/
protected $options = [];
/**
* Should NTLM authentication be used?
* @var boolean
*/
protected $useNtlm = false;
/**
* Constructor
*
* @param string $wsdl
* @param array $options
*/
public function __construct($wsdl = null, $options = null)
{
// Use SOAP 1.1 as default
$this->setSoapVersion(SOAP_1_1);
parent::__construct($wsdl, $options);
}
// @codingStandardsIgnoreStart
/**
* Do request proxy method.
*
* @param CommonClient $client Actual SOAP client.
* @param string $request The request body.
* @param string $location The SOAP URI.
* @param string $action The SOAP action to call.
* @param int $version The SOAP version to use.
* @param int $oneWay (Optional) The number 1 if a response is not expected.
* @return string The XML SOAP response.
*/
public function _doRequest(CommonClient $client, $request, $location, $action, $version, $oneWay = null)
{
if (! $this->useNtlm) {
return parent::_doRequest(
$client,
$request,
$location,
$action,
$version,
$oneWay
);
}
$curlClient = $this->getCurlClient();
// @todo persistent connection ?
$headers = [
'Content-Type' => 'text/xml; charset=utf-8',
'Method' => 'POST',
'SOAPAction' => '"' . $action . '"',
'User-Agent' => 'PHP-SOAP-CURL',
];
$uri = new HttpUri($location);
// @todo use parent set* options for ssl certificate authorization
$curlClient
->setCurlOption(CURLOPT_HTTPAUTH, CURLAUTH_NTLM)
->setCurlOption(CURLOPT_SSL_VERIFYHOST, false)
->setCurlOption(CURLOPT_SSL_VERIFYPEER, false)
->setCurlOption(CURLOPT_USERPWD, sprintf(
'%s:%s',
$this->options['login'],
$this->options['password']
));
// Perform the cURL request and get the response
$curlClient->connect($uri->getHost(), $uri->getPort());
$curlClient->write('POST', $uri, 1.1, $headers, $request);
$response = HttpResponse::fromString($curlClient->read());
// @todo persistent connection ?
$curlClient->close();
// Save headers
$this->lastRequestHeaders = $this->flattenHeaders($headers);
$this->lastResponseHeaders = $response->getHeaders()->toString();
// Return only the XML body
return $response->getBody();
}
// @codingStandardsIgnoreEnd
/**
* Returns the cURL client that is being used.
*
* @return CurlClient
*/
public function getCurlClient()
{
if ($this->curlClient === null) {
$this->curlClient = new CurlClient();
}
return $this->curlClient;
}
/**
* Retrieve request headers.
*
* @return string Request headers.
*/
public function getLastRequestHeaders()
{
return $this->lastRequestHeaders;
}
/**
* Retrieve response headers (as string)
*
* @return string Response headers.
*/
public function getLastResponseHeaders()
{
return $this->lastResponseHeaders;
}
/**
* Sets the cURL client to use.
*
* @param CurlClient $curlClient The cURL client.
* @return self
*/
public function setCurlClient(CurlClient $curlClient)
{
$this->curlClient = $curlClient;
return $this;
}
/**
* Sets options.
*
* Allows setting options as an associative array of option => value pairs.
*
* @param array|\Traversable $options Options.
* @throws \InvalidArgumentException If an unsupported option is passed.
* @return self
*/
public function setOptions($options)
{
if (isset($options['authentication']) && $options['authentication'] === 'ntlm') {
$this->useNtlm = true;
unset($options['authentication']);
}
$this->options = $options;
return parent::setOptions($options);
}
// @codingStandardsIgnoreStart
/**
* Perform arguments pre-processing
*
* My be overridden in descendant classes
*
* @param array $arguments
* @return array
* @throws Exception\RuntimeException
*/
protected function _preProcessArguments($arguments)
{
if (count($arguments) > 1
|| (count($arguments) == 1 && ! is_array(reset($arguments)))
) {
throw new Exception\RuntimeException(
'.Net webservice arguments must be grouped into an array: array("a" => $a, "b" => $b, ...).'
);
}
// Do nothing
return $arguments;
}
// @codingStandardsIgnoreEnd
// @codingStandardsIgnoreStart
/**
* Perform result pre-processing
*
* My be overridden in descendant classes
*
* @param object $result
* @return mixed
*/
protected function _preProcessResult($result)
{
$resultProperty = $this->getLastMethod() . 'Result';
if (property_exists($result, $resultProperty)) {
return $result->$resultProperty;
}
return $result;
}
// @codingStandardsIgnoreEnd
/**
* Flattens an HTTP headers array into a string.
*
* @param array $headers The headers to flatten.
* @return string The headers string.
*/
protected function flattenHeaders(array $headers)
{
$result = '';
foreach ($headers as $name => $value) {
$result .= $name . ': ' . $value . "\r\n";
}
return $result;
}
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\Client;
use Zend\Soap\Client as SOAPClient;
use Zend\Soap\Server as SOAPServer;
/**
* Class is intended to be used as local SOAP client which works
* with a provided Server object.
*
* Could be used for development or testing purposes.
*/
class Local extends SOAPClient
{
/**
* Server object
* @var SOAPServer
*/
protected $server;
/**
* Local client constructor
*
* @param SOAPServer $server
* @param string $wsdl
* @param array $options
*/
public function __construct(SOAPServer $server, $wsdl, $options = null)
{
$this->server = $server;
// Use Server specified SOAP version as default
$this->setSoapVersion($server->getSoapVersion());
parent::__construct($wsdl, $options);
}
// @codingStandardsIgnoreStart
/**
* Actual "do request" method.
*
* @param Common $client
* @param string $request
* @param string $location
* @param string $action
* @param int $version
* @param int $oneWay
* @return mixed
*/
public function _doRequest(Common $client, $request, $location, $action, $version, $oneWay = null)
{
// Perform request as is
ob_start();
$this->server->handle($request);
$response = ob_get_clean();
if ($response === null || $response === '') {
$serverResponse = $this->server->getResponse();
if ($serverResponse !== null) {
$response = $serverResponse;
}
}
return $response;
}
// @codingStandardsIgnoreEnd
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\Exception;
use BadMethodCallException as SPLBadMethodCallException;
/**
* Exception thrown when unrecognized method is called via overloading
*/
class BadMethodCallException extends SPLBadMethodCallException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\Exception;
/**
* Common Exception interface
*/
interface ExceptionInterface
{
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\Exception;
use RuntimeException;
/**
* Exception thrown when SOAP PHP extension is not loaded
*/
class ExtensionNotLoadedException extends RuntimeException
{
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\Exception;
use InvalidArgumentException as SPLInvalidArgumentException;
/**
* Exception thrown when one or more method arguments are invalid
*/
class InvalidArgumentException extends SPLInvalidArgumentException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\Exception;
use RuntimeException as SPLRuntimeException;
/**
* Exception thrown when there is an error during program execution
*/
class RuntimeException extends SPLRuntimeException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\Exception;
use UnexpectedValueException as SPLUnexpectedValueException;
/**
* Exception thrown when provided arguments are invalid
*/
class UnexpectedValueException extends SPLUnexpectedValueException implements ExceptionInterface
{
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,190 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\Server;
use ReflectionObject;
use Zend\Soap\Exception;
/**
* Wraps WSDL Document/Literal Style service objects to hide SOAP request
* message abstraction from the actual service object.
*
* When using the document/literal SOAP message pattern you end up with one
* object passed to your service methods that contains all the parameters of
* the method. This obviously leads to a problem since Zend\Soap\Wsdl tightly
* couples method parameters to request message parameters.
*
* Example:
*
* <code>
* class MyCalculatorService
* {
* /**
* * @param int $x
* * @param int $y
* * @return int
* *
* public function add($x, $y)
* {
* }
* }
* </code>
*
* The document/literal wrapper pattern would lead php ext/soap to generate a
* single "request" object that contains $x and $y properties. To solve this a
* wrapper service is needed that extracts the properties and delegates a
* proper call to the underlying service.
*
* The input variable from a document/literal SOAP-call to the client
* MyCalculatorServiceClient#add(10, 20) would lead PHP ext/soap to create
* the following request object:
*
* <code>
* $addRequest = new \stdClass;
* $addRequest->x = 10;
* $addRequest->y = 20;
* </code>
*
* This object does not match the signature of the server-side
* MyCalculatorService and lead to failure.
*
* Also the response object in this case is supposed to be an array
* or object with a property "addResult":
*
* <code>
* $addResponse = new \stdClass;
* $addResponse->addResult = 30;
* </code>
*
* To keep your service object code free from this implementation detail
* of SOAP this wrapper service handles the parsing between the formats.
*
* @example
* <code>
* $service = new MyCalculatorService();
* $soap = new \Zend\Soap\Server($wsdlFile);
* $soap->setObject(new \Zend\Soap\Server\DocumentLiteralWrapper($service));
* $soap->handle();
* </code>
*/
class DocumentLiteralWrapper
{
/**
* @var object
*/
protected $object;
/**
* @var ReflectionObject
*/
protected $reflection;
/**
* Pass Service object to the constructor
*
* @param object $object
*/
public function __construct($object)
{
$this->object = $object;
$this->reflection = new ReflectionObject($this->object);
}
/**
* Proxy method that does the heavy document/literal decomposing.
*
* @param string $method
* @param array $args
* @return mixed
*/
public function __call($method, $args)
{
$this->assertOnlyOneArgument($args);
$this->assertServiceDelegateHasMethod($method);
$delegateArgs = $this->parseArguments($method, $args[0]);
$ret = call_user_func_array([$this->object, $method], $delegateArgs);
return $this->getResultMessage($method, $ret);
}
/**
* Parse the document/literal wrapper into arguments to call the real
* service.
*
* @param string $method
* @param object $document
* @return array
* @throws Exception\UnexpectedValueException
*/
protected function parseArguments($method, $document)
{
$reflMethod = $this->reflection->getMethod($method);
$params = [];
foreach ($reflMethod->getParameters() as $param) {
$params[$param->getName()] = $param;
}
$delegateArgs = [];
foreach (get_object_vars($document) as $argName => $argValue) {
if (!isset($params[$argName])) {
throw new Exception\UnexpectedValueException(sprintf(
"Received unknown argument %s which is not an argument to %s::%s",
$argName,
get_class($this->object),
$method
));
}
$delegateArgs[$params[$argName]->getPosition()] = $argValue;
}
return $delegateArgs;
}
/**
* Returns result message content
*
* @param string $method
* @param mixed $ret
* @return array
*/
protected function getResultMessage($method, $ret)
{
return [$method . 'Result' => $ret];
}
/**
* @param string $method
* @throws Exception\BadMethodCallException
*/
protected function assertServiceDelegateHasMethod($method)
{
if (!$this->reflection->hasMethod($method)) {
throw new Exception\BadMethodCallException(sprintf(
"Method %s does not exist on delegate object %s",
$method,
get_class($this->object)
));
}
}
/**
* @param array $args
* @throws Exception\UnexpectedValueException
*/
protected function assertOnlyOneArgument(array $args)
{
if (count($args) != 1) {
throw new Exception\UnexpectedValueException(sprintf(
"Expecting exactly one argument that is the document/literal wrapper, got %d",
count($args)
));
}
}
}

View File

@@ -0,0 +1,922 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap;
use DOMNode;
use DOMDocument;
use DOMDocumentFragment;
use DOMElement;
use DOMXPath;
use Zend\Soap\Wsdl\ComplexTypeStrategy\ComplexTypeStrategyInterface as ComplexTypeStrategy;
use Zend\Uri\Uri;
class Wsdl
{
/**#@+
* XML Namespace uris and prefixes.
*/
const XML_NS = 'xmlns';
const XML_NS_URI = 'http://www.w3.org/2000/xmlns/';
const WSDL_NS = 'wsdl';
const WSDL_NS_URI = 'http://schemas.xmlsoap.org/wsdl/';
const SOAP_11_NS = 'soap';
const SOAP_11_NS_URI = 'http://schemas.xmlsoap.org/wsdl/soap/';
const SOAP_12_NS = 'soap12';
const SOAP_12_NS_URI = 'http://schemas.xmlsoap.org/wsdl/soap12/';
const SOAP_ENC_NS = 'soap-enc';
const SOAP_ENC_URI = 'http://schemas.xmlsoap.org/soap/encoding/';
const XSD_NS = 'xsd';
const XSD_NS_URI = 'http://www.w3.org/2001/XMLSchema';
const TYPES_NS = 'tns';
/**#@-*/
/**
* Map of PHP Class names to WSDL QNames.
* @var array
*/
protected $classMap = [];
/**
* DOM Instance
* @var DOMDocument
*/
protected $dom;
/**
* Types defined on schema
* @var array
*/
protected $includedTypes = [];
/**
* @var DOMElement
*/
protected $schema = null;
/**
* Strategy for detection of complex types
*/
protected $strategy = null;
/**
* URI where the WSDL will be available
* @var string
*/
protected $uri;
/**
* Root XML_Tree_Node
* @var DOMElement WSDL
*/
protected $wsdl;
/**
* @param string $name Name of the Web Service being Described
* @param string|Uri $uri URI where the WSDL will be available
* @param null|ComplexTypeStrategy $strategy Strategy for detection of complex types
* @param null|array $classMap Map of PHP Class names to WSDL QNames
* @throws Exception\RuntimeException
*/
public function __construct(
$name,
$uri,
ComplexTypeStrategy $strategy = null,
array $classMap = []
) {
if ($uri instanceof Uri) {
$uri = $uri->toString();
}
$this->setUri($uri);
$this->classMap = $classMap;
$this->dom = $this->getDOMDocument($name, $this->getUri());
$this->wsdl = $this->dom->documentElement;
$this->setComplexTypeStrategy($strategy ?: new Wsdl\ComplexTypeStrategy\DefaultComplexType);
}
/**
* Get the wsdl XML document with all namespaces and required attributes
*
* @param string $uri
* @param string $name
* @return DOMDocument
*/
protected function getDOMDocument($name, $uri = null)
{
$dom = new DOMDocument();
// @todo new option for debug mode ?
$dom->preserveWhiteSpace = false;
$dom->formatOutput = false;
$dom->resolveExternals = false;
$dom->encoding = 'UTF-8';
$dom->substituteEntities = false;
$definitions = $dom->createElementNS(self::WSDL_NS_URI, 'definitions');
$dom->appendChild($definitions);
$uri = $this->sanitizeUri($uri);
$this->setAttributeWithSanitization($definitions, 'name', $name);
$this->setAttributeWithSanitization($definitions, 'targetNamespace', $uri);
$definitions->setAttributeNS(self::XML_NS_URI, 'xmlns:'. self::WSDL_NS, self::WSDL_NS_URI);
$definitions->setAttributeNS(self::XML_NS_URI, 'xmlns:'. self::TYPES_NS, $uri);
$definitions->setAttributeNS(self::XML_NS_URI, 'xmlns:'. self::SOAP_11_NS, self::SOAP_11_NS_URI);
$definitions->setAttributeNS(self::XML_NS_URI, 'xmlns:'. self::XSD_NS, self::XSD_NS_URI);
$definitions->setAttributeNS(self::XML_NS_URI, 'xmlns:'. self::SOAP_ENC_NS, self::SOAP_ENC_URI);
$definitions->setAttributeNS(self::XML_NS_URI, 'xmlns:'. self::SOAP_12_NS, self::SOAP_12_NS_URI);
return $dom;
}
/**
* Retrieve target namespace of the WSDL document.
*
* @return string
*/
public function getTargetNamespace()
{
$targetNamespace = null;
if ($this->wsdl !== null) {
$targetNamespace = $this->wsdl->getAttribute('targetNamespace');
}
return $targetNamespace;
}
/**
* Get the class map of php to wsdl mappings..
*
* @return array
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* Set the class map of php to wsdl mappings..
*
* @param array $classMap
* @return self
*/
public function setClassMap(array $classMap)
{
$this->classMap = $classMap;
return $this;
}
/**
* Set a new uri for this WSDL
*
* @param string|Uri $uri
* @return self
*/
public function setUri($uri)
{
if ($uri instanceof Uri) {
$uri = $uri->toString();
}
$uri = $this->sanitizeUri($uri);
$oldUri = $this->uri;
$this->uri = $uri;
if ($this->dom instanceof DOMDocument) {
// namespace declarations are NOT true attributes so one must explicitly set on root element
// xmlns:tns = $uri
$this->dom->documentElement->setAttributeNS(self::XML_NS_URI, self::XML_NS . ':' . self::TYPES_NS, $uri);
$xpath = new DOMXPath($this->dom);
$xpath->registerNamespace('default', self::WSDL_NS_URI);
$xpath->registerNamespace(self::TYPES_NS, $uri);
$xpath->registerNamespace(self::SOAP_11_NS, self::SOAP_11_NS_URI);
$xpath->registerNamespace(self::SOAP_12_NS, self::SOAP_12_NS_URI);
$xpath->registerNamespace(self::XSD_NS, self::XSD_NS_URI);
$xpath->registerNamespace(self::SOAP_ENC_NS, self::SOAP_ENC_URI);
$xpath->registerNamespace(self::WSDL_NS, self::WSDL_NS_URI);
// Select only attribute nodes. Data nodes does not contain uri
// except for documentation node but this is for the user to decide.
// This list does not include xmlns:tsn attribute of document root.
// That attribute is changed above.
$attributeNodes = $xpath->query('//attribute::*[contains(., "' . $oldUri . '")]');
foreach ($attributeNodes as $node) {
$attributeValue = $this->dom->createTextNode(str_replace($oldUri, $uri, $node->nodeValue));
$node->replaceChild($attributeValue, $node->childNodes->item(0));
}
}
return $this;
}
/**
* Return WSDL uri
*
* @return string
*/
public function getUri()
{
return $this->uri;
}
/**
* Function for sanitizing uri
*
* @param string|Uri $uri
* @return string
* @throws Exception\InvalidArgumentException
*/
public function sanitizeUri($uri)
{
if ($uri instanceof Uri) {
$uri = $uri->toString();
}
$uri = trim($uri);
$uri = htmlspecialchars($uri, ENT_QUOTES, 'UTF-8', false);
if (empty($uri)) {
throw new Exception\InvalidArgumentException('Uri contains invalid characters or is empty');
}
return $uri;
}
/**
* Set a strategy for complex type detection and handling
*
* @param ComplexTypeStrategy $strategy
* @return self
*/
public function setComplexTypeStrategy(ComplexTypeStrategy $strategy)
{
$this->strategy = $strategy;
return $this;
}
/**
* Get the current complex type strategy
*
* @return ComplexTypeStrategy
*/
public function getComplexTypeStrategy()
{
return $this->strategy;
}
/**
* Add a {@link http://www.w3.org/TR/wsdl#_messages message} element to the WSDL
*
* @param string $messageName Name for the {@link http://www.w3.org/TR/wsdl#_messages message}
* @param array $parts An array of {@link http://www.w3.org/TR/wsdl#_message parts}
* The array is constructed like:
* 'name of part' => 'part xml schema data type' or
* 'name of part' => array('type' => 'part xml schema type') or
* 'name of part' => array('element' => 'part xml element name')
* @return DOMElement The new message's XML_Tree_Node for use in {@link function addDocumentation}
*/
public function addMessage($messageName, $parts)
{
$message = $this->dom->createElementNS(self::WSDL_NS_URI, 'message');
$message->setAttribute('name', $messageName);
if (count($parts) > 0) {
foreach ($parts as $name => $type) {
$part = $this->dom->createElementNS(self::WSDL_NS_URI, 'part');
$message->appendChild($part);
$part->setAttribute('name', $name);
if (is_array($type)) {
$this->arrayToAttributes($part, $type);
} else {
$this->setAttributeWithSanitization($part, 'type', $type);
}
}
}
$this->wsdl->appendChild($message);
return $message;
}
/**
* Add a {@link http://www.w3.org/TR/wsdl#_porttypes portType} element to the WSDL
*
* @param string $name portType element's name
* @return DOMElement The new portType's XML_Tree_Node for use in
* {@link addPortOperation} and {@link addDocumentation}
*/
public function addPortType($name)
{
$portType = $this->dom->createElementNS(self::WSDL_NS_URI, 'portType');
$this->wsdl->appendChild($portType);
$portType->setAttribute('name', $name);
return $portType;
}
/**
* Add an {@link http://www.w3.org/TR/wsdl#request-response operation} element to a portType element
*
* @param DOMElement $portType a portType XML_Tree_Node, from {@link function addPortType}
* @param string $name Operation name
* @param bool|string $input Input Message
* @param bool|string $output Output Message
* @param bool|string $fault Fault Message
* @return DOMElement The new operation's XML_Tree_Node for use in {@link function addDocumentation}
*/
public function addPortOperation($portType, $name, $input = false, $output = false, $fault = false)
{
$operation = $this->dom->createElementNS(self::WSDL_NS_URI, 'operation');
$portType->appendChild($operation);
$operation->setAttribute('name', $name);
if (is_string($input) && (strlen(trim($input)) >= 1)) {
$node = $this->dom->createElementNS(self::WSDL_NS_URI, 'input');
$operation->appendChild($node);
$node->setAttribute('message', $input);
}
if (is_string($output) && (strlen(trim($output)) >= 1)) {
$node= $this->dom->createElementNS(self::WSDL_NS_URI, 'output');
$operation->appendChild($node);
$node->setAttribute('message', $output);
}
if (is_string($fault) && (strlen(trim($fault)) >= 1)) {
$node = $this->dom->createElementNS(self::WSDL_NS_URI, 'fault');
$operation->appendChild($node);
$node->setAttribute('message', $fault);
}
return $operation;
}
/**
* Add a {@link http://www.w3.org/TR/wsdl#_bindings binding} element to WSDL
*
* @param string $name Name of the Binding
* @param string $portType name of the portType to bind
* @return DOMElement The new binding's XML_Tree_Node for use with
* {@link function addBindingOperation} and {@link function addDocumentation}
*/
public function addBinding($name, $portType)
{
$binding = $this->dom->createElementNS(self::WSDL_NS_URI, 'binding');
$this->wsdl->appendChild($binding);
$this->setAttribute($binding, 'name', $name);
$this->setAttribute($binding, 'type', $portType);
return $binding;
}
/**
* Add an operation to a binding element
*
* @param DOMElement $binding A binding XML_Tree_Node returned by
* {@link function addBinding}
* @param string $name
* @param array|bool $input An array of attributes for the input element,
* allowed keys are: 'use', 'namespace', 'encodingStyle'.
* {@link http://www.w3.org/TR/wsdl#_soap:body More Information}
* @param array|bool $output An array of attributes for the output element,
* allowed keys are: 'use', 'namespace', 'encodingStyle'.
* {@link http://www.w3.org/TR/wsdl#_soap:body More Information}
* @param array|bool $fault An array with attributes for the fault element,
* allowed keys are: 'name', 'use', 'namespace', 'encodingStyle'.
* {@link http://www.w3.org/TR/wsdl#_soap:body More Information}
* @param int $soapVersion SOAP version: SOAP_1_1 or SOAP_1_2, default: SOAP_1_1
* @return DOMElement The new Operation's XML_Tree_Node for use with {@link
* function addSoapOperation} and {@link function addDocumentation}
*/
public function addBindingOperation(
$binding,
$name,
$input = false,
$output = false,
$fault = false,
$soapVersion = SOAP_1_1
) {
$operation = $this->dom->createElementNS(self::WSDL_NS_URI, 'operation');
$binding->appendChild($operation);
$this->setAttribute($operation, 'name', $name);
if (is_array($input) && !empty($input)) {
$node = $this->dom->createElementNS(self::WSDL_NS_URI, 'input');
$operation->appendChild($node);
$soapNode = $this->dom->createElementNS($this->getSoapNamespaceUriByVersion($soapVersion), 'body');
$node->appendChild($soapNode);
$this->arrayToAttributes($soapNode, $input);
}
if (is_array($output) && !empty($output)) {
$node = $this->dom->createElementNS(self::WSDL_NS_URI, 'output');
$operation->appendChild($node);
$soapNode = $this->dom->createElementNS($this->getSoapNamespaceUriByVersion($soapVersion), 'body');
$node->appendChild($soapNode);
$this->arrayToAttributes($soapNode, $output);
}
if (is_array($fault) && !empty($fault)) {
$node = $this->dom->createElementNS(self::WSDL_NS_URI, 'fault');
$operation->appendChild($node);
$this->arrayToAttributes($node, $fault);
}
return $operation;
}
/**
* Add a {@link http://www.w3.org/TR/wsdl#_soap:binding SOAP binding} element to a Binding element
*
* @param DOMElement $binding A binding XML_Tree_Node returned by {@link function addBinding}
* @param string $style binding style, possible values are "rpc" (the default) and "document"
* @param string $transport Transport method (defaults to HTTP)
* @param int $soapVersion SOAP version: SOAP_1_1 or SOAP_1_2, default: SOAP_1_1
* @return DOMElement
*/
public function addSoapBinding(
$binding,
$style = 'document',
$transport = 'http://schemas.xmlsoap.org/soap/http',
$soapVersion = SOAP_1_1
) {
$soapBinding = $this->dom->createElementNS($this->getSoapNamespaceUriByVersion($soapVersion), 'binding');
$binding->appendChild($soapBinding);
$soapBinding->setAttribute('style', $style);
$soapBinding->setAttribute('transport', $transport);
return $soapBinding;
}
/**
* Add a {@link http://www.w3.org/TR/wsdl#_soap:operation SOAP operation} to an operation element
*
* @param DOMElement $operation An operation XML_Tree_Node returned by {@link function addBindingOperation}
* @param string $soapAction SOAP Action
* @param int $soapVersion SOAP version: SOAP_1_1 or SOAP_1_2, default: SOAP_1_1
* @return DOMElement
*/
public function addSoapOperation($operation, $soapAction, $soapVersion = SOAP_1_1)
{
if ($soapAction instanceof Uri) {
$soapAction = $soapAction->toString();
}
$soapOperation = $this->dom->createElementNS($this->getSoapNamespaceUriByVersion($soapVersion), 'operation');
$operation->insertBefore($soapOperation, $operation->firstChild);
$this->setAttributeWithSanitization($soapOperation, 'soapAction', $soapAction);
return $soapOperation;
}
/**
* Add a {@link http://www.w3.org/TR/wsdl#_services service} element to the WSDL
*
* @param string $name Service Name
* @param string $portName Name of the port for the service
* @param string $binding Binding for the port
* @param string $location SOAP Address for the service
* @param int $soapVersion SOAP version: SOAP_1_1 or SOAP_1_2, default: SOAP_1_1
* @return DOMElement The new service's XML_Tree_Node for use with {@link function addDocumentation}
*/
public function addService($name, $portName, $binding, $location, $soapVersion = SOAP_1_1)
{
if ($location instanceof Uri) {
$location = $location->toString();
}
$service = $this->dom->createElementNS(WSDL::WSDL_NS_URI, 'service');
$this->wsdl->appendChild($service);
$service->setAttribute('name', $name);
$port = $this->dom->createElementNS(WSDL::WSDL_NS_URI, 'port');
$service->appendChild($port);
$port->setAttribute('name', $portName);
$port->setAttribute('binding', $binding);
$soapAddress = $this->dom->createElementNS($this->getSoapNamespaceUriByVersion($soapVersion), 'address');
$port->appendChild($soapAddress);
$this->setAttributeWithSanitization($soapAddress, 'location', $location);
return $service;
}
/**
* Add a documentation element to any element in the WSDL.
*
* Note that the WSDL specification uses 'document', but the WSDL schema
* uses 'documentation' instead.
*
* The WS-I Basic Profile 1.1 recommends using 'documentation'.
*
* @see http://www.w3.org/TR/wsdl#_documentation WSDL specification
* @see http://schemas.xmlsoap.org/wsdl/ WSDL schema
* @see http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html#WSDL_documentation_Element WS-I Basic
* Profile 1.1
* @param DOMElement $inputNode An XML_Tree_Node returned by another
* method to add the documentation to
* @param string $documentation Human readable documentation for the node
* @return DOMElement The documentation element
*/
public function addDocumentation($inputNode, $documentation)
{
if ($inputNode === $this) {
$node = $this->dom->documentElement;
} else {
$node = $inputNode;
}
$doc = $this->dom->createElementNS(WSDL::WSDL_NS_URI, 'documentation');
if ($node->hasChildNodes()) {
$node->insertBefore($doc, $node->firstChild);
} else {
$node->appendChild($doc);
}
$docCData = $this->dom->createTextNode(str_replace(["\r\n", "\r"], "\n", $documentation));
$doc->appendChild($docCData);
return $doc;
}
/**
* Add WSDL Types element
*
* @param DOMDocument|DOMNode|DOMElement|DOMDocumentFragment $types A
* DOMDocument|DOMNode|DOMElement|DOMDocumentFragment with all the XML
* Schema types defined in it
*/
public function addTypes(DOMNode $types)
{
if ($types instanceof DOMDocument) {
$dom = $this->dom->importNode($types->documentElement);
$this->wsdl->appendChild($dom);
} elseif ($types instanceof DOMNode || $types instanceof DOMElement || $types instanceof DOMDocumentFragment) {
$dom = $this->dom->importNode($types);
$this->wsdl->appendChild($dom);
}
}
/**
* Add a complex type name that is part of this WSDL and can be used in signatures.
*
* @param string $type
* @param string $wsdlType
* @return self
*/
public function addType($type, $wsdlType)
{
if (!isset($this->includedTypes[$type])) {
$this->includedTypes[$type] = $wsdlType;
}
return $this;
}
/**
* Return an array of all currently included complex types
*
* @return array
*/
public function getTypes()
{
return $this->includedTypes;
}
/**
* Return the Schema node of the WSDL
*
* @return DOMElement
*/
public function getSchema()
{
if ($this->schema === null) {
$this->addSchemaTypeSection();
}
return $this->schema;
}
/**
* Return the WSDL as XML
*
* @return string WSDL as XML
*/
public function toXML()
{
$this->dom->normalizeDocument();
return $this->dom->saveXML();
}
/**
* Return DOM Document
*
* @return DOMDocument
*/
public function toDomDocument()
{
$this->dom->normalizeDocument();
return $this->dom;
}
/**
* Echo the WSDL as XML
*
* @param bool $filename
* @return bool
*/
public function dump($filename = false)
{
$this->dom->normalizeDocument();
if (!$filename) {
echo $this->toXML();
return true;
}
return (bool) file_put_contents($filename, $this->toXML());
}
/**
* Returns an XSD Type for the given PHP type
*
* @param string $type PHP Type to get the XSD type for
* @return string
*/
public function getType($type)
{
switch (strtolower($type)) {
case 'string':
case 'str':
return self::XSD_NS . ':string';
case 'long':
return self::XSD_NS . ':long';
case 'int':
case 'integer':
return self::XSD_NS . ':int';
case 'float':
return self::XSD_NS . ':float';
case 'double':
return self::XSD_NS . ':double';
case 'boolean':
case 'bool':
return self::XSD_NS . ':boolean';
case 'array':
return self::SOAP_ENC_NS . ':Array';
case 'object':
return self::XSD_NS . ':struct';
case 'mixed':
return self::XSD_NS . ':anyType';
case 'void':
return '';
default:
// delegate retrieval of complex type to current strategy
return $this->addComplexType($type);
}
}
/**
* This function makes sure a complex types section and schema additions are set.
*
* @return self
*/
public function addSchemaTypeSection()
{
if ($this->schema === null) {
$types = $this->dom->createElementNS(self::WSDL_NS_URI, 'types');
$this->wsdl->appendChild($types);
$this->schema = $this->dom->createElementNS(WSDL::XSD_NS_URI, 'schema');
$types->appendChild($this->schema);
$this->setAttributeWithSanitization($this->schema, 'targetNamespace', $this->getUri());
}
return $this;
}
/**
* 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];
}
$type = trim($type, '\\');
// remove namespace,
$pos = strrpos($type, '\\');
if ($pos) {
$type = substr($type, $pos+1);
}
return $type;
}
/**
* Add a {@link http://www.w3.org/TR/wsdl#_types types} data type definition
*
* @param string $type Name of the class to be specified
* @return string XSD Type for the given PHP type
*/
public function addComplexType($type)
{
if (isset($this->includedTypes[$type])) {
return $this->includedTypes[$type];
}
$this->addSchemaTypeSection();
$strategy = $this->getComplexTypeStrategy();
$strategy->setContext($this);
// delegates the detection of a complex type to the current strategy
return $strategy->addComplexType($type);
}
/**
* Parse an xsd:element represented as an array into a DOMElement.
*
* @param array $element an xsd:element represented as an array
* @return DOMElement parsed element
* @throws Exception\RuntimeException if $element is not an array
*/
protected function parseElement($element)
{
if (!is_array($element)) {
throw new Exception\RuntimeException('The "element" parameter needs to be an associative array.');
}
$elementXML = $this->dom->createElementNS(self::XSD_NS_URI, 'element');
foreach ($element as $key => $value) {
if (in_array($key, ['sequence', 'all', 'choice'])) {
if (is_array($value)) {
$complexType = $this->dom->createElementNS(self::XSD_NS_URI, 'complexType');
if (count($value) > 0) {
$container = $this->dom->createElementNS(self::XSD_NS_URI, $key);
foreach ($value as $subElement) {
$subElementXML = $this->parseElement($subElement);
$container->appendChild($subElementXML);
}
$complexType->appendChild($container);
}
$elementXML->appendChild($complexType);
}
} else {
$elementXML->setAttribute($key, $value);
}
}
return $elementXML;
}
/**
* Prepare attribute value for specific attributes
*
* @param string $name
* @param mixed $value
* @return string safe value or original $value
*/
protected function sanitizeAttributeValueByName($name, $value)
{
switch (strtolower($name)) {
case 'targetnamespace':
case 'encodingstyle':
case 'soapaction':
case 'location':
return $this->sanitizeUri($value);
default:
return $value;
}
}
/**
* Convert associative array to attributes of given node
*
* Optionally uses {@link function sanitizeAttributeValueByName}.
*
* @param DOMNode $node
* @param array $attributes
* @param bool $withSanitizer
*/
protected function arrayToAttributes(\DOMNode $node, array $attributes, $withSanitizer = true)
{
foreach ($attributes as $attributeName => $attributeValue) {
if ($withSanitizer) {
$this->setAttributeWithSanitization($node, $attributeName, $attributeValue);
} else {
$this->setAttribute($node, $attributeName, $attributeValue);
}
}
}
/**
* Set attribute to given node using {@link function sanitizeAttributeValueByName}
*
* @param DOMNode $node
* @param string $attributeName
* @param mixed $attributeValue
*/
protected function setAttributeWithSanitization(\DOMNode $node, $attributeName, $attributeValue)
{
$attributeValue = $this->sanitizeAttributeValueByName($attributeName, $attributeValue);
$this->setAttribute($node, $attributeName, $attributeValue);
}
/**
* Set attribute to given node
*
* @param DOMNode $node
* @param string $attributeName
* @param mixed $attributeValue
*/
protected function setAttribute(\DOMNode $node, $attributeName, $attributeValue)
{
$attributeNode = $node->ownerDocument->createAttribute($attributeName);
$node->appendChild($attributeNode);
$attributeNodeValue = $node->ownerDocument->createTextNode($attributeValue);
$attributeNode->appendChild($attributeNodeValue);
}
/**
* Return soap namespace uri according to $soapVersion
*
* @param int $soapVersion SOAP_1_1 or SOAP_1_2 constants
* @return string
* @throws Exception\InvalidArgumentException
*/
protected function getSoapNamespaceUriByVersion($soapVersion)
{
if ($soapVersion != SOAP_1_1 and $soapVersion != SOAP_1_2) {
throw new Exception\InvalidArgumentException('Invalid SOAP version, use constants: SOAP_1_1 or SOAP_1_2');
}
if ($soapVersion == SOAP_1_1) {
return self::SOAP_11_NS_URI;
}
return self::SOAP_12_NS_URI;
}
/**
* Add an xsd:element represented as an array to the schema.
*
* Array keys represent attribute names and values their respective value.
* The 'sequence', 'all' and 'choice' keys must have an array of elements as their value,
* to add them to a nested complexType.
*
* Example: array( 'name' => 'MyElement',
* 'sequence' => array( array('name' => 'myString', 'type' => 'string'),
* array('name' => 'myInteger', 'type' => 'int') ) );
* Resulting XML: <xsd:element name="MyElement"><xsd:complexType><xsd:sequence>
* <xsd:element name="myString" type="string"/>
* <xsd:element name="myInteger" type="int"/>
* </xsd:sequence></xsd:complexType></xsd:element>
*
* @param array $element an xsd:element represented as an array
* @return string xsd:element for the given element array
*/
public function addElement($element)
{
$schema = $this->getSchema();
$elementXml = $this->parseElement($element);
$schema->appendChild($elementXml);
return self::TYPES_NS . ':' . $element['name'];
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\Wsdl\ComplexTypeStrategy;
use Zend\Soap\Wsdl;
/**
* Abstract class for Zend\Soap\Wsdl\Strategy.
*/
abstract class AbstractComplexTypeStrategy implements ComplexTypeStrategyInterface
{
/**
* Context object
* @var Wsdl
*/
protected $context;
/**
* Set the WSDL Context object this strategy resides in.
*
* @param Wsdl $context
*/
public function setContext(Wsdl $context)
{
$this->context = $context;
}
/**
* Return the current WSDL context object
*
* @return Wsdl
*/
public function getContext()
{
return $this->context;
}
/**
* Look through registered types
*
* @param string $phpType
* @return string
*/
public function scanRegisteredTypes($phpType)
{
if (array_key_exists($phpType, $this->getContext()->getTypes())) {
$soapTypes = $this->getContext()->getTypes();
return $soapTypes[$phpType];
}
return;
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\Wsdl\ComplexTypeStrategy;
use Zend\Soap\Wsdl;
class AnyType implements ComplexTypeStrategyInterface
{
/**
* Not needed in this strategy.
*
* @param Wsdl $context
*/
public function setContext(Wsdl $context)
{
}
/**
* Returns xsd:anyType regardless of the input.
*
* @param string $type
* @return string
*/
public function addComplexType($type)
{
return Wsdl::XSD_NS . ':anyType';
}
}

View File

@@ -0,0 +1,121 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\Wsdl\ComplexTypeStrategy;
use Zend\Soap\Exception;
use Zend\Soap\Wsdl;
class ArrayOfTypeComplex extends DefaultComplexType
{
/**
* Add an ArrayOfType based on the xsd:complexType syntax if type[] is
* detected in return value doc comment.
*
* @param string $type
* @return string tns:xsd-type
* @throws Exception\InvalidArgumentException
*/
public function addComplexType($type)
{
if (($soapType = $this->scanRegisteredTypes($type)) !== null) {
return $soapType;
}
$singularType = $this->getSingularPhpType($type);
$nestingLevel = $this->getNestedCount($type);
if ($nestingLevel == 0) {
return parent::addComplexType($singularType);
}
if ($nestingLevel != 1) {
throw new Exception\InvalidArgumentException(
'ArrayOfTypeComplex cannot return nested ArrayOfObject deeper than one level. '
. 'Use array object properties to return deep nested data.'
);
}
// The following blocks define the Array of Object structure
return $this->addArrayOfComplexType($singularType, $type);
}
/**
* Add an ArrayOfType based on the xsd:complexType syntax if type[] is
* detected in return value doc comment.
*
* @param string $singularType e.g. '\MyNamespace\MyClassname'
* @param string $type e.g. '\MyNamespace\MyClassname[]'
* @return string tns:xsd-type e.g. 'tns:ArrayOfMyNamespace.MyClassname'
*/
protected function addArrayOfComplexType($singularType, $type)
{
if (($soapType = $this->scanRegisteredTypes($type)) !== null) {
return $soapType;
}
$xsdComplexTypeName = 'ArrayOf' . $this->getContext()->translateType($singularType);
$xsdComplexType = Wsdl::TYPES_NS . ':' . $xsdComplexTypeName;
// Register type here to avoid recursion
$this->getContext()->addType($type, $xsdComplexType);
// Process singular type using DefaultComplexType strategy
parent::addComplexType($singularType);
// Add array type structure to WSDL document
$dom = $this->getContext()->toDomDocument();
$complexType = $dom->createElementNS(Wsdl::XSD_NS_URI, 'complexType');
$this->getContext()->getSchema()->appendChild($complexType);
$complexType->setAttribute('name', $xsdComplexTypeName);
$complexContent = $dom->createElementNS(Wsdl::XSD_NS_URI, 'complexContent');
$complexType->appendChild($complexContent);
$xsdRestriction = $dom->createElementNS(Wsdl::XSD_NS_URI, 'restriction');
$complexContent->appendChild($xsdRestriction);
$xsdRestriction->setAttribute('base', Wsdl::SOAP_ENC_NS . ':Array');
$xsdAttribute = $dom->createElementNS(Wsdl::XSD_NS_URI, 'attribute');
$xsdRestriction->appendChild($xsdAttribute);
$xsdAttribute->setAttribute('ref', Wsdl::SOAP_ENC_NS . ':arrayType');
$xsdAttribute->setAttributeNS(
Wsdl::WSDL_NS_URI,
'arrayType',
Wsdl::TYPES_NS . ':' . $this->getContext()->translateType($singularType) . '[]'
);
return $xsdComplexType;
}
/**
* From a nested definition with type[], get the singular PHP Type
*
* @param string $type
* @return string
*/
protected function getSingularPhpType($type)
{
return str_replace('[]', '', $type);
}
/**
* Return the array nesting level based on the type name
*
* @param string $type
* @return int
*/
protected function getNestedCount($type)
{
return substr_count($type, '[]');
}
}

View File

@@ -0,0 +1,130 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\Wsdl\ComplexTypeStrategy;
use Zend\Soap\Wsdl;
class ArrayOfTypeSequence extends DefaultComplexType
{
/**
* Add an unbounded ArrayOfType based on the xsd:sequence syntax if
* type[] is detected in return value doc comment.
*
* @param string $type
* @return string tns:xsd-type
*/
public function addComplexType($type)
{
$nestedCounter = $this->getNestedCount($type);
if ($nestedCounter > 0) {
$singularType = $this->getSingularType($type);
$complexType = '';
for ($i = 1; $i <= $nestedCounter; $i++) {
$complexType = $this->getTypeBasedOnNestingLevel($singularType, $i);
$complexTypePhp = $singularType . str_repeat('[]', $i);
$childType = $this->getTypeBasedOnNestingLevel($singularType, $i-1);
$this->addSequenceType($complexType, $childType, $complexTypePhp);
}
return $complexType;
}
if (($soapType = $this->scanRegisteredTypes($type)) !== null) {
// Existing complex type
return $soapType;
}
// New singular complex type
return parent::addComplexType($type);
}
/**
* Return the ArrayOf or simple type name based on the singular xsdtype
* and the nesting level
*
* @param string $singularType
* @param int $level
* @return string
*/
protected function getTypeBasedOnNestingLevel($singularType, $level)
{
if ($level == 0) {
// This is not an Array anymore, return the xsd simple type
return $this->getContext()->getType($singularType);
}
return Wsdl::TYPES_NS
. ':'
. str_repeat('ArrayOf', $level)
. ucfirst($this->getContext()->translateType($singularType));
}
/**
* From a nested definition with type[], get the singular xsd:type
*
* @param string $type
* @return string
*/
protected function getSingularType($type)
{
return str_replace('[]', '', $type);
}
/**
* Return the array nesting level based on the type name
*
* @param string $type
* @return int
*/
protected function getNestedCount($type)
{
return substr_count($type, '[]');
}
/**
* Append the complex type definition to the WSDL via the context access
*
* @param string $arrayType Array type name (e.g. 'tns:ArrayOfArrayOfInt')
* @param string $childType Qualified array items type (e.g. 'xsd:int', 'tns:ArrayOfInt')
* @param string $phpArrayType PHP type (e.g. 'int[][]', '\MyNamespace\MyClassName[][][]')
*/
protected function addSequenceType($arrayType, $childType, $phpArrayType)
{
if ($this->scanRegisteredTypes($phpArrayType) !== null) {
return;
}
// Register type here to avoid recursion
$this->getContext()->addType($phpArrayType, $arrayType);
$dom = $this->getContext()->toDomDocument();
$arrayTypeName = substr($arrayType, strpos($arrayType, ':') + 1);
$complexType = $dom->createElementNS(Wsdl::XSD_NS_URI, 'complexType');
$this->getContext()->getSchema()->appendChild($complexType);
$complexType->setAttribute('name', $arrayTypeName);
$sequence = $dom->createElementNS(Wsdl::XSD_NS_URI, 'sequence');
$complexType->appendChild($sequence);
$element = $dom->createElementNS(Wsdl::XSD_NS_URI, 'element');
$sequence->appendChild($element);
$element->setAttribute('name', 'item');
$element->setAttribute('type', $childType);
$element->setAttribute('minOccurs', 0);
$element->setAttribute('maxOccurs', 'unbounded');
}
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\Wsdl\ComplexTypeStrategy;
use Zend\Soap\Wsdl;
/**
* Interface strategies that generate an XSD-Schema for complex data types in WSDL files.
*/
interface ComplexTypeStrategyInterface
{
/**
* Method accepts the current WSDL context file.
*
* @param Wsdl $context
*/
public function setContext(Wsdl $context);
/**
* Create a complex type based on a strategy
*
* @param string $type
* @return string XSD type
*/
public function addComplexType($type);
}

View File

@@ -0,0 +1,154 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\Wsdl\ComplexTypeStrategy;
use Zend\Soap\Exception;
use Zend\Soap\Wsdl;
use Zend\Soap\Wsdl\ComplexTypeStrategy\ComplexTypeStrategyInterface as ComplexTypeStrategy;
class Composite implements ComplexTypeStrategy
{
/**
* Typemap of Complex Type => Strategy pairs.
* @var array
*/
protected $typeMap = [];
/**
* Default Strategy of this composite
* @var string|ComplexTypeStrategy
*/
protected $defaultStrategy;
/**
* Context WSDL file that this composite serves
* @var Wsdl|null
*/
protected $context;
/**
* Construct Composite WSDL Strategy.
*
* @param array $typeMap
* @param string|ComplexTypeStrategy $defaultStrategy
*/
public function __construct(
array $typeMap = [],
$defaultStrategy = 'Zend\Soap\Wsdl\ComplexTypeStrategy\DefaultComplexType'
) {
foreach ($typeMap as $type => $strategy) {
$this->connectTypeToStrategy($type, $strategy);
}
$this->defaultStrategy = $defaultStrategy;
}
/**
* Connect a complex type to a given strategy.
*
* @param string $type
* @param string|ComplexTypeStrategy $strategy
* @return Composite
* @throws Exception\InvalidArgumentException
*/
public function connectTypeToStrategy($type, $strategy)
{
if (!is_string($type)) {
throw new Exception\InvalidArgumentException('Invalid type given to Composite Type Map.');
}
$this->typeMap[$type] = $strategy;
return $this;
}
/**
* Return default strategy of this composite
*
* @return ComplexTypeStrategy
* @throws Exception\InvalidArgumentException
*/
public function getDefaultStrategy()
{
$strategy = $this->defaultStrategy;
if (is_string($strategy) && class_exists($strategy)) {
$strategy = new $strategy;
}
if (!($strategy instanceof ComplexTypeStrategy)) {
throw new Exception\InvalidArgumentException(
'Default Strategy for Complex Types is not a valid strategy object.'
);
}
$this->defaultStrategy = $strategy;
return $strategy;
}
/**
* Return specific strategy or the default strategy of this type.
*
* @param string $type
* @return ComplexTypeStrategy
* @throws Exception\InvalidArgumentException
*/
public function getStrategyOfType($type)
{
if (isset($this->typeMap[$type])) {
$strategy = $this->typeMap[$type];
if (is_string($strategy) && class_exists($strategy)) {
$strategy = new $strategy();
}
if (!($strategy instanceof ComplexTypeStrategy)) {
throw new Exception\InvalidArgumentException(sprintf(
'Strategy for Complex Type "%s" is not a valid strategy object.',
$type
));
}
$this->typeMap[$type] = $strategy;
} else {
$strategy = $this->getDefaultStrategy();
}
return $strategy;
}
/**
* Method accepts the current WSDL context file.
*
* @param Wsdl $context
* @return Composite
*/
public function setContext(Wsdl $context)
{
$this->context = $context;
return $this;
}
/**
* Create a complex type based on a strategy
*
* @param string $type
* @return string XSD type
* @throws Exception\InvalidArgumentException
*/
public function addComplexType($type)
{
if (!($this->context instanceof Wsdl)) {
throw new Exception\InvalidArgumentException(sprintf(
'Cannot add complex type "%s", no context is set for this composite strategy.',
$type
));
}
$strategy = $this->getStrategyOfType($type);
$strategy->setContext($this->context);
return $strategy->addComplexType($type);
}
}

View File

@@ -0,0 +1,81 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Soap\Wsdl\ComplexTypeStrategy;
use ReflectionClass;
use Zend\Soap\Exception;
use Zend\Soap\Wsdl;
class DefaultComplexType extends AbstractComplexTypeStrategy
{
/**
* Add a complex type by recursively 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
* @throws Exception\InvalidArgumentException if class does not exist
*/
public function addComplexType($type)
{
if (!class_exists($type)) {
throw new Exception\InvalidArgumentException(sprintf(
'Cannot add a complex type %s that is not an object or where '
. 'class could not be found in "DefaultComplexType" strategy.',
$type
));
}
$class = new ReflectionClass($type);
$phpType = $class->getName();
if (($soapType = $this->scanRegisteredTypes($phpType)) !== null) {
return $soapType;
}
$dom = $this->getContext()->toDomDocument();
$soapTypeName = $this->getContext()->translateType($phpType);
$soapType = Wsdl::TYPES_NS . ':' . $soapTypeName;
// Register type here to avoid recursion
$this->getContext()->addType($phpType, $soapType);
$defaultProperties = $class->getDefaultProperties();
$complexType = $dom->createElementNS(Wsdl::XSD_NS_URI, 'complexType');
$complexType->setAttribute('name', $soapTypeName);
$all = $dom->createElementNS(Wsdl::XSD_NS_URI, 'all');
foreach ($class->getProperties() as $property) {
if ($property->isPublic() && preg_match_all('/@var\s+([^\s]+)/m', $property->getDocComment(), $matches)) {
/**
* @todo check if 'xsd:element' must be used here (it may not be
* compatible with using 'complexType' node for describing other
* classes used as attribute types for current class
*/
$element = $dom->createElementNS(Wsdl::XSD_NS_URI, 'element');
$element->setAttribute('name', $propertyName = $property->getName());
$element->setAttribute('type', $this->getContext()->getType(trim($matches[1][0])));
// If the default value is null, then this property is nillable.
if ($defaultProperties[$propertyName] === null) {
$element->setAttribute('nillable', 'true');
}
$all->appendChild($element);
}
}
$complexType->appendChild($all);
$this->getContext()->getSchema()->appendChild($complexType);
return $soapType;
}
}