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,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;
}
}