diff --git a/composer.json b/composer.json index 148c708d..0c3f9e3c 100644 --- a/composer.json +++ b/composer.json @@ -29,8 +29,8 @@ }, "extra": { "branch-alias": { - "dev-master": "2.4-dev", - "dev-develop": "2.5-dev" + "dev-master": "2.2-dev", + "dev-develop": "2.3-dev" } }, "autoload-dev": { diff --git a/src/AutoDiscover.php b/src/AutoDiscover.php index 47f12cca..2f0ecea5 100644 --- a/src/AutoDiscover.php +++ b/src/AutoDiscover.php @@ -3,28 +3,20 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap; use Zend\Server\Reflection; -use Zend\Server\Reflection\AbstractFunction; use Zend\Soap\AutoDiscover\DiscoveryStrategy\DiscoveryStrategyInterface as DiscoveryStrategy; use Zend\Soap\AutoDiscover\DiscoveryStrategy\ReflectionDiscovery; +use Zend\Soap\Exception; use Zend\Soap\Wsdl; use Zend\Soap\Wsdl\ComplexTypeStrategy\ComplexTypeStrategyInterface as ComplexTypeStrategy; use Zend\Uri; -/** - * \Zend\Soap\AutoDiscover - * - * @category Zend - * @package Zend_Soap - * @subpackage AutoDiscover - */ class AutoDiscover { /** @@ -33,67 +25,65 @@ class AutoDiscover protected $serviceName; /** - * @var \Zend\Server\Reflection + * @var Reflection */ protected $reflection = null; /** * Service function names - * * @var array */ protected $functions = array(); /** * Service class name - * * @var string */ protected $class; /** - * @var boolean + * @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 = array('use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/"); + protected $operationBodyStyle = array( + 'use' => 'encoded', + 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/" + ); /** * soap:operation style - * * @var array */ - protected $bindingStyle = array('style' => 'rpc', 'transport' => 'http://schemas.xmlsoap.org/soap/http'); + protected $bindingStyle = array( + '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 = array(); /** * Discovery strategy for types and other method details. - * * @var DiscoveryStrategy */ protected $discoveryStrategy; @@ -101,34 +91,37 @@ class AutoDiscover /** * Constructor * - * @param ComplexTypeStrategy $strategy - * @param string|Uri\Uri $endpointUri - * @param string $wsdlClass - * @param array $classMap - */ - public function __construct(ComplexTypeStrategy $strategy = null, $endpointUri=null, $wsdlClass=null, array $classMap = array()) - { + * @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 = array() + ) { $this->reflection = new Reflection(); - $this->discoveryStrategy = new ReflectionDiscovery(); + $this->setDiscoveryStrategy(new ReflectionDiscovery()); - if ($strategy !== null) { + if (null !== $strategy) { $this->setComplexTypeStrategy($strategy); } - - if ($endpointUri !== null) { + if (null !== $endpointUri) { $this->setUri($endpointUri); } - - if ($wsdlClass !== null) { + if (null !== $wsdlClass) { $this->setWsdlClass($wsdlClass); } + $this->setClassMap($classMap); } /** * Set the discovery strategy for method type and other information. * * @param DiscoveryStrategy $discoveryStrategy - * @return AutoDiscover + * @return self */ public function setDiscoveryStrategy(DiscoveryStrategy $discoveryStrategy) { @@ -137,6 +130,8 @@ public function setDiscoveryStrategy(DiscoveryStrategy $discoveryStrategy) } /** + * Get the discovery strategy. + * * @return DiscoveryStrategy */ public function getDiscoveryStrategy() @@ -145,7 +140,7 @@ public function getDiscoveryStrategy() } /** - * Get the class map of php to wsdl qname types. + * Get the class map of php to wsdl mappings. * * @return array */ @@ -155,10 +150,22 @@ public function getClassMap() } /** - * Set the class map of php to wsdl qname types. + * 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; } @@ -167,10 +174,19 @@ public function setClassMap($classMap) * Set service name * * @param string $serviceName - * @return AutoDiscover + * @return self + * @throws Exception\InvalidArgumentException */ public function setServiceName($serviceName) { + $matches = array(); + + // 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; } @@ -185,35 +201,38 @@ public function getServiceName() { if (!$this->serviceName) { if ($this->class) { - return $this->reflection->reflectClass($this->class) - ->getShortName(); + return $this->reflection->reflectClass($this->class)->getShortName(); } else { - throw new Exception\RuntimeException( - "No service name given. Call Autodiscover::setServiceName()." - ); + throw new Exception\RuntimeException('No service name given. Call AutoDiscover::setServiceName().'); } } - return $this->serviceName; } /** - * Set the location at which the WSDL file will be availabe. + * Set the location at which the WSDL file will be available. * * @param Uri\Uri|string $uri - * @return AutoDiscover + * @return self * @throws Exception\InvalidArgumentException */ public function setUri($uri) { if (!is_string($uri) && !($uri instanceof Uri\Uri)) { throw new Exception\InvalidArgumentException( - 'No uri given to \Zend\Soap\AutoDiscover::setUri as string or \Zend\Uri\Uri instance.' + 'Argument to \Zend\Soap\AutoDiscover::setUri should be string or \Zend\Uri\Uri instance.' ); } - $this->uri = $uri; + $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; } @@ -226,12 +245,13 @@ public function setUri($uri) 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()."); + 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; } @@ -239,18 +259,18 @@ public function getUri() * Set the name of the WSDL handling class. * * @param string $wsdlClass - * @return AutoDiscover + * @return self * @throws Exception\InvalidArgumentException */ public function setWsdlClass($wsdlClass) { - if (!is_string($wsdlClass) && !is_subclass_of($wsdlClass, 'Zend\Soap\Wsdl')) { + 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; + $this->wsdlClass = $wsdlClass; return $this; } @@ -271,13 +291,13 @@ public function getWsdlClass() * 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/". * * @param array $operationStyle - * @return AutoDiscover + * @return self * @throws Exception\InvalidArgumentException */ - public function setOperationBodyStyle(array $operationStyle=array()) + public function setOperationBodyStyle(array $operationStyle = array()) { if (!isset($operationStyle['use'])) { - throw new Exception\InvalidArgumentException("Key 'use' is required in Operation soap:body style."); + throw new Exception\InvalidArgumentException('Key "use" is required in Operation soap:body style.'); } $this->operationBodyStyle = $operationStyle; return $this; @@ -289,9 +309,9 @@ public function setOperationBodyStyle(array $operationStyle=array()) * By default 'style' is 'rpc' and 'transport' is 'http://schemas.xmlsoap.org/soap/http'. * * @param array $bindingStyle - * @return AutoDiscover + * @return self */ - public function setBindingStyle(array $bindingStyle=array()) + public function setBindingStyle(array $bindingStyle = array()) { if (isset($bindingStyle['style'])) { $this->bindingStyle['style'] = $bindingStyle['style']; @@ -306,12 +326,11 @@ public function setBindingStyle(array $bindingStyle=array()) * Set the strategy that handles functions and classes that are added AFTER this call. * * @param ComplexTypeStrategy $strategy - * @return AutoDiscover + * @return self */ public function setComplexTypeStrategy(ComplexTypeStrategy $strategy) { $this->strategy = $strategy; - return $this; } @@ -319,7 +338,7 @@ public function setComplexTypeStrategy(ComplexTypeStrategy $strategy) * Set the Class the SOAP server will use * * @param string $class Class Name - * @return AutoDiscover + * @return self */ public function setClass($class) { @@ -330,12 +349,30 @@ public function setClass($class) /** * Add a Single or Multiple Functions to the WSDL * - * @param string $function Function Name - * @return AutoDiscover + * @param string $function Function Name + * @return self + * @throws Exception\InvalidArgumentException */ public function addFunction($function) { - $this->functions[] = $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; } @@ -360,14 +397,13 @@ protected function _generateFunctions() 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 + * @param array $reflectionMethods * @return Wsdl */ protected function _generateWsdl(array $reflectionMethods) @@ -375,16 +411,17 @@ 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', 'tns:' . $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', 'tns:' . $serviceName . 'Binding', $uri); + $wsdl->addService($serviceName . 'Service', $serviceName . 'Port', Wsdl::TYPES_NS . ':' . $serviceName . 'Binding', $uri); foreach ($reflectionMethods as $method) { $this->_addFunctionToWsdl($method, $wsdl, $port, $binding); @@ -396,12 +433,11 @@ protected function _generateWsdl(array $reflectionMethods) /** * Add a function to the WSDL document. * - * @param $function \Zend\Server\Reflection\AbstractFunction function to add - * @param $wsdl \Zend\Soap\Wsdl WSDL document - * @param $port object wsdl:portType - * @param $binding object wsdl:binding + * @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 - * @return void */ protected function _addFunctionToWsdl($function, $wsdl, $port, $binding) { @@ -418,7 +454,10 @@ protected function _addFunctionToWsdl($function, $wsdl, $port, $binding) } } if ($prototype === null) { - throw new Exception\InvalidArgumentException("No prototypes could be found for the '" . $function->getName() . "' function"); + throw new Exception\InvalidArgumentException(sprintf( + 'No prototypes could be found for the "%s" function', + $function->getName() + )); } $functionName = $wsdl->translateType($function->getName()); @@ -438,16 +477,21 @@ protected function _addFunctionToWsdl($function, $wsdl, $port, $binding) } $sequence[] = $sequenceElement; } + $element = array( - 'name' => $functionName, - 'sequence' => $sequence + 'name' => $functionName, + 'sequence' => $sequence ); + // Add the wrapper element part, which must be named 'parameters' $args['parameters'] = array('element' => $wsdl->addElement($element)); + } else { // RPC style: add each parameter as a typed part foreach ($prototype->getParameters() as $param) { - $args[$param->getName()] = array('type' => $wsdl->getType($this->discoveryStrategy->getFunctionParameterType($param))); + $args[$param->getName()] = array( + 'type' => $wsdl->getType($this->discoveryStrategy->getFunctionParameterType($param)) + ); } } $wsdl->addMessage($functionName . 'In', $args); @@ -466,31 +510,47 @@ protected function _addFunctionToWsdl($function, $wsdl, $port, $binding) 'type' => $wsdl->getType($this->discoveryStrategy->getFunctionReturnType($function, $prototype)) ); } + $element = array( - 'name' => $functionName . 'Response', - 'sequence' => $sequence + 'name' => $functionName . 'Response', + 'sequence' => $sequence ); + // Add the wrapper element part, which must be named 'parameters' $args['parameters'] = array('element' => $wsdl->addElement($element)); + } elseif ($prototype->getReturnType() != "void") { // RPC style: add the return value as a typed part - $args['return'] = array('type' => $wsdl->getType($this->discoveryStrategy->getFunctionReturnType($function, $prototype))); + $args['return'] = array( + '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, 'tns:' . $functionName . 'In', 'tns:' . $functionName . 'Out'); + $portOperation = $wsdl->addPortOperation( + $port, + $functionName, + Wsdl::TYPES_NS . ':' . $functionName . 'In', Wsdl::TYPES_NS . ':' . $functionName . 'Out' + ); } else { - $portOperation = $wsdl->addPortOperation($port, $functionName, 'tns:' . $functionName . 'In', false); + $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) + // 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; @@ -508,13 +568,13 @@ protected function _addFunctionToWsdl($function, $wsdl, $port, $binding) /** * Generate the WSDL file from the configured input. * - * @throws Exception\RuntimeException * @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."); + throw new Exception\RuntimeException('Can either dump functions or a class as a service, not both.'); } if ($this->class) { @@ -529,9 +589,9 @@ public function generate() /** * Proxy to WSDL dump function * - * @param string $filename + * @param string $filename * @return bool - * @throws \Zend\Soap\Exception\RuntimeException + * @throws Exception\RuntimeException */ public function dump($filename) { @@ -542,10 +602,19 @@ public function dump($filename) * Proxy to WSDL toXml() function * * @return string - * @throws \Zend\Soap\Exception\RuntimeException + * @throws Exception\RuntimeException */ public function toXml() { return $this->generate()->toXml(); } + + /** + * Handle WSDL document. + */ + public function handle() + { + header('Content-Type: text/xml'); + echo $this->toXml(); + } } diff --git a/src/AutoDiscover/DiscoveryStrategy/DiscoveryStrategyInterface.php b/src/AutoDiscover/DiscoveryStrategy/DiscoveryStrategyInterface.php index e02ff7f8..22f33dbf 100644 --- a/src/AutoDiscover/DiscoveryStrategy/DiscoveryStrategyInterface.php +++ b/src/AutoDiscover/DiscoveryStrategy/DiscoveryStrategyInterface.php @@ -3,9 +3,8 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap\AutoDiscover\DiscoveryStrategy; @@ -15,11 +14,8 @@ use Zend\Server\Reflection\ReflectionParameter; /** - * Describes how types, return values and method details are detected during AutoDiscovery of a WSDL. - * - * @category Zend - * @package Zend_Soap - * @subpackage WSDL + * Describes how types, return values and method details are detected during + * AutoDiscovery of a WSDL. */ interface DiscoveryStrategyInterface { @@ -28,7 +24,7 @@ interface DiscoveryStrategyInterface * * Default implementation assumes the default param doc-block tag. * - * @param ReflectionParameter $param + * @param ReflectionParameter $param * @return string */ public function getFunctionParameterType(ReflectionParameter $param); @@ -38,8 +34,8 @@ public function getFunctionParameterType(ReflectionParameter $param); * * Default implementation assumes the value of the return doc-block tag. * - * @param AbstractFunction $function - * @param Prototype $prototype + * @param AbstractFunction $function + * @param Prototype $prototype * @return string */ public function getFunctionReturnType(AbstractFunction $function, Prototype $prototype); @@ -49,8 +45,8 @@ public function getFunctionReturnType(AbstractFunction $function, Prototype $pro * * Default implementation assumes one-way, when return value is "void". * - * @param AbstractFunction $function - * @param Prototype $prototype + * @param AbstractFunction $function + * @param Prototype $prototype * @return bool */ public function isFunctionOneWay(AbstractFunction $function, Prototype $prototype); @@ -60,7 +56,7 @@ public function isFunctionOneWay(AbstractFunction $function, Prototype $prototyp * * Default implementation uses docblock description. * - * @param AbstractFunction $function + * @param AbstractFunction $function * @return string */ public function getFunctionDocumentation(AbstractFunction $function); diff --git a/src/AutoDiscover/DiscoveryStrategy/ReflectionDiscovery.php b/src/AutoDiscover/DiscoveryStrategy/ReflectionDiscovery.php index bc025d2b..44559de9 100644 --- a/src/AutoDiscover/DiscoveryStrategy/ReflectionDiscovery.php +++ b/src/AutoDiscover/DiscoveryStrategy/ReflectionDiscovery.php @@ -3,9 +3,8 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap\AutoDiscover\DiscoveryStrategy; @@ -15,30 +14,52 @@ use Zend\Server\Reflection\ReflectionParameter; /** - * Describes how types, return values and method details are detected during AutoDiscovery of a WSDL. - * - * @category Zend - * @package Zend_Soap - * @subpackage WSDL + * 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'; diff --git a/src/Client.php b/src/Client.php index daf34b54..ef9b9ef0 100644 --- a/src/Client.php +++ b/src/Client.php @@ -3,9 +3,8 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap; @@ -16,27 +15,20 @@ use Zend\Server\Client as ServerClient; use Zend\Stdlib\ArrayUtils; -/** - * \Zend\Soap\Client - * - * @category Zend - * @package Zend_Soap - * @subpackage Client - */ class Client implements ServerClient { - /** - * Encoding - * @var string - */ - protected $encoding = 'UTF-8'; - /** * Array of SOAP type => PHP class pairings for handling return/incoming values * @var array */ protected $classmap = null; + /** + * Encoding + * @var string + */ + protected $encoding = 'UTF-8'; + /** * Registered fault exceptions * @var array @@ -44,83 +36,82 @@ class Client implements ServerClient protected $faultExceptions = array(); /** - * SOAP version to use; SOAP_1_2 by default, to allow processing of headers - * @var int + * Last invoked method + * @var string */ - protected $soapVersion = SOAP_1_2; - - /** Set of other SoapClient options */ - protected $uri = null; - protected $location = null; - protected $style = null; - protected $use = null; - protected $login = null; - protected $password = null; - protected $proxy_host = null; - protected $proxy_port = null; - protected $proxy_login = null; - protected $proxy_password = null; - protected $local_cert = null; - protected $passphrase = null; - protected $compression = null; - protected $connection_timeout = null; - protected $stream_context = null; - protected $features = null; - protected $cache_wsdl = null; - protected $user_agent = null; + protected $lastMethod = ''; /** - * WSDL used to access server - * It also defines Client working mode (WSDL vs non-WSDL) - * - * @var string + * Permanent SOAP request headers (shared between requests). + * @var array */ - protected $wsdl = null; + protected $permanentSoapInputHeaders = array(); /** * SoapClient object - * * @var SoapClient */ protected $soapClient; /** - * Last invoked method - * - * @var string + * Array of SoapHeader objects + * @var SoapHeader[] */ - protected $lastMethod = ''; + protected $soapInputHeaders = array(); /** - * SOAP request headers. - * * Array of SoapHeader objects - * * @var array */ - protected $soapInputHeaders = array(); + protected $soapOutputHeaders = array(); /** - * Permanent SOAP request headers (shared between requests). - * - * Array of SoapHeader objects - * - * @var array + * SOAP version to use; SOAP_1_2 by default, to allow processing of headers + * @var int */ - protected $permanentSoapInputHeaders = array(); + protected $soapVersion = SOAP_1_2; /** - * Output SOAP headers. - * - * Array of SoapHeader objects - * * @var array */ - protected $soapOutputHeaders = array(); + protected $typemap = null; + + /** + * WSDL used to access server + * It also defines Client working mode (WSDL vs non-WSDL) + * @var string + */ + protected $wsdl = null; + + /**#@+ + * @var string + */ + protected $connectionTimeout = null; + protected $localCert = null; + protected $location = null; + protected $login = null; + protected $passphrase = null; + protected $password = null; + protected $proxyHost = null; + protected $proxyLogin = null; + protected $proxyPassword = null; + protected $proxyPort = null; + protected $streamContext = null; + protected $style = null; + protected $uri = null; + protected $use = null; + protected $userAgent = null; + /**#@-*/ + + /**#@+ + * @var int + */ + protected $cacheWsdl = null; + protected $compression = null; + protected $features = null; + /**#@-*/ /** - * Constructor - * * @param string $wsdl * @param array|Traversable $options * @throws Exception\ExtensionNotLoadedException @@ -142,8 +133,8 @@ public function __construct($wsdl = null, $options = null) /** * Set wsdl * - * @param string $wsdl - * @return Client + * @param string $wsdl + * @return self */ public function setWSDL($wsdl) { @@ -169,7 +160,7 @@ public function getWSDL() * Allows setting options as an associative array of option => value pairs. * * @param array|Traversable $options - * @return Client + * @return self * @throws Exception\InvalidArgumentException */ public function setOptions($options) @@ -179,78 +170,102 @@ public function setOptions($options) } foreach ($options as $key => $value) { - switch ($key) { + switch (strtolower($key)) { case 'classmap': - case 'classMap': + case 'class_map': $this->setClassmap($value); break; + case 'encoding': $this->setEncoding($value); break; + case 'soapVersion': case 'soap_version': $this->setSoapVersion($value); break; + case 'wsdl': $this->setWSDL($value); break; + case 'uri': $this->setUri($value); break; + case 'location': $this->setLocation($value); break; + case 'style': $this->setStyle($value); break; + case 'use': $this->setEncodingMethod($value); break; + case 'login': $this->setHttpLogin($value); break; + case 'password': $this->setHttpPassword($value); break; + case 'proxy_host': $this->setProxyHost($value); break; + case 'proxy_port': $this->setProxyPort($value); break; + case 'proxy_login': $this->setProxyLogin($value); break; + case 'proxy_password': $this->setProxyPassword($value); break; + case 'local_cert': $this->setHttpsCertificate($value); break; + case 'passphrase': $this->setHttpsCertPassphrase($value); break; + case 'compression': $this->setCompressionOptions($value); break; + case 'stream_context': $this->setStreamContext($value); break; + case 'features': $this->setSoapFeatures($value); break; + case 'cache_wsdl': $this->setWSDLCache($value); break; + case 'useragent': - case 'userAgent': case 'user_agent': $this->setUserAgent($value); break; + case 'type_map': + case 'typemap': + $this->setTypemap($value); + break; + // Not used now // case 'connection_timeout': - // $this->connection_timeout = $value; + // $this->connectionTimeout = $value; // break; default: @@ -272,6 +287,7 @@ public function getOptions() $options = array(); $options['classmap'] = $this->getClassmap(); + $options['typemap'] = $this->getTypemap(); $options['encoding'] = $this->getEncoding(); $options['soap_version'] = $this->getSoapVersion(); $options['wsdl'] = $this->getWSDL(); @@ -288,7 +304,7 @@ public function getOptions() $options['local_cert'] = $this->getHttpsCertificate(); $options['passphrase'] = $this->getHttpsCertPassphrase(); $options['compression'] = $this->getCompressionOptions(); - //$options['connection_timeout'] = $this->connection_timeout; + //$options['connection_timeout'] = $this->connectionTimeout; $options['stream_context'] = $this->getStreamContext(); $options['cache_wsdl'] = $this->getWSDLCache(); $options['features'] = $this->getSoapFeatures(); @@ -317,18 +333,19 @@ public function getOptions() * Set SOAP version * * @param int $version One of the SOAP_1_1 or SOAP_1_2 constants - * @return Client - * @throws Exception\ExceptionInterface with invalid soap version argument + * @return self + * @throws Exception\InvalidArgumentException with invalid soap version argument */ public function setSoapVersion($version) { if (!in_array($version, array(SOAP_1_1, SOAP_1_2))) { - throw new Exception\InvalidArgumentException('Invalid soap version specified. Use SOAP_1_1 or SOAP_1_2 constants.'); + throw new Exception\InvalidArgumentException( + 'Invalid soap version specified. Use SOAP_1_1 or SOAP_1_2 constants.' + ); } - $this->soapVersion = $version; - - $this->soapClient = null; + $this->soapVersion = $version; + $this->soapClient = null; return $this; } @@ -346,20 +363,19 @@ public function getSoapVersion() * Set classmap * * @param array $classmap - * @return Client - * @throws Exception\ExceptionInterface for any invalid class in the class map + * @return self + * @throws Exception\InvalidArgumentException for any invalid class in the class map */ public function setClassmap(array $classmap) { foreach ($classmap as $class) { if (!class_exists($class)) { - throw new Exception\InvalidArgumentException('Invalid class in class map'); + throw new Exception\InvalidArgumentException('Invalid class in class map: ' . $class); } } $this->classmap = $classmap; $this->soapClient = null; - return $this; } @@ -373,12 +389,45 @@ public function getClassmap() return $this->classmap; } + /** + * Set typemap with xml to php type mappings with appropriate validation. + * + * @param array $typeMap + * @return self + * @throws Exception\InvalidArgumentException + */ + public function setTypemap(array $typeMap) + { + foreach ($typeMap as $type) { + if (!is_callable($type['from_xml'])) { + throw new Exception\InvalidArgumentException('Invalid from_xml callback for type: ' . $type['type_name']); + } + if (!is_callable($type['to_xml'])) { + throw new Exception\InvalidArgumentException('Invalid to_xml callback for type: ' . $type['type_name']); + } + } + + $this->typemap = $typeMap; + $this->soapClient = null; + return $this; + } + + /** + * Retrieve typemap + * + * @return array + */ + public function getTypemap() + { + return $this->typemap; + } + /** * Set encoding * * @param string $encoding - * @return Client - * @throws Exception\ExceptionInterface with invalid encoding argument + * @return self + * @throws Exception\InvalidArgumentException with invalid encoding argument */ public function setEncoding($encoding) { @@ -386,10 +435,8 @@ public function setEncoding($encoding) throw new Exception\InvalidArgumentException('Invalid encoding specified'); } - $this->encoding = $encoding; - + $this->encoding = $encoding; $this->soapClient = null; - return $this; } @@ -407,8 +454,8 @@ public function getEncoding() * Check for valid URN * * @param string $urn - * @return boolean - * @throws Exception\ExceptionInterface on invalid URN + * @return bool + * @throws Exception\InvalidArgumentException on invalid URN */ public function validateUrn($urn) { @@ -416,9 +463,7 @@ public function validateUrn($urn) if ($scheme === false || $scheme === null) { throw new Exception\InvalidArgumentException('Invalid URN'); } - return true; - } /** @@ -427,16 +472,14 @@ public function validateUrn($urn) * URI in Web Service the target namespace * * @param string $uri - * @return Client - * @throws Exception\ExceptionInterface with invalid uri argument + * @return self + * @throws Exception\InvalidArgumentException with invalid uri argument */ public function setUri($uri) { $this->validateUrn($uri); - $this->uri = $uri; - + $this->uri = $uri; $this->soapClient = null; - return $this; } @@ -456,16 +499,14 @@ public function getUri() * URI in Web Service the target namespace * * @param string $location - * @return Client - * @throws Exception\ExceptionInterface with invalid uri argument + * @return self + * @throws Exception\InvalidArgumentException with invalid uri argument */ public function setLocation($location) { $this->validateUrn($location); - $this->location = $location; - + $this->location = $location; $this->soapClient = null; - return $this; } @@ -483,19 +524,19 @@ public function getLocation() * Set request style * * @param int $style One of the SOAP_RPC or SOAP_DOCUMENT constants - * @return Client - * @throws Exception\ExceptionInterface with invalid style argument + * @return self + * @throws Exception\InvalidArgumentException with invalid style argument */ public function setStyle($style) { if (!in_array($style, array(SOAP_RPC, SOAP_DOCUMENT))) { - throw new Exception\InvalidArgumentException('Invalid request style specified. Use SOAP_RPC or SOAP_DOCUMENT constants.'); + throw new Exception\InvalidArgumentException( + 'Invalid request style specified. Use SOAP_RPC or SOAP_DOCUMENT constants.' + ); } - $this->style = $style; - + $this->style = $style; $this->soapClient = null; - return $this; } @@ -513,19 +554,19 @@ public function getStyle() * Set message encoding method * * @param int $use One of the SOAP_ENCODED or SOAP_LITERAL constants - * @return Client - * @throws Exception\ExceptionInterface with invalid message encoding method argument + * @return self + * @throws Exception\InvalidArgumentException with invalid message encoding method argument */ public function setEncodingMethod($use) { if (!in_array($use, array(SOAP_ENCODED, SOAP_LITERAL))) { - throw new Exception\InvalidArgumentException('Invalid message encoding method. Use SOAP_ENCODED or SOAP_LITERAL constants.'); + throw new Exception\InvalidArgumentException( + 'Invalid message encoding method. Use SOAP_ENCODED or SOAP_LITERAL constants.' + ); } - $this->use = $use; - + $this->use = $use; $this->soapClient = null; - return $this; } @@ -543,14 +584,12 @@ public function getEncodingMethod() * Set HTTP login * * @param string $login - * @return Client + * @return self */ public function setHttpLogin($login) { - $this->login = $login; - + $this->login = $login; $this->soapClient = null; - return $this; } @@ -568,14 +607,12 @@ public function getHttpLogin() * Set HTTP password * * @param string $password - * @return Client + * @return self */ public function setHttpPassword($password) { - $this->password = $password; - + $this->password = $password; $this->soapClient = null; - return $this; } @@ -593,14 +630,12 @@ public function getHttpPassword() * Set proxy host * * @param string $proxyHost - * @return Client + * @return self */ public function setProxyHost($proxyHost) { - $this->proxy_host = $proxyHost; - + $this->proxyHost = $proxyHost; $this->soapClient = null; - return $this; } @@ -611,21 +646,19 @@ public function setProxyHost($proxyHost) */ public function getProxyHost() { - return $this->proxy_host; + return $this->proxyHost; } /** * Set proxy port * * @param int $proxyPort - * @return Client + * @return self */ public function setProxyPort($proxyPort) { - $this->proxy_port = (int) $proxyPort; - + $this->proxyPort = (int) $proxyPort; $this->soapClient = null; - return $this; } @@ -636,21 +669,19 @@ public function setProxyPort($proxyPort) */ public function getProxyPort() { - return $this->proxy_port; + return $this->proxyPort; } /** * Set proxy login * * @param string $proxyLogin - * @return Client + * @return self */ public function setProxyLogin($proxyLogin) { - $this->proxy_login = $proxyLogin; - + $this->proxyLogin = $proxyLogin; $this->soapClient = null; - return $this; } @@ -661,21 +692,19 @@ public function setProxyLogin($proxyLogin) */ public function getProxyLogin() { - return $this->proxy_login; + return $this->proxyLogin; } /** * Set proxy password * * @param string $proxyPassword - * @return Client + * @return self */ public function setProxyPassword($proxyPassword) { - $this->proxy_password = $proxyPassword; - - $this->soapClient = null; - + $this->proxyPassword = $proxyPassword; + $this->soapClient = null; return $this; } @@ -683,8 +712,8 @@ public function setProxyPassword($proxyPassword) * Set HTTPS client certificate path * * @param string $localCert local certificate path - * @return Client - * @throws Exception\ExceptionInterface with invalid local certificate path argument + * @return self + * @throws Exception\InvalidArgumentException with invalid local certificate path argument */ public function setHttpsCertificate($localCert) { @@ -692,10 +721,8 @@ public function setHttpsCertificate($localCert) throw new Exception\InvalidArgumentException('Invalid HTTPS client certificate path.'); } - $this->local_cert = $localCert; - + $this->localCert = $localCert; $this->soapClient = null; - return $this; } @@ -706,21 +733,19 @@ public function setHttpsCertificate($localCert) */ public function getHttpsCertificate() { - return $this->local_cert; + return $this->localCert; } /** * Set HTTPS client certificate passphrase * * @param string $passphrase - * @return Client + * @return self */ public function setHttpsCertPassphrase($passphrase) { $this->passphrase = $passphrase; - $this->soapClient = null; - return $this; } @@ -738,7 +763,7 @@ public function getHttpsCertPassphrase() * Set compression options * * @param int|null $compressionOptions - * @return Client + * @return self */ public function setCompressionOptions($compressionOptions) { @@ -769,14 +794,15 @@ public function getCompressionOptions() */ public function getProxyPassword() { - return $this->proxy_password; + return $this->proxyPassword; } /** * Set Stream Context * * @param resource $context - * @return Client + * @return self + * @throws Exception\InvalidArgumentException */ public function setStreamContext($context) { @@ -784,7 +810,7 @@ public function setStreamContext($context) throw new Exception\InvalidArgumentException('Invalid stream context resource given.'); } - $this->stream_context = $context; + $this->streamContext = $context; return $this; } @@ -795,19 +821,18 @@ public function setStreamContext($context) */ public function getStreamContext() { - return $this->stream_context; + return $this->streamContext; } /** * Set the SOAP Feature options. * * @param string|int $feature - * @return Client + * @return self */ public function setSoapFeatures($feature) { - $this->features = $feature; - + $this->features = $feature; $this->soapClient = null; return $this; } @@ -825,16 +850,18 @@ public function getSoapFeatures() /** * Set the SOAP WSDL Caching Options * - * @param string|int|boolean|null $caching - * @return Client + * @param string|int|bool|null $caching + * @return self */ public function setWSDLCache($caching) { + //@todo check WSDL_CACHE_* constants? if ($caching === null) { - $this->cache_wsdl = null; + $this->cacheWsdl = null; } else { - $this->cache_wsdl = (int) $caching; + $this->cacheWsdl = (int) $caching; } + return $this; } @@ -845,22 +872,23 @@ public function setWSDLCache($caching) */ public function getWSDLCache() { - return $this->cache_wsdl; + return $this->cacheWsdl; } /** * Set the string to use in User-Agent header * * @param string|null $userAgent - * @return Client + * @return self */ public function setUserAgent($userAgent) { if ($userAgent === null) { - $this->user_agent = null; + $this->userAgent = null; } else { - $this->user_agent = (string) $userAgent; + $this->userAgent = (string) $userAgent; } + return $this; } @@ -871,7 +899,7 @@ public function setUserAgent($userAgent) */ public function getUserAgent() { - return $this->user_agent; + return $this->userAgent; } /** @@ -898,7 +926,6 @@ public function getLastResponse() if ($this->soapClient !== null) { return $this->soapClient->__getLastResponse(); } - return ''; } @@ -912,7 +939,6 @@ public function getLastRequestHeaders() if ($this->soapClient !== null) { return $this->soapClient->__getLastRequestHeaders(); } - return ''; } @@ -926,7 +952,6 @@ public function getLastResponseHeaders() if ($this->soapClient !== null) { return $this->soapClient->__getLastResponseHeaders(); } - return ''; } @@ -945,22 +970,21 @@ public function getLastMethod() * * May be overridden in subclasses * - * @internal - * @param Client\Common $client - * @param string $request - * @param string $location - * @param string $action - * @param int $version - * @param int $one_way + * @param Client\Common $client + * @param string $request + * @param string $location + * @param string $action + * @param int $version + * @param int $oneWay * @return mixed */ - public function _doRequest(Client\Common $client, $request, $location, $action, $version, $one_way = null) + public function _doRequest(Client\Common $client, $request, $location,$action, $version, $oneWay = null) { // Perform request as is - if ($one_way === null) { - return call_user_func(array($client,'SoapClient::__doRequest'), $request, $location, $action, $version); + if ($oneWay === null) { + return call_user_func(array($client, 'SoapClient::__doRequest'), $request, $location, $action, $version); } - return call_user_func(array($client, 'SoapClient::__doRequest'), $request, $location, $action, $version, $one_way); + return call_user_func(array($client, 'SoapClient::__doRequest'), $request, $location, $action, $version, $oneWay); } /** @@ -975,17 +999,17 @@ protected function _initSoapClientObject() if ($wsdl == null) { if (!isset($options['location'])) { - throw new Exception\UnexpectedValueException('\'location\' parameter is required in non-WSDL mode.'); + throw new Exception\UnexpectedValueException('"location" parameter is required in non-WSDL mode.'); } if (!isset($options['uri'])) { - throw new Exception\UnexpectedValueException('\'uri\' parameter is required in non-WSDL mode.'); + throw new Exception\UnexpectedValueException('"uri" parameter is required in non-WSDL mode.'); } } else { if (isset($options['use'])) { - throw new Exception\UnexpectedValueException('\'use\' parameter only works in non-WSDL mode.'); + throw new Exception\UnexpectedValueException('"use" parameter only works in non-WSDL mode.'); } if (isset($options['style'])) { - throw new Exception\UnexpectedValueException('\'style\' parameter only works in non-WSDL mode.'); + throw new Exception\UnexpectedValueException('"style" parameter only works in non-WSDL mode.'); } } unset($options['wsdl']); @@ -999,7 +1023,7 @@ protected function _initSoapClientObject() * * My be overridden in descendant classes * - * @param array $arguments + * @param array $arguments * @return array */ protected function _preProcessArguments($arguments) @@ -1013,7 +1037,7 @@ protected function _preProcessArguments($arguments) * * My be overridden in descendant classes * - * @param array $result + * @param array $result * @return array */ protected function _preProcessResult($result) @@ -1025,9 +1049,9 @@ protected function _preProcessResult($result) /** * Add SOAP input header * - * @param SoapHeader $header - * @param boolean $permanent - * @return Client + * @param SoapHeader $header + * @param bool $permanent + * @return self */ public function addSoapInputHeader(SoapHeader $header, $permanent = false) { @@ -1036,20 +1060,18 @@ public function addSoapInputHeader(SoapHeader $header, $permanent = false) } else { $this->soapInputHeaders[] = $header; } - return $this; } /** * Reset SOAP input headers * - * @return Client + * @return self */ public function resetSoapInputHeaders() { $this->permanentSoapInputHeaders = array(); - $this->soapInputHeaders = array(); - + $this->soapInputHeaders = array(); return $this; } @@ -1066,13 +1088,13 @@ public function getLastSoapOutputHeaderObjects() /** * Perform a SOAP call * - * @param string $name - * @param array $arguments + * @param string $name + * @param array $arguments * @return mixed */ public function __call($name, $arguments) { - if(!is_array($arguments)) { + if (!is_array($arguments)) { $arguments = array($arguments); } $soapClient = $this->getSoapClient(); @@ -1080,11 +1102,13 @@ public function __call($name, $arguments) $this->lastMethod = $name; $soapHeaders = array_merge($this->permanentSoapInputHeaders, $this->soapInputHeaders); - $result = $soapClient->__soapCall($name, - $this->_preProcessArguments($arguments), - null, /* Options are already set to the SOAP client object */ - (count($soapHeaders) > 0)? $soapHeaders : null, - $this->soapOutputHeaders); + $result = $soapClient->__soapCall( + $name, + $this->_preProcessArguments($arguments), + null, /* Options are already set to the SOAP client object */ + (count($soapHeaders) > 0)? $soapHeaders : null, + $this->soapOutputHeaders + ); // Reset non-permanent input headers $this->soapInputHeaders = array(); @@ -1108,45 +1132,45 @@ public function call($method, $params = array()) * Return a list of available functions * * @return array - * @throws Exception\ExceptionInterface + * @throws Exception\UnexpectedValueException */ public function getFunctions() { if ($this->getWSDL() == null) { - throw new Exception\UnexpectedValueException(__METHOD__ . ' is available only in WSDL mode.'); + throw new Exception\UnexpectedValueException(sprintf( + '%s method is available only in WSDL mode.', + __METHOD__ + )); } $soapClient = $this->getSoapClient(); return $soapClient->__getFunctions(); } - - /** - * Get used types. - * - * @return array - */ - /** * Return a list of SOAP types * * @return array - * @throws Exception\ExceptionInterface + * @throws Exception\UnexpectedValueException */ public function getTypes() { if ($this->getWSDL() == null) { - throw new Exception\UnexpectedValueException(__METHOD__ . ' method is available only in WSDL mode.'); + throw new Exception\UnexpectedValueException(sprintf( + '%s method is available only in WSDL mode.', + __METHOD__ + )); } $soapClient = $this->getSoapClient(); - return $soapClient->__getTypes(); } /** - * @param SoapClient $soapClient - * @return Client + * Set SoapClient object + * + * @param SoapClient $soapClient + * @return self */ public function setSoapClient(SoapClient $soapClient) { @@ -1155,6 +1179,8 @@ public function setSoapClient(SoapClient $soapClient) } /** + * Get SoapClient object + * * @return SoapClient */ public function getSoapClient() @@ -1166,9 +1192,11 @@ public function getSoapClient() } /** - * @param string $cookieName - * @param string $cookieValue - * @return Client + * Set cookie + * + * @param string $cookieName + * @param string $cookieValue + * @return self */ public function setCookie($cookieName, $cookieValue=null) { diff --git a/src/Client/Common.php b/src/Client/Common.php index 779264da..bf9eb5c8 100644 --- a/src/Client/Common.php +++ b/src/Client/Common.php @@ -3,21 +3,17 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap\Client; +use SoapClient; + if (extension_loaded('soap')) { -/** - * @category Zend - * @package Zend_Soap - * @subpackage Client - */ -class Common extends \SoapClient +class Common extends SoapClient { /** * doRequest() pre-processing method @@ -36,30 +32,30 @@ class Common extends \SoapClient 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. + * 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 $one_way + * @param string $request + * @param string $location + * @param string $action + * @param int $version + * @param int $oneWay * @return mixed */ - public function __doRequest($request, $location, $action, $version, $one_way = null) + public function __doRequest($request, $location, $action, $version, $oneWay = null) { - if ($one_way === null) { - return call_user_func($this->doRequestCallback, $this, $request, $location, $action, $version); - } else { - return call_user_func($this->doRequestCallback, $this, $request, $location, $action, $version, $one_way); + // 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); + } } } // end if (extension_loaded('soap') diff --git a/src/Client/DotNet.php b/src/Client/DotNet.php index 112968cc..b65e167b 100644 --- a/src/Client/DotNet.php +++ b/src/Client/DotNet.php @@ -3,31 +3,56 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ 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. - * - * Important! Class is at experimental stage now. - * Please leave your notes, compatibility issues reports or - * suggestions in fw-webservices@lists.zend.com or fw-general@lists.com - * - * @category Zend - * @package Zend_Soap - * @subpackage 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 = array(); + + /** + * Should NTLM authentication be used? + * @var boolean + */ + protected $useNtlm = false; + /** * Constructor * @@ -42,22 +67,138 @@ public function __construct($wsdl = null, $options = null) parent::__construct($wsdl, $options); } + /** + * 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 = array( + '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, $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(); + } + + /** + * 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); + } /** * Perform arguments pre-processing * * My be overridden in descendant classes * - * @param array $arguments - * @throws Exception\RuntimeException + * @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 have to be grouped into array: array(\'a\' => $a, \'b\' => $b, ...).'); + throw new Exception\RuntimeException( + '.Net webservice arguments have to be grouped into array: array("a" => $a, "b" => $b, ...).' + ); } // Do nothing @@ -69,14 +210,29 @@ protected function _preProcessArguments($arguments) * * My be overridden in descendant classes * - * @param object $result + * @param object $result * @return mixed */ protected function _preProcessResult($result) { $resultProperty = $this->getLastMethod() . 'Result'; - return $result->$resultProperty; } + /** + * 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; + } } diff --git a/src/Client/Local.php b/src/Client/Local.php index 380ddf66..6c634206 100644 --- a/src/Client/Local.php +++ b/src/Client/Local.php @@ -3,9 +3,8 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap\Client; @@ -14,30 +13,23 @@ use Zend\Soap\Server as SOAPServer; /** - * \Zend\Soap\Client\Local - * * 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. - * - * @category Zend - * @package Zend_Soap - * @subpackage Client */ class Local extends SOAPClient { /** * Server object - * - * @var \Zend\Soap\Server + * @var SOAPServer */ protected $server; /** * Local client constructor * - * @param \Zend\Soap\Server $server + * @param SOAPServer $server * @param string $wsdl * @param array $options */ @@ -54,16 +46,15 @@ public function __construct(SOAPServer $server, $wsdl, $options = null) /** * Actual "do request" method. * - * @internal - * @param \Zend\Soap\Client\Common $client - * @param string $request - * @param string $location - * @param string $action - * @param int $version - * @param int $one_way + * @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, $one_way = null) + public function _doRequest(Common $client, $request, $location, $action, $version, $oneWay = null) { // Perform request as is ob_start(); diff --git a/src/Exception/BadMethodCallException.php b/src/Exception/BadMethodCallException.php index 432500d6..30af2d0c 100644 --- a/src/Exception/BadMethodCallException.php +++ b/src/Exception/BadMethodCallException.php @@ -3,14 +3,16 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap\Exception; -class BadMethodCallException - extends \BadMethodCallException - implements ExceptionInterface +use BadMethodCallException as SPLBadMethodCallException; + +/** + * Exception thrown when unrecognized method is called via overloading + */ +class BadMethodCallException extends SPLBadMethodCallException implements ExceptionInterface {} diff --git a/src/Exception/ExceptionInterface.php b/src/Exception/ExceptionInterface.php index 74767761..e1bc31c0 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -3,17 +3,14 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap\Exception; /** - * @category Zend - * @package Zend_Soap - * @subpackage AutoDiscover + * Common Exception interface */ interface ExceptionInterface {} diff --git a/src/Exception/ExtensionNotLoadedException.php b/src/Exception/ExtensionNotLoadedException.php index 4af12ffe..94bb9efe 100644 --- a/src/Exception/ExtensionNotLoadedException.php +++ b/src/Exception/ExtensionNotLoadedException.php @@ -3,12 +3,16 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap\Exception; +use RuntimeException; + +/** + * Exception thrown when SOAP PHP extension is not loaded + */ class ExtensionNotLoadedException extends RuntimeException {} diff --git a/src/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php index c4a92c9a..c71eec6f 100644 --- a/src/Exception/InvalidArgumentException.php +++ b/src/Exception/InvalidArgumentException.php @@ -3,14 +3,16 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap\Exception; -class InvalidArgumentException - extends \InvalidArgumentException - implements ExceptionInterface +use InvalidArgumentException as SPLInvalidArgumentException; + +/** + * Exception thrown when one or more method arguments are invalid + */ +class InvalidArgumentException extends SPLInvalidArgumentException implements ExceptionInterface {} diff --git a/src/Exception/RuntimeException.php b/src/Exception/RuntimeException.php index 1fde750e..b1290234 100644 --- a/src/Exception/RuntimeException.php +++ b/src/Exception/RuntimeException.php @@ -3,14 +3,16 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap\Exception; -class RuntimeException - extends \RuntimeException - implements ExceptionInterface +use RuntimeException as SPLRuntimeException; + +/** + * Exception thrown when there is an error during program execution + */ +class RuntimeException extends SPLRuntimeException implements ExceptionInterface {} diff --git a/src/Exception/UnexpectedValueException.php b/src/Exception/UnexpectedValueException.php index 44472bb1..a3133d35 100644 --- a/src/Exception/UnexpectedValueException.php +++ b/src/Exception/UnexpectedValueException.php @@ -3,14 +3,16 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap\Exception; -class UnexpectedValueException - extends \UnexpectedValueException - implements ExceptionInterface +use UnexpectedValueException as SPLUnexpectedValueException; + +/** + * Exception thrown when provided arguments are invalid + */ +class UnexpectedValueException extends SPLUnexpectedValueException implements ExceptionInterface {} diff --git a/src/Server.php b/src/Server.php index c1d33a28..5d276a26 100644 --- a/src/Server.php +++ b/src/Server.php @@ -3,29 +3,23 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap; +use SoapServer; +use SoapFault; +use Traversable; use DOMDocument; use DOMNode; use SimpleXMLElement; -use SoapFault; -use stdClass; -use Traversable; +use ReflectionClass; +use Zend\Server\Server as ZendServerServer; use Zend\Stdlib\ArrayUtils; -/** - * Zend_Soap_Server - * - * @category Zend - * @package Zend_Soap - * @subpackage Server - */ -class Server implements \Zend\Server\Server +class Server implements ZendServerServer { /** * Actor URI @@ -45,11 +39,6 @@ class Server implements \Zend\Server\Server */ protected $classArgs = array(); - /** - * Object registered with this server - */ - protected $object; - /** * Array of SOAP type => PHP class pairings for handling return/incoming values * @var array @@ -63,32 +52,27 @@ class Server implements \Zend\Server\Server protected $encoding; /** - * SOAP Server Features - * - * @var int + * Registered fault exceptions + * @var array */ - protected $features; + protected $faultExceptions = array(); /** - * WSDL Caching Options of SOAP Server - * - * @var mixed + * SOAP Server Features + * @var int */ - protected $wsdlCache; - + protected $features; /** - * Registered fault exceptions - * @var array + * Functions registered with this server; may be either an array or the SOAP_FUNCTIONS_ALL constant + * @var array|int */ - protected $faultExceptions = array(); + protected $functions = array(); /** - * Functions registered with this server; may be either an array or the SOAP_FUNCTIONS_ALL - * constant - * @var array|int + * Object registered with this server */ - protected $functions = array(); + protected $object; /** * Persistence mode; should be one of the SOAP persistence constants @@ -109,9 +93,8 @@ class Server implements \Zend\Server\Server protected $response; /** - * Flag: whether or not {@link handle()} should return a response instead - * of automatically emitting it. - * @var boolean + * Flag: whether or not {@link handle()} should return a response instead of automatically emitting it. + * @var bool */ protected $returnResponse = false; @@ -122,10 +105,10 @@ class Server implements \Zend\Server\Server protected $soapVersion = SOAP_1_2; /** - * URI or path to WSDL - * @var string + * Array of type mappings + * @var array */ - protected $wsdl; + protected $typemap; /** * URI namespace for SOAP server @@ -133,6 +116,18 @@ class Server implements \Zend\Server\Server */ protected $uri; + /** + * URI or path to WSDL + * @var string + */ + protected $wsdl; + + /** + * WSDL Caching Options of SOAP Server + * @var mixed + */ + protected $wsdlCache; + /** * Constructor * @@ -143,8 +138,8 @@ class Server implements \Zend\Server\Server * If $wsdl is provided, it is passed on to {@link setWSDL()}; if any * options are specified, they are passed on to {@link setOptions()}. * - * @param string $wsdl - * @param array $options + * @param string $wsdl + * @param array $options * @throws Exception\ExtensionNotLoadedException */ public function __construct($wsdl = null, array $options = null) @@ -167,8 +162,8 @@ public function __construct($wsdl = null, array $options = null) * * Allows setting options as an associative array of option => value pairs. * - * @param array|Traversable $options - * @return \Zend\Soap\Server + * @param array|\Traversable $options + * @return self */ public function setOptions($options) { @@ -177,35 +172,46 @@ public function setOptions($options) } foreach ($options as $key => $value) { - switch ($key) { + switch (strtolower($key)) { case 'actor': $this->setActor($value); break; + case 'classmap': - case 'classMap': + case 'class_map': $this->setClassmap($value); break; + + case 'typemap': + case 'type_map': + $this->setTypemap($value); + break; + case 'encoding': $this->setEncoding($value); break; - case 'soapVersion': + + case 'soapversion': case 'soap_version': $this->setSoapVersion($value); break; + case 'uri': $this->setUri($value); break; + case 'wsdl': $this->setWSDL($value); break; - case 'featues': - trigger_error(__METHOD__ . ': the option "featues" is deprecated as of 1.10.x and will be removed with 2.0.0; use "features" instead', E_USER_NOTICE); - case 'features': - $this->setSoapFeatures($value); - break; + case 'cache_wsdl': $this->setWSDLCache($value); break; + + case 'features': + $this->setSoapFeatures($value); + break; + default: break; } @@ -223,31 +229,35 @@ public function getOptions() { $options = array(); if (null !== $this->actor) { - $options['actor'] = $this->actor; + $options['actor'] = $this->getActor(); } if (null !== $this->classmap) { - $options['classmap'] = $this->classmap; + $options['classmap'] = $this->getClassmap(); + } + + if (null !== $this->typemap) { + $options['typemap'] = $this->getTypemap(); } if (null !== $this->encoding) { - $options['encoding'] = $this->encoding; + $options['encoding'] = $this->getEncoding(); } if (null !== $this->soapVersion) { - $options['soap_version'] = $this->soapVersion; + $options['soap_version'] = $this->getSoapVersion(); } if (null !== $this->uri) { - $options['uri'] = $this->uri; + $options['uri'] = $this->getUri(); } if (null !== $this->features) { - $options['features'] = $this->features; + $options['features'] = $this->getSoapFeatures(); } if (null !== $this->wsdlCache) { - $options['cache_wsdl'] = $this->wsdlCache; + $options['cache_wsdl'] = $this->getWSDLCache(); } return $options; @@ -257,7 +267,7 @@ public function getOptions() * Set encoding * * @param string $encoding - * @return Server + * @return self * @throws Exception\InvalidArgumentException with invalid encoding argument */ public function setEncoding($encoding) @@ -284,7 +294,7 @@ public function getEncoding() * Set SOAP version * * @param int $version One of the SOAP_1_1 or SOAP_1_2 constants - * @return Server + * @return self * @throws Exception\InvalidArgumentException with invalid soap version argument */ public function setSoapVersion($version) @@ -330,7 +340,7 @@ public function validateUrn($urn) * Actor is the actor URI for the server. * * @param string $actor - * @return Server + * @return self */ public function setActor($actor) { @@ -355,7 +365,7 @@ public function getActor() * URI in SoapServer is actually the target namespace, not a URI; $uri must begin with 'urn:'. * * @param string $uri - * @return Server + * @return self */ public function setUri($uri) { @@ -378,7 +388,7 @@ public function getUri() * Set classmap * * @param array $classmap - * @return Server + * @return self * @throws Exception\InvalidArgumentException for any invalid class in the class map */ public function setClassmap($classmap) @@ -406,11 +416,47 @@ public function getClassmap() return $this->classmap; } + /** + * Set typemap with xml to php type mappings with appropriate validation. + * + * @param array $typeMap + * @return self + * @throws Exception\InvalidArgumentException + */ + public function setTypemap($typeMap) + { + if (!is_array($typeMap)) { + throw new Exception\InvalidArgumentException('Typemap must be an array'); + } + + foreach ($typeMap as $type) { + if (!is_callable($type['from_xml'])) { + throw new Exception\InvalidArgumentException('Invalid from_xml callback for type: ' . $type['type_name']); + } + if (!is_callable($type['to_xml'])) { + throw new Exception\InvalidArgumentException('Invalid to_xml callback for type: ' . $type['type_name']); + } + } + + $this->typemap = $typeMap; + return $this; + } + + /** + * Retrieve typemap + * + * @return array + */ + public function getTypemap() + { + return $this->typemap; + } + /** * Set wsdl * - * @param string $wsdl URI or path to a WSDL - * @return Server + * @param string $wsdl URI or path to a WSDL + * @return self */ public function setWSDL($wsdl) { @@ -432,7 +478,7 @@ public function getWSDL() * Set the SOAP Feature options. * * @param string|int $feature - * @return Server + * @return self */ public function setSoapFeatures($feature) { @@ -453,8 +499,8 @@ public function getSoapFeatures() /** * Set the SOAP WSDL Caching Options * - * @param string|int|boolean $options - * @return Server + * @param string|int|bool $options + * @return self */ public function setWSDLCache($options) { @@ -473,10 +519,10 @@ public function getWSDLCache() /** * Attach a function as a server method * - * @param array|string $function Function name, array of function names to attach, - * or SOAP_FUNCTIONS_ALL to attach all functions + * @param array|string $function Function name, array of function names to attach, + * or SOAP_FUNCTIONS_ALL to attach all functions * @param string $namespace Ignored - * @return Server + * @return self * @throws Exception\InvalidArgumentException on invalid functions */ public function addFunction($function, $namespace = '') @@ -494,11 +540,13 @@ public function addFunction($function, $namespace = '') throw new Exception\InvalidArgumentException('One or more invalid functions specified in array'); } } - $this->functions = array_merge($this->functions, $function); + } elseif (is_string($function) && function_exists($function)) { $this->functions[] = $function; + } elseif ($function == SOAP_FUNCTIONS_ALL) { $this->functions = SOAP_FUNCTIONS_ALL; + } else { throw new Exception\InvalidArgumentException('Invalid function specified'); } @@ -516,14 +564,14 @@ public function addFunction($function, $namespace = '') * Accepts a class name to use when handling requests. Any additional * arguments will be passed to that class' constructor when instantiated. * - * See {@link setObject()} to set preconfigured object instances as request handlers. + * See {@link setObject()} to set pre-configured object instances as request handlers. * - * @param string|object $class Class name or object instance which executes SOAP Requests at endpoint. - * @param string $namespace - * @param $argv - * @return Server - * @throws Exception\InvalidArgumentException if called more than once, or if class - * does not exist + * @param string|object $class Class name or object instance which executes + * SOAP Requests at endpoint. + * @param string $namespace + * @param null|array $argv + * @return self + * @throws Exception\InvalidArgumentException if called more than once, or if class does not exist */ public function setClass($class, $namespace = '', $argv = null) { @@ -536,11 +584,17 @@ public function setClass($class, $namespace = '', $argv = null) } if (!is_string($class)) { - throw new Exception\InvalidArgumentException('Invalid class argument (' . gettype($class) . ')'); + throw new Exception\InvalidArgumentException(sprintf( + 'Invalid class argument (%s)', + gettype($class) + )); } if (!class_exists($class)) { - throw new Exception\InvalidArgumentException('Class "' . $class . '" does not exist'); + throw new Exception\InvalidArgumentException(sprintf( + 'Class "%s" does not exist', + $class + )); } $this->class = $class; @@ -555,24 +609,28 @@ public function setClass($class, $namespace = '', $argv = null) /** * Attach an object to a server * - * Accepts an instanciated object to use when handling requests. + * Accepts an instantiated object to use when handling requests. * - * @param object $object + * @param object $object + * @return self * @throws Exception\InvalidArgumentException - * @return Server */ public function setObject($object) { if (!is_object($object)) { - throw new Exception\InvalidArgumentException('Invalid object argument ('.gettype($object).')'); + throw new Exception\InvalidArgumentException(sprintf( + 'Invalid object argument (%s)', + gettype($object) + )); } if (isset($this->object)) { - throw new Exception\InvalidArgumentException('An object has already been registered with this soap server instance'); + throw new Exception\InvalidArgumentException( + 'An object has already been registered with this soap server instance' + ); } $this->object = $object; - return $this; } @@ -583,7 +641,6 @@ public function setObject($object) * merged with all public methods of the class set with {@link setClass()} * (if any). * - * @access public * @return array */ public function getFunctions() @@ -601,8 +658,7 @@ public function getFunctions() /** * Unimplemented: Load server definition * - * @param array $definition - * @return void + * @param array $definition * @throws Exception\RuntimeException Unimplemented */ public function loadFunctions($definition) @@ -613,9 +669,9 @@ public function loadFunctions($definition) /** * Set server persistence * - * @param int $mode + * @param int $mode SOAP_PERSISTENCE_SESSION or SOAP_PERSISTENCE_REQUEST constants + * @return self * @throws Exception\InvalidArgumentException - * @return Server */ public function setPersistence($mode) { @@ -630,7 +686,7 @@ public function setPersistence($mode) /** * Get server persistence * - * @return Server + * @return int */ public function getPersistence() { @@ -647,38 +703,49 @@ public function getPersistence() * - stdClass; if so, calls __toString() and verifies XML * - string; if so, verifies XML * - * @param DOMDocument|DOMNode|SimpleXMLElement|stdClass|string $request + * @param DOMDocument|DOMNode|SimpleXMLElement|stdClass|string $request + * @return self * @throws Exception\InvalidArgumentException - * @return Server */ protected function _setRequest($request) { + $xml = null; + if ($request instanceof DOMDocument) { $xml = $request->saveXML(); + } elseif ($request instanceof DOMNode) { $xml = $request->ownerDocument->saveXML(); + } elseif ($request instanceof SimpleXMLElement) { $xml = $request->asXML(); + } elseif (is_object($request) || is_string($request)) { if (is_object($request)) { $xml = $request->__toString(); } else { $xml = $request; } + $xml = trim($xml); + libxml_disable_entity_loader(true); + $dom = new DOMDocument(); - if (strlen($xml) == 0 || !$dom->loadXML($xml)) { + $loadStatus = $dom->loadXML($xml); + + // @todo check libxml errors ? validate document ? + if (strlen($xml) == 0 || !$loadStatus) { throw new Exception\InvalidArgumentException('Invalid XML'); } + foreach ($dom->childNodes as $child) { if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) { - throw new Exception\InvalidArgumentException( - 'Invalid XML: Detected use of illegal DOCTYPE' - ); + throw new Exception\InvalidArgumentException('Invalid XML: Detected use of illegal DOCTYPE'); } } libxml_disable_entity_loader(false); } + $this->request = $xml; return $this; } @@ -701,8 +768,8 @@ public function getLastRequest() * * The response is always available via {@link getResponse()}. * - * @param boolean $flag - * @return Server + * @param bool $flag + * @return self */ public function setReturnResponse($flag = true) { @@ -713,7 +780,7 @@ public function setReturnResponse($flag = true) /** * Retrieve return response flag * - * @return boolean + * @return bool */ public function getReturnResponse() { @@ -737,12 +804,12 @@ public function getResponse() * SoapServer object, and then registers any functions or class with it, as * well as persistence. * - * @return \SoapServer + * @return SoapServer */ protected function _getSoap() { $options = $this->getOptions(); - $server = new \SoapServer($this->wsdl, $options); + $server = new SoapServer($this->wsdl, $options); if (!empty($this->functions)) { $server->addFunction($this->functions); @@ -781,7 +848,7 @@ protected function _getSoap() * If no request is passed, pulls request using php:://input (for * cross-platform compatibility purposes). * - * @param DOMDocument|DOMNode|SimpleXMLElement|stdClass|string $request Optional request + * @param DOMDocument|DOMNode|SimpleXMLElement|stdClass|string $request Optional request * @return void|string */ public function handle($request = null) @@ -808,8 +875,7 @@ public function handle($request = null) if ($setRequestException instanceof \Exception) { // Create SOAP fault message if we've caught a request exception $fault = $this->fault($setRequestException->getMessage(), 'Sender'); - } - if (!$setRequestException instanceof \Exception) { + } else { ob_start(); try { $soap->handle($this->request); @@ -826,12 +892,14 @@ public function handle($request = null) // Send a fault, if we have one if ($fault instanceof SoapFault && !$this->returnResponse) { $soap->fault($fault->faultcode, $fault->getMessage()); + return; } // Echo the response, if we're not returning it if (!$this->returnResponse) { echo $this->response; + return; } @@ -847,7 +915,7 @@ public function handle($request = null) /** * Method initializes the error context that the SOAPServer environment will run in. * - * @return boolean display_errors original value + * @return bool display_errors original value */ protected function _initializeSoapErrorContext() { @@ -858,22 +926,51 @@ protected function _initializeSoapErrorContext() } /** - * Register a valid fault exception + * Validate and register fault exception * * @param string|array $class Exception class or array of exception classes - * @return Server + * @return self + * @throws Exception\InvalidArgumentException */ public function registerFaultException($class) { - $this->faultExceptions = array_merge($this->faultExceptions, (array) $class); + if (is_array($class)) { + foreach($class as $row) { + $this->registerFaultException($row); + } + + } elseif (is_string($class) && class_exists($class) && is_subclass_of($class, 'Exception')) { + $ref = new ReflectionClass($class); + + $this->faultExceptions[] = $ref->getName(); + $this->faultExceptions = array_unique($this->faultExceptions); + } else { + throw new Exception\InvalidArgumentException( + 'Argument for Zend\Soap\Server::registerFaultException should be string or array of strings with valid exception names' + ); + } + return $this; } + /** + * Checks if provided fault name is registered as valid in this server. + * + * @param $fault Name of a fault class + * @return bool + */ + public function isRegisteredAsFaultException($fault) + { + $ref = new ReflectionClass($fault); + $classNames = $ref->getName(); + return in_array($classNames, $this->faultExceptions); + } + /** * Deregister a fault exception from the fault exception stack * * @param string $class - * @return boolean + * @return bool */ public function deregisterFaultException($class) { @@ -910,11 +1007,10 @@ public function getFaultExceptions() * @param string $code SOAP Fault Codes * @return SoapFault */ - public function fault($fault = null, $code = "Receiver") + public function fault($fault = null, $code = 'Receiver') { if ($fault instanceof \Exception) { - $class = get_class($fault); - if (in_array($class, $this->faultExceptions)) { + if ($this->isRegisteredAsFaultException($fault)) { $message = $fault->getMessage(); $eCode = $fault->getCode(); $code = empty($eCode) ? $code : $eCode; @@ -927,12 +1023,9 @@ public function fault($fault = null, $code = "Receiver") $message = 'Unknown error'; } - $allowedFaultModes = array( - 'VersionMismatch', 'MustUnderstand', 'DataEncodingUnknown', - 'Sender', 'Receiver', 'Server' - ); + $allowedFaultModes = array('VersionMismatch', 'MustUnderstand', 'DataEncodingUnknown', 'Sender', 'Receiver', 'Server'); if (!in_array($code, $allowedFaultModes)) { - $code = "Receiver"; + $code = 'Receiver'; } return new SoapFault($code, $message); @@ -941,12 +1034,11 @@ public function fault($fault = null, $code = "Receiver") /** * Throw PHP errors as SoapFaults * - * @param int $errno - * @param string $errstr - * @param string $errfile - * @param int $errline - * @param array $errcontext - * @return void + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + * @param array $errcontext * @throws SoapFault */ public function handlePhpErrors($errno, $errstr, $errfile = null, $errline = null, array $errcontext = null) diff --git a/src/Server/DocumentLiteralWrapper.php b/src/Server/DocumentLiteralWrapper.php index 986da171..7c30208b 100644 --- a/src/Server/DocumentLiteralWrapper.php +++ b/src/Server/DocumentLiteralWrapper.php @@ -3,16 +3,14 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap\Server; use ReflectionObject; -use Zend\Soap\Exception\BadMethodCallException; -use Zend\Soap\Exception\UnexpectedValueException; +use Zend\Soap\Exception; /** * Wraps WSDL Document/Literal Style service objects to hide SOAP request @@ -61,15 +59,12 @@ * of SOAP this wrapper service handles the parsing between the formats. * * @example - * + * * $service = new MyCalculatorService(); * $soap = new \Zend\Soap\Server($wsdlFile); * $soap->setObject(new \Zend\Soap\Server\DocumentLiteralWrapper($service)); * $soap->handle(); - * - * @category Zend - * @package Zend_Soap - * @subpackage Server + * */ class DocumentLiteralWrapper { @@ -97,8 +92,8 @@ public function __construct($object) /** * Proxy method that does the heavy document/literal decomposing. * - * @param string $method - * @param array $args + * @param string $method + * @param array $args * @return mixed */ public function __call($method, $args) @@ -107,7 +102,7 @@ public function __call($method, $args) $this->_assertServiceDelegateHasMethod($method); $delegateArgs = $this->_parseArguments($method, $args[0]); - $ret = call_user_func_array(array($this->object, $method), $delegateArgs); + $ret = call_user_func_array(array($this->object, $method), $delegateArgs); return $this->_getResultMessage($method, $ret); } @@ -115,10 +110,10 @@ public function __call($method, $args) * Parse the document/literal wrapper into arguments to call the real * service. * - * @param string $method - * @param object $document - * @throws UnexpectedValueException + * @param string $method + * @param object $document * @return array + * @throws Exception\UnexpectedValueException */ protected function _parseArguments($method, $document) { @@ -131,37 +126,57 @@ protected function _parseArguments($method, $document) $delegateArgs = array(); foreach (get_object_vars($document) as $argName => $argValue) { if (!isset($params[$argName])) { - throw new UnexpectedValueException(sprintf( + throw new Exception\UnexpectedValueException(sprintf( "Received unknown argument %s which is not an argument to %s::%s", - $argName, get_class($this->object), $method + $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 array($method . 'Result' => $ret); } + /** + * @param string $method + * @throws Exception\BadMethodCallException + */ protected function _assertServiceDelegateHasMethod($method) { if (!$this->reflection->hasMethod($method)) { - throw new BadMethodCallException(sprintf( + throw new Exception\BadMethodCallException(sprintf( "Method %s does not exist on delegate object %s", - $method, get_class($this->object) + $method, + get_class($this->object) )); } } - protected function _assertOnlyOneArgument($args) + /** + * @param array $args + * @throws Exception\UnexpectedValueException + */ + protected function _assertOnlyOneArgument(array $args) { if (count($args) != 1) { - throw new UnexpectedValueException(sprintf( + throw new Exception\UnexpectedValueException(sprintf( "Expecting exactly one argument that is the document/literal wrapper, got %d", - count($args))); + count($args) + )); } } } diff --git a/src/Wsdl.php b/src/Wsdl.php index 5f11825a..cab0c17b 100644 --- a/src/Wsdl.php +++ b/src/Wsdl.php @@ -3,114 +3,158 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap; +use DOMNode; use DOMDocument; +use DOMDocumentFragment; use DOMElement; +use DOMXPath; +use Zend\Soap\Exception\InvalidArgumentException; use Zend\Soap\Wsdl\ComplexTypeStrategy\ComplexTypeStrategyInterface as ComplexTypeStrategy; use Zend\Uri\Uri; -/** - * \Zend\Soap\Wsdl - * - * @category Zend - * @package Zend_Soap - */ 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'; + /**#@-*/ + /** - * @var object DomDocument Instance + * Map of PHP Class names to WSDL QNames. + * @var array */ - private $dom; + protected $classMap = array(); /** - * @var object WSDL Root XML_Tree_Node + * DOM Instance + * @var DOMDocument */ - private $wsdl; + protected $dom; /** - * @var string URI where the WSDL will be available + * Types defined on schema + * @var array */ - private $uri; + protected $includedTypes = array(); /** * @var DOMElement */ - private $schema = null; + protected $schema = null; /** - * Types defined on schema - * - * @var array + * Strategy for detection of complex types */ - private $includedTypes = array(); + protected $strategy = null; /** - * Strategy for detection of complex types + * URI where the WSDL will be available + * @var string */ - protected $strategy = null; + protected $uri; /** - * Map of PHP Class names to WSDL QNames. - * - * @var array + * Root XML_Tree_Node + * @var DOMElement WSDL */ - protected $classMap = array(); + protected $wsdl; /** - * Constructor - * - * @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 + * @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 = array()) - { + public function __construct( + $name, + $uri, + ComplexTypeStrategy $strategy = null, + array $classMap = array() + ) { if ($uri instanceof Uri) { $uri = $uri->toString(); } - $this->uri = $uri; + + $this->setUri($uri); + $this->classMap = $classMap; + $this->dom = $this->getDOMDocument($name, $this->getUri()); + $this->wsdl = $this->dom->documentElement; - /** - * @todo change DomDocument object creation from cparsing to constructing using API - * It also should authomatically escape $name and $uri values if necessary - */ - $wsdl = " - "; - libxml_disable_entity_loader(true); - $this->dom = new DOMDocument(); - if (!$this->dom->loadXML($wsdl)) { - throw new Exception\RuntimeException('Unable to create DomDocument'); - } else { - foreach ($this->dom->childNodes as $child) { - if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) { - throw new Exception\RuntimeException( - 'Invalid XML: Detected use of illegal DOCTYPE' - ); - } - } - $this->wsdl = $this->dom->documentElement; - } - libxml_disable_entity_loader(false); $this->setComplexTypeStrategy($strategy ?: new Wsdl\ComplexTypeStrategy\DefaultComplexType); } /** - * Get the class map of php to wsdl qname types. + * 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 */ @@ -120,45 +164,101 @@ public function getClassMap() } /** - * Set the class map of php to wsdl qname types. + * Set the class map of php to wsdl mappings.. + * + * @return self */ public function setClassMap($classMap) { $this->classMap = $classMap; + return $this; } /** * Set a new uri for this WSDL * * @param string|Uri $uri - * @return \Zend\Soap\Wsdl + * @return self */ public function setUri($uri) { - if ($uri instanceof Uri) { + if ($uri instanceof Uri){ $uri = $uri->toString(); } + + $uri = $this->sanitizeUri($uri); + $oldUri = $this->uri; $this->uri = $uri; - if ($this->dom !== null) { - // @todo: This is the worst hack ever, but its needed due to design and non BC issues of WSDL generation - $xml = $this->dom->saveXML(); - $xml = str_replace($oldUri, $uri, $xml); - libxml_disable_entity_loader(true); - $this->dom = new DOMDocument(); - $this->dom->loadXML($xml); - libxml_disable_entity_loader(false); + 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 \Zend\Soap\Wsdl + * @param ComplexTypeStrategy $strategy + * @return self */ public function setComplexTypeStrategy(ComplexTypeStrategy $strategy) { @@ -179,156 +279,157 @@ public function getComplexTypeStrategy() /** * Add a {@link http://www.w3.org/TR/wsdl#_messages message} element to the WSDL * - * @param string $name 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 object The new message's XML_Tree_Node for use in {@link function addDocumentation} + * @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($name, $parts) + public function addMessage($messageName, $parts) { - $message = $this->dom->createElement('message'); - - $message->setAttribute('name', $name); + $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->createElement('part'); + $part = $this->dom->createElementNS(self::WSDL_NS_URI, 'part'); + $message->appendChild($part); + $part->setAttribute('name', $name); if (is_array($type)) { - foreach ($type as $key => $value) { - $part->setAttribute($key, $value); - } + $this->arrayToAttributes($part, $type); } else { - $part->setAttribute('type', $type); + $this->setAttributeWithSanitization($part, 'type', $type); } - $message->appendChild($part); } } $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 object The new portType's XML_Tree_Node for use in {@link function addPortOperation} and {@link function addDocumentation} + * @param string $name portType element's name + * @return DOMElement The new portType's XML_Tree_Node for use in {@link function addPortOperation} and addDocumentation@link function addDocumentation} */ public function addPortType($name) { - $portType = $this->dom->createElement('portType'); - $portType->setAttribute('name', $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 object $portType a portType XML_Tree_Node, from {@link function addPortType} - * @param string $name Operation name - * @param string $input Input Message - * @param string $output Output Message - * @param string $fault Fault Message - * @return object The new operation's XML_Tree_Node for use in {@link function addDocumentation} + * @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->createElement('operation'); + $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->createElement('input'); - $node->setAttribute('message', $input); + $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->createElement('output'); - $node->setAttribute('message', $output); + $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->createElement('fault'); - $node->setAttribute('message', $fault); + $node = $this->dom->createElementNS(self::WSDL_NS_URI, 'fault'); $operation->appendChild($node); + $node->setAttribute('message', $fault); } - $portType->appendChild($operation); - 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 object The new binding's XML_Tree_Node for use with {@link function addBindingOperation} and {@link function addDocumentation} + * @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->createElement('binding'); - $binding->setAttribute('name', $name); - $binding->setAttribute('type', $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 object $binding A binding XML_Tree_Node returned by {@link function addBinding} - * @param array $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 $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 $fault An array of attributes for the fault element, allowed keys are: 'name', 'use', 'namespace', 'encodingStyle'. {@link http://www.w3.org/TR/wsdl#_soap:body More Information} - * @return object 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) + * @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->createElement('operation'); - $operation->setAttribute('name', $name); + $operation = $this->dom->createElementNS(self::WSDL_NS_URI, 'operation'); + $binding->appendChild($operation); - if (is_array($input)) { - $node = $this->dom->createElement('input'); - $soap_node = $this->dom->createElement('soap:body'); - foreach ($input as $name => $value) { - $soap_node->setAttribute($name, $value); - } - $node->appendChild($soap_node); + $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)) { - $node = $this->dom->createElement('output'); - $soap_node = $this->dom->createElement('soap:body'); - foreach ($output as $name => $value) { - $soap_node->setAttribute($name, $value); - } - $node->appendChild($soap_node); + 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)) { - $node = $this->dom->createElement('fault'); - if (isset($fault['name'])) { - $node->setAttribute('name', $fault['name']); - } - $soap_node = $this->dom->createElement('soap:fault'); - foreach ($fault as $name => $value) { - $soap_node->setAttribute($name, $value); - } - $node->appendChild($soap_node); + if (is_array($fault) && !empty($fault)) { + $node = $this->dom->createElementNS(self::WSDL_NS_URI, 'fault'); $operation->appendChild($node); - } - $binding->appendChild($operation); + $this->arrayToAttributes($node, $fault); + } return $operation; } @@ -336,71 +437,75 @@ public function addBindingOperation($binding, $name, $input = false, $output = f /** * Add a {@link http://www.w3.org/TR/wsdl#_soap:binding SOAP binding} element to a Binding element * - * @param object $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) - * @return boolean + * @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') + public function addSoapBinding($binding, $style = 'document', $transport = 'http://schemas.xmlsoap.org/soap/http', $soapVersion = SOAP_1_1) { - $soap_binding = $this->dom->createElement('soap:binding'); - $soap_binding->setAttribute('style', $style); - $soap_binding->setAttribute('transport', $transport); + $soapBinding = $this->dom->createElementNS($this->getSoapNamespaceUriByVersion($soapVersion), 'binding'); + $binding->appendChild($soapBinding); - $binding->appendChild($soap_binding); + $soapBinding->setAttribute('style', $style); + $soapBinding->setAttribute('transport', $transport); - return $soap_binding; + return $soapBinding; } /** * Add a {@link http://www.w3.org/TR/wsdl#_soap:operation SOAP operation} to an operation element * - * @param object $operation An operation XML_Tree_Node returned by {@link function addBindingOperation} - * @param string $soap_action SOAP Action - * @return boolean + * @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($binding, $soap_action) + public function addSoapOperation($operation, $soapAction, $soapVersion = SOAP_1_1) { - if ($soap_action instanceof Uri) { - $soap_action = $soap_action->toString(); + if ($soapAction instanceof Uri) { + $soapAction = $soapAction->toString(); } - $soap_operation = $this->dom->createElement('soap:operation'); - $soap_operation->setAttribute('soapAction', $soap_action); + $soapOperation = $this->dom->createElementNS($this->getSoapNamespaceUriByVersion($soapVersion), 'operation'); + $operation->insertBefore($soapOperation, $operation->firstChild); - $binding->insertBefore($soap_operation, $binding->firstChild); + $this->setAttributeWithSanitization($soapOperation, 'soapAction', $soapAction); - return $soap_operation; + return $soapOperation; } /** * Add a {@link http://www.w3.org/TR/wsdl#_services service} element to the WSDL * - * @param string $name Service Name - * @param string $port_name Name of the port for the service - * @param string $binding Binding for the port - * @param string $location SOAP Address for the service - * @return object The new service's XML_Tree_Node for use with {@link function addDocumentation} + * @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, $port_name, $binding, $location) + public function addService($name, $portName, $binding, $location, $soapVersion = SOAP_1_1) { if ($location instanceof Uri) { $location = $location->toString(); } - $service = $this->dom->createElement('service'); - $service->setAttribute('name', $name); + $service = $this->dom->createElementNS(WSDL::WSDL_NS_URI, 'service'); + $this->wsdl->appendChild($service); - $port = $this->dom->createElement('port'); - $port->setAttribute('name', $port_name); - $port->setAttribute('binding', $binding); + $service->setAttribute('name', $name); - $soap_address = $this->dom->createElement('soap:address'); - $soap_address->setAttribute('location', $location); - $port->appendChild($soap_address); + $port = $this->dom->createElementNS(WSDL::WSDL_NS_URI, 'port'); $service->appendChild($port); - $this->wsdl->appendChild($service); + $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; } @@ -411,42 +516,41 @@ public function addService($name, $port_name, $binding, $location) * but the WSDL {@link http://schemas.xmlsoap.org/wsdl/ schema} uses 'documentation' instead. * The {@link http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html#WSDL_documentation_Element WS-I Basic Profile 1.1} recommends using 'documentation'. * - * @param object $input_node An XML_Tree_Node returned by another method to add the documentation to - * @param string $documentation Human readable documentation for the node + * @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($input_node, $documentation) + public function addDocumentation($inputNode, $documentation) { - if ($input_node === $this) { + if ($inputNode === $this) { $node = $this->dom->documentElement; } else { - $node = $input_node; + $node = $inputNode; } - $doc = $this->dom->createElement('documentation'); - $doc_cdata = $this->dom->createTextNode(str_replace(array("\r\n", "\r"), "\n", $documentation)); - $doc->appendChild($doc_cdata); - + $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(array("\r\n", "\r"), "\n", $documentation)); + $doc->appendChild($docCData); return $doc; } /** * Add WSDL Types element * - * @param object $types A DomDocument|DomNode|DomElement|DomDocumentFragment with all the XML Schema types defined in it + * @param DOMDocument|DOMNode|DOMElement|DOMDocumentFragment $types A DOMDocument|DOMNode|DOMElement|DOMDocumentFragment with all the XML Schema types defined in it */ - public function addTypes($types) + public function addTypes(DOMNode $types) { - if ($types instanceof \DomDocument) { + if ($types instanceof DOMDocument) { $dom = $this->dom->importNode($types->documentElement); - $this->wsdl->appendChild($types->documentElement); - } elseif ($types instanceof \DomNode || $types instanceof \DomElement || $types instanceof \DomDocumentFragment ) { + $this->wsdl->appendChild($dom); + } elseif ($types instanceof DOMNode || $types instanceof DOMElement || $types instanceof DOMDocumentFragment ) { $dom = $this->dom->importNode($types); $this->wsdl->appendChild($dom); } @@ -455,9 +559,9 @@ public function addTypes($types) /** * Add a complex type name that is part of this WSDL and can be used in signatures. * - * @param string $type - * @param string $wsdlType - * @return \Zend\Soap\Wsdl + * @param string $type + * @param string $wsdlType + * @return self */ public function addType($type, $wsdlType) { @@ -487,7 +591,6 @@ public function getSchema() if ($this->schema == null) { $this->addSchemaTypeSection(); } - return $this->schema; } @@ -498,37 +601,43 @@ public function getSchema() */ public function toXML() { - return $this->dom->saveXML(); + $this->dom->normalizeDocument(); + return $this->dom->saveXML(); } /** * Return DOM Document * - * @return object DomDocum ent + * @return DOMDocument */ public function toDomDocument() { + $this->dom->normalizeDocument(); return $this->dom; } /** * Echo the WSDL as XML * - * @return boolean + * @param bool $filename + * @return bool */ public function dump($filename = false) { + $this->dom->normalizeDocument(); + if (!$filename) { echo $this->toXML(); return true; } - return file_put_contents($filename, $this->toXML()); + + 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 + * @param string $type PHP Type to get the XSD type for * @return string */ public function getType($type) @@ -536,27 +645,37 @@ public function getType($type) switch (strtolower($type)) { case 'string': case 'str': - return 'xsd:string'; + return self::XSD_NS . ':string'; + case 'long': - return 'xsd:long'; + return self::XSD_NS . ':long'; + case 'int': case 'integer': - return 'xsd:int'; + return self::XSD_NS . ':int'; + case 'float': - return 'xsd:float'; + return self::XSD_NS . ':float'; + case 'double': - return 'xsd:double'; + return self::XSD_NS . ':double'; + case 'boolean': case 'bool': - return 'xsd:boolean'; + return self::XSD_NS . ':boolean'; + case 'array': - return 'soap-enc:Array'; + return self::SOAP_ENC_NS . ':Array'; + case 'object': - return 'xsd:struct'; + return self::XSD_NS . ':struct'; + case 'mixed': - return 'xsd:anyType'; + return self::XSD_NS . ':anyType'; + case 'void': return ''; + default: // delegate retrieval of complex type to current strategy return $this->addComplexType($type); @@ -566,24 +685,27 @@ public function getType($type) /** * This function makes sure a complex types section and schema additions are set. * - * @return \Zend\Soap\Wsdl + * @return self */ public function addSchemaTypeSection() { if ($this->schema === null) { - $this->schema = $this->dom->createElement('xsd:schema'); - $this->schema->setAttribute('targetNamespace', $this->uri); - $types = $this->dom->createElement('types'); - $types->appendChild($this->schema); + $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 + * @param string $type * @return string QName */ public function translateType($type) @@ -592,22 +714,21 @@ public function translateType($type) return $this->classMap[$type]; } - if ($type[0] == '\\') { - $type = substr($type, 1); - } + $type = trim($type,'\\'); + // remove namespace, $pos = strrpos($type, '\\'); if ($pos) { $type = substr($type, $pos+1); } - return str_replace('\\', '.', $type); + 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 + * @param string $type Name of the class to be specified * @return string XSD Type for the given PHP type */ public function addComplexType($type) @@ -619,6 +740,7 @@ public function addComplexType($type) $strategy = $this->getComplexTypeStrategy(); $strategy->setContext($this); + // delegates the detection of a complex type to the current strategy return $strategy->addComplexType($type); } @@ -626,36 +748,127 @@ public function addComplexType($type) /** * Parse an xsd:element represented as an array into a DOMElement. * - * @param array $element an xsd:element represented as an array - * @throws Exception\RuntimeException if $element is not an array + * @param array $element an xsd:element represented as an array * @return DOMElement parsed element + * @throws Exception\RuntimeException if $element is not an array */ - private function _parseElement($element) + protected function _parseElement($element) { if (!is_array($element)) { - throw new Exception\RuntimeException("The 'element' parameter needs to be an associative array."); + throw new Exception\RuntimeException('The "element" parameter needs to be an associative array.'); } - $elementXml = $this->dom->createElement('xsd:element'); + $elementXML = $this->dom->createElementNS(self::XSD_NS_URI, 'element'); foreach ($element as $key => $value) { if (in_array($key, array('sequence', 'all', 'choice'))) { if (is_array($value)) { - $complexType = $this->dom->createElement('xsd:complexType'); + $complexType = $this->dom->createElementNS(self::XSD_NS_URI, 'complexType'); if (count($value) > 0) { - $container = $this->dom->createElement('xsd:' . $key); - foreach ($value as $subelement) { - $subelementXml = $this->_parseElement($subelement); - $container->appendChild($subelementXml); + $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); + $elementXML->appendChild($complexType); } } else { - $elementXml->setAttribute($key, $value); + $elementXML->setAttribute($key, $value); } } - return $elementXml; + + 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); + break; + + default: + return $value; + break; + } + } + + /** + * Convert associative array to attributes of given node using optional {@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; } /** @@ -673,7 +886,7 @@ private function _parseElement($element) * * * - * @param array $element an xsd:element represented as an array + * @param array $element an xsd:element represented as an array * @return string xsd:element for the given element array */ public function addElement($element) @@ -681,6 +894,7 @@ public function addElement($element) $schema = $this->getSchema(); $elementXml = $this->_parseElement($element); $schema->appendChild($elementXml); - return 'tns:' . $element['name']; + + return self::TYPES_NS . ':' . $element['name']; } } diff --git a/src/Wsdl/ComplexTypeStrategy/AbstractComplexTypeStrategy.php b/src/Wsdl/ComplexTypeStrategy/AbstractComplexTypeStrategy.php index fc53d90f..36bc8bfc 100644 --- a/src/Wsdl/ComplexTypeStrategy/AbstractComplexTypeStrategy.php +++ b/src/Wsdl/ComplexTypeStrategy/AbstractComplexTypeStrategy.php @@ -3,44 +3,39 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap\Wsdl\ComplexTypeStrategy; +use Zend\Soap\Wsdl; + /** - * Abstract class for Zend_Soap_Wsdl_Strategy. - * - * @category Zend - * @package Zend_Soap - * @subpackage WSDL + * Abstract class for Zend\Soap\Wsdl\Strategy. */ abstract class AbstractComplexTypeStrategy implements ComplexTypeStrategyInterface { /** * Context object - * - * @var \Zend\Soap\Wsdl + * @var Wsdl */ protected $context; /** - * Set the Zend_Soap_Wsdl Context object this strategy resides in. + * Set the WSDL Context object this strategy resides in. * - * @param \Zend\Soap\Wsdl $context - * @return void + * @param Wsdl $context */ - public function setContext(\Zend\Soap\Wsdl $context) + public function setContext(Wsdl $context) { $this->context = $context; } /** - * Return the current Zend_Soap_Wsdl context object + * Return the current WSDL context object * - * @return \Zend\Soap\Wsdl + * @return Wsdl */ public function getContext() { @@ -50,16 +45,16 @@ public function getContext() /** * Look through registered types * - * @param string $phpType + * @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 null; } } diff --git a/src/Wsdl/ComplexTypeStrategy/AnyType.php b/src/Wsdl/ComplexTypeStrategy/AnyType.php index a64556a0..26365054 100644 --- a/src/Wsdl/ComplexTypeStrategy/AnyType.php +++ b/src/Wsdl/ComplexTypeStrategy/AnyType.php @@ -3,40 +3,33 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap\Wsdl\ComplexTypeStrategy; -/** - * Zend_Soap_Wsdl_Strategy_AnyType - * - * @category Zend - * @package Zend_Soap - * @subpackage WSDL - */ +use Zend\Soap\Wsdl; + class AnyType implements ComplexTypeStrategyInterface { /** * Not needed in this strategy. * - * @param \Zend\Soap\Wsdl $context + * @param Wsdl $context */ - public function setContext(\Zend\Soap\Wsdl $context) + public function setContext(Wsdl $context) { - } /** * Returns xsd:anyType regardless of the input. * - * @param string $type + * @param string $type * @return string */ public function addComplexType($type) { - return 'xsd:anyType'; + return Wsdl::XSD_NS . ':anyType'; } } diff --git a/src/Wsdl/ComplexTypeStrategy/ArrayOfTypeComplex.php b/src/Wsdl/ComplexTypeStrategy/ArrayOfTypeComplex.php index 116ae818..b9b4a4c7 100644 --- a/src/Wsdl/ComplexTypeStrategy/ArrayOfTypeComplex.php +++ b/src/Wsdl/ComplexTypeStrategy/ArrayOfTypeComplex.php @@ -3,30 +3,24 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap\Wsdl\ComplexTypeStrategy; use Zend\Soap\Exception; +use Zend\Soap\Wsdl; -/** - * ArrayOfTypeComplex strategy - * - * @category Zend - * @package Zend_Soap - * @subpackage WSDL - */ class ArrayOfTypeComplex extends DefaultComplexType { /** - * Add an ArrayOfType based on the xsd:complexType syntax if type[] is detected in return value doc comment. + * Add an ArrayOfType based on the xsd:complexType syntax if type[] is + * detected in return value doc comment. * - * @param string $type - * @throws Exception\InvalidArgumentException + * @param string $type * @return string tns:xsd-type + * @throws Exception\InvalidArgumentException */ public function addComplexType($type) { @@ -39,22 +33,25 @@ public function addComplexType($type) if ($nestingLevel == 0) { return parent::addComplexType($singularType); - } elseif ($nestingLevel == 1) { - // The following blocks define the Array of Object structure - return $this->_addArrayOfComplexType($singularType, $type); - } else { + } + + 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.' + '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. + * 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[]' + * @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) @@ -64,7 +61,7 @@ protected function _addArrayOfComplexType($singularType, $type) } $xsdComplexTypeName = 'ArrayOf' . $this->getContext()->translateType($singularType); - $xsdComplexType = 'tns:' . $xsdComplexTypeName; + $xsdComplexType = Wsdl::TYPES_NS . ':' . $xsdComplexTypeName; // Register type here to avoid recursion $this->getContext()->addType($type, $xsdComplexType); @@ -76,23 +73,27 @@ protected function _addArrayOfComplexType($singularType, $type) // Add array type structure to WSDL document $dom = $this->getContext()->toDomDocument(); - $complexType = $dom->createElement('xsd:complexType'); + $complexType = $dom->createElementNS(Wsdl::XSD_NS_URI, 'complexType'); + $this->getContext()->getSchema()->appendChild($complexType); + $complexType->setAttribute('name', $xsdComplexTypeName); - $complexContent = $dom->createElement('xsd:complexContent'); + $complexContent = $dom->createElementNS(Wsdl::XSD_NS_URI, 'complexContent'); $complexType->appendChild($complexContent); - $xsdRestriction = $dom->createElement('xsd:restriction'); - $xsdRestriction->setAttribute('base', 'soap-enc:Array'); + $xsdRestriction = $dom->createElementNS(Wsdl::XSD_NS_URI, 'restriction'); $complexContent->appendChild($xsdRestriction); + $xsdRestriction->setAttribute('base', Wsdl::SOAP_ENC_NS . ':Array'); - $xsdAttribute = $dom->createElement('xsd:attribute'); - $xsdAttribute->setAttribute('ref', 'soap-enc:arrayType'); - $xsdAttribute->setAttribute('wsdl:arrayType', - 'tns:' . $this->getContext()->translateType($singularType) . '[]'); + $xsdAttribute = $dom->createElementNS(Wsdl::XSD_NS_URI, 'attribute'); $xsdRestriction->appendChild($xsdAttribute); - $this->getContext()->getSchema()->appendChild($complexType); + $xsdAttribute->setAttribute('ref', Wsdl::SOAP_ENC_NS . ':arrayType'); + $xsdAttribute->setAttributeNS( + Wsdl::WSDL_NS_URI, + 'arrayType', + Wsdl::TYPES_NS . ':' . $this->getContext()->translateType($singularType) . '[]' + ); return $xsdComplexType; } @@ -112,7 +113,7 @@ protected function _getSingularPhpType($type) * Return the array nesting level based on the type name * * @param string $type - * @return integer + * @return int */ protected function _getNestedCount($type) { diff --git a/src/Wsdl/ComplexTypeStrategy/ArrayOfTypeSequence.php b/src/Wsdl/ComplexTypeStrategy/ArrayOfTypeSequence.php index 66f1a771..0c223bda 100644 --- a/src/Wsdl/ComplexTypeStrategy/ArrayOfTypeSequence.php +++ b/src/Wsdl/ComplexTypeStrategy/ArrayOfTypeSequence.php @@ -3,26 +3,21 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap\Wsdl\ComplexTypeStrategy; -/** - * Zend_Soap_Wsdl_Strategy_ArrayOfTypeSequence - * - * @category Zend - * @package Zend_Soap - * @subpackage WSDL - */ +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. + * Add an unbounded ArrayOfType based on the xsd:sequence syntax if + * type[] is detected in return value doc comment. * - * @param string $type + * @param string $type * @return string tns:xsd-type */ public function addComplexType($type) @@ -31,6 +26,7 @@ public function addComplexType($type) if ($nestedCounter > 0) { $singularType = $this->_getSingularType($type); + $complexType = ''; for ($i = 1; $i <= $nestedCounter; $i++) { $complexType = $this->_getTypeBasedOnNestingLevel($singularType, $i); @@ -41,17 +37,20 @@ public function addComplexType($type) } return $complexType; - } elseif (($soapType = $this->scanRegisteredTypes($type)) !== null) { + } + + if (($soapType = $this->scanRegisteredTypes($type)) !== null) { // Existing complex type return $soapType; - } else { - // New singular complex type - return parent::addComplexType($type); } + + // New singular complex type + return parent::addComplexType($type); } /** - * Return the ArrayOf or simple type name based on the singular xsdtype and the nesting level + * Return the ArrayOf or simple type name based on the singular xsdtype + * and the nesting level * * @param string $singularType * @param int $level @@ -62,9 +61,9 @@ protected function _getTypeBasedOnNestingLevel($singularType, $level) if ($level == 0) { // This is not an Array anymore, return the xsd simple type return $this->getContext()->getType($singularType); - } else { - return 'tns:' . str_repeat('ArrayOf', $level) . ucfirst($this->getContext()->translateType($singularType)); } + + return Wsdl::TYPES_NS . ':' . str_repeat('ArrayOf', $level) . ucfirst($this->getContext()->translateType($singularType)); } /** @@ -82,7 +81,7 @@ protected function _getSingularType($type) * Return the array nesting level based on the type name * * @param string $type - * @return integer + * @return int */ protected function _getNestedCount($type) { @@ -95,7 +94,6 @@ protected function _getNestedCount($type) * @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[][][]') - * @return void */ protected function _addSequenceType($arrayType, $childType, $phpArrayType) { @@ -111,20 +109,20 @@ protected function _addSequenceType($arrayType, $childType, $phpArrayType) $arrayTypeName = substr($arrayType, strpos($arrayType, ':') + 1); - $complexType = $dom->createElement('xsd:complexType'); + $complexType = $dom->createElementNS(Wsdl::XSD_NS_URI, 'complexType'); + $this->getContext()->getSchema()->appendChild($complexType); + $complexType->setAttribute('name', $arrayTypeName); - $sequence = $dom->createElement('xsd:sequence'); + $sequence = $dom->createElementNS(Wsdl::XSD_NS_URI, 'sequence'); + $complexType->appendChild($sequence); + + $element = $dom->createElementNS(Wsdl::XSD_NS_URI, 'element'); + $sequence->appendChild($element); - $element = $dom->createElement('xsd:element'); $element->setAttribute('name', 'item'); $element->setAttribute('type', $childType); $element->setAttribute('minOccurs', 0); $element->setAttribute('maxOccurs', 'unbounded'); - $sequence->appendChild($element); - - $complexType->appendChild($sequence); - - $this->getContext()->getSchema()->appendChild($complexType); } } diff --git a/src/Wsdl/ComplexTypeStrategy/ComplexTypeStrategyInterface.php b/src/Wsdl/ComplexTypeStrategy/ComplexTypeStrategyInterface.php index 1b6c1880..1324ecf5 100644 --- a/src/Wsdl/ComplexTypeStrategy/ComplexTypeStrategyInterface.php +++ b/src/Wsdl/ComplexTypeStrategy/ComplexTypeStrategyInterface.php @@ -3,9 +3,8 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap\Wsdl\ComplexTypeStrategy; @@ -14,10 +13,6 @@ /** * Interface strategies that generate an XSD-Schema for complex data types in WSDL files. - * - * @category Zend - * @package Zend_Soap - * @subpackage WSDL */ interface ComplexTypeStrategyInterface { diff --git a/src/Wsdl/ComplexTypeStrategy/Composite.php b/src/Wsdl/ComplexTypeStrategy/Composite.php index 5f4db6db..ea8f0a12 100644 --- a/src/Wsdl/ComplexTypeStrategy/Composite.php +++ b/src/Wsdl/ComplexTypeStrategy/Composite.php @@ -3,9 +3,8 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap\Wsdl\ComplexTypeStrategy; @@ -14,33 +13,23 @@ use Zend\Soap\Wsdl; use Zend\Soap\Wsdl\ComplexTypeStrategy\ComplexTypeStrategyInterface as ComplexTypeStrategy; -/** - * Zend_Soap_Wsdl_Strategy_Composite - * - * @category Zend - * @package Zend_Soap - * @subpackage WSDL - */ class Composite implements ComplexTypeStrategy { /** * Typemap of Complex Type => Strategy pairs. - * * @var array */ protected $typeMap = array(); /** * Default Strategy of this composite - * * @var string|ComplexTypeStrategy */ protected $defaultStrategy; /** * Context WSDL file that this composite serves - * - * @var \Zend\Soap\Wsdl|null + * @var Wsdl|null */ protected $context; @@ -50,21 +39,24 @@ class Composite implements ComplexTypeStrategy * @param array $typeMap * @param string|ComplexTypeStrategy $defaultStrategy */ - public function __construct(array $typeMap=array(), $defaultStrategy='\Zend\Soap\Wsdl\ComplexTypeStrategy\DefaultComplexType') - { - foreach ($typeMap AS $type => $strategy) { + public function __construct( + array $typeMap = array(), + $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. * - * @throws Exception\InvalidArgumentException * @param string $type * @param string|ComplexTypeStrategy $strategy * @return Composite + * @throws Exception\InvalidArgumentException */ public function connectTypeToStrategy($type, $strategy) { @@ -78,8 +70,8 @@ public function connectTypeToStrategy($type, $strategy) /** * Return default strategy of this composite * - * @throws Exception\InvalidArgumentException * @return ComplexTypeStrategy + * @throws Exception\InvalidArgumentException */ public function getDefaultStrategy() { @@ -87,7 +79,7 @@ public function getDefaultStrategy() if (is_string($strategy) && class_exists($strategy)) { $strategy = new $strategy; } - if ( !($strategy instanceof ComplexTypeStrategy) ) { + if (!($strategy instanceof ComplexTypeStrategy)) { throw new Exception\InvalidArgumentException( 'Default Strategy for Complex Types is not a valid strategy object.' ); @@ -99,9 +91,9 @@ public function getDefaultStrategy() /** * Return specific strategy or the default strategy of this type. * - * @throws Exception\InvalidArgumentException - * @param string $type + * @param string $type * @return ComplexTypeStrategy + * @throws Exception\InvalidArgumentException */ public function getStrategyOfType($type) { @@ -112,22 +104,24 @@ public function getStrategyOfType($type) $strategy = new $strategy(); } - if ( !($strategy instanceof ComplexTypeStrategy) ) { - throw new Exception\InvalidArgumentException( - "Strategy for Complex Type '$type' is not a valid strategy object." - ); + 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 \Zend\Soap\Wsdl $context + * @param Wsdl $context * @return Composite */ public function setContext(Wsdl $context) @@ -139,20 +133,22 @@ public function setContext(Wsdl $context) /** * Create a complex type based on a strategy * - * @throws Exception\InvalidArgumentException * @param string $type * @return string XSD type + * @throws Exception\InvalidArgumentException */ public function addComplexType($type) { - if (!($this->context instanceof Wsdl) ) { - throw new Exception\InvalidArgumentException( - "Cannot add complex type '$type', no context is set for this composite strategy." - ); + 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); } } diff --git a/src/Wsdl/ComplexTypeStrategy/DefaultComplexType.php b/src/Wsdl/ComplexTypeStrategy/DefaultComplexType.php index f8bcc61f..779cd2bf 100644 --- a/src/Wsdl/ComplexTypeStrategy/DefaultComplexType.php +++ b/src/Wsdl/ComplexTypeStrategy/DefaultComplexType.php @@ -3,37 +3,32 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License - * @package Zend_Soap */ namespace Zend\Soap\Wsdl\ComplexTypeStrategy; +use ReflectionClass; use Zend\Soap\Exception; +use Zend\Soap\Wsdl; -/** - * Zend_Soap_Wsdl_Strategy_DefaultComplexType - * - * @category Zend - * @package Zend_Soap - * @subpackage WSDL - */ class DefaultComplexType extends AbstractComplexTypeStrategy { /** - * Add a complex type by recursivly using all the class properties fetched via Reflection. + * Add a complex type by recursively using all the class properties fetched via Reflection. * * @param string $type Name of the class to be specified - * @throws Exception\InvalidArgumentException if class does not exist * @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 could not be found in "DefaultComplexType" strategy.', + $type )); } @@ -42,10 +37,10 @@ public function addComplexType($type) } $dom = $this->getContext()->toDomDocument(); - $class = new \ReflectionClass($type); + $class = new ReflectionClass($type); $soapTypeName = $this->getContext()->translateType($type); - $soapType = 'tns:' . $soapTypeName; + $soapType = Wsdl::TYPES_NS . ':' . $soapTypeName; // Register type here to avoid recursion $this->getContext()->addType($type, $soapType); @@ -53,19 +48,20 @@ public function addComplexType($type) $defaultProperties = $class->getDefaultProperties(); - $complexType = $dom->createElement('xsd:complexType'); + $complexType = $dom->createElementNS(Wsdl::XSD_NS_URI, 'complexType'); $complexType->setAttribute('name', $soapTypeName); - $all = $dom->createElement('xsd:all'); + $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 + * @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->createElement('xsd:element'); + $element = $dom->createElementNS(Wsdl::XSD_NS_URI, 'element'); $element->setAttribute('name', $propertyName = $property->getName()); $element->setAttribute('type', $this->getContext()->getType(trim($matches[1][0]))); diff --git a/test/AutoDiscover/OnlineTest.php b/test/AutoDiscover/OnlineTest.php index df9961f5..b77d55c6 100644 --- a/test/AutoDiscover/OnlineTest.php +++ b/test/AutoDiscover/OnlineTest.php @@ -3,7 +3,7 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @package Zend_Soap */ diff --git a/test/AutoDiscoverTest.php b/test/AutoDiscoverTest.php index 94d5f44d..d02f1ab1 100644 --- a/test/AutoDiscoverTest.php +++ b/test/AutoDiscoverTest.php @@ -3,7 +3,7 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @package Zend_Soap */ @@ -14,6 +14,8 @@ require_once 'TestAsset/commontypes.php'; use Zend\Soap\AutoDiscover; +use Zend\Soap\Wsdl; +use Zend\Uri\Uri; /** PHPUnit Test Case */ @@ -27,19 +29,67 @@ */ class AutoDiscoverTest extends \PHPUnit_Framework_TestCase { - protected function createAutodiscoverService() + + /** + * @var AutoDiscover + */ + protected $server; + + /** + * @var string + */ + protected $defaultServiceName = 'MyService'; + + /** + * @var string + */ + protected $defaultServiceUri = 'http://localhost/MyService.php'; + + /** + * @var \DOMDocument + */ + protected $dom; + + /** + * @var \DOMXPath + */ + protected $xpath; + + public function setUp() { - $server = new AutoDiscover(); - $server->setUri('http://localhost/my_script.php'); - $server->setServiceName('TestService'); - return $server; + $this->server = new AutoDiscover(); + $this->server->setUri($this->defaultServiceUri); + $this->server->setServiceName($this->defaultServiceName); } - protected function sanitizeWsdlXmlOutputForOsCompability($xmlstring) + /** + * + * + * @param \Zend\Soap\Wsdl $wsdl + * @param null $documentNamespace + */ + public function bindWsdl(Wsdl $wsdl, $documentNamespace = null) { - $xmlstring = str_replace(array("\r", "\n"), "", $xmlstring); - $xmlstring = preg_replace('/(>[\s]{1,}<)/', '', $xmlstring); - return $xmlstring; + $this->dom = new \DOMDocument(); + $this->dom->formatOutput = true; + $this->dom->preserveWhiteSpace = false; + + $this->dom->loadXML($wsdl->toXML()); + + if (empty($documentNamespace)) { + $documentNamespace = $this->defaultServiceUri; + } + + $this->xpath = new \DOMXPath($this->dom); + + $this->xpath->registerNamespace('unittest', Wsdl::WSDL_NS_URI); + + $this->xpath->registerNamespace('tns', $documentNamespace); + $this->xpath->registerNamespace('soap', Wsdl::SOAP_11_NS_URI); + $this->xpath->registerNamespace('soap12', Wsdl::SOAP_12_NS_URI); + $this->xpath->registerNamespace('xsd', Wsdl::XSD_NS_URI); + $this->xpath->registerNamespace('soap-enc', Wsdl::SOAP_ENC_URI); + $this->xpath->registerNamespace('wsdl', Wsdl::WSDL_NS_URI); } /** @@ -50,7 +100,8 @@ protected function sanitizeWsdlXmlOutputForOsCompability($xmlstring) protected function assertValidWSDL(\DOMDocument $dom) { // this code is necessary to support some libxml stupidities. - $file = __DIR__.'/TestAsset/validate.wsdl'; + // @todo memory streams ? + $file = __DIR__ . '/TestAsset/validate.wsdl'; if (file_exists($file)) { unlink($file); } @@ -59,242 +110,609 @@ protected function assertValidWSDL(\DOMDocument $dom) $dom = new \DOMDocument(); $dom->load($file); - $this->assertTrue($dom->schemaValidate(__DIR__ .'/schemas/wsdl.xsd'), "WSDL Did not validate"); + $this->assertTrue( + $dom->schemaValidate(__DIR__ . '/schemas/wsdl.xsd'), + "WSDL Did not validate" + ); unlink($file); } - public function testSetClass() + /** + * @param \DOMElement $element + */ + public function testDocumentNodes($element = null) + { + if (!($this->dom instanceof \DOMDocument)) { + return; + } + + if (is_null($element)) { + $element = $this->dom->documentElement; + } + + /** @var $node \DOMElement */ + foreach ($element->childNodes as $node) { + if (in_array($node->nodeType, array(XML_ELEMENT_NODE))) { + $this->assertNotEmpty( + $node->namespaceURI, 'Document element: ' + . $node->nodeName . ' has no valid namespace. Line: ' + . $node->getLineNo() + ); + $this->testDocumentNodes($node); + } + } + } + + /** + * @dataProvider dataProviderValidUris + */ + public function testAutoDiscoverConstructorUri($uri, $expectedUri) + { + $server = new AutoDiscover(null, $uri); + + $this->assertEquals($expectedUri, $server->getUri()->toString()); + } + + /** + * @dataProvider dataProviderForAutoDiscoverConstructorStrategy + */ + public function testAutoDiscoverConstructorStrategy($strategy) + { + $server = new AutoDiscover($strategy); + + $server->addFunction('\ZendTest\Soap\TestAsset\TestFunc'); + $server->setServiceName('TestService'); + $server->setUri('http://example.com'); + $wsdl = $server->generate(); + + $this->assertEquals( + get_class($strategy), get_class($wsdl->getComplexTypeStrategy()) + ); + } + + /** + * @return array + */ + public function dataProviderForAutoDiscoverConstructorStrategy() + { + return array( + array(new Wsdl\ComplexTypeStrategy\AnyType()), + array(new Wsdl\ComplexTypeStrategy\ArrayOfTypeComplex()), + array(new Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence()), + array(new Wsdl\ComplexTypeStrategy\Composite()), + array(new Wsdl\ComplexTypeStrategy\DefaultComplexType()), + ); + } + + /** + */ + public function testGetDiscoveryStrategy() + { + $server = new AutoDiscover(); + + $this->assertEquals( + 'Zend\Soap\AutoDiscover\DiscoveryStrategy\ReflectionDiscovery', + get_class($server->getDiscoveryStrategy()) + ); + } + + /** + */ + public function testAutoDiscoverConstructorWsdlClass() + { + $server = new AutoDiscover(null, null, '\Zend\Soap\Wsdl'); + + $server->addFunction('\ZendTest\Soap\TestAsset\TestFunc'); + $server->setServiceName('TestService'); + $server->setUri('http://example.com'); + $wsdl = $server->generate(); + + $this->assertEquals('Zend\Soap\Wsdl', trim(get_class($wsdl), '\\')); + $this->assertEquals( + 'Zend\Soap\Wsdl', trim($server->getWsdlClass(), '\\') + ); + } + + /** + * @expectedException \Zend\Soap\Exception\InvalidArgumentException + */ + public function testAutoDiscoverConstructorWsdlClassException() + { + $server = new AutoDiscover(); + $server->setWsdlClass(new \stdClass()); + } + + /** + * @dataProvider dataProviderForSetServiceName + */ + public function testSetServiceName($newName, $shouldBeValid) + { + + if ($shouldBeValid == false) { + $this->setExpectedException('InvalidArgumentException'); + } + + $this->server->setServiceName($newName); + $this->bindWsdl($this->server->generate()); + $this->assertSpecificNodeNumberInXPath( + 1, '/wsdl:definitions[@name="' . $newName . '"]' + ); + } + + /** + * @return array + */ + public function dataProviderForSetServiceName() + { + return array( + array('MyServiceName123', true), + array('1MyServiceName123', false), + array('$MyServiceName123', false), + array('!MyServiceName123', false), + array('&MyServiceName123', false), + array('(MyServiceName123', false), + array('\MyServiceName123', false), + ); + } + + public function testGetServiceName() { - $scriptUri = 'http://localhost/my_script.php'; + $server = new AutoDiscover(); - $server = $this->createAutodiscoverService(); $server->setClass('\ZendTest\Soap\TestAsset\Test'); - $dom = $server->generate()->toDomDocument(); - - $wsdl = '' - . '' - . '' - . '' - . '' - . '' - . '' - . 'Test Function 1' - . '' - . '' - . '' - . '' - . 'Test Function 2' - . '' - . '' - . '' - . '' - . 'Test Function 3' - . '' - . '' - . '' - . 'Test Function 4' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . ''; - - $this->assertEquals($wsdl, $this->sanitizeWsdlXmlOutputForOsCompability($dom->saveXML())); - $this->assertValidWSDL($dom); + + $this->assertEquals('Test', $server->getServiceName()); + } + + /** + * @expectedException \Zend\Soap\Exception\RuntimeException + */ + public function testGetServiceNameException() + { + $server = new AutoDiscover(); + + $server->addFunction('\ZendTest\Soap\TestAsset\TestFunc'); + + $this->assertEquals('Test', $server->getServiceName()); + } + + /** + * @expectedException \Zend\Soap\Exception\InvalidArgumentException + */ + public function testSetUriException() + { + $server = new AutoDiscover(); + + $server->setUri(' '); + } + + /** + * @expectedException \Zend\Soap\Exception\RuntimeException + */ + public function testGetUriException() + { + $server = new AutoDiscover(); + $server->getUri(); + } + + public function testClassMap() + { + + $classMap = array( + 'TestClass' => 'test_class' + ); + + $this->server->setClassMap($classMap); + + $this->assertEquals($classMap, $this->server->getClassMap()); + } + + public function testSetClass() + { + $this->server->setClass('\ZendTest\Soap\TestAsset\Test'); + + $this->bindWsdl($this->server->generate()); + + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:types/xsd:schema[@targetNamespace="' + . $this->defaultServiceUri . '"]', 'Invalid schema definition' + ); + + for ($i = 1; $i <= 4; $i++) { + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="testFunc' + . $i . '"]', + 'Invalid func' . $i . ' operation definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="testFunc' + . $i . '"]/wsdl:documentation', + 'Invalid func' . $i . ' port definition - documentation node' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="testFunc' + . $i . '"]/wsdl:input[@message="tns:testFunc' . $i . 'In"]', + 'Invalid func' . $i . ' port definition - input node' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="testFunc' + . $i . '"]/wsdl:output[@message="tns:testFunc' . $i . 'Out"]', + 'Invalid func' . $i . ' port definition - output node' + ); + } + + $nodes = $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding"]', + 'Invalid service binding definition' + ); + $this->assertEquals( + 'tns:MyServicePort', $nodes->item(0)->getAttribute('type'), + 'Invalid type attribute value in service binding definition' + ); + + $nodes = $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding"]/soap:binding', + 'Invalid service binding definition' + ); + $this->assertEquals( + 'rpc', $nodes->item(0)->getAttribute('style'), + 'Invalid style attribute value in service binding definition' + ); + $this->assertEquals( + 'http://schemas.xmlsoap.org/soap/http', + $nodes->item(0)->getAttribute('transport'), + 'Invalid transport attribute value in service binding definition' + ); + + for ($i = 1; $i <= 4; $i++) { + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding"]/wsdl:operation[@name="testFunc'. $i . '"]', + 'Invalid func' . $i . ' operation binding definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding"]/wsdl:operation[@name="testFunc' + . $i . '"]/soap:operation[@soapAction="' . $this->defaultServiceUri . + '#testFunc' . $i . '"]', + 'Invalid func' . $i . ' operation action binding definition' + ); + } + + $xpath + = '//wsdl:binding[@name="MyServiceBinding"]/wsdl:operation[wsdl:input or wsdl:output]/*/soap:body'; + $this->assertSpecificNodeNumberInXPath(8, $xpath); + $nodes = $this->xpath->query($xpath); + $this->assertAttributesOfNodes( + array( + "use" => "encoded", + "encodingStyle" => "http://schemas.xmlsoap.org/soap/encoding/", + "namespace" => "http://localhost/MyService.php" + ), $nodes + ); + + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:service[@name="MyServiceService"]', + 'Invalid service definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:service[@name="MyServiceService"]/' + . 'wsdl:port[@name="MyServicePort" and @binding="tns:MyServiceBinding"]', + 'Invalid service port definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:service[@name="MyServiceService"]/' + . 'wsdl:port[@name="MyServicePort" and @binding="tns:MyServiceBinding"]/soap:address[@location="' + . $this->defaultServiceUri . '"]', + 'Invalid service address definition' + ); + + + $nodes = $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:message[@name="testFunc1In"]', + 'Invalid message definition' + ); + $this->assertFalse($nodes->item(0)->hasChildNodes()); + + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:message[@name="testFunc2In"]', + 'Invalid message definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:message[@name="testFunc2In"]/wsdl:part[@name="who" and @type="xsd:string"]', + 'Invalid message definition' + ); + + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:message[@name="testFunc2Out"]', + 'Invalid message definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:message[@name="testFunc2Out"]/wsdl:part[@name="return" and @type="xsd:string"]', + 'Invalid message definition' + ); + + + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:message[@name="testFunc3In"]', + 'Invalid message definition' + ); + $this->assertSpecificNodeNumberInXPath( + 2, + '//wsdl:message[@name="testFunc3In"][(wsdl:part[@name="who" and @type="xsd:string"]) or (wsdl:part[@name="when" and @type="xsd:int"])]/wsdl:part', + 'Invalid message definition' + ); + + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:message[@name="testFunc3Out"]', + 'Invalid message definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:message[@name="testFunc3Out"]/wsdl:part[@name="return" and @type="xsd:string"]', + 'Invalid message definition' + ); + + + $nodes = $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:message[@name="testFunc4In"]', + 'Invalid message definition' + ); + $this->assertFalse($nodes->item(0)->hasChildNodes()); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:message[@name="testFunc4Out"]/wsdl:part[@name="return" and @type="xsd:string"]', + 'Invalid message definition' + ); + + + $this->assertValidWSDL($this->dom); + $this->testDocumentNodes(); } public function testSetClassWithDifferentStyles() { - $scriptUri = 'http://localhost/my_script.php'; + $this->server->setBindingStyle( + array('style' => 'document', + 'transport' => $this->defaultServiceUri) + ); + $this->server->setOperationBodyStyle( + array('use' => 'literal', 'namespace' => $this->defaultServiceUri) + ); + $this->server->setClass('\ZendTest\Soap\TestAsset\Test'); - $server = $this->createAutodiscoverService(); - $server->setBindingStyle(array('style' => 'document', 'transport' => 'http://framework.zend.com')); - $server->setOperationBodyStyle(array('use' => 'literal', 'namespace' => 'http://framework.zend.com')); - $server->setClass('\ZendTest\Soap\TestAsset\Test'); - $dom = $server->generate()->toDomDocument(); - - $wsdl = '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . 'Test Function 1' - . '' - . '' - . '' - . '' - . 'Test Function 2' - . '' - . '' - . '' - . '' - . 'Test Function 3' - . '' - . '' - . '' - . 'Test Function 4' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . ''; - - $this->assertEquals($wsdl, $this->sanitizeWsdlXmlOutputForOsCompability($dom->saveXML())); - $this->assertValidWSDL($dom); + $this->bindWsdl($this->server->generate()); + + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:types/xsd:schema/xsd:element[@name="testFunc1"]', + 'Missing test func1 definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:types/xsd:schema/xsd:element[@name="testFunc1"]/xsd:complexType', + 'Missing test func1 type definition' + ); + $this->assertSpecificNodeNumberInXPath( + 0, + '//wsdl:types/xsd:schema/xsd:element[@name="testFunc1"]/xsd:complexType/*', + 'Test func1 does not have children' + ); + + $nodes = $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:types/xsd:schema/xsd:element[@name="testFunc1Response"]/' + .'xsd:complexType/xsd:sequence/xsd:element', + 'Test func1 return element is invalid' + ); + $this->assertAttributesOfNodes( + array( + 'name' => "testFunc1Result", + 'type' => "xsd:string", + ), $nodes + ); + + + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:types/xsd:schema/xsd:element[@name="testFunc2"]', + 'Missing test func2 definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:types/xsd:schema/xsd:element[@name="testFunc2"]/xsd:complexType', + 'Missing test func2 type definition' + ); + $nodes = $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:types/xsd:schema/xsd:element[@name="testFunc2"]/xsd:complexType/' + .'xsd:sequence/xsd:element', + 'Test func2 does not have children' + ); + $this->assertAttributesOfNodes( + array( + 'name' => "who", + 'type' => "xsd:string", + ), $nodes + ); + + $nodes = $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:types/xsd:schema/xsd:element[@name="testFunc2Response"]/' + .'xsd:complexType/xsd:sequence/xsd:element', + 'Test func2 return element is invalid' + ); + $this->assertAttributesOfNodes( + array( + 'name' => "testFunc2Result", + 'type' => "xsd:string", + ), $nodes + ); + + + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:types/xsd:schema/xsd:element[@name="testFunc3"]', + 'Missing test func3 definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:types/xsd:schema/xsd:element[@name="testFunc3"]/xsd:complexType', + 'Missing test func3 type definition' + ); + $this->assertSpecificNodeNumberInXPath( + 2, + '//wsdl:types/xsd:schema/xsd:element[@name="testFunc3"]/xsd:complexType/' + .'xsd:sequence/xsd:element', + 'Test func3 does not have children' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:types/xsd:schema/xsd:element[@name="testFunc3"]/xsd:complexType/' + .'xsd:sequence/xsd:element[@name="who" and @type="xsd:string"]', + 'Test func3 does not have children' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:types/xsd:schema/xsd:element[@name="testFunc3"]/xsd:complexType/' + .'xsd:sequence/xsd:element[@name="when" and @type="xsd:int"]', + 'Test func3 does not have children' + ); + + $nodes = $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:types/xsd:schema/xsd:element[@name="testFunc3Response"]/' + .'xsd:complexType/xsd:sequence/xsd:element', + 'Test func3 return element is invalid' + ); + $this->assertAttributesOfNodes( + array( + 'name' => "testFunc3Result", + 'type' => "xsd:string", + ), $nodes + ); + + + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:types/xsd:schema/xsd:element[@name="testFunc4"]', + 'Missing test func1 definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:types/xsd:schema/xsd:element[@name="testFunc4"]/xsd:complexType', + 'Missing test func1 type definition' + ); + $this->assertSpecificNodeNumberInXPath( + 0, + '//wsdl:types/xsd:schema/xsd:element[@name="testFunc4"]/xsd:complexType/*', + 'Test func1 does not have children' + ); + + $nodes = $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:types/xsd:schema/xsd:element[@name="testFunc4Response"]/' + .'xsd:complexType/xsd:sequence/xsd:element', + 'Test func1 return element is invalid' + ); + $this->assertAttributesOfNodes( + array( + 'name' => "testFunc4Result", + 'type' => "xsd:string", + ), $nodes + ); + + + for ($i = 1; $i <= 4; $i++) { + $this->assertSpecificNodeNumberInXPath( + 3, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="testFunc' + . $i . '"]/*', + 'Missing test func' . $i . ' port definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="testFunc' + . $i . '"]/wsdl:documentation', + 'Missing test func' . $i . ' port documentation' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="testFunc' + . $i . '"]/wsdl:input[@message="tns:testFunc' . $i . 'In"]', + 'Missing test func' . $i . ' port input message' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="testFunc' + . $i . '"]/wsdl:output[@message="tns:testFunc' . $i + . 'Out"]', + 'Missing test func' . $i . ' port output message' + ); + } + + + for ($i = 1; $i <= 4; $i++) { + $this->assertSpecificNodeNumberInXPath( + 3, + '//wsdl:binding[@name="MyServiceBinding"]/wsdl:operation[@name="testFunc' + . $i . '"]/*', + 'Missing test func' . $i . ' binding definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding"]/wsdl:operation[@name="testFunc' + . $i . '"]/soap:operation[@soapAction="' + . $this->defaultServiceUri . '#testFunc' . $i . '"]', + 'Missing test func' . $i . ' binding operation definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding"]/wsdl:operation[@name="testFunc' + . $i + . '"]/wsdl:input/soap:body[@use="literal" and @namespace="' + . $this->defaultServiceUri . '"]', + 'Missing test func' . $i . ' binding input message' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding"]/wsdl:operation[@name="testFunc' + . $i + . '"]/wsdl:output/soap:body[@use="literal" and @namespace="' + . $this->defaultServiceUri . '"]', + 'Missing test func' . $i . ' binding input message' + ); + } + + + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:service[@name="MyServiceService"]/wsdl:port[@name="MyServicePort"' + . ' and @binding="tns:MyServiceBinding"]/soap:address[@location="' + . $this->defaultServiceUri . '"]' + ); + + + for ($i = 1; $i <= 4; $i++) { + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:message[@name="testFunc' . $i + . 'In"]/wsdl:part[@name="parameters" and @element="tns:testFunc' . $i . '"]', + 'Missing test testFunc' . $i . ' input message definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:message[@name="testFunc' . $i + . 'Out"]/wsdl:part[@name="parameters" and @element="tns:testFunc' . $i . 'Response"]', + 'Missing test testFunc' . $i . ' output message definition' + ); + } + + + $this->assertValidWSDL($this->dom); + $this->testDocumentNodes(); } /** @@ -302,99 +720,247 @@ public function testSetClassWithDifferentStyles() */ public function testSetClassWithResponseReturnPartCompabilityMode() { - $scriptUri = 'http://localhost/my_script.php'; + $this->server->setClass('\ZendTest\Soap\TestAsset\Test'); + $this->bindWsdl($this->server->generate()); - $server = $this->createAutodiscoverService(); - $server->setClass('\ZendTest\Soap\TestAsset\Test'); - $dom = $server->generate()->toDomDocument(); - $dom->save(__DIR__.'/TestAsset/setclass.wsdl'); - $this->assertContains('sanitizeWsdlXmlOutputForOsCompability($dom->saveXML())); - $this->assertContains('sanitizeWsdlXmlOutputForOsCompability($dom->saveXML())); - $this->assertContains('sanitizeWsdlXmlOutputForOsCompability($dom->saveXML())); - $this->assertContains('sanitizeWsdlXmlOutputForOsCompability($dom->saveXML())); + for ($i = 1; $i <= 4; $i++) { + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:message[@name="testFunc' . $i . 'Out"]/wsdl:part[@name="return"]' + ); + } - unlink(__DIR__.'/TestAsset/setclass.wsdl'); + + $this->assertValidWSDL($this->dom); + } + + /** + * @expectedException \Zend\Soap\Exception\InvalidArgumentException + * @dataProvider dataProviderForAddFunctionException + */ + public function testAddFunctionException($function) + { + $this->server->addFunction($function); + } + + /** + * @return array + */ + public function dataProviderForAddFunctionException() + { + return array( + array('InvalidFunction'), + array(1), + array(array(1, 2)), + ); } public function testAddFunctionSimple() { - $scriptUri = 'http://localhost/my_script.php'; + $this->server->addFunction('\ZendTest\Soap\TestAsset\TestFunc'); + $this->bindWsdl($this->server->generate()); - $server = $this->createAutodiscoverService(); - $server->addFunction('\ZendTest\Soap\TestAsset\TestFunc'); - $dom = $server->generate()->toDomDocument(); - - $name = "TestService"; - - $wsdl = ''. - ''. - ''. - ''. - 'Test Function'. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''; - $this->assertEquals($wsdl, $this->sanitizeWsdlXmlOutputForOsCompability($dom->saveXML()), "Bad WSDL generated"); - $this->assertValidWSDL($dom); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="TestFunc"]', + 'Missing service port definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="TestFunc"]/wsdl:documentation', + 'Missing service port definition documentation' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="TestFunc"]/wsdl:input', + 'Missing service port definition input message' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="TestFunc"]/wsdl:output', + 'Missing service port definition input message' + ); + + + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]/' + . 'wsdl:operation[@name="TestFunc"]', + 'Missing service binding definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]/' + . ' soap:binding[@style="rpc" and @transport="http://schemas.xmlsoap.org/soap/http"]', + 'Missing service binding transport definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]/' + . ' wsdl:operation[@name="TestFunc"]/soap:operation[@soapAction="' + . $this->defaultServiceUri . '#TestFunc"]', + 'Missing service operation action definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]/' + . 'wsdl:operation[@name="TestFunc"]/wsdl:input/soap:body[@use="encoded" ' + . 'and @encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" and @namespace="' + . $this->defaultServiceUri . '"]', + 'Missing operation input body definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]/' + . 'wsdl:operation[@name="TestFunc"]/wsdl:output/soap:body[@use="encoded"' + . 'and @encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" and @namespace="' + . $this->defaultServiceUri . '"]', + 'Missing operation input body definition' + ); + + + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:service[@name="MyServiceService"]/wsdl:port[@name="MyServicePort"' + . ' and @binding="tns:MyServiceBinding"]/soap:address[@location="' + . $this->defaultServiceUri . '"]', + 'Missing service port definition' + ); + + + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:message[@name="TestFuncIn"]/wsdl:part[@name="who" and @type="xsd:string"]', + 'Missing test testFunc input message definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:message[@name="TestFuncOut"]/wsdl:part[@name="return" and @type="xsd:string"]', + 'Missing test testFunc input message definition' + ); + + + $this->assertValidWSDL($this->dom); + $this->testDocumentNodes(); } public function testAddFunctionSimpleWithDifferentStyle() { - $scriptUri = 'http://localhost/my_script.php'; + $this->server->setBindingStyle( + array('style' => 'document', + 'transport' => $this->defaultServiceUri) + ); + $this->server->setOperationBodyStyle( + array('use' => 'literal', 'namespace' => $this->defaultServiceUri) + ); + $this->server->addFunction('\ZendTest\Soap\TestAsset\TestFunc'); + $this->bindWsdl($this->server->generate()); + + + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:types/xsd:schema[@targetNamespace="' + . $this->defaultServiceUri . '"]', 'Missing service port definition' + ); + + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:types/xsd:schema[@targetNamespace="' . $this->defaultServiceUri + . '"]/xsd:element[@name="TestFunc"]/xsd:complexType/xsd:sequence/' + . 'xsd:element[@name="who" and @type="xsd:string"]', + 'Missing complex type definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:types/xsd:schema[@targetNamespace="' . $this->defaultServiceUri + . '"]/xsd:element[@name="TestFuncResponse"]/xsd:complexType/xsd:sequence' + . '/xsd:element[@name="TestFuncResult" and @type="xsd:string"]', + 'Missing complex type definition' + ); + + + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="TestFunc"]', + 'Missing service port definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="TestFunc"]/wsdl:documentation', + 'Missing service port definition documentation' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="TestFunc"]/wsdl:input', + 'Missing service port definition input message' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="TestFunc"]/wsdl:output', + 'Missing service port definition input message' + ); - $server = $this->createAutodiscoverService(); - $server->setBindingStyle(array('style' => 'document', 'transport' => 'http://framework.zend.com')); - $server->setOperationBodyStyle(array('use' => 'literal', 'namespace' => 'http://framework.zend.com')); - $server->addFunction('\ZendTest\Soap\TestAsset\TestFunc'); - $dom = $server->generate()->toDomDocument(); - - $name = "TestService"; - $wsdl = ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - 'Test Function'. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''; - $this->assertEquals($wsdl, $this->sanitizeWsdlXmlOutputForOsCompability($dom->saveXML()), "Bad WSDL generated"); - $this->assertValidWSDL($dom); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]/' + . 'wsdl:operation[@name="TestFunc"]', + 'Missing service binding definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]/' + . 'soap:binding[@style="document" and @transport="' . $this->defaultServiceUri . '"]', + 'Missing service binding transport definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]/' + . 'wsdl:operation[@name="TestFunc"]/soap:operation[@soapAction="' + . $this->defaultServiceUri . '#TestFunc"]', + 'Missing service operation action definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]/' + . 'wsdl:operation[@name="TestFunc"]/wsdl:input/soap:body[@use="literal" and @namespace="' + . $this->defaultServiceUri . '"]', + 'Missing operation input body definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]/' + . 'wsdl:operation[@name="TestFunc"]/wsdl:output/soap:body[@use="literal" and @namespace="' + . $this->defaultServiceUri . '"]', + 'Missing operation input body definition' + ); + + + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:service[@name="MyServiceService"]/wsdl:port[@name="MyServicePort"' + . ' and @binding="tns:MyServiceBinding"]/soap:address[@location="' + . $this->defaultServiceUri . '"]', + 'Missing service port definition' + ); + + + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:message[@name="TestFuncIn"]/wsdl:part[@name="parameters" and @element="tns:TestFunc"]', + 'Missing test testFunc input message definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:message[@name="TestFuncOut"]/wsdl:part[@name="parameters" and @element="tns:TestFuncResponse"]', + 'Missing test testFunc input message definition' + ); + + + $this->assertValidWSDL($this->dom); + $this->testDocumentNodes(); } /** @@ -402,184 +968,296 @@ public function testAddFunctionSimpleWithDifferentStyle() */ public function testAddFunctionSimpleInReturnNameCompabilityMode() { - $scriptUri = 'http://localhost/my_script.php'; + $this->server->addFunction('\ZendTest\Soap\TestAsset\TestFunc'); + $this->bindWsdl($this->server->generate()); + + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:types/xsd:schema[@targetNamespace="' + . $this->defaultServiceUri . '"]', 'Missing service port definition' + ); - $server = $this->createAutodiscoverService(); - $server->addFunction('\ZendTest\Soap\TestAsset\TestFunc'); - $dom = $server->generate()->toDomDocument(); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="TestFunc"]', + 'Missing service port definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="TestFunc"]/' + . 'wsdl:documentation', + 'Missing service port definition documentation' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="TestFunc"]/' + . 'wsdl:input', + 'Missing service port definition input message' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="TestFunc"]/' + . 'wsdl:output', + 'Missing service port definition input message' + ); - $name = "TestService"; - $wsdl = $this->sanitizeWsdlXmlOutputForOsCompability($dom->saveXML()); - $this->assertContains('', $wsdl); - $this->assertNotContains('assertValidWSDL($dom); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]/' + . 'wsdl:operation[@name="TestFunc"]', + 'Missing service binding definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]/' + . 'soap:binding[@style="rpc" and @transport="http://schemas.xmlsoap.org/soap/http"]', + 'Missing service binding transport definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]/' + . 'wsdl:operation[@name="TestFunc"]/soap:operation[@soapAction="' + . $this->defaultServiceUri . '#TestFunc"]', + 'Missing service operation action definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]/' + . 'wsdl:operation[@name="TestFunc"]/wsdl:input/soap:body[@use="encoded"' + . ' and @encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" ' + . 'and @namespace="http://localhost/MyService.php"]', + 'Missing operation input body definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]/' + . 'wsdl:operation[@name="TestFunc"]/wsdl:output/soap:body[@use="encoded"' + . 'and @encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" and' + . '@namespace="http://localhost/MyService.php"]', + 'Missing operation input body definition' + ); + + + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:service[@name="MyServiceService"]/wsdl:port[@name="MyServicePort"' + . 'and @binding="tns:MyServiceBinding"]/soap:address[@location="' + . $this->defaultServiceUri . '"]', + 'Missing service port definition' + ); + + + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:message[@name="TestFuncIn"]/wsdl:part[@name="who" and @type="xsd:string"]', + 'Missing test testFunc input message definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:message[@name="TestFuncOut"]/wsdl:part[@name="return" and @type="xsd:string"]', + 'Missing test testFunc input message definition' + ); + + + $this->assertValidWSDL($this->dom); + $this->testDocumentNodes(); } public function testAddFunctionMultiple() { - $scriptUri = 'http://localhost/my_script.php'; + $this->server->addFunction('\ZendTest\Soap\TestAsset\TestFunc'); + $this->server->addFunction('\ZendTest\Soap\TestAsset\TestFunc2'); + $this->server->addFunction('\ZendTest\Soap\TestAsset\TestFunc3'); + $this->server->addFunction('\ZendTest\Soap\TestAsset\TestFunc4'); + $this->server->addFunction('\ZendTest\Soap\TestAsset\TestFunc5'); + $this->server->addFunction('\ZendTest\Soap\TestAsset\TestFunc6'); + $this->server->addFunction('\ZendTest\Soap\TestAsset\TestFunc7'); + $this->server->addFunction('\ZendTest\Soap\TestAsset\TestFunc9'); - $server = $this->createAutodiscoverService(); - $server->addFunction('\ZendTest\Soap\TestAsset\TestFunc'); - $server->addFunction('\ZendTest\Soap\TestAsset\TestFunc2'); - $server->addFunction('\ZendTest\Soap\TestAsset\TestFunc3'); - $server->addFunction('\ZendTest\Soap\TestAsset\TestFunc4'); - $server->addFunction('\ZendTest\Soap\TestAsset\TestFunc5'); - $server->addFunction('\ZendTest\Soap\TestAsset\TestFunc6'); - $server->addFunction('\ZendTest\Soap\TestAsset\TestFunc7'); - $server->addFunction('\ZendTest\Soap\TestAsset\TestFunc9'); - - $dom = $server->generate()->toDomDocument(); - - $name = "TestService"; - - $wsdl = ''. - ''. - ''. - ''. - 'Test Function'. - 'Test Function 2'. - 'Return false'. - 'Return true'. - 'Return integer'. - 'Return string'. - 'Return array'. - 'Multiple Args'. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''. - ''; - $this->assertEquals($wsdl, $this->sanitizeWsdlXmlOutputForOsCompability($dom->saveXML()), "Generated WSDL did not match expected XML"); - $this->assertValidWSDL($dom); - } + $this->bindWsdl($this->server->generate()); - /** - * @group ZF-4117 - */ - public function testChangeWsdlUriInConstructor() - { - $scriptUri = 'http://localhost/my_script.php'; - $server = new AutoDiscover(null, "http://example.com/service.php"); - $server->setServiceName("TestService"); - $server->addFunction('\ZendTest\Soap\TestAsset\TestFunc'); + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:types/xsd:schema[@targetNamespace="' + . $this->defaultServiceUri . '"]', 'Missing service port definition' + ); + + + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]/' + . 'soap:binding[@style="rpc" and @transport="http://schemas.xmlsoap.org/soap/http"]', + 'Missing service port definition' + ); + + + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:service[@name="MyServiceService"]/wsdl:port[@name="MyServicePort"' + . ' and @binding="tns:MyServiceBinding"]/soap:address[@location="' + . $this->defaultServiceUri . '"]', + 'Missing service port definition' + ); + + foreach (array('', 2, 3, 4, 5, 6, 7, 9) as $i) { + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="TestFunc' + . $i . '"]', + 'Missing service port definition for TestFunc' . $i . '' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="TestFunc' + . $i . '"]/wsdl:documentation', + 'Missing service port definition documentation for TestFunc' + . $i . '' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="TestFunc' + . $i . '"]/wsdl:input[@message="tns:TestFunc' . $i . 'In"]', + 'Missing service port definition input message for TestFunc' + . $i . '' + ); + + + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]/' + . 'wsdl:operation[@name="TestFunc' . $i . '"]/soap:operation[@soapAction="' + . $this->defaultServiceUri . '#TestFunc' . $i . '"]', + 'Missing service operation action definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]/' + . 'wsdl:operation[@name="TestFunc' . $i . '"]/wsdl:input/soap:body' + . '[@use="encoded" and @encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' + . ' and @namespace="' . $this->defaultServiceUri . '"]', + 'Missing operation input for TestFunc' . $i . ' body definition' + ); + + + if ($i != 2) { + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:portType[@name="MyServicePort"]/wsdl:operation[@name="TestFunc' + . $i . '"]/wsdl:output[@message="tns:TestFunc' . $i + . 'Out"]', + 'Missing service port definition input message for TestFunc' + . $i . '' + ); + + + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:binding[@name="MyServiceBinding" and @type="tns:MyServicePort"]' + . '/wsdl:operation[@name="TestFunc'. $i . '"]/wsdl:output/soap:body' + . '[@use="encoded" and @encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' + . ' and @namespace="' . $this->defaultServiceUri . '"]', + 'Missing operation input for TestFunc' . $i + . ' body definition' + ); + + + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:message[@name="TestFunc' . $i . 'In"]', + 'Missing test testFunc' . $i . ' input message definition' + ); + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:message[@name="TestFunc' . $i . 'Out"]', + 'Missing test testFunc' . $i . ' input message definition' + ); + } + } - $wsdlOutput = $server->toXml(); - $this->assertNotContains($scriptUri, $wsdlOutput); - $this->assertContains("http://example.com/service.php", $wsdlOutput); + $this->assertValidWSDL($this->dom); + $this->testDocumentNodes(); } /** * @group ZF-4117 + * + * @dataProvider dataProviderValidUris */ - public function testChangeWsdlUriViaSetUri() + public function testChangeWsdlUriInConstructor($uri, $expectedUri) { - $scriptUri = 'http://localhost/my_script.php'; + $this->server->addFunction('\ZendTest\Soap\TestAsset\TestFunc'); + $this->server->setUri($uri); + $this->bindWsdl($this->server->generate()); - $server = $this->createAutodiscoverService(); - $server->setUri("http://example.com/service.php"); - $server->addFunction('\ZendTest\Soap\TestAsset\TestFunc'); - $wsdlOutput = $server->toXml(); + $this->assertEquals( + $expectedUri, + $this->dom->documentElement->getAttribute('targetNamespace') + ); + $this->assertNotContains( + $this->defaultServiceUri, $this->dom->saveXML() + ); - $this->assertNotContains($scriptUri, $wsdlOutput); - $this->assertContains("http://example.com/service.php", $wsdlOutput); + + $this->assertValidWSDL($this->dom); + $this->testDocumentNodes(); } public function testSetNonStringNonZendUriUriThrowsException() { - $server = $this->createAutodiscoverService(); - $this->setExpectedException('Zend\Soap\Exception\InvalidArgumentException', 'No uri given to'); + $server = new AutoDiscover(); + + $this->setExpectedException( + '\Zend\Soap\Exception\InvalidArgumentException', + 'Argument to \Zend\Soap\AutoDiscover::setUri should be string or \Zend\Uri\Uri instance.' + ); $server->setUri(array("bogus")); } /** * @group ZF-4117 + * @dataProvider dataProviderValidUris */ - public function testChangingWsdlUriAfterGenerationIsPossible() - { - $scriptUri = 'http://localhost/my_script.php'; - - $server = $this->createAutodiscoverService(); - $server->setUri("http://example.com/service.php"); - $server->addFunction('\ZendTest\Soap\TestAsset\TestFunc'); - - $wsdlOutput = $server->toXml(); - - $this->assertNotContains($scriptUri, $wsdlOutput); - $this->assertContains("http://example.com/service.php", $wsdlOutput); - - $server->setUri("http://example2.com/service2.php"); + public function testChangingWsdlUriAfterGenerationIsPossible( + $uri, $expectedUri + ) { + $this->server->addFunction('\ZendTest\Soap\TestAsset\TestFunc'); + $wsdl = $this->server->generate(); + $wsdl->setUri($uri); + + $this->assertEquals( + $expectedUri, $wsdl->toDomDocument()->documentElement->getAttribute( + 'targetNamespace' + ) + ); - $wsdlOutput = $server->toXml(); + $this->assertValidWSDL($wsdl->toDomDocument()); + $this->testDocumentNodes(); + } - $this->assertNotContains($scriptUri, $wsdlOutput); - $this->assertNotContains("http://example.com/service.php", $wsdlOutput); - $this->assertContains("http://example2.com/service2.php", $wsdlOutput); + /** + * @return array + */ + public function dataProviderValidUris() + { + return array( + array('http://example.com/service.php', + 'http://example.com/service.php'), + array('http://example.com/?a=b&b=c', + 'http://example.com/?a=b&b=c'), + array('http://example.com/?a=b&b=c', + 'http://example.com/?a=b&b=c'), + array('urn:uuid:550e8400-e29b-41d4-a716-446655440000', + 'urn:uuid:550e8400-e29b-41d4-a716-446655440000'), + array('urn:acme:servicenamespace', 'urn:acme:servicenamespace'), + array(new Uri('http://example.com/service.php'), + 'http://example.com/service.php'), + array(new Uri('http://example.com/?a=b&b=c'), + 'http://example.com/?a=b&b=c'), + array(new Uri('http://example.com/?a=b&b=c'), + 'http://example.com/?a=b&b=c'), + ); } /** @@ -587,17 +1265,25 @@ public function testChangingWsdlUriAfterGenerationIsPossible() * @group ZF-4125 * */ - public function testUsingClassWithMultipleMethodPrototypesProducesValidWsdl() + public function testUsingClassWithMethodsWithMultipleDefaultParameterValues( + ) { - $scriptUri = 'http://localhost/my_script.php'; + $this->server->setClass( + '\ZendTest\Soap\TestAsset\TestFixingMultiplePrototypes' + ); + $this->bindWsdl($this->server->generate()); + - $server = $this->createAutodiscoverService(); - $server->setClass('\ZendTest\Soap\TestAsset\TestFixingMultiplePrototypes'); + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:message[@name="testFuncIn"]' + ); + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:message[@name="testFuncOut"]' + ); - $wsdlOutput = $server->toXml(); - $this->assertEquals(1, substr_count($wsdlOutput, '')); - $this->assertEquals(1, substr_count($wsdlOutput, '')); + $this->assertValidWSDL($this->dom); + $this->testDocumentNodes(); } /** @@ -605,74 +1291,97 @@ public function testUsingClassWithMultipleMethodPrototypesProducesValidWsdl() */ public function testComplexTypesThatAreUsedMultipleTimesAreRecoginzedOnce() { - $server = $this->createAutodiscoverService(); - $server->setComplexTypeStrategy(new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeComplex); - $server->setClass('\ZendTest\Soap\TestAsset\AutoDiscoverTestClass2'); + $this->server->setComplexTypeStrategy( + new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeComplex + ); + $this->server->setClass( + '\ZendTest\Soap\TestAsset\AutoDiscoverTestClass2' + ); + $this->bindWsdl($this->server->generate()); - $wsdlOutput = $server->toXml(); - $this->assertEquals(1, - substr_count($wsdlOutput, 'wsdl:arrayType="tns:AutoDiscoverTestClass1[]"'), - 'wsdl:arrayType definition of TestClass1 has to occour once.' + $this->assertSpecificNodeNumberInXPath( + 1, + '//xsd:attribute[@wsdl:arrayType="tns:AutoDiscoverTestClass1[]"]', + 'Definition of TestClass1 has to occour once.' ); - $this->assertEquals(1, - substr_count($wsdlOutput, ''), - '\ZendTest\Soap\TestAsset\AutoDiscoverTestClass1 has to be defined once.' + $this->assertSpecificNodeNumberInXPath( + 1, '//xsd:complexType[@name="AutoDiscoverTestClass1"]', + 'AutoDiscoverTestClass1 has to be defined once.' ); - $this->assertEquals(1, - substr_count($wsdlOutput, ''), - '\ZendTest\Soap\TestAsset\AutoDiscoverTestClass1 should be defined once.' + $this->assertSpecificNodeNumberInXPath( + 1, '//xsd:complexType[@name="ArrayOfAutoDiscoverTestClass1"]', + 'AutoDiscoverTestClass1 should be defined once.' + ); + $nodes = $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:part[@name="test" and @type="tns:AutoDiscoverTestClass1"]', + 'AutoDiscoverTestClass1 appears once or more than once in the message parts section.' ); $this->assertTrue( - substr_count($wsdlOutput, '') >= 1, - '\ZendTest\Soap\TestAsset\AutoDiscoverTestClass1 appears once or more than once in the message parts section.' + $nodes->length >= 1, + 'AutoDiscoverTestClass1 appears once or more than once in the message parts section.' ); + + + $this->assertValidWSDL($this->dom); + $this->testDocumentNodes(); } /** * @group ZF-5604 */ - public function testReturnSameArrayOfObjectsResponseOnDifferentMethodsWhenArrayComplex() + public function testReturnSameArrayOfObjectsResponseOnDifferentMethodsWhenArrayComplex( + ) { - $autodiscover = $this->createAutodiscoverService(); - $autodiscover->setComplexTypeStrategy(new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeComplex); - $autodiscover->setClass('\ZendTest\Soap\TestAsset\MyService'); - $wsdl = $autodiscover->toXml(); + $this->server->setComplexTypeStrategy( + new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeComplex + ); + $this->server->setClass('\ZendTest\Soap\TestAsset\MyService'); + $this->bindWsdl($this->server->generate()); + + + $this->assertSpecificNodeNumberInXPath( + 1, '//xsd:complexType[@name="ArrayOfMyResponse"]' + ); + $this->assertSpecificNodeNumberInXPath( + 0, '//wsdl:part[@type="tns:My_Response[]"]' + ); - $this->assertEquals(1, substr_count($wsdl, '')); - $this->assertEquals(0, substr_count($wsdl, 'tns:My_Response[]')); + $this->assertValidWSDL($this->dom); + $this->testDocumentNodes(); } /** * @group ZF-5430 */ - public function testReturnSameArrayOfObjectsResponseOnDifferentMethodsWhenArraySequence() + public function testReturnSameArrayOfObjectsResponseOnDifferentMethodsWhenArraySequence( + ) { - $autodiscover = $this->createAutodiscoverService(); - $autodiscover->setComplexTypeStrategy(new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence); - $autodiscover->setClass('\ZendTest\Soap\TestAsset\MyServiceSequence'); - $wsdl = $autodiscover->toXml(); + $this->server->setComplexTypeStrategy( + new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence + ); + $this->server->setClass('\ZendTest\Soap\TestAsset\MyServiceSequence'); + $this->bindWsdl($this->server->generate()); - $this->assertEquals(1, substr_count($wsdl, '')); - $this->assertEquals(1, substr_count($wsdl, '')); - $this->assertEquals(1, substr_count($wsdl, '')); - $this->assertEquals(0, substr_count($wsdl, 'tns:string[]')); - } + $this->assertSpecificNodeNumberInXPath( + 1, '//xsd:complexType[@name="ArrayOfString"]' + ); + $this->assertSpecificNodeNumberInXPath( + 1, '//xsd:complexType[@name="ArrayOfArrayOfString"]' + ); + $this->assertSpecificNodeNumberInXPath( + 1, '//xsd:complexType[@name="ArrayOfArrayOfArrayOfString"]' + ); - /** - * @group ZF-5736 - */ - public function testAmpersandInUrlIsCorrectlyEncoded() - { - $autodiscover = new AutoDiscover(); - $autodiscover->setUri("http://example.com/?a=b&b=c"); - $autodiscover->setClass('\ZendTest\Soap\TestAsset\Test'); - $wsdl = $autodiscover->toXml(); + $this->assertNotContains('tns:string[]', $this->dom->saveXML()); + - $this->assertContains("http://example.com/?a=b&b=c", $wsdl); + $this->assertValidWSDL($this->dom); + $this->testDocumentNodes(); } /** @@ -680,14 +1389,20 @@ public function testAmpersandInUrlIsCorrectlyEncoded() */ public function testNoReturnIsOneWayCallInSetClass() { - $autodiscover = $this->createAutodiscoverService(); - $autodiscover->setClass('\ZendTest\Soap\TestAsset\NoReturnType'); - $wsdl = $autodiscover->toXml(); + $this->server->setClass('\ZendTest\Soap\TestAsset\NoReturnType'); + $this->bindWsdl($this->server->generate()); + - $this->assertContains( - '@param string $message', - $wsdl + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:portType/wsdl:operation[@name="pushOneWay"]/wsdl:input' ); + $this->assertSpecificNodeNumberInXPath( + 0, '//wsdl:portType/wsdl:operation[@name="pushOneWay"]/wsdl:output' + ); + + + $this->assertValidWSDL($this->dom); + $this->testDocumentNodes(); } /** @@ -695,15 +1410,20 @@ public function testNoReturnIsOneWayCallInSetClass() */ public function testNoReturnIsOneWayCallInAddFunction() { - $autodiscover = $this->createAutodiscoverService(); - $autodiscover->setServiceName('TestService'); - $autodiscover->addFunction('\ZendTest\Soap\TestAsset\OneWay'); - $wsdl = $autodiscover->toXml(); + $this->server->addFunction('\ZendTest\Soap\TestAsset\OneWay'); + $this->bindWsdl($this->server->generate()); - $this->assertContains( - '@param string $message', - $wsdl + + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:portType/wsdl:operation[@name="OneWay"]/wsdl:input' + ); + $this->assertSpecificNodeNumberInXPath( + 0, '//wsdl:portType/wsdl:operation[@name="OneWay"]/wsdl:output' ); + + + $this->assertValidWSDL($this->dom); + $this->testDocumentNodes(); } /** @@ -712,32 +1432,79 @@ public function testNoReturnIsOneWayCallInAddFunction() */ public function testRecursiveWsdlDependencies() { - $autodiscover = $this->createAutodiscoverService(); - $autodiscover->setComplexTypeStrategy(new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence); - $autodiscover->setClass('\ZendTest\Soap\TestAsset\Recursion'); - $wsdl = $autodiscover->toXml(); + $this->server->setComplexTypeStrategy( + new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence + ); + $this->server->setClass('\ZendTest\Soap\TestAsset\Recursion'); + + $this->bindWsdl($this->server->generate()); + // // // // // + $this->assertSpecificNodeNumberInXPath( + 1, + '//wsdl:types/xsd:schema/xsd:complexType[@name="Recursion"]/xsd:all/' + . 'xsd:element[@name="recursion" and @type="tns:Recursion"]' + ); - $path = '//wsdl:types/xsd:schema/xsd:complexType[@name="Recursion"]/xsd:all/xsd:element[@name="recursion" and @type="tns:Recursion"]'; - $this->assertWsdlPathExists($wsdl, $path); + $this->assertValidWSDL($this->dom); + $this->testDocumentNodes(); } - public function assertWsdlPathExists($xml, $path) + /** + * @runInSeparateProcess + */ + public function testHandle() { - $doc = new \DOMDocument('UTF-8'); - $doc->loadXML($xml); + $scriptUri = 'http://localhost/MyService.php'; - $xpath = new \DOMXPath($doc); - $xpath->registerNamespace('wsdl', 'http://schemas.xmlsoap.org/wsdl/'); + $this->server->setClass('\ZendTest\Soap\TestAsset\Test'); - $nodes = $xpath->query($path); + ob_start(); + $this->server->handle(); + $actualWsdl = ob_get_clean(); + $this->assertNotEmpty($actualWsdl, "WSDL content was not outputted."); + $this->assertContains($scriptUri, $actualWsdl, "Script URL was not found in WSDL content."); + } - $this->assertTrue($nodes->length >= 1, "Could not assert that XML Document contains a node that matches the XPath Expression: " . $path); + /** + * @param int $n + * @param string $xpath + * @param string $msg + * + * @return \DOMNodeList + */ + public function assertSpecificNodeNumberInXPath($n, $xpath, $msg = null) + { + + $nodes = $this->xpath->query($xpath); + if (!($nodes instanceof \DOMNodeList)) { + $this->fail('Nodes not found. Invalid XPath expression ?'); + } + $this->assertEquals($n, $nodes->length, $msg . "\nXPath: " . $xpath); + + return $nodes; + } + + public function assertAttributesOfNodes($attributes, $nodeList) + { + + $c = count($attributes); + + $keys = array_keys($attributes); + + foreach ($nodeList as $node) { + for ($i = 0; $i < $c; $i++) { + $this->assertEquals( + $attributes[$keys[$i]], $node->getAttribute($keys[$i]), + 'Invalid attribute value.' + ); + } + } } } diff --git a/test/Client/DotNetTest.php b/test/Client/DotNetTest.php new file mode 100644 index 00000000..68930024 --- /dev/null +++ b/test/Client/DotNetTest.php @@ -0,0 +1,204 @@ +client = new DotNetClient(null, array('location' => 'http://unithost/test', + 'uri' => 'http://unithost/test')); + } + + /** + * Tests that a default cURL client is used if none is injected. + * + * @return void + * @covers Zend\Soap\Client\DotNet::getCurlClient + */ + public function testADefaultCurlClientIsUsedIfNoneIsInjected() + { + $this->assertInstanceOf('Zend\Http\Client\Adapter\Curl', $this->client->getCurlClient()); + } + + /** + * Tests that the cURL client can be injected. + * + * @return void + * @covers Zend\Soap\Client\DotNet::getCurlClient + * @covers Zend\Soap\Client\DotNet::setCurlClient + */ + public function testCurlClientCanBeInjected() + { + $this->mockCurlClient(); + $this->assertSame($this->curlClient, $this->client->getCurlClient()); + } + + /** + * Tests that a cURL client request is done when using NTLM + * authentication. + * + * @return void + * @covers Zend\Soap\Client\DotNet::_doRequest + */ + public function testCurlClientRequestIsDoneWhenUsingNtlmAuthentication() + { + $this->mockNtlmRequest(); + $this->assertInstanceOf('stdClass', $this->client->TestMethod()); + } + + /** + * Tests that the default SOAP client request is done when not using NTLM authentication. + * + * @return void + * @covers Zend\Soap\Client\DotNet::_doRequest + */ + public function testDefaultSoapClientRequestIsDoneWhenNotUsingNtlmAuthentication() + { + $soapClient = $this->getMock('Zend\Soap\Client\Common', + array('_doRequest'), + array(array($this->client, '_doRequest'), + null, + array('location' => 'http://unit/test', + 'uri' => 'http://unit/test'))); + + MockCallUserFunc::$mock = true; + $this->client->setSoapClient($soapClient); + $this->client->TestMethod(); + + $this->assertSame('http://unit/test#TestMethod', MockCallUserFunc::$params[3]); + + MockCallUserFunc::$mock = false; + } + + /** + * Tests that the last request headers can be fetched correctly. + * + * @return void + * @covers Zend\Soap\Client\DotNet::getLastRequestHeaders + */ + public function testLastRequestHeadersCanBeFetchedCorrectly() + { + $expectedHeaders = "Content-Type: text/xml; charset=utf-8\r\n" + . "Method: POST\r\n" + . "SOAPAction: \"http://unithost/test#TestMethod\"\r\n" + . "User-Agent: PHP-SOAP-CURL\r\n"; + + $this->mockNtlmRequest(); + $this->client->TestMethod(); + + $this->assertSame($expectedHeaders, $this->client->getLastRequestHeaders()); + } + + /** + * Tests that the last response headers can be fetched correctly. + * + * @return void + * @covers Zend\Soap\Client\DotNet::getLastResponseHeaders + */ + public function testLastResponseHeadersCanBeFetchedCorrectly() + { + $expectedHeaders = "Cache-Control: private\r\n" + . "Content-Type: text/xml; charset=utf-8\r\n"; + + $this->mockNtlmRequest(); + $this->client->TestMethod(); + + $this->assertSame($expectedHeaders, $this->client->getLastResponseHeaders()); + } + + /** + * Mocks the cURL client. + * + * @return void + */ + private function mockCurlClient() + { + $this->curlClient = $this->getMock('Zend\Http\Client\Adapter\Curl', + array('close', 'connect', 'read', 'write')); + $this->client->setCurlClient($this->curlClient); + } + + /** + * Mocks an NTLM SOAP request. + * + * @return void + */ + private function mockNtlmRequest() + { + $headers = array('Content-Type' => 'text/xml; charset=utf-8', + 'Method' => 'POST', + 'SOAPAction' => '"http://unithost/test#TestMethod"', + 'User-Agent' => 'PHP-SOAP-CURL'); + $response = "HTTP/1.1 200 OK\n" + . "Cache-Control: private\n" + . "Content-Type: text/xml; charset=utf-8\n" + . "\n\n" + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . '' + . ''; + + $this->mockCurlClient(); + + $this->curlClient->expects($this->once()) + ->method('connect') + ->with('unithost', 80); + $this->curlClient->expects($this->once()) + ->method('read') + ->will($this->returnValue($response)); + $this->curlClient->expects($this->any()) + ->method('write') + ->with('POST', $this->isInstanceOf('Zend\Uri\Http'), 1.1, $headers, $this->stringContains('client->setOptions(array('authentication' => 'ntlm', + 'login' => 'username', + 'password' => 'testpass')); + } +} diff --git a/test/ClientTest.php b/test/ClientTest.php index a8e57044..c4305336 100644 --- a/test/ClientTest.php +++ b/test/ClientTest.php @@ -3,7 +3,7 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @package Zend_Soap */ @@ -12,8 +12,10 @@ require_once __DIR__ . '/TestAsset/commontypes.php'; +use Zend\Soap\AutoDiscover; use Zend\Soap\Client; use Zend\Soap\Server; +use Zend\Soap\Wsdl; /** * @category Zend @@ -41,6 +43,21 @@ public function testSetOptions() $ctx = stream_context_create(); + $typeMap = array( + array( + 'type_name' => 'dateTime', + 'type_ns' => 'http://www.w3.org/2001/XMLSchema', + 'from_xml' => 'strtotime', + 'to_xml' => 'strtotime', + ), + array( + 'type_name' => 'date', + 'type_ns' => 'http://www.w3.org/2001/XMLSchema', + 'from_xml' => 'strtotime', + 'to_xml' => 'strtotime', + ) + ); + $nonWSDLOptions = array('soap_version' => SOAP_1_1, 'classmap' => array('TestData1' => '\ZendTest\Soap\TestAsset\TestData1', 'TestData2' => '\ZendTest\Soap\TestAsset\TestData2',), @@ -65,7 +82,9 @@ public function testSetOptions() 'cache_wsdl' => 8, 'features' => 4, - 'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP | 5); + 'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP | 5, + 'typemap' => $typeMap + ); $client->setOptions($nonWSDLOptions); $this->assertTrue($client->getOptions() == $nonWSDLOptions); @@ -96,7 +115,9 @@ public function testSetOptions() 'stream_context' => $ctx, - 'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP | 5); + 'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP | 5, + 'typemap' => $typeMap + ); $client1->setOptions($wsdlOptions); $this->assertTrue($client1->getOptions() == $wsdlOptions); @@ -108,6 +129,21 @@ public function testGetOptions() $this->assertTrue($client->getOptions() == array('encoding' => 'UTF-8', 'soap_version' => SOAP_1_2)); + $typeMap = array( + array( + 'type_name' => 'dateTime', + 'type_ns' => 'http://www.w3.org/2001/XMLSchema', + 'from_xml' => 'strtotime', + 'to_xml' => 'strtotime', + ), + array( + 'type_name' => 'date', + 'type_ns' => 'http://www.w3.org/2001/XMLSchema', + 'from_xml' => 'strtotime', + 'to_xml' => 'strtotime', + ) + ); + $options = array('soap_version' => SOAP_1_1, 'wsdl' => __DIR__.'/TestAsset/wsdl_example.wsdl', @@ -130,7 +166,9 @@ public function testGetOptions() 'local_cert' => __DIR__.'/TestAsset/cert_file', 'passphrase' => 'some pass phrase', - 'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP | 5); + 'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP | 5, + 'typemap' => $typeMap + ); $client->setOptions($options); $this->assertTrue($client->getOptions() == $options); @@ -243,24 +281,50 @@ public function testGetFunctions() } /** - * @todo Implement testGetTypes(). */ public function testGetTypes() { - // Remove the following line when you implement this test. - $this->markTestIncomplete( - "This test has not been implemented yet." - ); + $wsdlFilename = __DIR__ . '/TestAsset/GetTypesWsdlTest.wsdl'; + + $autodiscover = new AutoDiscover(); + $autodiscover->setServiceName('ExampleService'); + $autodiscover->setComplexTypeStrategy(new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeComplex); + $autodiscover->setClass('\ZendTest\Soap\TestAsset\AutoDiscoverTestClass2'); + $autodiscover->setUri('http://example.com'); + $wsdl = $autodiscover->generate(); + $wsdl->dump($wsdlFilename); + + $server = new Server($wsdlFilename); + $server->setClass('\ZendTest\Soap\TestAsset\AutoDiscoverTestClass2'); + + $client = new Client\Local($server, $wsdlFilename); + $soapClient = $client->getSoapClient(); + + $typesArray = $soapClient->__getTypes(); + + $this->assertCount(2, $typesArray); + + $count = 0; + foreach ($typesArray as $element) { + if (strpos($element, 'struct AutoDiscoverTestClass1') === 0 OR strpos($element, 'AutoDiscoverTestClass1 ArrayOfAutoDiscoverTestClass1') === 0) { + $count++; + } + } + $this->assertEquals(2, $count, 'Invalid types'); + + unlink($wsdlFilename); } + /** + * @outputBuffering enabled + */ public function testGetLastRequest() { - if (headers_sent()) { - $this->markTestSkipped('Cannot run testGetLastRequest() when headers have already been sent; enable output buffering to run this test'); + if (headers_sent($file, $line)) { + $this->markTestSkipped('Cannot run testGetLastRequest() when headers have already been sent. Output started in '.$file.'@'.$line.' enable output buffering to run this test'); return; } - $server = new Server(__DIR__ . '/TestAsset/wsdl_example.wsdl'); $server->setClass('\ZendTest\Soap\TestAsset\TestClass'); @@ -520,4 +584,27 @@ public function testSetSoapClient() $this->assertSame($clientMock, $soap->getSoapClient()); } + + /** + * @expectedException \Zend\Soap\Exception\UnexpectedValueException + * @dataProvider dataProviderForInitSoapClientObjectException + */ + public function testInitSoapClientObjectException($wsdl, $options) + { + $client = new Client($wsdl, $options); + $client->getSoapClient(); + } + + /** + * @return array + */ + public function dataProviderForInitSoapClientObjectException() + { + return array( + array(null, array()), + array(null, array('location'=>'http://example.com')), + array(__DIR__ . './TestAsset/wsdl_example.wsdl', array('use'=>SOAP_ENCODED)), + array(__DIR__ . './TestAsset/wsdl_example.wsdl', array('style'=>SOAP_DOCUMENT)) + ); + } } diff --git a/test/Server/DocumentLiteralWrapperTest.php b/test/Server/DocumentLiteralWrapperTest.php index 3d9a1b2c..a19c801c 100644 --- a/test/Server/DocumentLiteralWrapperTest.php +++ b/test/Server/DocumentLiteralWrapperTest.php @@ -3,7 +3,7 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @package Zend_Soap */ diff --git a/test/ServerTest.php b/test/ServerTest.php index b87ed324..1a127b6b 100644 --- a/test/ServerTest.php +++ b/test/ServerTest.php @@ -3,7 +3,7 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @package Zend_Soap */ @@ -12,6 +12,7 @@ require_once __DIR__ . '/TestAsset/commontypes.php'; +use Zend\Soap\AutoDiscover; use Zend\Soap\Server; /** @@ -494,8 +495,7 @@ public function testGetLastRequest() $server->setClass('\ZendTest\Soap\TestAsset\ServerTestClass'); $request = - '' . "\n" - . 'World' . '' . '' - . '' . "\n"; + . ''; $response = $server->handle($request); @@ -649,89 +649,115 @@ public function testHandle() } /** - * @todo Implement testRegisterFaultException(). + * @dataProvider dataProviderForRegisterFaultException + * + * @param string|array $exception */ - public function testRegisterFaultException() + public function testRegisterFaultException($exception) { $server = new Server(); - $server->registerFaultException("Zend_Soap_Server_Exception"); - $server->registerFaultException(array("OutOfBoundsException", "BogusException")); + $server->registerFaultException($exception); - $this->assertEquals(array( - 'Zend_Soap_Server_Exception', - 'OutOfBoundsException', - 'BogusException', - ), $server->getFaultExceptions()); + if (!is_array($exception)) { + $this->assertContains($exception, $server->getFaultExceptions()); + } else { + foreach($exception as $row) { + $this->assertContains($row, $server->getFaultExceptions()); + } + } } /** - * @todo Implement testDeregisterFaultException(). + * @dataProvider dataProviderForRegisterFaultException + * + * @param string|array $exception */ - public function testDeregisterFaultException() + public function testDeregisterFaultException($exception) { $server = new Server(); - $server->registerFaultException(array("OutOfBoundsException", "BogusException")); - $ret = $server->deregisterFaultException("BogusException"); - $this->assertTrue($ret); + $server->registerFaultException($exception); + if (is_array($exception)) { + $exception = array_shift($exception); + } - $this->assertEquals(array( - 'OutOfBoundsException', - ), $server->getFaultExceptions()); + $this->assertTrue($server->deregisterFaultException($exception)); - $ret = $server->deregisterFaultException("NonRegisteredException"); - $this->assertFalse($ret); + $this->assertNotContains($exception, $server->getFaultExceptions()); } /** - * @todo Implement testGetFaultExceptions(). + * @dataProvider dataProviderForRegisterFaultException + * + * @param string|array $exception */ - public function testGetFaultExceptions() + public function testIsRegisteredAsFaultException($exception) { + $server = new Server(); + $server->registerFaultException($exception); + + + if (!is_array($exception)) { + $this->assertTrue($server->isRegisteredAsFaultException($exception)); + } else { + foreach($exception as $row) { + $this->assertTrue($server->isRegisteredAsFaultException($row)); + } + } + } - $this->assertEquals(array(), $server->getFaultExceptions()); - $server->registerFaultException("Exception"); - $this->assertEquals(array("Exception"), $server->getFaultExceptions()); + /** + * @return array + */ + public function dataProviderForRegisterFaultException() + { + return array( + array('Zend\Soap\Exception\InvalidArgumentException'), + array('InvalidArgumentException'), + array('Zend\Server\Exception\RuntimeException'), + array(array('Zend\Server\Exception\RuntimeException')), + array(array('Zend\Server\Exception\RuntimeException', 'InvalidArgumentException')), + ); } public function testFaultWithTextMessage() { $server = new Server(); - $fault = $server->fault("Faultmessage!"); + $fault = $server->fault('FaultMessage!'); - $this->assertTrue($fault instanceof \SOAPFault); - $this->assertContains("Faultmessage!", $fault->getMessage()); + $this->assertTrue($fault instanceof \SoapFault); + $this->assertContains('FaultMessage!', $fault->getMessage()); } public function testFaultWithUnregisteredException() { $server = new Server(); - $fault = $server->fault(new \Exception("MyException")); + $fault = $server->fault(new \Exception('MyException')); - $this->assertTrue($fault instanceof \SOAPFault); - $this->assertContains("Unknown error", $fault->getMessage()); - $this->assertNotContains("MyException", $fault->getMessage()); + $this->assertTrue($fault instanceof \SoapFault); + $this->assertContains('Unknown error', $fault->getMessage()); + $this->assertNotContains('MyException', $fault->getMessage()); } public function testFaultWithRegisteredException() { $server = new Server(); - $server->registerFaultException("Exception"); - $fault = $server->fault(new \Exception("MyException")); - - $this->assertTrue($fault instanceof \SOAPFault); - $this->assertNotContains("Unknown error", $fault->getMessage()); - $this->assertContains("MyException", $fault->getMessage()); + $server->registerFaultException('\Zend\Soap\Exception\RuntimeException'); + $server->registerFaultException('\Zend\Soap\Exception\InvalidArgumentException'); + $fault = $server->fault(new \Zend\Soap\Exception\RuntimeException('MyException')); + $this->assertTrue($fault instanceof \SoapFault); + $this->assertNotContains('Unknown error', $fault->getMessage()); + $this->assertContains('MyException', $fault->getMessage()); } - public function testFautlWithBogusInput() + public function testFaultWithBogusInput() { $server = new Server(); - $fault = $server->fault(array("Here", "There", "Bogus")); + $fault = $server->fault(array('Here', 'There', 'Bogus')); - $this->assertContains("Unknown error", $fault->getMessage()); + $this->assertContains('Unknown error', $fault->getMessage()); } /** @@ -740,22 +766,49 @@ public function testFautlWithBogusInput() public function testFaultWithIntegerFailureCodeDoesNotBreakClassSoapFault() { $server = new Server(); - $fault = $server->fault("Faultmessage!", 5000); + $fault = $server->fault("FaultMessage!", 5000); - $this->assertTrue($fault instanceof \SOAPFault); + $this->assertTrue($fault instanceof \SoapFault); } /** - * @todo Implement testHandlePhpErrors(). + * @expectedException \SoapFault */ public function testHandlePhpErrors() { - $server = new Server(); + if (headers_sent()) { + $this->markTestSkipped('Cannot run ' . __METHOD__ . '() when headers have already been sent; enable output buffering to run this test'); + return; + } - // Remove the following line when you implement this test. - $this->markTestIncomplete( - "This test has not been implemented yet." - ); + $wsdlFilename = __DIR__ . '/TestAsset/testHandlePhpErrors.wsdl'; + $autodiscover = new AutoDiscover(); + $autodiscover->setOperationBodyStyle(array( + 'use' => 'literal', + )); + + $autodiscover->setBindingStyle(array( + 'style' => 'document', + 'transport' => 'http://schemas.xmlsoap.org/soap/http' + )); + + + $autodiscover->setServiceName('ExampleService'); + $autodiscover->setUri('http://example.com'); + + + $autodiscover->setClass('\ZendTest\Soap\TestAsset\errorClass'); + + $wsdl = $autodiscover->generate(); + $wsdl->dump($wsdlFilename); + + $server = new Server($wsdlFilename); + + $server->setClass('\ZendTest\Soap\TestAsset\errorClass'); + + $client = new \Zend\Soap\Client\Local($server, $wsdlFilename); + $client->triggerError(); + unlink($wsdlFilename); } public function testLoadFunctionsIsNotImplemented() @@ -883,6 +936,7 @@ public function testShouldThrowExceptionIfHandledRequestContainsDoctype() . '' . '' . "\n"; $response = $server->handle($request); + $this->assertContains('Invalid XML', $response->getMessage()); } diff --git a/test/TestAsset/MockCallUserFunc.php b/test/TestAsset/MockCallUserFunc.php new file mode 100644 index 00000000..11af2921 --- /dev/null +++ b/test/TestAsset/MockCallUserFunc.php @@ -0,0 +1,31 @@ +' + . ''; + + $result .= '' + . '' + . '' + . '' + . ''; + + $result .= '' + . ''; + + return $result; +} diff --git a/test/TestAsset/commontypes.php b/test/TestAsset/commontypes.php index 5c2cd3b2..1e96fbb2 100644 --- a/test/TestAsset/commontypes.php +++ b/test/TestAsset/commontypes.php @@ -3,7 +3,7 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @package Zend_Soap */ @@ -84,7 +84,7 @@ function TestFunc7() /** * Return Object * - * @return StdClass + * @return stdClass */ function TestFunc8() { @@ -199,7 +199,7 @@ class AutoDiscoverTestClass2 /** * * @param \ZendTest\Soap\TestAsset\AutoDiscoverTestClass1 $test - * @return boolean + * @return bool */ public function add(AutoDiscoverTestClass1 $test) { @@ -277,7 +277,7 @@ class ComplexTest class ComplexObjectStructure { /** - * @var boolean + * @var bool */ public $boolean = true; @@ -634,3 +634,62 @@ public function __doRequest($request, $location, $action, $version, $one_way = 0 } } + +class SequenceTest +{ + /** + * @var int + */ + public $var = 5; +} + + + +class Book +{ + /** + * @var int + */ + public $somevar; +} +class Cookie +{ + /** + * @var int + */ + public $othervar; +} +class Anything +{ +} + + + +class PublicPrivateProtected +{ + const PROTECTED_VAR_NAME = 'bar'; + const PRIVATE_VAR_NAME = 'baz'; + + /** + * @var string + */ + public $foo; + + /** + * @var string + */ + protected $bar; + + /** + * @var string + */ + private $baz; +} + +class errorClass +{ + public function triggerError() + { + trigger_error('TestError', E_USER_ERROR); + } +} diff --git a/test/TestAsset/fulltests/server1.php b/test/TestAsset/fulltests/server1.php index 98d14332..5fb76e40 100644 --- a/test/TestAsset/fulltests/server1.php +++ b/test/TestAsset/fulltests/server1.php @@ -3,7 +3,7 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @package Zend_Soap */ diff --git a/test/TestAsset/fulltests/server2.php b/test/TestAsset/fulltests/server2.php index 5b7d4fee..57a0b2d1 100644 --- a/test/TestAsset/fulltests/server2.php +++ b/test/TestAsset/fulltests/server2.php @@ -3,7 +3,7 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @package Zend_Soap */ diff --git a/test/TestAsset/testHandlePhpErrors.wsdl b/test/TestAsset/testHandlePhpErrors.wsdl new file mode 100644 index 00000000..b44c6037 --- /dev/null +++ b/test/TestAsset/testHandlePhpErrors.wsdl @@ -0,0 +1,33 @@ + + + + + + + + + + + + triggerError + + + + + + + + + + + + + + + + + + + + + diff --git a/test/TestAsset/wsdl_example.wsdl b/test/TestAsset/wsdl_example.wsdl index 129f28de..41412402 100644 --- a/test/TestAsset/wsdl_example.wsdl +++ b/test/TestAsset/wsdl_example.wsdl @@ -1,2 +1,89 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Wsdl/ArrayOfTypeComplexStrategyTest.php b/test/Wsdl/ArrayOfTypeComplexStrategyTest.php index 00298010..289951f0 100644 --- a/test/Wsdl/ArrayOfTypeComplexStrategyTest.php +++ b/test/Wsdl/ArrayOfTypeComplexStrategyTest.php @@ -3,17 +3,18 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @package Zend_Soap */ namespace ZendTest\Soap\Wsdl; -require_once __DIR__."/../TestAsset/commontypes.php"; +require_once __DIR__ . "/../TestAsset/commontypes.php"; use Zend\Soap\Wsdl; use Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeComplex; +use ZendTest\Soap\WsdlTestHelper; /** * @category Zend @@ -22,29 +23,29 @@ * @group Zend_Soap * @group Zend_Soap_Wsdl */ -class ArrayOfTypeComplexStrategyTest extends \PHPUnit_Framework_TestCase +class ArrayOfTypeComplexStrategyTest extends WsdlTestHelper { - /** @var \Zend\Soap\Wsdl */ - private $wsdl; - - /** @var \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeComplex */ - private $strategy; public function setUp() { $this->strategy = new ArrayOfTypeComplex(); - $this->wsdl = new Wsdl('MyService', 'http://localhost/MyService.php', $this->strategy); + + parent::setUp(); } public function testNestingObjectsDeepMakesNoSenseThrowingException() { - $this->setExpectedException('Zend\Soap\Exception\InvalidArgumentException', 'ArrayOfTypeComplex cannot return nested ArrayOfObject deeper than one level'); + $this->setExpectedException('Zend\Soap\Exception\InvalidArgumentException', + 'ArrayOfTypeComplex cannot return nested ArrayOfObject deeper than one level' + ); $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\ComplexTest[][]'); } public function testAddComplexTypeOfNonExistingClassThrowsException() { - $this->setExpectedException('Zend\Soap\Exception\InvalidArgumentException', 'Cannot add a complex type \ZendTest\Soap\TestAsset\UnknownClass that is not an object or where class'); + $this->setExpectedException('Zend\Soap\Exception\InvalidArgumentException', + 'Cannot add a complex type \ZendTest\Soap\TestAsset\UnknownClass that is not an object or where class' + ); $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\UnknownClass[]'); } @@ -53,30 +54,42 @@ public function testAddComplexTypeOfNonExistingClassThrowsException() */ public function testArrayOfSimpleObject() { + $return = $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\ComplexTest[]'); $return = $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\ComplexTest[]'); $this->assertEquals("tns:ArrayOfComplexTest", $return); - $wsdl = $this->wsdl->toXML(); + // single element + $nodes = $this->xpath->query('//wsdl:types/*/xsd:complexType[@name="ComplexTest"]/xsd:all/xsd:element'); + $this->assertEquals(1, $nodes->length, 'Unable to find complex type in wsdl.'); + + $this->assertEquals('var', $nodes->item(0)->getAttribute('name'), 'Invalid attribute name'); + $this->assertEquals('xsd:int', $nodes->item(0)->getAttribute('type'), 'Invalid type name'); - $this->assertContains( - '', - $wsdl, - $wsdl + // array of elements + $nodes = $this->xpath->query( + '//wsdl:types/*/xsd:complexType[@name="ArrayOfComplexTest"]/xsd:complexContent/xsd:restriction' + ); + $this->assertEquals(1, $nodes->length, 'Unable to find complex type array definition in wsdl.'); + $this->assertEquals('soap-enc:Array', $nodes->item(0)->getAttribute('base'), + 'Invalid base encoding in complex type.' ); - $this->assertContains( - '', - $wsdl + $nodes = $this->xpath->query('xsd:attribute', $nodes->item(0)); + + $this->assertEquals('soap-enc:arrayType', $nodes->item(0)->getAttribute('ref'), + 'Invalid attribute reference value in complex type.' + ); + $this->assertEquals('tns:ComplexTest[]', $nodes->item(0)->getAttributeNS(Wsdl::WSDL_NS_URI, 'arrayType'), + 'Invalid array type reference.' ); + + $this->testDocumentNodes(); } public function testThatOverridingStrategyIsReset() { $return = $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\ComplexTest[]'); $this->assertEquals("tns:ArrayOfComplexTest", $return); - // $this->assertTrue($this->wsdl->getComplexTypeStrategy() instanceof \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeComplexStrategy); - - $wsdl = $this->wsdl->toXML(); } /** @@ -87,18 +100,47 @@ public function testArrayOfComplexObjects() $return = $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\ComplexObjectStructure[]'); $this->assertEquals("tns:ArrayOfComplexObjectStructure", $return); - $wsdl = $this->wsdl->toXML(); - - $this->assertContains( - '', - $wsdl, - $wsdl + $nodes = $this->xpath->query( + '//wsdl:types/xsd:schema/xsd:complexType[@name="ComplexObjectStructure"]/xsd:all' + ); + $this->assertEquals(4, $nodes->item(0)->childNodes->length, 'Invalid complex object definition.'); + + foreach (array( + 'boolean' => 'xsd:boolean', + 'string' => 'xsd:string', + 'int' => 'xsd:int', + 'array' => 'soap-enc:Array' + ) as $name => $type) { + $node = $this->xpath->query('xsd:element[@name="'.$name.'"]', $nodes->item(0)); + $this->assertEquals($name, $node->item(0)->getAttribute('name'), + 'Invalid name attribute value in complex object definition' + ); + $this->assertEquals($type, $node->item(0)->getAttribute('type'), + 'Invalid type name in complex object definition' + ); + } + + // array of elements + $nodes = $this->xpath->query( + '//wsdl:types/*/xsd:complexType[@name="ArrayOfComplexObjectStructure"]/xsd:complexContent/xsd:restriction' ); + $this->assertEquals(1, $nodes->length, 'Unable to find complex type array definition in wsdl.'); + $this->assertEquals('soap-enc:Array', $nodes->item(0)->getAttribute('base'), + 'Invalid base encoding in complex type.' + ); + + $nodes = $this->xpath->query('xsd:attribute', $nodes->item(0)); - $this->assertContains( - '', - $wsdl + $this->assertEquals('soap-enc:arrayType', $nodes->item(0)->getAttribute('ref'), + 'Invalid attribute reference value in complex type.' ); + $this->assertEquals('tns:ComplexObjectStructure[]', + $nodes->item(0)->getAttributeNS(Wsdl::WSDL_NS_URI, 'arrayType'), + 'Invalid array type reference.' + ); + + + $this->testDocumentNodes(); } public function testArrayOfObjectWithObject() @@ -106,23 +148,50 @@ public function testArrayOfObjectWithObject() $return = $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\ComplexObjectWithObjectStructure[]'); $this->assertEquals("tns:ArrayOfComplexObjectWithObjectStructure", $return); - $wsdl = $this->wsdl->toXML(); + // single element + $nodes = $this->xpath->query('//wsdl:types/*/xsd:complexType[@name="ComplexTest"]/xsd:all/xsd:element'); + $this->assertEquals(1, $nodes->length, 'Unable to find complex type in wsdl.'); + + $this->assertEquals('var', $nodes->item(0)->getAttribute('name'), 'Invalid attribute name'); + $this->assertEquals('xsd:int', $nodes->item(0)->getAttribute('type'), 'Invalid type name'); - $this->assertContains( - '', - $wsdl + // single object element + $nodes = $this->xpath->query( + '//wsdl:types/*/xsd:complexType[@name="ComplexObjectWithObjectStructure"]/xsd:all/xsd:element' ); + $this->assertEquals(1, $nodes->length, 'Unable to find complex object in wsdl.'); - $this->assertContains( - '', - $wsdl, - $wsdl + $this->assertEquals('object', $nodes->item(0)->getAttribute('name'), + 'Invalid attribute name' + ); + $this->assertEquals('tns:ComplexTest', $nodes->item(0)->getAttribute('type'), + 'Invalid type name' ); + $this->assertEquals('true', $nodes->item(0)->getAttribute('nillable'), + 'Invalid nillable attribute value' + ); + + // array of elements + $nodes = $this->xpath->query( + '//wsdl:types/*/xsd:complexType[@name="ArrayOfComplexObjectWithObjectStructure"]/' + .'xsd:complexContent/xsd:restriction' + ); + $this->assertEquals(1, $nodes->length, 'Unable to find complex type array definition in wsdl.'); + $this->assertEquals('soap-enc:Array', $nodes->item(0)->getAttribute('base'), + 'Invalid base encoding in complex type.' + ); + + $nodes = $this->xpath->query('xsd:attribute', $nodes->item(0)); - $this->assertContains( - '', - $wsdl + $this->assertEquals('soap-enc:arrayType', $nodes->item(0)->getAttribute('ref'), + 'Invalid attribute reference value in complex type.' + ); + $this->assertEquals('tns:ComplexObjectWithObjectStructure[]', + $nodes->item(0)->getAttributeNS(Wsdl::WSDL_NS_URI, 'arrayType'), + 'Invalid array type reference.' ); + + $this->testDocumentNodes(); } /** @@ -130,20 +199,26 @@ public function testArrayOfObjectWithObject() */ public function testAddingTypesMultipleTimesIsSavedOnlyOnce() { - $return = $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\ComplexObjectWithObjectStructure[]'); - $return = $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\ComplexObjectWithObjectStructure[]'); - - $wsdl = $this->wsdl->toXML(); + $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\ComplexObjectWithObjectStructure[]'); + $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\ComplexObjectWithObjectStructure[]'); - $this->assertEquals(1, - substr_count($wsdl, 'wsdl:arrayType="tns:ComplexObjectWithObjectStructure[]"') + // this xpath is proper version of simpler: //*[wsdl:arrayType="tns:ComplexObjectWithObjectStructure[]"] - namespaces in attributes and xpath + $nodes = $this->xpath->query('//*[@*[namespace-uri()="'.Wsdl::WSDL_NS_URI + .'" and local-name()="arrayType"]="tns:ComplexObjectWithObjectStructure[]"]' ); - $this->assertEquals(1, - substr_count($wsdl, '') + $this->assertEquals(1, $nodes->length, + 'Invalid array of complex type array type reference detected' ); - $this->assertEquals(1, - substr_count($wsdl, '') + + $nodes = $this->xpath->query( + '//xsd:complexType[@name="ArrayOfComplexObjectWithObjectStructure"]' ); + $this->assertEquals(1, $nodes->length, 'Invalid array complex type detected'); + + $nodes = $this->xpath->query('//xsd:complexType[@name="ComplexTest"]'); + $this->assertEquals(1, $nodes->length, 'Invalid complex type detected'); + + $this->testDocumentNodes(); } /** @@ -151,30 +226,26 @@ public function testAddingTypesMultipleTimesIsSavedOnlyOnce() */ public function testAddingSingularThenArrayTypeIsRecognizedCorretly() { - $return = $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\ComplexObjectWithObjectStructure'); - $return = $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\ComplexObjectWithObjectStructure[]'); - - $wsdl = $this->wsdl->toXML(); + $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\ComplexObjectWithObjectStructure'); + $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\ComplexObjectWithObjectStructure[]'); - $this->assertEquals(1, - substr_count($wsdl, 'wsdl:arrayType="tns:ComplexObjectWithObjectStructure[]"') + // this xpath is proper version of simpler: //*[wsdl:arrayType="tns:ComplexObjectWithObjectStructure[]"] - namespaces in attributes and xpath + $nodes = $this->xpath->query('//*[@*[namespace-uri()="'.Wsdl::WSDL_NS_URI. + '" and local-name()="arrayType"]="tns:ComplexObjectWithObjectStructure[]"]' ); - $this->assertEquals(1, - substr_count($wsdl, '') + $this->assertEquals(1, $nodes->length, + 'Invalid array of complex type array type reference detected' ); - $this->assertEquals(1, - substr_count($wsdl, '') + + $nodes = $this->xpath->query( + '//xsd:complexType[@name="ArrayOfComplexObjectWithObjectStructure"]' ); - } + $this->assertEquals(1, $nodes->length, 'Invalid array complex type detected'); - /** - * @group ZF-5149 - */ - public function testArrayOfComplexNestedObjectsIsCoveredByStrategy() - { - $return = $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\ComplexTypeA'); - $wsdl = $this->wsdl->toXml(); - $this->assertTrue(is_string($wsdl)); // no exception was thrown + $nodes = $this->xpath->query('//xsd:complexType[@name="ComplexTest"]'); + $this->assertEquals(1, $nodes->length, 'Invalid complex type detected'); + + $this->testDocumentNodes(); } /** @@ -183,19 +254,63 @@ public function testArrayOfComplexNestedObjectsIsCoveredByStrategy() public function testArrayOfComplexNestedObjectsIsCoveredByStrategyAndAddsAllTypesRecursivly() { $return = $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\ComplexTypeA'); - $wsdl = $this->wsdl->toXml(); + $this->assertEquals("tns:ComplexTypeA", $return); + + + $nodes = $this->xpath->query('//wsdl:types/xsd:schema/xsd:complexType[@name="ComplexTypeB"]/xsd:all'); + $this->assertEquals(2, $nodes->item(0)->childNodes->length, 'Invalid complex object definition.'); + + foreach (array( + 'bar' => 'xsd:string', + 'foo' => 'xsd:string', + ) as $name => $type) { + $node = $this->xpath->query('xsd:element[@name="'.$name.'"]', $nodes->item(0)); + $this->assertEquals($name, $node->item(0)->getAttribute('name'), + 'Invalid name attribute value in complex object definition' + ); + $this->assertEquals($type, $node->item(0)->getAttribute('type'), + 'Invalid type name in complex object definition' + ); + $this->assertEquals('true', $node->item(0)->getAttribute('nillable'), + 'Invalid nillable attribute value' + ); + } + + // single object element + $nodes = $this->xpath->query( + '//wsdl:types/*/xsd:complexType[@name="ComplexTypeA"]/xsd:all/xsd:element' + ); + $this->assertEquals(1, $nodes->length, 'Unable to find complex object in wsdl.'); - $this->assertEquals(1, - substr_count($wsdl, ''), - 'No definition of complex type A found.' + $this->assertEquals('baz', + $nodes->item(0)->getAttribute('name'), 'Invalid attribute name' + ); + $this->assertEquals('tns:ArrayOfComplexTypeB', + $nodes->item(0)->getAttribute('type'), 'Invalid type name' ); - $this->assertEquals(1, - substr_count($wsdl, ''), - 'No definition of complex type B array found.' + + // array of elements + $nodes = $this->xpath->query( + '//wsdl:types/*/xsd:complexType[@name="ArrayOfComplexTypeB"]/xsd:complexContent/xsd:restriction' ); - $this->assertEquals(1, - substr_count($wsdl, 'wsdl:arrayType="tns:ComplexTypeB[]"'), - 'No usage of Complex Type B array found.' + $this->assertEquals(1, $nodes->length, + 'Unable to find complex type array definition in wsdl.' ); + $this->assertEquals('soap-enc:Array', $nodes->item(0)->getAttribute('base'), + 'Invalid base encoding in complex type.' + ); + + $nodes = $this->xpath->query('xsd:attribute', $nodes->item(0)); + + $this->assertEquals('soap-enc:arrayType', + $nodes->item(0)->getAttribute('ref'), + 'Invalid attribute reference value in complex type.' + ); + $this->assertEquals('tns:ComplexTypeB[]', + $nodes->item(0)->getAttributeNS(Wsdl::WSDL_NS_URI, 'arrayType'), + 'Invalid array type reference.' + ); + + $this->testDocumentNodes(); } } diff --git a/test/Wsdl/ArrayOfTypeSequenceStrategyTest.php b/test/Wsdl/ArrayOfTypeSequenceStrategyTest.php index 0df20436..3a45834c 100644 --- a/test/Wsdl/ArrayOfTypeSequenceStrategyTest.php +++ b/test/Wsdl/ArrayOfTypeSequenceStrategyTest.php @@ -3,13 +3,15 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @package Zend_Soap */ namespace ZendTest\Soap\Wsdl; +use ZendTest\Soap\WsdlTestHelper; + require_once __DIR__ . '/../TestAsset/commontypes.php'; /** @@ -19,118 +21,234 @@ * @group Zend_Soap * @group Zend_Soap_Wsdl */ -class ArrayOfTypeSequenceStrategyTest extends \PHPUnit_Framework_TestCase +class ArrayOfTypeSequenceStrategyTest extends WsdlTestHelper { - private $wsdl; - private $strategy; - public function setUp() { $this->strategy = new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence(); - $this->wsdl = new \Zend\Soap\Wsdl('MyService', 'http://localhost/MyService.php', $this->strategy); + + parent::setUp(); } - public function testFunctionReturningSimpleArrayOfInts() + /** + * @dataProvider dataProviderForFunctionReturningSimpleArrayOfBasicTypes + * + * @param $type + * @param $arrayTypeName + */ + public function testFunctionReturningSimpleArrayOfBasicTypes($type, $arrayTypeName) { - $this->wsdl->addComplexType('int[]'); + $this->wsdl->addComplexType($type.'[]'); + // test duplicates also + $this->wsdl->addComplexType($type.'[]'); + + $nodes = $this->xpath->query('//wsdl:types/xsd:schema/xsd:complexType[@name="'.$arrayTypeName.'"]'); + $this->assertEquals(1, $nodes->length, 'Missing complex type declaration'); - $this->assertContains( - ''. - ''. - '', - $this->wsdl->toXML() + $nodes = $this->xpath->query('xsd:sequence/xsd:element', $nodes->item(0)); + $this->assertEquals(1, $nodes->length, 'Missing complex type element declaration'); + + $this->assertEquals('item', $nodes->item(0)->getAttribute('name'), + 'Wrong complex type element name attribute' + ); + $this->assertEquals('xsd:'.$type, $nodes->item(0)->getAttribute('type'), + 'Wrong complex type type attribute value' + ); + $this->assertEquals('0', $nodes->item(0)->getAttribute('minOccurs'), + 'Wrong complex type minOccurs attribute value' ); + $this->assertEquals('unbounded', $nodes->item(0)->getAttribute('maxOccurs'), + 'Wrong complex type maxOccurs attribute value' + ); + + $this->testDocumentNodes(); } - public function testFunctionReturningSimpleArrayOfString() + public function dataProviderForFunctionReturningSimpleArrayOfBasicTypes() { - $this->wsdl->addComplexType('string[]'); - - $this->assertContains( - ''. - ''. - '', - $this->wsdl->toXML() + return array( + array('int', 'ArrayOfInt'), + array('string', 'ArrayOfString'), + array('boolean', 'ArrayOfBoolean'), + array('float', 'ArrayOfFloat'), + array('double', 'ArrayOfDouble'), ); } - public function testFunctionReturningNestedArrayOfString() + /** + * @dataProvider dataProviderForNestedTypesDefinitions + * + * @param $stringDefinition + * @param $nestedTypeNames + */ + public function testNestedTypesDefinitions($stringDefinition, $definedTypeName, $nestedTypeNames) { - $return = $this->wsdl->addComplexType('string[][]'); - $this->assertEquals('tns:ArrayOfArrayOfString', $return); + $return = $this->wsdl->addComplexType($stringDefinition); + $this->assertEquals('tns:'.$definedTypeName, $return); - $wsdl = $this->wsdl->toXML(); + foreach ($nestedTypeNames as $nestedTypeName => $typeName) { - // Check for ArrayOfArrayOfString - $this->assertContains( - '', - $wsdl - ); - // Check for ArrayOfString - $this->assertContains( - '', - $wsdl - ); + $nodes = $this->xpath->query('//wsdl:types/xsd:schema/xsd:complexType[@name="'.$nestedTypeName.'"]'); + $this->assertEquals(1, $nodes->length, 'Invalid first level of nested element definition'); + + $nodes = $this->xpath->query('xsd:sequence/xsd:element', $nodes->item(0)); + $this->assertEquals(1, $nodes->length, 'Invalid element in first level of nested element definition'); + + $this->assertEquals('item', $nodes->item(0)->getAttribute('name'), + 'Wrong complex type element name attribute' + ); + $this->assertEquals('0', $nodes->item(0)->getAttribute('minOccurs'), + 'Wrong complex type minOccurs attribute value' + ); + $this->assertEquals('unbounded', $nodes->item(0)->getAttribute('maxOccurs'), + 'Wrong complex type maxOccurs attribute value' + ); + $this->assertEquals($typeName, $nodes->item(0)->getAttribute('type'), + 'Wrong complex type type attribute value' + ); + } + + $this->testDocumentNodes(); } - public function testFunctionReturningMultipleNestedArrayOfType() + /** + * @return array + */ + public function dataProviderForNestedTypesDefinitions() { - $return = $this->wsdl->addComplexType('string[][][]'); - $this->assertEquals('tns:ArrayOfArrayOfArrayOfString', $return); + return array( + array( + 'string[][]', + 'ArrayOfArrayOfString', + array( + 'ArrayOfString' =>'xsd:string', + 'ArrayOfArrayOfString' =>'tns:ArrayOfString' + ) + ), - $wsdl = $this->wsdl->toXML(); + array( + 'string[][][]', + 'ArrayOfArrayOfArrayOfString', + array( + 'ArrayOfString' =>'xsd:string', + 'ArrayOfArrayOfString' =>'tns:ArrayOfString', + 'ArrayOfArrayOfArrayOfString' =>'tns:ArrayOfArrayOfString' + ) + ), - // Check for ArrayOfArrayOfArrayOfString - $this->assertContains( - '', - $wsdl - ); - // Check for ArrayOfArrayOfString - $this->assertContains( - '', - $wsdl - ); - // Check for ArrayOfString - $this->assertContains( - '', - $wsdl + array( + 'string[][][][]', + 'ArrayOfArrayOfArrayOfArrayOfString', + array( + 'ArrayOfString' =>'xsd:string', + 'ArrayOfArrayOfString' =>'tns:ArrayOfString', + 'ArrayOfArrayOfArrayOfString' =>'tns:ArrayOfArrayOfString', + 'ArrayOfArrayOfArrayOfArrayOfString' =>'tns:ArrayOfArrayOfArrayOfString' + ) + ), + + array( + 'int[][]', + 'ArrayOfArrayOfInt', + array( + 'ArrayOfInt' =>'xsd:int', + 'ArrayOfArrayOfInt' =>'tns:ArrayOfInt' + ) + ), ); } - public function testAddComplexTypeObject() { - $return = $this->wsdl->addComplexType('\ZendTest\Soap\Wsdl\SequenceTest'); + $return = $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\SequenceTest'); $this->assertEquals('tns:SequenceTest', $return); - $wsdl = $this->wsdl->toXML(); + $nodes = $this->xpath->query('//xsd:complexType[@name="SequenceTest"]'); + $this->assertEquals(1, $nodes->length, 'Missing complex type: SequenceTest'); - $this->assertContains( - '', - $wsdl - ); + $nodes = $this->xpath->query('xsd:all/xsd:element', $nodes->item(0)); + $this->assertEquals(1, $nodes->length, 'Missing element definition in complex type: SequenceTest'); + + $this->assertEquals('var', $nodes->item(0)->getAttribute('name'), 'Invalid name attribute value'); + $this->assertEquals('xsd:int', $nodes->item(0)->getAttribute('type'), 'Invalid type attribute value'); + + $this->testDocumentNodes(); } public function testAddComplexTypeArrayOfObject() { + $return = $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\ComplexTypeA[]'); + $this->assertEquals('tns:ArrayOfComplexTypeA', $return); + + + // class a + $nodes = $this->xpath->query('//wsdl:types/xsd:schema/xsd:complexType[@name="ComplexTypeA"]'); + $this->assertEquals(1, $nodes->length, 'Missing complex type definition.'); + + $nodes = $this->xpath->query('xsd:all/xsd:element', $nodes->item(0)); + + $this->assertEquals(1, $nodes->length, 'Missing complex type element declaration'); + + $this->assertEquals('baz', $nodes->item(0)->getAttribute('name'), + 'Wrong complex type element name attribute' + ); + $this->assertEquals('tns:ArrayOfComplexTypeB', $nodes->item(0)->getAttribute('type'), + 'Wrong complex type type attribute value' + ); + + + // class b + $nodes = $this->xpath->query('//wsdl:types/xsd:schema/xsd:complexType[@name="ComplexTypeB"]'); + $this->assertEquals(1, $nodes->length, 'Missing complex type definition.'); + + foreach (array( + 'bar' => 'xsd:string', + 'foo' => 'xsd:string', + ) as $name => $type) { + $node = $this->xpath->query('xsd:all/xsd:element[@name="'.$name.'"]', $nodes->item(0)); + + $this->assertEquals($name, $node->item(0)->getAttribute('name'), + 'Invalid name attribute value in complex object definition' + ); + $this->assertEquals($type, $node->item(0)->getAttribute('type'), + 'Invalid type name in complex object definition' + ); + $this->assertEquals('true', $node->item(0)->getAttribute('nillable'), + 'Invalid nillable attribute value' + ); + } + - $return = $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\ComplexTypeA[]'); + // array of class a and class b + foreach(array( + 'ArrayOfComplexTypeB' => 'ComplexTypeB', + 'ArrayOfComplexTypeA' => 'ComplexTypeA' + ) as $arrayTypeName => $typeName) { - $this->assertEquals('tns:ArrayOfComplexTypeA', $return); + $nodes = $this->xpath->query( + '//wsdl:types/xsd:schema/xsd:complexType[@name="'.$arrayTypeName.'"]' + ); + $this->assertEquals(1, $nodes->length, 'Missing complex type definition.'); - $wsdl = $this->wsdl->toXML(); + $nodes = $this->xpath->query('xsd:sequence/xsd:element', $nodes->item(0)); + $this->assertEquals(1, $nodes->length, 'Missing complex type element declaration'); - $this->assertContains( - '', - $wsdl, - $wsdl - ); + $this->assertEquals('item', $nodes->item(0)->getAttribute('name'), + 'Wrong complex type element name attribute' + ); + $this->assertEquals('tns:'.$typeName, $nodes->item(0)->getAttribute('type'), + 'Wrong complex type type attribute value' + ); + $this->assertEquals('0', $nodes->item(0)->getAttribute('minOccurs'), + 'Wrong complex type minOccurs attribute value' + ); + $this->assertEquals('unbounded', $nodes->item(0)->getAttribute('maxOccurs'), + 'Wrong complex type maxOccurs attribute value' + ); + } - $this->assertContains( - '', - $wsdl - ); + $this->testDocumentNodes(); } public function testAddComplexTypeOfNonExistingClassThrowsException() @@ -139,11 +257,3 @@ public function testAddComplexTypeOfNonExistingClassThrowsException() $this->wsdl->addComplexType('ZendTest\Soap\Wsdl\UnknownClass[]'); } } - -class SequenceTest -{ - /** - * @var int - */ - public $var = 5; -} diff --git a/test/Wsdl/CompositeStrategyTest.php b/test/Wsdl/CompositeStrategyTest.php index 135ba55e..f239d080 100644 --- a/test/Wsdl/CompositeStrategyTest.php +++ b/test/Wsdl/CompositeStrategyTest.php @@ -3,18 +3,19 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @package Zend_Soap */ -namespace ZendTest\Soap\Wsdl; +namespace ZendTest\Soap\Wsdl\ComplexTypeStrategy; use Zend\Soap\Wsdl\ComplexTypeStrategy; use Zend\Soap\Wsdl; use Zend\Soap\Wsdl\ComplexTypeStrategy\Composite; use Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeComplex; use Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence; +use ZendTest\Soap\WsdlTestHelper; /** * @package Zend_Soap @@ -32,8 +33,14 @@ * @group Zend_Soap * @group Zend_Soap_Wsdl */ -class CompositeStrategyTest extends \PHPUnit_Framework_TestCase +class CompositeStrategyTest extends WsdlTestHelper { + + public function setUp() + { + // override parent setup because it is needed only in one method + } + public function testCompositeApiAddingStragiesToTypes() { $strategy = new Composite(array(), new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence); @@ -50,7 +57,9 @@ public function testConstructorTypeMapSyntax() { $typeMap = array('Book' => '\Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeComplex'); - $strategy = new ComplexTypeStrategy\Composite($typeMap, new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence); + $strategy = new ComplexTypeStrategy\Composite($typeMap, + new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence + ); $bookStrategy = $strategy->getStrategyOfType('Book'); $cookieStrategy = $strategy->getStrategyOfType('Cookie'); @@ -63,7 +72,9 @@ public function testCompositeThrowsExceptionOnInvalidType() { $strategy = new ComplexTypeStrategy\Composite(); - $this->setExpectedException('Zend\Soap\Exception\InvalidArgumentException', 'Invalid type given to Composite Type Map'); + $this->setExpectedException('Zend\Soap\Exception\InvalidArgumentException', + 'Invalid type given to Composite Type Map' + ); $strategy->connectTypeToStrategy(array(), 'strategy'); } @@ -72,8 +83,10 @@ public function testCompositeThrowsExceptionOnInvalidStrategy() $strategy = new ComplexTypeStrategy\Composite(array(), 'invalid'); $strategy->connectTypeToStrategy('Book', 'strategy'); - $this->setExpectedException('Zend\Soap\Exception\InvalidArgumentException', 'Strategy for Complex Type \'Book\' is not a valid strategy'); - $book = $strategy->getStrategyOfType('Book'); + $this->setExpectedException('Zend\Soap\Exception\InvalidArgumentException', + 'Strategy for Complex Type "Book" is not a valid strategy' + ); + $strategy->getStrategyOfType('Book'); } public function testCompositeThrowsExceptionOnInvalidStrategyPart2() @@ -81,49 +94,48 @@ public function testCompositeThrowsExceptionOnInvalidStrategyPart2() $strategy = new ComplexTypeStrategy\Composite(array(), 'invalid'); $strategy->connectTypeToStrategy('Book', 'strategy'); - $this->setExpectedException('Zend\Soap\Exception\InvalidArgumentException', 'Default Strategy for Complex Types is not a valid strategy object'); - $book = $strategy->getStrategyOfType('Anything'); + $this->setExpectedException('Zend\Soap\Exception\InvalidArgumentException', + 'Default Strategy for Complex Types is not a valid strategy object' + ); + $strategy->getStrategyOfType('Anything'); } - - public function testCompositeDelegatesAddingComplexTypesToSubStrategies() { - $strategy = new ComplexTypeStrategy\Composite(array(), new \Zend\Soap\Wsdl\ComplexTypeStrategy\AnyType); - $strategy->connectTypeToStrategy('\ZendTest\Soap\Wsdl\Book', new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeComplex); - $strategy->connectTypeToStrategy('\ZendTest\Soap\Wsdl\Cookie', new \Zend\Soap\Wsdl\ComplexTypeStrategy\DefaultComplexType); + $this->strategy = new ComplexTypeStrategy\Composite(array(), new \Zend\Soap\Wsdl\ComplexTypeStrategy\AnyType); + $this->strategy->connectTypeToStrategy('\ZendTest\Soap\TestAsset\Book', + new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeComplex + ); + $this->strategy->connectTypeToStrategy('\ZendTest\Soap\TestAsset\Cookie', + new \Zend\Soap\Wsdl\ComplexTypeStrategy\DefaultComplexType + ); + + parent::setUp(); - $wsdl = new Wsdl('SomeService', 'http://example.com'); - $strategy->setContext($wsdl); + $this->assertEquals('tns:Book', $this->strategy->addComplexType('\ZendTest\Soap\TestAsset\Book')); + $this->assertEquals('tns:Cookie', $this->strategy->addComplexType('\ZendTest\Soap\TestAsset\Cookie')); + $this->assertEquals('xsd:anyType', $this->strategy->addComplexType('\ZendTest\Soap\TestAsset\Anything')); - $this->assertEquals('tns:Book', $strategy->addComplexType('\ZendTest\Soap\Wsdl\Book')); - $this->assertEquals('tns:Cookie', $strategy->addComplexType('\ZendTest\Soap\Wsdl\Cookie')); - $this->assertEquals('xsd:anyType', $strategy->addComplexType('\ZendTest\Soap\Wsdl\Anything')); + $this->testDocumentNodes(); } public function testCompositeRequiresContextForAddingComplexTypesOtherwiseThrowsException() { $strategy = new ComplexTypeStrategy\Composite(); - $this->setExpectedException('Zend\Soap\Exception\InvalidArgumentException', 'Cannot add complex type \'Test\''); + $this->setExpectedException('Zend\Soap\Exception\InvalidArgumentException', 'Cannot add complex type "Test"'); $strategy->addComplexType('Test'); } -} -class Book -{ /** - * @var int + * */ - public $somevar; -} -class Cookie -{ - /** - * @var int - */ - public $othervar; -} -class Anything -{ + public function testGetDefaultStrategy() + { + $strategyClass = 'Zend\Soap\Wsdl\ComplexTypeStrategy\AnyType'; + + $strategy = new Composite(array(), $strategyClass); + + $this->assertEquals($strategyClass, get_class($strategy->getDefaultStrategy())); + } } diff --git a/test/Wsdl/DefaultComplexTypeTest.php b/test/Wsdl/DefaultComplexTypeTest.php index 69a4ff47..1914ba14 100644 --- a/test/Wsdl/DefaultComplexTypeTest.php +++ b/test/Wsdl/DefaultComplexTypeTest.php @@ -3,7 +3,7 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @package Zend_Soap */ @@ -12,6 +12,10 @@ use Zend\Soap\Wsdl\ComplexTypeStrategy\DefaultComplexType; use Zend\Soap\Wsdl; +use ZendTest\Soap\TestAsset\PublicPrivateProtected; +use ZendTest\Soap\WsdlTestHelper; + +require_once __DIR__ . '/../TestAsset/commontypes.php'; /** * @category Zend @@ -20,24 +24,18 @@ * @group Zend_Soap * @group Zend_Soap_Wsdl */ -class DefaultComplexTypeTest extends \PHPUnit_Framework_TestCase +class DefaultComplexTypeTest extends WsdlTestHelper { /** - * @var Zend_Soap_Wsdl + * @var DefaultComplexType */ - private $wsdl; - - /** - * @var Zend_Soap_Wsdl_Strategy_DefaultComplexType - */ - private $strategy; + protected $strategy; public function setUp() { $this->strategy = new DefaultComplexType(); - $this->wsdl = new Wsdl("TestService", "http://framework.zend.com/soap/unittests"); - $this->wsdl->setComplexTypeStrategy($this->strategy); - $this->strategy->setContext($this->wsdl); + + parent::setUp(); } /** @@ -45,31 +43,14 @@ public function setUp() */ public function testOnlyPublicPropertiesAreDiscoveredByStrategy() { - $this->strategy->addComplexType('\ZendTest\Soap\Wsdl\PublicPrivateProtected'); + $this->strategy->addComplexType('ZendTest\Soap\TestAsset\PublicPrivateProtected'); - $xml = $this->wsdl->toXML(); - $this->assertNotContains( PublicPrivateProtected::PROTECTED_VAR_NAME, $xml); - $this->assertNotContains( PublicPrivateProtected::PRIVATE_VAR_NAME, $xml); - } -} + $nodes = $this->xpath->query('//xsd:element[@name="'.(PublicPrivateProtected::PROTECTED_VAR_NAME).'"]'); + $this->assertEquals(0, $nodes->length, 'Document should not contain protected fields'); -class PublicPrivateProtected -{ - const PROTECTED_VAR_NAME = 'bar'; - const PRIVATE_VAR_NAME = 'baz'; + $nodes = $this->xpath->query('//xsd:element[@name="'.(PublicPrivateProtected::PRIVATE_VAR_NAME).'"]'); + $this->assertEquals(0, $nodes->length, 'Document should not contain private fields'); - /** - * @var string - */ - public $foo; - - /** - * @var string - */ - protected $bar; - - /** - * @var string - */ - private $baz; + $this->testDocumentNodes(); + } } diff --git a/test/WsdlTest.php b/test/WsdlTest.php index eaef3fce..d50e0d3e 100644 --- a/test/WsdlTest.php +++ b/test/WsdlTest.php @@ -3,615 +3,792 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @package Zend_Soap */ namespace ZendTest\Soap; - use Zend\Soap\Wsdl; -use Zend\Soap\Wsdl\ComplexTypeStrategy; + +use Zend\Uri\Uri; /** - * Test cases for Zend_Soap_Wsdl + * Zend_Soap_Server * * @category Zend * @package Zend_Soap * @subpackage UnitTests * @group Zend_Soap * @group Zend_Soap_Wsdl - */ -class WsdlTest extends \PHPUnit_Framework_TestCase + **/ +class WsdlTest extends WsdlTestHelper { - protected function sanitizeWsdlXmlOutputForOsCompability($xmlstring) + + public function testConstructor() { - $xmlstring = str_replace(array("\r", "\n"), "", $xmlstring); - $xmlstring = preg_replace('/(>[\s]{1,}<)/', '', $xmlstring); - return $xmlstring; + $this->assertEquals(Wsdl::WSDL_NS_URI, $this->dom->lookupNamespaceUri(null)); + $this->assertEquals(Wsdl::SOAP_11_NS_URI, $this->dom->lookupNamespaceUri('soap')); + $this->assertEquals(Wsdl::SOAP_12_NS_URI, $this->dom->lookupNamespaceUri('soap12')); + $this->assertEquals($this->defaultServiceUri, $this->dom->lookupNamespaceUri('tns')); + $this->assertEquals(Wsdl::SOAP_11_NS_URI, $this->dom->lookupNamespaceUri('soap')); + $this->assertEquals(Wsdl::XSD_NS_URI, $this->dom->lookupNamespaceUri('xsd')); + $this->assertEquals(Wsdl::SOAP_ENC_URI, $this->dom->lookupNamespaceUri('soap-enc')); + $this->assertEquals(Wsdl::WSDL_NS_URI, $this->dom->lookupNamespaceUri('wsdl')); + + $this->assertEquals(Wsdl::WSDL_NS_URI, $this->dom->documentElement->namespaceURI); + + $this->assertEquals($this->defaultServiceName, $this->dom->documentElement->getAttribute('name')); + $this->assertEquals($this->defaultServiceUri, $this->dom->documentElement->getAttribute('targetNamespace')); + + $this->testDocumentNodes(); } - public function swallowIncludeNotices($errno, $errstr) + /** + * @dataProvider dataProviderForURITesting + * + * @param string $uri + */ + public function testSetUriChangesDomDocumentWsdlStructureTnsAndTargetNamespaceAttributes($uri, $expectedUri) { - if ($errno != E_WARNING || !strstr($errstr, 'failed')) { - return false; + if ($uri instanceof Uri) { + $uri = $uri->toString(); } + + $this->wsdl->setUri($uri); + + $this->testDocumentNodes(); + + $this->assertEquals($expectedUri, $this->dom->lookupNamespaceUri('tns')); + $this->assertEquals($expectedUri, $this->dom->documentElement->getAttribute('targetNamespace')); } - public function testConstructor() + /** + * @dataProvider dataProviderForURITesting + * + * @param string $uri + */ + public function testSetUriWithZendUriChangesDomDocumentWsdlStructureTnsAndTargetNamespaceAttributes($uri, $expectedUri) { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); + $this->wsdl->setUri(new Uri($uri)); + $this->testDocumentNodes(); + + $this->assertEquals($expectedUri, $this->dom->lookupNamespaceUri('tns')); + $this->assertEquals($expectedUri, $this->dom->documentElement->getAttribute('targetNamespace')); - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($wsdl->toXml()), - '' . - '' ); } - public function testSetUriChangesDomDocumentWsdlStructureTnsAndTargetNamespaceAttributes() + /** + * @dataProvider dataProviderForURITesting + * + * @param string $uri + */ + public function testObjectConstructionWithDifferentURI($uri, $expectedUri) { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); - $wsdl->setUri('http://localhost/MyNewService.php'); + $wsdl = new Wsdl($this->defaultServiceName, $uri); + + $dom = $this->registerNamespaces($wsdl->toDomDocument(), $uri); + $this->testDocumentNodes(); + + $this->assertEquals($expectedUri, $dom->lookupNamespaceUri('tns')); + $this->assertEquals($expectedUri, $dom->documentElement->getAttribute('targetNamespace')); - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($wsdl->toXml()), - '' . - '' ); } - public function testAddMessage() + /** + * Data provider for uri testing + * + * @return array + */ + public function dataProviderForURITesting() { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); + return array( + array('http://localhost/MyService.php', 'http://localhost/MyService.php'), + array('http://localhost/MyNewService.php', 'http://localhost/MyNewService.php'), + array(new Uri('http://localhost/MyService.php'), 'http://localhost/MyService.php'), + /** + * @bug ZF-5736 + */ + array('http://localhost/MyService.php?a=b&b=c', 'http://localhost/MyService.php?a=b&b=c'), + + /** + * @bug ZF-5736 + */ + array('http://localhost/MyService.php?a=b&b=c', 'http://localhost/MyService.php?a=b&b=c'), + ); + } + + /** + * @dataProvider dataProviderForAddMessage + * + * @param array $parameters message parameters + */ + public function testAddMessage($parameters) + { + $messageParts = array(); + foreach($parameters as $i => $parameter) { + $messageParts['parameter'.$i] = $this->wsdl->getType($parameter); + } + + $messageName = 'myMessage'; + + $this->wsdl->addMessage($messageName, $messageParts); + $this->testDocumentNodes(); + + $messageNodes = $this->xpath->query('//wsdl:definitions/wsdl:message'); + + $this->assertGreaterThan(0, $messageNodes->length, 'Missing message node in definitions node.'); + $this->assertEquals($messageName, $messageNodes->item(0)->getAttribute('name')); + + foreach ($messageParts as $parameterName => $parameterType) { + $part = $this->xpath->query('wsdl:part[@name="'.$parameterName.'"]', $messageNodes->item(0)); + $this->assertEquals($parameterType, $part->item(0)->getAttribute('type')); + } + + } + + /** + * @dataProvider dataProviderForAddMessage + * + * @param array $parameters complex message parameters + */ + public function testAddComplexMessage($parameters) + { $messageParts = array(); - $messageParts['parameter1'] = $wsdl->getType('int'); - $messageParts['parameter2'] = $wsdl->getType('string'); - $messageParts['parameter3'] = $wsdl->getType('mixed'); - - $wsdl->addMessage('myMessage', $messageParts); - - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($wsdl->toXml()), - '' . - '' - . '' - . '' - . '' - . '' - . '' - . '' ); + foreach($parameters as $i => $parameter) { + $messageParts['parameter'.$i] = array( + 'type' => $this->wsdl->getType($parameter), + 'name' => 'parameter'.$i + ); + } + + $messageName = 'myMessage'; + + $this->wsdl->addMessage($messageName, $messageParts); + $this->testDocumentNodes(); + + $messageNodes = $this->xpath->query('//wsdl:definitions/wsdl:message'); + + $this->assertGreaterThan(0, $messageNodes->length, 'Missing message node in definitions node.'); + + foreach ($messageParts as $parameterName => $parameterDefinition) { + $part = $this->xpath->query('wsdl:part[@name="'.$parameterName.'"]', $messageNodes->item(0)); + $this->assertEquals($parameterDefinition['type'], $part->item(0)->getAttribute('type')); + $this->assertEquals($parameterDefinition['name'], $part->item(0)->getAttribute('name')); + } + + } + + /** + * @return array + */ + public function dataProviderForAddMessage() + { + return array( + array(array('int', 'int', 'int')), + array(array('string', 'string', 'string', 'string')), + array(array('mixed')), + array(array('int', 'int', 'string', 'string')), + array(array('int', 'string', 'int', 'string')), + ); } public function testAddPortType() { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); - - $wsdl->addPortType('myPortType'); - - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($wsdl->toXml()), - '' . - '' - . '' - . '' ); - } - - public function testAddPortOperation() - { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); - - $portType = $wsdl->addPortType('myPortType'); - - $wsdl->addPortOperation($portType, 'operation1'); - $wsdl->addPortOperation($portType, 'operation2', 'tns:operation2Request', 'tns:operation2Response'); - $wsdl->addPortOperation($portType, 'operation3', 'tns:operation3Request', 'tns:operation3Response', 'tns:operation3Fault'); - - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($wsdl->toXml()), - '' . - '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' ); + $portName = 'myPortType'; + $this->wsdl->addPortType($portName); + + $this->testDocumentNodes(); + + $portTypeNodes = $this->xpath->query('//wsdl:definitions/wsdl:portType'); + + $this->assertGreaterThan(0, $portTypeNodes->length, 'Missing portType node in definitions node.'); + + $this->assertTrue($portTypeNodes->item(0)->hasAttribute('name')); + $this->assertEquals($portName, $portTypeNodes->item(0)->getAttribute('name')); + } + + /** + * @dataProvider dataProviderForAddPortOperation + * + * @param string $operationName + */ + public function testAddPortOperation($operationName, $inputRequest = null, $outputResponse = null, $fail = null) + { + $portName = 'myPortType'; + $portType = $this->wsdl->addPortType($portName); + + $this->wsdl->addPortOperation($portType, $operationName, $inputRequest, $outputResponse, $fail); + + $this->testDocumentNodes(); + + $portTypeNodes = $this->xpath->query('//wsdl:definitions/wsdl:portType[@name="'.$portName.'"]'); + $this->assertGreaterThan(0, $portTypeNodes->length, 'Missing portType node in definitions node.'); + + $operationNodes = $this->xpath->query('wsdl:operation[@name="'.$operationName.'"]', $portTypeNodes->item(0)); + $this->assertGreaterThan(0, $operationNodes->length); + + if (empty($inputRequest) AND empty($outputResponse) AND empty($fail)) { + $this->assertFalse($operationNodes->item(0)->hasChildNodes()); + } else { + $this->assertTrue($operationNodes->item(0)->hasChildNodes()); + } + + if (!empty($inputRequest)) { + $inputNodes = $operationNodes->item(0)->getElementsByTagName('input'); + $this->assertEquals($inputRequest, $inputNodes->item(0)->getAttribute('message')); + } + + if (!empty($outputResponse)) { + $outputNodes = $operationNodes->item(0)->getElementsByTagName('output'); + $this->assertEquals($outputResponse, $outputNodes->item(0)->getAttribute('message')); + } + + if (!empty($fail)) { + $faultNodes = $operationNodes->item(0)->getElementsByTagName('fault'); + $this->assertEquals($fail, $faultNodes->item(0)->getAttribute('message')); + } + } + + /** + * + */ + public function dataProviderForAddPortOperation() + { + return array( + array('operation'), + array('operation', 'tns:operationRequest', 'tns:operationResponse'), + array('operation', 'tns:operationRequest', 'tns:operationResponse', 'tns:operationFault'), + array('operation', 'tns:operationRequest', null, 'tns:operationFault'), + array('operation', null, null, 'tns:operationFault'), + array('operation', null, 'tns:operationResponse', 'tns:operationFault'), + array('operation', null, 'tns:operationResponse'), + ); } public function testAddBinding() { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); - - $wsdl->addPortType('myPortType'); - $wsdl->addBinding('MyServiceBinding', 'myPortType'); - - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($wsdl->toXml()), - '' . - '' - . '' - . '' - . '' ); - } - - public function testAddBindingOperation() - { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); - - $wsdl->addPortType('myPortType'); - $binding = $wsdl->addBinding('MyServiceBinding', 'myPortType'); - - $wsdl->addBindingOperation($binding, 'operation1'); - $wsdl->addBindingOperation($binding, - 'operation2', - array('use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/"), - array('use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/") - ); - $wsdl->addBindingOperation($binding, - 'operation3', - array('use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/"), - array('use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/"), - array('name' => 'MyFault','use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/") - ); - - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($wsdl->toXml()), - '' . - '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' ); - } - - public function testAddSoapBinding() - { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); - - $wsdl->addPortType('myPortType'); - $binding = $wsdl->addBinding('MyServiceBinding', 'myPortType'); - - $wsdl->addSoapBinding($binding); - - $wsdl->addBindingOperation($binding, 'operation1'); - $wsdl->addBindingOperation($binding, - 'operation2', - array('use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/"), - array('use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/") - ); - - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($wsdl->toXml()), - '' . - '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' ); - - $wsdl1 = new Wsdl('MyService', 'http://localhost/MyService.php'); - - $wsdl1->addPortType('myPortType'); - $binding = $wsdl1->addBinding('MyServiceBinding', 'myPortType'); - - $wsdl1->addSoapBinding($binding, 'rpc'); - - $wsdl1->addBindingOperation($binding, 'operation1'); - $wsdl1->addBindingOperation($binding, - 'operation2', - array('use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/"), - array('use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/") - ); - - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($wsdl1->toXml()), - '' . - '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' ); - } - - - public function testAddSoapOperation() - { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); - - $wsdl->addPortType('myPortType'); - $binding = $wsdl->addBinding('MyServiceBinding', 'myPortType'); - - $wsdl->addSoapOperation($binding, 'http://localhost/MyService.php#myOperation'); - - $wsdl->addBindingOperation($binding, 'operation1'); - $wsdl->addBindingOperation($binding, - 'operation2', - array('use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/"), - array('use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/") - ); - - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($wsdl->toXml()), - '' . - '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' ); - } - - public function testAddService() - { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); - - $wsdl->addPortType('myPortType'); - $wsdl->addBinding('MyServiceBinding', 'myPortType'); - - $wsdl->addService('Service1', 'myPortType', 'MyServiceBinding', 'http://localhost/MyService.php'); - - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($wsdl->toXml()), - '' . - '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' ); + $this->wsdl->addBinding('MyServiceBinding', 'myPortType'); + + $this->testDocumentNodes(); + + $bindingNodes = $this->xpath->query('//wsdl:definitions/wsdl:binding'); + + if ($bindingNodes->length === 0) { + $this->fail('Missing binding node in definitions node.'.$bindingNodes->length); + } + + $this->assertEquals('MyServiceBinding', $bindingNodes->item(0)->getAttribute('name')); + $this->assertEquals('myPortType', $bindingNodes->item(0)->getAttribute('type')); + } - public function testAddDocumentation() + /** + * @dataProvider dataProviderForAddBindingOperation + * + * @param $operationName + * @param null $input + * @param null $inputEncoding + * @param null $output + * @param null $outputEncoding + * @param null $fault + * @param null $faultEncoding + * @param null $faultName + */ + public function testAddBindingOperation($operationName, + $input = null, $inputEncoding = null, + $output = null, $outputEncoding = null, + $fault = null, $faultEncoding = null, $faultName = null) { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); + $binding = $this->wsdl->addBinding('MyServiceBinding', 'myPortType'); + + $inputArray = array(); + if (!empty($input) AND !empty($inputEncoding)) { + $inputArray = array('use' => $input, 'encodingStyle' => $inputEncoding); + } + + $outputArray = array(); + if (!empty($output) AND !empty($outputEncoding)) { + $outputArray = array('use' => $output, 'encodingStyle' => $outputEncoding); + } + + $faultArray = array(); + if (!empty($fault) AND !empty($faultEncoding) AND !empty($faultName)) { + $faultArray = array('use' => $fault, 'encodingStyle' => $faultEncoding, 'name'=>$faultName); + } + + $this->wsdl->addBindingOperation($binding, + $operationName, + $inputArray, + $outputArray, + $faultArray + ); + + $this->testDocumentNodes(); + + $bindingNodes = $this->xpath->query('//wsdl:binding'); + + $this->assertGreaterThan(0, $bindingNodes->length, 'Missing binding node in definition.'); + + $this->assertEquals('MyServiceBinding', $bindingNodes->item(0)->getAttribute('name')); + $this->assertEquals('myPortType', $bindingNodes->item(0)->getAttribute('type')); - $portType = $wsdl->addPortType('myPortType'); + $operationNodes = $this->xpath->query('wsdl:operation[@name="'.$operationName.'"]', $bindingNodes->item(0)); + $this->assertEquals(1, $operationNodes->length, 'Missing operation node in definition.'); - $wsdl->addDocumentation($portType, 'This is a description for Port Type node.'); + if (empty($inputArray) AND empty($outputArray) AND empty($faultArray)) { + $this->assertFalse($operationNodes->item(0)->hasChildNodes()); + } + + foreach (array( + '//wsdl:input/soap:body' => $inputArray, + '//wsdl:output/soap:body' => $outputArray, + '//wsdl:fault' => $faultArray + ) as $query => $ar) { + + if (!empty($ar)) { + $nodes = $this->xpath->query($query); - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($wsdl->toXml()), - '' . - '' - . '' - . 'This is a description for Port Type node.' - . '' - . '' ); + $this->assertGreaterThan(0, $nodes->length, 'Missing operation body.'); + + foreach ($ar as $key => $val) { + $this->assertEquals($ar[$key], $nodes->item(0)->getAttribute($key), + 'Bad attribute in operation definition: '.$key); + } + } + } } - public function testAddDocumentationToSetInsertsBefore() + /** + * + */ + public function dataProviderForAddBindingOperation() { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); - $messageParts = array(); - $messageParts['parameter1'] = $wsdl->getType('int'); - $messageParts['parameter2'] = $wsdl->getType('string'); - $messageParts['parameter3'] = $wsdl->getType('mixed'); + $enc = 'http://schemas.xmlsoap.org/soap/encoding/'; - $message = $wsdl->addMessage('myMessage', $messageParts); - $wsdl->addDocumentation($message, "foo"); + return array( + array('operation'), + array('operation', 'encoded', $enc, 'encoded', $enc, 'encoded', $enc, 'myFaultName'), + array('operation', null, null, 'encoded', $enc, 'encoded', $enc, 'myFaultName'), + array('operation', null, null, 'encoded', $enc, 'encoded'), + array('operation', 'encoded', $enc), + array('operation', null, null, null, null, 'encoded', $enc, 'myFaultName'), + array('operation', 'encoded1', $enc.'1', 'encoded2', $enc.'2', 'encoded3', $enc.'3', 'myFaultName'), - $this->assertEquals( - '' . - '' - . '' - . 'foo' - . '' - . '' - . '' - . '' - . '', - $this->sanitizeWsdlXmlOutputForOsCompability($wsdl->toXml()) ); } - public function testToXml() + /** + * @dataProvider dataProviderForSoapBindingStyle + */ + public function testAddSoapBinding($style) { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); + $this->wsdl->addPortType('myPortType'); + $binding = $this->wsdl->addBinding('MyServiceBinding', 'myPortType'); + + $this->wsdl->addSoapBinding($binding, $style); + + $this->testDocumentNodes(); - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($wsdl->toXml()), - '' . - '' ); + $nodes = $this->xpath->query('//soap:binding'); + + $this->assertGreaterThan(0, $nodes->length); + $this->assertEquals($style, $nodes->item(0)->getAttribute('style')); } - public function testToDomDocument() + public function dataProviderForSoapBindingStyle() { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); - $dom = $wsdl->toDomDocument(); + return array( + array('document'), + array('rpc'), + ); + } - $this->assertTrue($dom instanceOf \DOMDocument); + /** + * @dataProvider dataProviderForAddSoapOperation + */ + public function testAddSoapOperation($operationUrl) + { + $this->wsdl->addPortType('myPortType'); + $binding = $this->wsdl->addBinding('MyServiceBinding', 'myPortType'); + + $this->wsdl->addSoapOperation($binding, $operationUrl); - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($dom->saveXML()), - '' . - '' ); + $this->testDocumentNodes(); + + $node = $this->xpath->query('//soap:operation'); + $this->assertGreaterThan(0, $node->length); + $this->assertEquals($operationUrl, $node->item(0)->getAttribute('soapAction')); } - public function testDump() + public function dataProviderForAddSoapOperation() { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); + return array( + array('http://localhost/MyService.php#myOperation'), + array(new Uri('http://localhost/MyService.php#myOperation')) + ); + } + /** + * @dataProvider dataProviderForAddService + */ + public function testAddService($serviceUrl) + { + $this->wsdl->addPortType('myPortType'); + $this->wsdl->addBinding('MyServiceBinding', 'myPortType'); + + $this->wsdl->addService('Service1', 'myPortType', 'MyServiceBinding', $serviceUrl); + + $this->testDocumentNodes(); + + $nodes = $this->xpath->query('//wsdl:service[@name="Service1"]/wsdl:port/soap:address'); + $this->assertGreaterThan(0, $nodes->length); + + $this->assertEquals($serviceUrl, $nodes->item(0)->getAttribute('location')); + } + + /** + * @return array + */ + public function dataProviderForAddService() + { + return array( + array('http://localhost/MyService.php'), + array(new Uri('http://localhost/MyService.php')) + ); + } + + /** + * @dataProvider ampersandInUrlDataProvider + */ + public function testAddBindingOperationWithAmpersandInUrl($actualUrl, $expectedUrl) + { + + $this->wsdl->addPortType('myPortType'); + $binding = $this->wsdl->addBinding('MyServiceBinding', 'myPortType'); + + $this->wsdl->addBindingOperation( + $binding, + 'operation1', + array('use' => 'encoded', 'encodingStyle' => $actualUrl), + array('use' => 'encoded', 'encodingStyle' => $actualUrl), + array('name' => 'MyFault','use' => 'encoded', 'encodingStyle' => $actualUrl) + ); + + $nodes = $this->xpath->query('//wsdl:binding[@type="myPortType" and @name="MyServiceBinding"]/wsdl:operation[@name="operation1"]/wsdl:input/soap:body'); + + $this->assertGreaterThanOrEqual(1, $nodes->length); + $this->assertEquals($expectedUrl, $nodes->item(0)->getAttribute('encodingStyle')); + } + + /** + * @dataProvider ampersandInUrlDataProvider() + */ + public function testAddSoapOperationWithAmpersandInUrl($actualUrl, $expectedUrl) + { + $this->wsdl->addPortType('myPortType'); + $binding = $this->wsdl->addBinding('MyServiceBinding', 'myPortType'); + + $this->wsdl->addSoapOperation($binding, $actualUrl); + + $this->testDocumentNodes(); + + $nodes = $this->xpath->query('//wsdl:binding/soap:operation'); + $this->assertGreaterThanOrEqual(1, $nodes->length); + $this->assertEquals($expectedUrl, $nodes->item(0)->getAttribute('soapAction')); + } + + /** + * @dataProvider ampersandInUrlDataProvider() + */ + public function testAddServiceWithAmpersandInUrl($actualUrl, $expectedUrl) + { + $this->wsdl->addPortType('myPortType'); + $this->wsdl->addBinding('MyServiceBinding', 'myPortType'); + + $this->wsdl->addService('Service1', 'myPortType', 'MyServiceBinding', $actualUrl); + + $this->testDocumentNodes(); + + $nodes = $this->xpath->query('//wsdl:port/soap:address'); + $this->assertGreaterThanOrEqual(1, $nodes->length); + $this->assertEquals($expectedUrl, $nodes->item(0)->getAttribute('location')); + } + + public function ampersandInUrlDataProvider() + { + return array( + 'Decoded ampersand' => array( + 'http://localhost/MyService.php?foo=bar&baz=qux', + 'http://localhost/MyService.php?foo=bar&baz=qux', + ), + 'Encoded ampersand' => array( + 'http://localhost/MyService.php?foo=bar&baz=qux', + 'http://localhost/MyService.php?foo=bar&baz=qux', + ), + 'Encoded and decoded ampersand' => array( + 'http://localhost/MyService.php?foo=bar&&baz=qux', + 'http://localhost/MyService.php?foo=bar&&baz=qux', + ), + ); + } + + + /** + * + */ + public function testAddDocumentation() + { + $doc = 'This is a description for Port Type node.'; + $this->wsdl->addDocumentation($this->wsdl, $doc); + + $this->testDocumentNodes(); + + $nodes = $this->wsdl->toDomDocument()->childNodes; + $this->assertEquals(1, $nodes->length); + $this->assertEquals($doc, $nodes->item(0)->nodeValue); + + } + + public function testAddDocumentationToSomeElmenet() + { + $portType = $this->wsdl->addPortType('myPortType'); + + $doc = 'This is a description for Port Type node.'; + $this->wsdl->addDocumentation($portType, $doc); + + $this->testDocumentNodes(); + + $nodes = $this->xpath->query('//wsdl:portType[@name="myPortType"]/wsdl:documentation'); + $this->assertEquals(1, $nodes->length); + $this->assertEquals($doc, $nodes->item(0)->nodeValue); + } + + public function testAddDocumentationToSetInsertsBefore() + { + $messageParts = array(); + $messageParts['parameter1'] = $this->wsdl->getType('int'); + $messageParts['parameter2'] = $this->wsdl->getType('string'); + $messageParts['parameter3'] = $this->wsdl->getType('mixed'); + + $message = $this->wsdl->addMessage('myMessage', $messageParts); + $this->wsdl->addDocumentation($message, "foo"); + + $this->testDocumentNodes(); + + $nodes = $this->xpath->query('//wsdl:message[@name="myMessage"]/*[1]'); + $this->assertEquals('documentation', $nodes->item(0)->nodeName); + + } + + public function testDumpToFile() + { + $file = tempnam(sys_get_temp_dir(), 'zfunittest'); + + $dumpStatus = $this->wsdl->dump($file); + + $fileContent = file_get_contents($file); + unlink($file); + + $this->assertTrue($dumpStatus, 'WSDL Dump fail'); + + $this->checkXMLContent($fileContent); + } + + public function testDumpToOutput() + { ob_start(); - $wsdl->dump(); - $wsdlDump = ob_get_clean(); - - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($wsdlDump), - '' . - '' ); - - $wsdl->dump(__DIR__ . '/TestAsset/dumped.wsdl'); - $dumpedContent = file_get_contents(__DIR__ . '/TestAsset/dumped.wsdl'); - - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($dumpedContent), - '' . - '' ); - - unlink(__DIR__ . '/TestAsset/dumped.wsdl'); + $dumpStatus = $this->wsdl->dump(); + $screenContent = ob_get_clean(); + + $this->assertTrue($dumpStatus, 'Dump to output failed'); + + $this->checkXMLContent($screenContent); } - public function testGetType() + public function checkXMLContent($content) { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); + libxml_use_internal_errors(true); + libxml_disable_entity_loader(false); + $xml = new \DOMDocument(); + $xml->preserveWhiteSpace = false; + $xml->encoding = 'UTF-8'; + $xml->formatOutput = false; + $xml->loadXML($content); - $this->assertEquals('xsd:string', $wsdl->getType('string'), 'xsd:string detection failed.'); - $this->assertEquals('xsd:string', $wsdl->getType('str'), 'xsd:string detection failed.'); - $this->assertEquals('xsd:int', $wsdl->getType('int'), 'xsd:int detection failed.'); - $this->assertEquals('xsd:int', $wsdl->getType('integer'), 'xsd:int detection failed.'); - $this->assertEquals('xsd:float', $wsdl->getType('float'), 'xsd:float detection failed.'); - $this->assertEquals('xsd:double', $wsdl->getType('double'), 'xsd:double detection failed.'); - $this->assertEquals('xsd:boolean', $wsdl->getType('boolean'), 'xsd:boolean detection failed.'); - $this->assertEquals('xsd:boolean', $wsdl->getType('bool'), 'xsd:boolean detection failed.'); - $this->assertEquals('soap-enc:Array', $wsdl->getType('array'), 'soap-enc:Array detection failed.'); - $this->assertEquals('xsd:struct', $wsdl->getType('object'), 'xsd:struct detection failed.'); - $this->assertEquals('xsd:anyType', $wsdl->getType('mixed'), 'xsd:anyType detection failed.'); - $this->assertEquals('', $wsdl->getType('void'), 'void detection failed.'); + $errors = libxml_get_errors(); + $this->assertEmpty($errors, 'Libxml parsing errors: '.print_r($errors, 1)); + + $this->dom = $this->registerNamespaces($xml); + + $this->testConstructor(); + + $this->testDocumentNodes(); } - public function testGetComplexTypeBasedOnStrategiesBackwardsCompabilityBoolean() + public function testGetType() { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); - $this->assertEquals('tns:WsdlTestClass', $wsdl->getType('\ZendTest\Soap\TestAsset\WsdlTestClass')); - $this->assertTrue($wsdl->getComplexTypeStrategy() instanceof ComplexTypeStrategy\DefaultComplexType); + $this->assertEquals('xsd:string', $this->wsdl->getType('string'), 'xsd:string detection failed.'); + $this->assertEquals('xsd:string', $this->wsdl->getType('str'), 'xsd:string detection failed.'); + $this->assertEquals('xsd:int', $this->wsdl->getType('int'), 'xsd:int detection failed.'); + $this->assertEquals('xsd:int', $this->wsdl->getType('integer'), 'xsd:int detection failed.'); + $this->assertEquals('xsd:float', $this->wsdl->getType('float'), 'xsd:float detection failed.'); + $this->assertEquals('xsd:double', $this->wsdl->getType('double'), 'xsd:double detection failed.'); + $this->assertEquals('xsd:boolean', $this->wsdl->getType('boolean'), 'xsd:boolean detection failed.'); + $this->assertEquals('xsd:boolean', $this->wsdl->getType('bool'), 'xsd:boolean detection failed.'); + $this->assertEquals('soap-enc:Array', $this->wsdl->getType('array'), 'soap-enc:Array detection failed.'); + $this->assertEquals('xsd:struct', $this->wsdl->getType('object'), 'xsd:struct detection failed.'); + $this->assertEquals('xsd:anyType', $this->wsdl->getType('mixed'), 'xsd:anyType detection failed.'); + $this->assertEquals('', $this->wsdl->getType('void'), 'void detection failed.'); + } -// $wsdl2 = new Wsdl('MyService', 'http://localhost/MyService.php', false); -// $this->assertEquals('xsd:anyType', $wsdl2->getType('\ZendTest\Soap\TestAsset\WsdlTestClass')); -// $this->assertTrue($wsdl2->getComplexTypeStrategy() instanceof ComplexTypeStrategy\AnyType); + public function testGetComplexTypeBasedOnStrategiesBackwardsCompabilityBoolean() + { + $this->assertEquals('tns:WsdlTestClass', $this->wsdl->getType('\ZendTest\Soap\TestAsset\WsdlTestClass')); + $this->assertTrue($this->wsdl->getComplexTypeStrategy() instanceof Wsdl\ComplexTypeStrategy\DefaultComplexType); } public function testGetComplexTypeBasedOnStrategiesStringNames() { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php', new \Zend\Soap\Wsdl\ComplexTypeStrategy\DefaultComplexType); - $this->assertEquals('tns:WsdlTestClass', $wsdl->getType('\ZendTest\Soap\TestAsset\WsdlTestClass')); - $this->assertTrue($wsdl->getComplexTypeStrategy() instanceof ComplexTypeStrategy\DefaultComplexType); + $this->wsdl = new Wsdl($this->defaultServiceName, 'http://localhost/MyService.php', new Wsdl\ComplexTypeStrategy\DefaultComplexType); + $this->assertEquals('tns:WsdlTestClass', $this->wsdl->getType('\ZendTest\Soap\TestAsset\WsdlTestClass')); + $this->assertTrue($this->wsdl->getComplexTypeStrategy() instanceof Wsdl\ComplexTypeStrategy\DefaultComplexType); - $wsdl2 = new Wsdl('MyService', 'http://localhost/MyService.php', new \Zend\Soap\Wsdl\ComplexTypeStrategy\AnyType); + $wsdl2 = new Wsdl($this->defaultServiceName, $this->defaultServiceUri, new Wsdl\ComplexTypeStrategy\AnyType); $this->assertEquals('xsd:anyType', $wsdl2->getType('\ZendTest\Soap\TestAsset\WsdlTestClass')); - $this->assertTrue($wsdl2->getComplexTypeStrategy() instanceof ComplexTypeStrategy\AnyType); + $this->assertTrue($wsdl2->getComplexTypeStrategy() instanceof Wsdl\ComplexTypeStrategy\AnyType); } public function testAddingSameComplexTypeMoreThanOnceIsIgnored() { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); - $wsdl->addType('\ZendTest\Soap\TestAsset\WsdlTestClass', 'tns:SomeTypeName'); - $wsdl->addType('\ZendTest\Soap\TestAsset\WsdlTestClass', 'tns:AnotherTypeName'); - $types = $wsdl->getTypes(); + $this->wsdl->addType('\ZendTest\Soap\TestAsset\WsdlTestClass', 'tns:SomeTypeName'); + $this->wsdl->addType('\ZendTest\Soap\TestAsset\WsdlTestClass', 'tns:AnotherTypeName'); + $types = $this->wsdl->getTypes(); $this->assertEquals(1, count($types)); - $this->assertEquals(array('\ZendTest\Soap\TestAsset\WsdlTestClass' => 'tns:SomeTypeName'), - $types); + $this->assertEquals( + array( + '\ZendTest\Soap\TestAsset\WsdlTestClass' => 'tns:SomeTypeName' + ), + $types + ); + + $this->testDocumentNodes(); } public function testUsingSameComplexTypeTwiceLeadsToReuseOfDefinition() { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); - $wsdl->addComplexType('\ZendTest\Soap\TestAsset\WsdlTestClass'); - $this->assertEquals(array('\ZendTest\Soap\TestAsset\WsdlTestClass' => - 'tns:WsdlTestClass'), - $wsdl->getTypes()); + $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\WsdlTestClass'); + $this->assertEquals( + array( + '\ZendTest\Soap\TestAsset\WsdlTestClass' => 'tns:WsdlTestClass' + ), + $this->wsdl->getTypes() + ); - $wsdl->addComplexType('\ZendTest\Soap\TestAsset\WsdlTestClass'); - $this->assertEquals(array('\ZendTest\Soap\TestAsset\WsdlTestClass' => - 'tns:WsdlTestClass'), - $wsdl->getTypes()); + $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\WsdlTestClass'); + $this->assertEquals( + array( + '\ZendTest\Soap\TestAsset\WsdlTestClass' => 'tns:WsdlTestClass' + ), + $this->wsdl->getTypes() + ); + + $this->testDocumentNodes(); + } + + public function testGetSchema() + { + $schema = $this->wsdl->getSchema(); + + $this->assertEquals($this->defaultServiceUri, $schema->getAttribute('targetNamespace')); } public function testAddComplexType() { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); - - $wsdl->addComplexType('\ZendTest\Soap\TestAsset\WsdlTestClass'); - - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($wsdl->toXml()), - '' . - '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' ); + $this->wsdl->addComplexType('\ZendTest\Soap\TestAsset\WsdlTestClass'); + + $this->testDocumentNodes(); + + $nodes = $this->xpath->query('//wsdl:types/xsd:schema/xsd:complexType/xsd:all/*'); + + $this->assertGreaterThan(0, $nodes->length, 'Unable to find object properties in wsdl'); + } + + public function testAddTypesFromDocument() + { + $dom = new \DOMDocument(); + $types = $dom->createElementNS(WSDL::WSDL_NS_URI, 'types'); + $dom->appendChild($types); + + $this->wsdl->addTypes($dom); + + $nodes = $this->xpath->query('//wsdl:types'); + $this->assertGreaterThanOrEqual(1, $nodes->length); + + $this->testDocumentNodes(); + } + + public function testAddTypesFromNode() + { + $dom = $this->dom->createElementNS(WSDL::WSDL_NS_URI, 'types'); + + $this->wsdl->addTypes($dom); + + $nodes = $this->xpath->query('//wsdl:types'); + $this->assertGreaterThanOrEqual(1, $nodes->length); + + $this->testDocumentNodes(); + } + + public function testTranslateTypeFromClassMap() + { + $this->wsdl->setClassMap(array( + 'SomeType'=>'SomeOtherType' + )); + + $this->assertEquals('SomeOtherType', $this->wsdl->translateType('SomeType')); } /** - * @group ZF-3910 + * @dataProvider dataProviderForTranslateType */ - public function testCaseOfDocBlockParamsDosNotMatterForSoapTypeDetectionZf3910() + public function testTranslateType($type, $expected) { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); + $this->assertEquals($expected, $this->wsdl->translateType($type)); + } - $this->assertEquals("xsd:string", $wsdl->getType("StrIng")); - $this->assertEquals("xsd:string", $wsdl->getType("sTr")); - $this->assertEquals("xsd:int", $wsdl->getType("iNt")); - $this->assertEquals("xsd:int", $wsdl->getType("INTEGER")); - $this->assertEquals("xsd:float", $wsdl->getType("FLOAT")); - $this->assertEquals("xsd:double", $wsdl->getType("douBLE")); + /** + * @return array + */ + public function dataProviderForTranslateType() + { + return array( + array('\\SomeType','SomeType'), + array('SomeType\\','SomeType'), + array('\\SomeType\\','SomeType'), + array('\\SomeNamespace\SomeType\\','SomeType'), + array('\\SomeNamespace\SomeType\\SomeOtherType','SomeOtherType'), + array('\\SomeNamespace\SomeType\\SomeOtherType\\YetAnotherType','YetAnotherType'), + ); } + /** + * @group ZF-3910 * @group ZF-11937 */ - public function testWsdlGetTypeWillAllowLongType() + public function testCaseOfDocBlockParamsDosNotMatterForSoapTypeDetectionZf3910() { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); - $this->assertEquals("xsd:long", $wsdl->getType("long")); + $this->assertEquals("xsd:string", $this->wsdl->getType("StrIng")); + $this->assertEquals("xsd:string", $this->wsdl->getType("sTr")); + $this->assertEquals("xsd:int", $this->wsdl->getType("iNt")); + $this->assertEquals("xsd:int", $this->wsdl->getType("INTEGER")); + $this->assertEquals("xsd:float", $this->wsdl->getType("FLOAT")); + $this->assertEquals("xsd:double", $this->wsdl->getType("douBLE")); + + $this->assertEquals("xsd:long", $this->wsdl->getType("long")); } /** @@ -619,37 +796,69 @@ public function testWsdlGetTypeWillAllowLongType() */ public function testMultipleSequenceDefinitionsOfSameTypeWillBeRecognizedOnceBySequenceStrategy() { - $wsdl = new Wsdl("MyService", "http://localhost/MyService.php"); - $wsdl->setComplexTypeStrategy(new ComplexTypeStrategy\ArrayOfTypeSequence()); + $this->wsdl->setComplexTypeStrategy(new Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence()); + + $this->wsdl->addComplexType("string[]"); + $this->wsdl->addComplexType("int[]"); + $this->wsdl->addComplexType("string[]"); + $this->wsdl->addComplexType("int[]"); + + $this->testDocumentNodes(); + + $nodes = $this->xpath->query('//wsdl:types/xsd:schema/xsd:complexType[@name="ArrayOfString"]'); + $this->assertEquals(1, $nodes->length, "ArrayOfString should appear only once."); - $wsdl->addComplexType("string[]"); - $wsdl->addComplexType("int[]"); - $wsdl->addComplexType("string[]"); + $nodes = $this->xpath->query('//wsdl:types/xsd:schema/xsd:complexType[@name="ArrayOfInt"]'); + $this->assertEquals(1, $nodes->length, "ArrayOfInt should appear only once."); - $xml = $wsdl->toXml(); - $this->assertEquals(1, substr_count($xml, "ArrayOfString"), "ArrayOfString should appear only once."); - $this->assertEquals(1, substr_count($xml, "ArrayOfInt"), "ArrayOfInt should appear only once."); } - const URI_WITH_EXPANDED_AMP = "http://localhost/MyService.php?a=b&b=c"; - const URI_WITHOUT_EXPANDED_AMP = "http://localhost/MyService.php?a=b&b=c"; + public function testClassMap() + { + $this->wsdl->setClassMap(array('foo'=>'bar')); + + $this->assertArrayHasKey('foo', $this->wsdl->getClassMap()); + } /** - * @group ZF-5736 + * @expectedException RuntimeException */ - public function testHtmlAmpersandInUrlInConstructorIsEncodedCorrectly() + public function testAddElementException () { - $wsdl = new Wsdl("MyService", self::URI_WITH_EXPANDED_AMP); - $this->assertContains(self::URI_WITH_EXPANDED_AMP, $wsdl->toXML()); + $this->wsdl->addElement(1); } - /** - * @group ZF-5736 - */ - public function testHtmlAmpersandInUrlInSetUriIsEncodedCorrectly() + public function testAddElement() { - $wsdl = new Wsdl("MyService", "http://example.com"); - $wsdl->setUri(self::URI_WITH_EXPANDED_AMP); - $this->assertContains(self::URI_WITH_EXPANDED_AMP, $wsdl->toXML()); + $element = array( + 'name' => 'MyElement', + 'sequence' => array( + array('name' => 'myString', 'type' => 'string'), + array('name' => 'myInt', 'type' => 'int') + ) + ); + + $newElementName = $this->wsdl->addElement($element); + + $this->testDocumentNodes(); + + $this->assertEquals('tns:'.$element['name'], $newElementName); + + $nodes = $this->xpath->query('//wsdl:types/xsd:schema/xsd:element[@name="'.$element['name'].'"]/xsd:complexType'); + + $this->assertEquals(1, $nodes->length); + + $this->assertEquals('sequence', $nodes->item(0)->firstChild->localName); + + $n = 0; + foreach($element['sequence'] as $elementDefinition) { + $n++; + $elementNode = $this->xpath->query('xsd:element[@name="'.$elementDefinition['name'].'"]', $nodes->item(0)->firstChild); + $this->assertEquals($elementDefinition['type'], $elementNode->item(0)->getAttribute('type')); + } + + $this->assertEquals(count($element['sequence']), $n); } + + } diff --git a/test/WsdlTestHelper.php b/test/WsdlTestHelper.php new file mode 100644 index 00000000..409b7690 --- /dev/null +++ b/test/WsdlTestHelper.php @@ -0,0 +1,121 @@ +strategy) OR !($this->strategy instanceof ComplexTypeStrategyInterface)) { + $this->strategy = new Wsdl\ComplexTypeStrategy\DefaultComplexType(); + } + + $this->wsdl = new Wsdl($this->defaultServiceName, $this->defaultServiceUri, $this->strategy); + + if ($this->strategy instanceof ComplexTypeStrategyInterface) { + $this->strategy->setContext($this->wsdl); + } + + $this->dom = $this->wsdl->toDomDocument(); + $this->dom = $this->registerNamespaces($this->dom); + } + + /** + * @param \DOMDocument $obj + * @param string $documentNamespace + * @return \DOMDocument + */ + public function registerNamespaces($obj, $documentNamespace = null) + { + + if (empty($documentNamespace)) { + $documentNamespace = $this->defaultServiceUri; + } + + $this->xpath = new \DOMXPath($obj); + $this->xpath->registerNamespace('unittest', Wsdl::WSDL_NS_URI); + + $this->xpath->registerNamespace('tns', $documentNamespace); + $this->xpath->registerNamespace('soap', Wsdl::SOAP_11_NS_URI); + $this->xpath->registerNamespace('soap12', Wsdl::SOAP_12_NS_URI); + $this->xpath->registerNamespace('xsd', Wsdl::XSD_NS_URI); + $this->xpath->registerNamespace('soap-enc', Wsdl::SOAP_ENC_URI); + $this->xpath->registerNamespace('wsdl', Wsdl::WSDL_NS_URI); + + return $obj; + } + + /** + * @param \DOMElement $element + */ + public function testDocumentNodes($element = null) + { + if (!($this->wsdl instanceof Wsdl)) { + return; + } + + if (is_null($element)) { + $element = $this->wsdl->toDomDocument()->documentElement; + } + + /** @var $node \DOMElement */ + foreach ($element->childNodes as $node) { + if (in_array($node->nodeType, array(XML_ELEMENT_NODE))) { + $this->assertNotEmpty($node->namespaceURI, 'Document element: ' . $node->nodeName . ' has no valid namespace. Line: ' . $node->getLineNo()); + $this->testDocumentNodes($node); + } + } + } +} diff --git a/test/_files/commontypes.php b/test/_files/commontypes.php index 6e96f881..946fc7d4 100644 --- a/test/_files/commontypes.php +++ b/test/_files/commontypes.php @@ -3,7 +3,7 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @package Zend_Soap */ @@ -82,7 +82,7 @@ function Zend_Soap_AutoDiscover_TestFunc7() /** * Return Object * - * @return StdClass + * @return stdClass */ function Zend_Soap_AutoDiscover_TestFunc8() { @@ -197,7 +197,7 @@ class Zend_Soap_AutoDiscoverTestClass2 /** * * @param Zend_Soap_AutoDiscoverTestClass1 $test - * @return boolean + * @return bool */ public function add(Zend_Soap_AutoDiscoverTestClass1 $test) { @@ -275,7 +275,7 @@ class Zend_Soap_Wsdl_ComplexTest class Zend_Soap_Wsdl_ComplexObjectStructure { /** - * @var boolean + * @var bool */ public $boolean = true; diff --git a/test/_files/fulltests/server1.php b/test/_files/fulltests/server1.php index a97f66f4..4483f864 100644 --- a/test/_files/fulltests/server1.php +++ b/test/_files/fulltests/server1.php @@ -3,7 +3,7 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @package Zend_Soap */ diff --git a/test/_files/fulltests/server2.php b/test/_files/fulltests/server2.php index df8790fd..55ef9ed2 100644 --- a/test/_files/fulltests/server2.php +++ b/test/_files/fulltests/server2.php @@ -3,7 +3,7 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @package Zend_Soap */ diff --git a/test/_files/wsdl_example.wsdl b/test/_files/wsdl_example.wsdl index 4eaa8761..19dc6878 100644 --- a/test/_files/wsdl_example.wsdl +++ b/test/_files/wsdl_example.wsdl @@ -1,2 +1,89 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/schemas/wsdl.xsd b/test/schemas/wsdl.xsd index 0546b338..4832b6f5 100644 --- a/test/schemas/wsdl.xsd +++ b/test/schemas/wsdl.xsd @@ -1,39 +1,39 @@ - - +--> - + @@ -51,7 +51,7 @@ No other rights are granted by implication, estoppel or otherwise. - + @@ -61,7 +61,7 @@ No other rights are granted by implication, estoppel or otherwise. This type is extended by component types to allow attributes from other namespaces to be added. - + @@ -118,7 +118,7 @@ No other rights are granted by implication, estoppel or otherwise. - + @@ -133,7 +133,7 @@ No other rights are granted by implication, estoppel or otherwise. - + @@ -149,7 +149,7 @@ No other rights are granted by implication, estoppel or otherwise. - + @@ -159,15 +159,15 @@ No other rights are granted by implication, estoppel or otherwise. - + - + - + - + - + @@ -175,22 +175,22 @@ No other rights are granted by implication, estoppel or otherwise. - + - + - + - + - + @@ -198,13 +198,13 @@ No other rights are granted by implication, estoppel or otherwise. - + - + - + - + @@ -214,16 +214,16 @@ No other rights are granted by implication, estoppel or otherwise. - + - + - - - + + + @@ -231,14 +231,14 @@ No other rights are granted by implication, estoppel or otherwise. - - - - + + + + - + @@ -257,7 +257,7 @@ No other rights are granted by implication, estoppel or otherwise. - + @@ -270,7 +270,7 @@ No other rights are granted by implication, estoppel or otherwise. - + @@ -279,7 +279,7 @@ No other rights are granted by implication, estoppel or otherwise. - + @@ -302,7 +302,7 @@ No other rights are granted by implication, estoppel or otherwise. - + @@ -314,7 +314,7 @@ No other rights are granted by implication, estoppel or otherwise. - + @@ -331,4 +331,4 @@ No other rights are granted by implication, estoppel or otherwise. - \ No newline at end of file +