diff --git a/src/AutoDiscover.php b/src/AutoDiscover.php index dbe6f606..2f0ecea5 100644 --- a/src/AutoDiscover.php +++ b/src/AutoDiscover.php @@ -12,13 +12,11 @@ use Zend\Server\Reflection; 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 - */ class AutoDiscover { /** @@ -27,20 +25,18 @@ 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; @@ -52,42 +48,42 @@ class AutoDiscover /** * 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; @@ -100,10 +96,14 @@ class AutoDiscover * @param null|string $wsdlClass * @param null|array $classMap */ - public function __construct(ComplexTypeStrategy $strategy = null, $endpointUri = null, $wsdlClass = null, array $classMap = array()) - { + 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 (null !== $strategy) { $this->setComplexTypeStrategy($strategy); @@ -121,7 +121,7 @@ public function __construct(ComplexTypeStrategy $strategy = null, $endpointUri = * Set the discovery strategy for method type and other information. * * @param DiscoveryStrategy $discoveryStrategy - * @return AutoDiscover + * @return self */ public function setDiscoveryStrategy(DiscoveryStrategy $discoveryStrategy) { @@ -130,6 +130,8 @@ public function setDiscoveryStrategy(DiscoveryStrategy $discoveryStrategy) } /** + * Get the discovery strategy. + * * @return DiscoveryStrategy */ public function getDiscoveryStrategy() @@ -138,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 */ @@ -148,10 +150,11 @@ 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 AutoDiscover + * @param array $classmap + * @return self + * @throws Exception\InvalidArgumentException */ public function setClassMap($classMap) { @@ -162,6 +165,7 @@ public function setClassMap($classMap) (is_object($classMap) ? get_class($classMap) : gettype($classMap)) )); } + $this->classMap = $classMap; return $this; } @@ -170,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; } @@ -188,16 +201,11 @@ 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(sprintf( - "No service name given. Call %s::setServiceName().", - __CLASS__ - )); + throw new Exception\RuntimeException('No service name given. Call AutoDiscover::setServiceName().'); } } - return $this->serviceName; } @@ -206,19 +214,25 @@ public function getServiceName() * 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(sprintf( - 'No uri given to %s() as string or \Zend\Uri\Uri instance.', - __METHOD__ - )); + throw new Exception\InvalidArgumentException( + '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; } @@ -231,15 +245,13 @@ public function setUri($uri) public function getUri() { if ($this->uri === null) { - throw new Exception\RuntimeException(sprintf( - "Missing uri. You have to explicitly configure the Endpoint Uri by calling %s::setUri().", - __CLASS__ - )); + 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; } @@ -247,20 +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')) { - throw new Exception\InvalidArgumentException(sprintf( - 'No %s\Wsdl subclass given to %s() as string.', - __NAMESPACE__, - __METHOD__ - )); + 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; } @@ -281,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()) { 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; @@ -299,7 +309,7 @@ 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()) { @@ -316,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; } @@ -329,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) { @@ -340,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; } @@ -370,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) @@ -385,17 +411,17 @@ protected function _generateWsdl(array $reflectionMethods) $uri = $this->getUri(); $serviceName = $this->getServiceName(); - /** @var Wsdl $wsdl */ + $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); @@ -407,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) { @@ -452,12 +477,15 @@ 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) { @@ -482,33 +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)) ); } + $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; @@ -526,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) { @@ -547,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) { @@ -560,7 +602,7 @@ public function dump($filename) * Proxy to WSDL toXml() function * * @return string - * @throws \Zend\Soap\Exception\RuntimeException + * @throws Exception\RuntimeException */ public function toXml() { diff --git a/src/AutoDiscover/DiscoveryStrategy/DiscoveryStrategyInterface.php b/src/AutoDiscover/DiscoveryStrategy/DiscoveryStrategyInterface.php index 1207ec48..22f33dbf 100644 --- a/src/AutoDiscover/DiscoveryStrategy/DiscoveryStrategyInterface.php +++ b/src/AutoDiscover/DiscoveryStrategy/DiscoveryStrategyInterface.php @@ -14,7 +14,8 @@ use Zend\Server\Reflection\ReflectionParameter; /** - * Describes how types, return values and method details are detected during AutoDiscovery of a WSDL. + * Describes how types, return values and method details are detected during + * AutoDiscovery of a WSDL. */ interface DiscoveryStrategyInterface { @@ -23,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); @@ -33,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); @@ -44,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); @@ -55,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 634b55b4..44559de9 100644 --- a/src/AutoDiscover/DiscoveryStrategy/ReflectionDiscovery.php +++ b/src/AutoDiscover/DiscoveryStrategy/ReflectionDiscovery.php @@ -14,26 +14,52 @@ use Zend\Server\Reflection\ReflectionParameter; /** - * Describes how types, return values and method details are detected during AutoDiscovery of a 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 5d409e6f..ef9b9ef0 100644 --- a/src/Client.php +++ b/src/Client.php @@ -15,23 +15,20 @@ use Zend\Server\Client as ServerClient; use Zend\Stdlib\ArrayUtils; -/** - * \Zend\Soap\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 @@ -39,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 $proxyHost = null; - protected $proxyPort = null; - protected $proxyLogin = null; - protected $proxyPassword = null; - protected $localCert = null; - protected $passphrase = null; - protected $compression = null; - protected $connectionTimeout = null; - protected $streamContext = null; - protected $features = null; - protected $cacheWsdl = null; - protected $userAgent = 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 @@ -137,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) { @@ -164,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) @@ -174,75 +170,99 @@ 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->connectionTimeout = $value; @@ -267,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(); @@ -312,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 + * @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; } @@ -341,20 +363,19 @@ public function getSoapVersion() * Set classmap * * @param array $classmap - * @return Client + * @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; } @@ -368,11 +389,44 @@ 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 + * @return self * @throws Exception\InvalidArgumentException with invalid encoding argument */ public function setEncoding($encoding) @@ -381,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; } @@ -411,9 +463,7 @@ public function validateUrn($urn) if ($scheme === false || $scheme === null) { throw new Exception\InvalidArgumentException('Invalid URN'); } - return true; - } /** @@ -422,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; } @@ -451,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; } @@ -478,19 +524,19 @@ public function getLocation() * Set request style * * @param int $style One of the SOAP_RPC or SOAP_DOCUMENT constants - * @return Client + * @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; } @@ -508,19 +554,19 @@ public function getStyle() * Set message encoding method * * @param int $use One of the SOAP_ENCODED or SOAP_LITERAL constants - * @return Client + * @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; } @@ -538,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; } @@ -563,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; } @@ -588,14 +630,12 @@ public function getHttpPassword() * Set proxy host * * @param string $proxyHost - * @return Client + * @return self */ public function setProxyHost($proxyHost) { - $this->proxyHost = $proxyHost; - + $this->proxyHost = $proxyHost; $this->soapClient = null; - return $this; } @@ -613,14 +653,12 @@ public function getProxyHost() * Set proxy port * * @param int $proxyPort - * @return Client + * @return self */ public function setProxyPort($proxyPort) { - $this->proxyPort = (int) $proxyPort; - + $this->proxyPort = (int) $proxyPort; $this->soapClient = null; - return $this; } @@ -638,14 +676,12 @@ public function getProxyPort() * Set proxy login * * @param string $proxyLogin - * @return Client + * @return self */ public function setProxyLogin($proxyLogin) { $this->proxyLogin = $proxyLogin; - $this->soapClient = null; - return $this; } @@ -663,14 +699,12 @@ public function getProxyLogin() * Set proxy password * * @param string $proxyPassword - * @return Client + * @return self */ public function setProxyPassword($proxyPassword) { $this->proxyPassword = $proxyPassword; - - $this->soapClient = null; - + $this->soapClient = null; return $this; } @@ -678,7 +712,7 @@ public function setProxyPassword($proxyPassword) * Set HTTPS client certificate path * * @param string $localCert local certificate path - * @return Client + * @return self * @throws Exception\InvalidArgumentException with invalid local certificate path argument */ public function setHttpsCertificate($localCert) @@ -687,10 +721,8 @@ public function setHttpsCertificate($localCert) throw new Exception\InvalidArgumentException('Invalid HTTPS client certificate path.'); } - $this->localCert = $localCert; - + $this->localCert = $localCert; $this->soapClient = null; - return $this; } @@ -708,14 +740,12 @@ public function getHttpsCertificate() * Set HTTPS client certificate passphrase * * @param string $passphrase - * @return Client + * @return self */ public function setHttpsCertPassphrase($passphrase) { $this->passphrase = $passphrase; - $this->soapClient = null; - return $this; } @@ -733,7 +763,7 @@ public function getHttpsCertPassphrase() * Set compression options * * @param int|null $compressionOptions - * @return Client + * @return self */ public function setCompressionOptions($compressionOptions) { @@ -771,8 +801,8 @@ public function getProxyPassword() * Set Stream Context * * @param resource $context - * @return Client - * @throws Exception\InvalidArgumentException if $context is not a valid stream resource + * @return self + * @throws Exception\InvalidArgumentException */ public function setStreamContext($context) { @@ -798,12 +828,11 @@ public function getStreamContext() * 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; } @@ -821,16 +850,18 @@ public function getSoapFeatures() /** * Set the SOAP WSDL Caching Options * - * @param string|int|bool|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->cacheWsdl = null; } else { $this->cacheWsdl = (int) $caching; } + return $this; } @@ -848,7 +879,7 @@ public function getWSDLCache() * Set the string to use in User-Agent header * * @param string|null $userAgent - * @return Client + * @return self */ public function setUserAgent($userAgent) { @@ -857,6 +888,7 @@ public function setUserAgent($userAgent) } else { $this->userAgent = (string) $userAgent; } + return $this; } @@ -894,7 +926,6 @@ public function getLastResponse() if ($this->soapClient !== null) { return $this->soapClient->__getLastResponse(); } - return ''; } @@ -908,7 +939,6 @@ public function getLastRequestHeaders() if ($this->soapClient !== null) { return $this->soapClient->__getLastRequestHeaders(); } - return ''; } @@ -922,7 +952,6 @@ public function getLastResponseHeaders() if ($this->soapClient !== null) { return $this->soapClient->__getLastResponseHeaders(); } - return ''; } @@ -941,20 +970,19 @@ 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 $oneWay + * @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, $oneWay = null) + public function _doRequest(Client\Common $client, $request, $location,$action, $version, $oneWay = null) { // Perform request as is 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); } return call_user_func(array($client, 'SoapClient::__doRequest'), $request, $location, $action, $version, $oneWay); } @@ -971,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']); @@ -995,7 +1023,7 @@ protected function _initSoapClientObject() * * My be overridden in descendant classes * - * @param array $arguments + * @param array $arguments * @return array */ protected function _preProcessArguments($arguments) @@ -1009,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) @@ -1021,9 +1049,9 @@ protected function _preProcessResult($result) /** * Add SOAP input header * - * @param SoapHeader $header + * @param SoapHeader $header * @param bool $permanent - * @return Client + * @return self */ public function addSoapInputHeader(SoapHeader $header, $permanent = false) { @@ -1032,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; } @@ -1062,8 +1088,8 @@ 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) @@ -1076,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(); @@ -1119,13 +1147,6 @@ public function getFunctions() return $soapClient->__getFunctions(); } - - /** - * Get used types. - * - * @return array - */ - /** * Return a list of SOAP types * @@ -1142,13 +1163,14 @@ public function getTypes() } $soapClient = $this->getSoapClient(); - return $soapClient->__getTypes(); } /** - * @param SoapClient $soapClient - * @return Client + * Set SoapClient object + * + * @param SoapClient $soapClient + * @return self */ public function setSoapClient(SoapClient $soapClient) { @@ -1157,6 +1179,8 @@ public function setSoapClient(SoapClient $soapClient) } /** + * Get SoapClient object + * * @return SoapClient */ public function getSoapClient() @@ -1168,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 be9edae1..bf9eb5c8 100644 --- a/src/Client/Common.php +++ b/src/Client/Common.php @@ -9,9 +9,11 @@ namespace Zend\Soap\Client; +use SoapClient; + if (extension_loaded('soap')) { -class Common extends \SoapClient +class Common extends SoapClient { /** * doRequest() pre-processing method @@ -30,19 +32,19 @@ 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 $oneWay + * @param string $request + * @param string $location + * @param string $action + * @param int $version + * @param int $oneWay * @return mixed */ public function __doRequest($request, $location, $action, $version, $oneWay = null) diff --git a/src/Client/DotNet.php b/src/Client/DotNet.php index 9228cec4..b65e167b 100644 --- a/src/Client/DotNet.php +++ b/src/Client/DotNet.php @@ -19,10 +19,40 @@ /** * .NET SOAP client * - * Class is intended to be used with .Net Web Services. + * 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 * @@ -44,23 +74,28 @@ public function __construct($wsdl = null, $options = null) * @param string $request The request body. * @param string $location The SOAP URI. * @param string $action The SOAP action to call. - * @param integer $version The SOAP version to use. - * @param integer $one_way (Optional) The number 1 if a response is not expected. + * @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, $one_way = null) + public function _doRequest(CommonClient $client, $request, $location, $action, $version, $oneWay = null) { if (!$this->useNtlm) { - return parent::_doRequest($client, $request, $location, $action, $version, $one_way); + return parent::_doRequest($client, $request, $location, $action, $version, $oneWay); } $curlClient = $this->getCurlClient(); - $headers = array('Content-Type' => 'text/xml; charset=utf-8', - 'Method' => 'POST', - 'SOAPAction' => '"' . $action . '"', - 'User-Agent' => 'PHP-SOAP-CURL'); - $uri = new HttpUri($location); + // @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) @@ -70,6 +105,8 @@ public function _doRequest(CommonClient $client, $request, $location, $action, $ $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 @@ -83,14 +120,13 @@ public function _doRequest(CommonClient $client, $request, $location, $action, $ /** * Returns the cURL client that is being used. * - * @return \Zend\Http\Client\Adapter\Curl The cURL client. + * @return CurlClient */ public function getCurlClient() { if ($this->curlClient === null) { $this->curlClient = new CurlClient(); } - return $this->curlClient; } @@ -118,7 +154,7 @@ public function getLastResponseHeaders() * Sets the cURL client to use. * * @param CurlClient $curlClient The cURL client. - * @return self Fluent interface. + * @return self */ public function setCurlClient(CurlClient $curlClient) { @@ -133,7 +169,7 @@ public function setCurlClient(CurlClient $curlClient) * * @param array|\Traversable $options Options. * @throws \InvalidArgumentException If an unsupported option is passed. - * @return self Fluent interface. + * @return self */ public function setOptions($options) { @@ -151,16 +187,18 @@ public function setOptions($options) * * 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 @@ -172,13 +210,12 @@ 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; } @@ -188,7 +225,7 @@ protected function _preProcessResult($result) * @param array $headers The headers to flatten. * @return string The headers string. */ - private function flattenHeaders(array $headers) + protected function flattenHeaders(array $headers) { $result = ''; @@ -198,39 +235,4 @@ private function flattenHeaders(array $headers) return $result; } - - /** - * Curl HTTP client adapter. - * - * @var \Zend\Http\Client\Adapter\Curl - */ - private $curlClient = null; - - /** - * The last request headers. - * - * @var string - */ - private $lastRequestHeaders = ''; - - /** - * The last response headers. - * - * @var string - */ - private $lastResponseHeaders = ''; - - /** - * SOAP client options. - * - * @var array - */ - private $options = array(); - - /** - * Should NTLM authentication be used? - * - * @var boolean - */ - private $useNtlm = false; } diff --git a/src/Client/Local.php b/src/Client/Local.php index ea712180..6c634206 100644 --- a/src/Client/Local.php +++ b/src/Client/Local.php @@ -13,8 +13,6 @@ 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. * @@ -24,15 +22,14 @@ 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 */ @@ -49,13 +46,12 @@ 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 $oneWay + * @param Common $client + * @param string $request + * @param string $location + * @param string $action + * @param int $version + * @param int $oneWay * @return mixed */ public function _doRequest(Common $client, $request, $location, $action, $version, $oneWay = null) diff --git a/src/Exception/BadMethodCallException.php b/src/Exception/BadMethodCallException.php index 735857f0..30af2d0c 100644 --- a/src/Exception/BadMethodCallException.php +++ b/src/Exception/BadMethodCallException.php @@ -9,7 +9,10 @@ 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 f54d014b..e1bc31c0 100644 --- a/src/Exception/ExceptionInterface.php +++ b/src/Exception/ExceptionInterface.php @@ -9,5 +9,8 @@ namespace Zend\Soap\Exception; +/** + * Common Exception interface + */ interface ExceptionInterface {} diff --git a/src/Exception/ExtensionNotLoadedException.php b/src/Exception/ExtensionNotLoadedException.php index 272eb381..94bb9efe 100644 --- a/src/Exception/ExtensionNotLoadedException.php +++ b/src/Exception/ExtensionNotLoadedException.php @@ -9,5 +9,10 @@ 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 6308e6de..c71eec6f 100644 --- a/src/Exception/InvalidArgumentException.php +++ b/src/Exception/InvalidArgumentException.php @@ -9,7 +9,10 @@ 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 75db3716..b1290234 100644 --- a/src/Exception/RuntimeException.php +++ b/src/Exception/RuntimeException.php @@ -9,7 +9,10 @@ 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 f5b0b2f6..a3133d35 100644 --- a/src/Exception/UnexpectedValueException.php +++ b/src/Exception/UnexpectedValueException.php @@ -9,7 +9,10 @@ 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 151b3f45..5d276a26 100644 --- a/src/Server.php +++ b/src/Server.php @@ -9,18 +9,17 @@ 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 - */ -class Server implements \Zend\Server\Server +class Server implements ZendServerServer { /** * Actor URI @@ -40,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 @@ -58,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 @@ -104,8 +93,7 @@ class Server implements \Zend\Server\Server protected $response; /** - * Flag: whether or not {@link handle()} should return a response instead - * of automatically emitting it. + * Flag: whether or not {@link handle()} should return a response instead of automatically emitting it. * @var bool */ protected $returnResponse = false; @@ -117,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 @@ -128,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 * @@ -138,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) @@ -162,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) { @@ -172,33 +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 'features': - $this->setSoapFeatures($value); - break; + case 'cache_wsdl': $this->setWSDLCache($value); break; + + case 'features': + $this->setSoapFeatures($value); + break; + default: break; } @@ -216,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; @@ -250,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) @@ -277,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) @@ -323,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) { @@ -348,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) { @@ -371,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) @@ -399,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) { @@ -425,7 +478,7 @@ public function getWSDL() * Set the SOAP Feature options. * * @param string|int $feature - * @return Server + * @return self */ public function setSoapFeatures($feature) { @@ -446,8 +499,8 @@ public function getSoapFeatures() /** * Set the SOAP WSDL Caching Options * - * @param string|int|bool $options - * @return Server + * @param string|int|bool $options + * @return self */ public function setWSDLCache($options) { @@ -466,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 = '') @@ -487,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'); } @@ -509,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) { @@ -554,11 +609,11 @@ 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) { @@ -576,7 +631,6 @@ public function setObject($object) } $this->object = $object; - return $this; } @@ -587,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() @@ -605,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) @@ -617,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) { @@ -651,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; } @@ -706,7 +769,7 @@ public function getLastRequest() * The response is always available via {@link getResponse()}. * * @param bool $flag - * @return Server + * @return self */ public function setReturnResponse($flag = true) { @@ -741,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); @@ -785,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) @@ -812,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); @@ -830,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; } @@ -862,17 +926,46 @@ 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 * @@ -914,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; @@ -931,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); @@ -945,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 90b415fd..7c30208b 100644 --- a/src/Server/DocumentLiteralWrapper.php +++ b/src/Server/DocumentLiteralWrapper.php @@ -10,8 +10,7 @@ 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 @@ -60,11 +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(); + * */ class DocumentLiteralWrapper { @@ -92,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) @@ -102,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); } @@ -110,15 +110,14 @@ 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) { $reflMethod = $this->reflection->getMethod($method); - /* @var \Zend\Server\Reflection\ReflectionParameter[] $params */ $params = array(); foreach ($reflMethod->getParameters() as $param) { $params[$param->getName()] = $param; @@ -127,8 +126,8 @@ protected function _parseArguments($method, $document) $delegateArgs = array(); foreach (get_object_vars($document) as $argName => $argValue) { if (!isset($params[$argName])) { - throw new UnexpectedValueException(sprintf( - "Received unknown argument %s which is not an argument to %s::%s()", + throw new Exception\UnexpectedValueException(sprintf( + "Received unknown argument %s which is not an argument to %s::%s", $argName, get_class($this->object), $method @@ -136,18 +135,30 @@ protected function _parseArguments($method, $document) } $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) @@ -155,10 +166,14 @@ protected function _assertServiceDelegateHasMethod($method) } } - 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) )); diff --git a/src/Wsdl.php b/src/Wsdl.php index 61918e47..cab0c17b 100644 --- a/src/Wsdl.php +++ b/src/Wsdl.php @@ -11,54 +11,55 @@ 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 - */ class Wsdl { /**#@+ - * XML Namespaces. - */ - 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'; + * 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 DOMDocument DOM document 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 DOMElement + * Types defined on schema + * @var array */ - private $schema = null; + protected $includedTypes = array(); /** - * Types defined on schema - * - * @var array + * @var DOMElement */ - private $includedTypes = array(); + protected $schema = null; /** * Strategy for detection of complex types @@ -66,71 +67,76 @@ class Wsdl protected $strategy = null; /** - * Map of PHP Class names to WSDL QNames. - * - * @var array + * URI where the WSDL will be available + * @var string */ - protected $classMap = array(); + protected $uri; /** - * 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 + * Root XML_Tree_Node + * @var DOMElement WSDL + */ + protected $wsdl; + + /** + * @param string $name Name of the Web Service being Described + * @param string|Uri $uri URI where the WSDL will be available + * @param null|ComplexTypeStrategy $strategy Strategy for detection of complex types + * @param null|array $classMap Map of PHP Class names to WSDL QNames * @throws Exception\RuntimeException */ - public function __construct($name, $uri, ComplexTypeStrategy $strategy = null, array $classMap = array()) - { + public function __construct( + $name, + $uri, + ComplexTypeStrategy $strategy = null, + array $classMap = array() + ) { if ($uri instanceof Uri) { $uri = $uri->toString(); } - $this->classMap = $classMap; - $this->dom = new DOMDocument('1.0', 'utf-8'); - $targetNamespace = $this->escapeUri($uri); - $definitions = $this->dom->createElement('definitions'); - $definitions->setAttributeNS(self::XML_NS_URI, self::XML_NS, self::WSDL_NS_URI); - $definitions->setAttributeNS(self::XML_NS_URI, self::XML_NS . ':' . self::TYPES_NS, $targetNamespace); - $definitions->setAttributeNS(self::XML_NS_URI, self::XML_NS . ':' . self::SOAP_11_NS, self::SOAP_11_NS_URI); - $definitions->setAttributeNS(self::XML_NS_URI, self::XML_NS . ':' . self::SOAP_12_NS, self::SOAP_12_NS_URI); - $definitions->setAttributeNS(self::XML_NS_URI, self::XML_NS . ':' . self::XSD_NS, self::XSD_NS_URI); - $definitions->setAttributeNS(self::XML_NS_URI, self::XML_NS . ':' . self::SOAP_ENC_NS, self::SOAP_ENC_URI); - $definitions->setAttributeNS(self::XML_NS_URI, self::XML_NS . ':' . self::WSDL_NS, self::WSDL_NS_URI); - $definitions->setAttribute('name', $name); - $definitions->setAttribute('targetNamespace', $targetNamespace); - $this->dom->appendChild($definitions); - $this->wsdl = $this->dom->documentElement; - $this->setComplexTypeStrategy($strategy ?: new Wsdl\ComplexTypeStrategy\DefaultComplexType); - } - /** - * URL encode query part of the URI if it is present. - * - * @param string $uri - * @return string - */ - protected function escapeUri($uri) - { - // normalize URL - $uri = urldecode($uri); - if (preg_match('/\?(.+)$/', $uri, $matches)) { - $query = $matches[1]; - $uri = str_replace($query, urlencode($query), $uri); - } + $this->setUri($uri); - return $uri; + $this->classMap = $classMap; + $this->dom = $this->getDOMDocument($name, $this->getUri()); + $this->wsdl = $this->dom->documentElement; + + $this->setComplexTypeStrategy($strategy ?: new Wsdl\ComplexTypeStrategy\DefaultComplexType); } /** - * Convert encoded ampersand back to decoded value, to avoid double encoding by DOMElement::setAttribute() + * Get the wsdl XML document with all namespaces and required attributes * - * @param $uri - * @return mixed + * @param string $uri + * @param string $name + * @return DOMDocument */ - protected function decodeAmpersand($uri) + protected function getDOMDocument($name, $uri = null) { - return str_replace('&', '&', $uri); + $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; } /** @@ -148,7 +154,7 @@ public function getTargetNamespace() } /** - * Get the class map of php to wsdl qname types. + * Get the class map of php to wsdl mappings.. * * @return array */ @@ -158,42 +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(); } - if ($this->wsdl !== null) { - $targetNamespace = $this->escapeUri($uri); - $this->wsdl->setAttributeNS(self::XML_NS_URI, self::XML_NS . ':' . self::TYPES_NS, $targetNamespace); - $this->wsdl->setAttribute('targetNamespace', $targetNamespace); - if ($this->schema !== null) { - $this->schema->setAttribute('targetNamespace', $targetNamespace); + $uri = $this->sanitizeUri($uri); + + $oldUri = $this->uri; + $this->uri = $uri; + + if ($this->dom instanceof DOMDocument ) { + // namespace declarations are NOT true attributes so one must explicitly set on root element + // xmlns:tns = $uri + $this->dom->documentElement->setAttributeNS(self::XML_NS_URI, self::XML_NS . ':' . self::TYPES_NS, $uri); + + $xpath = new DOMXPath($this->dom); + $xpath->registerNamespace('default', self::WSDL_NS_URI); + + $xpath->registerNamespace(self::TYPES_NS, $uri); + $xpath->registerNamespace(self::SOAP_11_NS, self::SOAP_11_NS_URI); + $xpath->registerNamespace(self::SOAP_12_NS, self::SOAP_12_NS_URI); + $xpath->registerNamespace(self::XSD_NS, self::XSD_NS_URI); + $xpath->registerNamespace(self::SOAP_ENC_NS, self::SOAP_ENC_URI); + $xpath->registerNamespace(self::WSDL_NS, self::WSDL_NS_URI); + + // Select only attribute nodes. Data nodes does not contain uri + // except for documentation node but this is for the user to decide. + // This list does not include xmlns:tsn attribute of document root. + // That attribute is changed above. + $attributeNodes = $xpath->query('//attribute::*[contains(., "' . $oldUri . '")]'); + + foreach ($attributeNodes as $node) { + $attributeValue = $this->dom->createTextNode(str_replace($oldUri, $uri, $node->nodeValue)); + $node->replaceChild($attributeValue, $node->childNodes->item(0)); } } return $this; } + /** + * Return WSDL uri + * + * @return string + */ + public function getUri() + { + return $this->uri; + } + + /** + * Function for sanitizing uri + * + * @param string|Uri $uri + * @return string + * @throws Exception\InvalidArgumentException + */ + public function sanitizeUri($uri) + { + if ($uri instanceof Uri) { + $uri = $uri->toString(); + } + + $uri = trim($uri); + $uri = htmlspecialchars($uri, ENT_QUOTES, 'UTF-8', false); + + if (empty($uri)) { + throw new Exception\InvalidArgumentException('Uri contains invalid characters or is empty'); + } + + return $uri; + } + /** * Set a strategy for complex type detection and handling * - * @param ComplexTypeStrategy $strategy - * @return \Zend\Soap\Wsdl + * @param ComplexTypeStrategy $strategy + * @return self */ public function setComplexTypeStrategy(ComplexTypeStrategy $strategy) { @@ -214,165 +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') + * @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 DOMElement 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 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 + * @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 + * @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 DOMElement $binding A binding XML_Tree_Node returned by {@link function addBinding} - * @param string $name - * @param bool|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 bool|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 bool|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} - * @param int $soapVersion SOAP version to be used in binding operation. 1.1 used by default. + * @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); + public function addBindingOperation($binding, $name, $input = false, $output = false, $fault = false, $soapVersion = SOAP_1_1) + { + $operation = $this->dom->createElementNS(self::WSDL_NS_URI, 'operation'); + $binding->appendChild($operation); - $soapNs = $soapVersion == SOAP_1_1 ? self::SOAP_11_NS : self::SOAP_12_NS; - if (is_array($input)) { - $node = $this->dom->createElement('input'); - $soapNode = $this->dom->createElement($soapNs . ':body'); - foreach ($input as $name => $value) { - $soapNode->setAttribute($name, $this->decodeAmpersand($value)); - } - $node->appendChild($soapNode); + $this->setAttribute($operation, 'name', $name); + + if (is_array($input) && !empty($input)) { + $node = $this->dom->createElementNS(self::WSDL_NS_URI, 'input'); $operation->appendChild($node); - } - if (is_array($output)) { - $node = $this->dom->createElement('output'); - $soapNode = $this->dom->createElement($soapNs . ':body'); - foreach ($output as $name => $value) { - $soapNode->setAttribute($name, $this->decodeAmpersand($value)); - } + $soapNode = $this->dom->createElementNS($this->getSoapNamespaceUriByVersion($soapVersion), 'body'); $node->appendChild($soapNode); - $operation->appendChild($node); + + $this->arrayToAttributes($soapNode, $input); } - if (is_array($fault)) { - $node = $this->dom->createElement('fault'); - if (isset($fault['name'])) { - $node->setAttribute('name', $fault['name']); - } - $soapNode = $this->dom->createElement($soapNs . ':fault'); - foreach ($fault as $name => $value) { - $soapNode->setAttribute($name, $this->decodeAmpersand($value)); - } - $node->appendChild($soapNode); + 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); } - $binding->appendChild($operation); + if (is_array($fault) && !empty($fault)) { + $node = $this->dom->createElementNS(self::WSDL_NS_URI, 'fault'); + $operation->appendChild($node); + + $this->arrayToAttributes($node, $fault); + } return $operation; } @@ -380,34 +437,29 @@ public function addBindingOperation( /** * Add a {@link http://www.w3.org/TR/wsdl#_soap:binding SOAP binding} element to a Binding element * - * @param DOMElement $binding A binding XML_Tree_Node returned by {@link function addBinding} - * @param string $style binding style, possible values are "rpc" (the default) and "document" - * @param string $transport Transport method (defaults to HTTP) - * @param int $soapVersion SOAP version to be used in binding. 1.1 used by default. + * @param DOMElement $binding A binding XML_Tree_Node returned by {@link function addBinding} + * @param string $style binding style, possible values are "rpc" (the default) and "document" + * @param string $transport Transport method (defaults to HTTP) + * @param int $soapVersion SOAP version: SOAP_1_1 or SOAP_1_2, default: SOAP_1_1 * @return DOMElement */ - public function addSoapBinding( - $binding, - $style = 'document', - $transport = 'http://schemas.xmlsoap.org/soap/http', - $soapVersion = SOAP_1_1 - ) { - $soapNs = $soapVersion == SOAP_1_1 ? self::SOAP_11_NS : self::SOAP_12_NS; - $soapBinding = $this->dom->createElement($soapNs . ':binding'); + public function addSoapBinding($binding, $style = 'document', $transport = 'http://schemas.xmlsoap.org/soap/http', $soapVersion = SOAP_1_1) + { + $soapBinding = $this->dom->createElementNS($this->getSoapNamespaceUriByVersion($soapVersion), 'binding'); + $binding->appendChild($soapBinding); + $soapBinding->setAttribute('style', $style); $soapBinding->setAttribute('transport', $transport); - $binding->appendChild($soapBinding); - return $soapBinding; } /** * Add a {@link http://www.w3.org/TR/wsdl#_soap:operation SOAP operation} to an operation element * - * @param DOMElement $operation An operation XML_Tree_Node returned by {@link function addBindingOperation} - * @param string $soapAction SOAP Action - * @param int $soapVersion SOAP version to be used in operation. 1.1 used by default. + * @param DOMElement $operation An operation XML_Tree_Node returned by {@link function addBindingOperation} + * @param string $soapAction SOAP Action + * @param int $soapVersion SOAP version: SOAP_1_1 or SOAP_1_2, default: SOAP_1_1 * @return DOMElement */ public function addSoapOperation($operation, $soapAction, $soapVersion = SOAP_1_1) @@ -415,23 +467,22 @@ public function addSoapOperation($operation, $soapAction, $soapVersion = SOAP_1_ if ($soapAction instanceof Uri) { $soapAction = $soapAction->toString(); } - $soapNs = $soapVersion == SOAP_1_1 ? self::SOAP_11_NS : self::SOAP_12_NS; - $soapOperation = $this->dom->createElement($soapNs . ':operation'); - $soapOperation->setAttribute('soapAction', $this->decodeAmpersand($soapAction)); - + $soapOperation = $this->dom->createElementNS($this->getSoapNamespaceUriByVersion($soapVersion), 'operation'); $operation->insertBefore($soapOperation, $operation->firstChild); + $this->setAttributeWithSanitization($soapOperation, 'soapAction', $soapAction); + return $soapOperation; } /** * Add a {@link http://www.w3.org/TR/wsdl#_services service} element to the WSDL * - * @param string $name Service Name - * @param string $portName Name of the port for the service - * @param string $binding Binding for the port - * @param string $location SOAP Address for the service - * @param int $soapVersion SOAP version to be used in service. 1.1 used by default. + * @param string $name Service Name + * @param string $portName Name of the port for the service + * @param string $binding Binding for the port + * @param string $location SOAP Address for the service + * @param int $soapVersion SOAP version: SOAP_1_1 or SOAP_1_2, default: SOAP_1_1 * @return DOMElement The new service's XML_Tree_Node for use with {@link function addDocumentation} */ public function addService($name, $portName, $binding, $location, $soapVersion = SOAP_1_1) @@ -439,22 +490,22 @@ public function addService($name, $portName, $binding, $location, $soapVersion = if ($location instanceof Uri) { $location = $location->toString(); } - $service = $this->dom->createElement('service'); + $service = $this->dom->createElementNS(WSDL::WSDL_NS_URI, 'service'); + $this->wsdl->appendChild($service); + $service->setAttribute('name', $name); - $port = $this->dom->createElement('port'); + + $port = $this->dom->createElementNS(WSDL::WSDL_NS_URI, 'port'); + $service->appendChild($port); + $port->setAttribute('name', $portName); $port->setAttribute('binding', $binding); - $soapNs = $soapVersion == SOAP_1_1 ? self::SOAP_11_NS : self::SOAP_12_NS; - $soapAddress = $this->dom->createElement($soapNs . ':address'); - $soapAddress->setAttribute('location', $this->decodeAmpersand($location)); - + $soapAddress = $this->dom->createElementNS($this->getSoapNamespaceUriByVersion($soapVersion), 'address'); $port->appendChild($soapAddress); - $service->appendChild($port); - - $this->wsdl->appendChild($service); + $this->setAttributeWithSanitization($soapAddress, 'location', $location); return $service; } @@ -465,8 +516,8 @@ public function addService($name, $portName, $binding, $location, $soapVersion = * 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 DOMElement $inputNode 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($inputNode, $documentation) @@ -477,30 +528,29 @@ public function addDocumentation($inputNode, $documentation) $node = $inputNode; } - $doc = $this->dom->createElement('documentation'); - $docCData = $this->dom->createTextNode(str_replace(array("\r\n", "\r"), "\n", $documentation)); - $doc->appendChild($docCData); - + $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 DOMNode $types A DOM Node 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(DOMNode $types) { if ($types instanceof DOMDocument) { - $this->dom->importNode($types->documentElement); - $this->wsdl->appendChild($types->documentElement); - } else { + $dom = $this->dom->importNode($types->documentElement); + $this->wsdl->appendChild($dom); + } elseif ($types instanceof DOMNode || $types instanceof DOMElement || $types instanceof DOMDocumentFragment ) { $dom = $this->dom->importNode($types); $this->wsdl->appendChild($dom); } @@ -509,9 +559,9 @@ public function addTypes(DOMNode $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) { @@ -541,7 +591,6 @@ public function getSchema() if ($this->schema == null) { $this->addSchemaTypeSection(); } - return $this->schema; } @@ -552,6 +601,7 @@ public function getSchema() */ public function toXML() { + $this->dom->normalizeDocument(); return $this->dom->saveXML(); } @@ -562,28 +612,32 @@ public function toXML() */ public function toDomDocument() { + $this->dom->normalizeDocument(); return $this->dom; } /** - * Echo the WSDL as XML to stdout or save the WSDL to a file + * Echo the WSDL as XML * - * @param bool|string $filename Filename to save the output (Optional) + * @param bool $filename * @return bool */ public function dump($filename = false) { + $this->dom->normalizeDocument(); + if (!$filename) { echo $this->toXML(); return true; } + return (bool) file_put_contents($filename, $this->toXML()); } /** * Returns an XSD Type for the given PHP type * - * @param string $type PHP Type to get the XSD type for + * @param string $type PHP Type to get the XSD type for * @return string */ public function getType($type) @@ -591,54 +645,67 @@ 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); - } + } } /** * 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(self::XSD_NS . ':schema'); - $this->schema->setAttribute('targetNamespace', $this->getTargetNamespace()); - $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) @@ -647,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) @@ -674,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); } @@ -681,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.'); } - $elementXml = $this->dom->createElement(self::XSD_NS . ':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(self::XSD_NS . ':complexType'); + $complexType = $this->dom->createElementNS(self::XSD_NS_URI, 'complexType'); if (count($value) > 0) { - $container = $this->dom->createElement(self::XSD_NS . ':' . $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; + } + + /** + * 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); } } - return $elementXml; + } + + /** + * 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; } /** @@ -728,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) @@ -736,6 +894,7 @@ public function addElement($element) $schema = $this->getSchema(); $elementXml = $this->_parseElement($element); $schema->appendChild($elementXml); + return self::TYPES_NS . ':' . $element['name']; } } diff --git a/src/Wsdl/ComplexTypeStrategy/AbstractComplexTypeStrategy.php b/src/Wsdl/ComplexTypeStrategy/AbstractComplexTypeStrategy.php index 1b626f01..36bc8bfc 100644 --- a/src/Wsdl/ComplexTypeStrategy/AbstractComplexTypeStrategy.php +++ b/src/Wsdl/ComplexTypeStrategy/AbstractComplexTypeStrategy.php @@ -9,33 +9,33 @@ namespace Zend\Soap\Wsdl\ComplexTypeStrategy; +use Zend\Soap\Wsdl; + /** - * Abstract class for Zend_Soap_Wsdl_Strategy. + * 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() { @@ -45,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 bca4780d..26365054 100644 --- a/src/Wsdl/ComplexTypeStrategy/AnyType.php +++ b/src/Wsdl/ComplexTypeStrategy/AnyType.php @@ -9,29 +9,27 @@ namespace Zend\Soap\Wsdl\ComplexTypeStrategy; -/** - * Zend_Soap_Wsdl_Strategy_AnyType - */ +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 275ae99d..b9b4a4c7 100644 --- a/src/Wsdl/ComplexTypeStrategy/ArrayOfTypeComplex.php +++ b/src/Wsdl/ComplexTypeStrategy/ArrayOfTypeComplex.php @@ -10,18 +10,17 @@ namespace Zend\Soap\Wsdl\ComplexTypeStrategy; use Zend\Soap\Exception; +use Zend\Soap\Wsdl; -/** - * ArrayOfTypeComplex strategy - */ 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) { @@ -34,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) @@ -59,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); @@ -71,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; } @@ -107,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 eec9b260..0c223bda 100644 --- a/src/Wsdl/ComplexTypeStrategy/ArrayOfTypeSequence.php +++ b/src/Wsdl/ComplexTypeStrategy/ArrayOfTypeSequence.php @@ -9,15 +9,15 @@ namespace Zend\Soap\Wsdl\ComplexTypeStrategy; -/** - * Zend_Soap_Wsdl_Strategy_ArrayOfTypeSequence - */ +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) @@ -26,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); @@ -36,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 @@ -59,7 +63,7 @@ protected function _getTypeBasedOnNestingLevel($singularType, $level) return $this->getContext()->getType($singularType); } - return 'tns:' . str_repeat('ArrayOf', $level) . ucfirst($this->getContext()->translateType($singularType)); + return Wsdl::TYPES_NS . ':' . str_repeat('ArrayOf', $level) . ucfirst($this->getContext()->translateType($singularType)); } /** @@ -77,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) { @@ -90,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) { @@ -106,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/Composite.php b/src/Wsdl/ComplexTypeStrategy/Composite.php index 9939c824..ea8f0a12 100644 --- a/src/Wsdl/ComplexTypeStrategy/Composite.php +++ b/src/Wsdl/ComplexTypeStrategy/Composite.php @@ -13,29 +13,23 @@ use Zend\Soap\Wsdl; use Zend\Soap\Wsdl\ComplexTypeStrategy\ComplexTypeStrategyInterface as ComplexTypeStrategy; -/** - * Zend_Soap_Wsdl_Strategy_Composite - */ 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; @@ -45,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) { @@ -73,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() { @@ -82,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.' ); @@ -94,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) { @@ -107,7 +104,7 @@ public function getStrategyOfType($type) $strategy = new $strategy(); } - if ( !($strategy instanceof ComplexTypeStrategy) ) { + if (!($strategy instanceof ComplexTypeStrategy)) { throw new Exception\InvalidArgumentException(sprintf( 'Strategy for Complex Type "%s" is not a valid strategy object.', $type @@ -117,13 +114,14 @@ public function getStrategyOfType($type) } 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) @@ -135,13 +133,13 @@ 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) ) { + if (!($this->context instanceof Wsdl)) { throw new Exception\InvalidArgumentException(sprintf( 'Cannot add complex type "%s", no context is set for this composite strategy.', $type @@ -150,6 +148,7 @@ public function addComplexType($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 2a10aaf4..779cd2bf 100644 --- a/src/Wsdl/ComplexTypeStrategy/DefaultComplexType.php +++ b/src/Wsdl/ComplexTypeStrategy/DefaultComplexType.php @@ -9,19 +9,18 @@ namespace Zend\Soap\Wsdl\ComplexTypeStrategy; +use ReflectionClass; use Zend\Soap\Exception; +use Zend\Soap\Wsdl; -/** - * Zend_Soap_Wsdl_Strategy_DefaultComplexType - */ class DefaultComplexType extends AbstractComplexTypeStrategy { /** * Add a complex type by recursively using all the class properties fetched via Reflection. * * @param string $type Name of the class to be specified - * @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) { @@ -38,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); @@ -49,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/AutoDiscoverTest.php b/test/AutoDiscoverTest.php index 57eded3d..d02f1ab1 100644 --- a/test/AutoDiscoverTest.php +++ b/test/AutoDiscoverTest.php @@ -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,244 +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() { - $scriptUri = 'http://localhost/my_script.php'; + $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() + { + $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(); } /** @@ -304,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' + ); - $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: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="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(); } /** @@ -404,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()); - $server = $this->createAutodiscoverService(); - $server->addFunction('\ZendTest\Soap\TestAsset\TestFunc'); + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:types/xsd:schema[@targetNamespace="' + . $this->defaultServiceUri . '"]', 'Missing service port 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' + ); - $dom = $server->generate()->toDomDocument(); - $name = "TestService"; + $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' + ); + - $wsdl = $this->sanitizeWsdlXmlOutputForOsCompability($dom->saveXML()); - $this->assertContains('', $wsdl); - $this->assertNotContains('assertValidWSDL($dom); + $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' + ); - $wsdlOutput = $server->toXml(); - $this->assertNotContains($scriptUri, $wsdlOutput); - $this->assertContains("http://example.com/service.php", $wsdlOutput); + $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' + ); + } + } + + + $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'), + ); } /** @@ -589,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'); - $wsdlOutput = $server->toXml(); + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:message[@name="testFuncIn"]' + ); + $this->assertSpecificNodeNumberInXPath( + 1, '//wsdl:message[@name="testFuncOut"]' + ); + - $this->assertEquals(1, substr_count($wsdlOutput, '')); - $this->assertEquals(1, substr_count($wsdlOutput, '')); + $this->assertValidWSDL($this->dom); + $this->testDocumentNodes(); } /** @@ -607,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->assertSpecificNodeNumberInXPath( + 1, '//xsd:complexType[@name="AutoDiscoverTestClass1"]', + 'AutoDiscoverTestClass1 has to be defined once.' ); - $this->assertEquals(1, - substr_count($wsdlOutput, ''), - '\ZendTest\Soap\TestAsset\AutoDiscoverTestClass1 has to be defined once.' + $this->assertSpecificNodeNumberInXPath( + 1, '//xsd:complexType[@name="ArrayOfAutoDiscoverTestClass1"]', + 'AutoDiscoverTestClass1 should be defined once.' ); - $this->assertEquals(1, - substr_count($wsdlOutput, ''), - '\ZendTest\Soap\TestAsset\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->assertEquals(1, substr_count($wsdl, '')); - $this->assertEquals(0, substr_count($wsdl, 'tns:My_Response[]')); + $this->assertSpecificNodeNumberInXPath( + 1, '//xsd:complexType[@name="ArrayOfMyResponse"]' + ); + $this->assertSpecificNodeNumberInXPath( + 0, '//wsdl:part[@type="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(); } /** @@ -682,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( - 'pushOneWay', - $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(); } /** @@ -697,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( - 'ZendTest\Soap\TestAsset\OneWay', - $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(); } /** @@ -714,20 +1432,28 @@ 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(); } /** @@ -735,28 +1461,50 @@ public function testRecursiveWsdlDependencies() */ public function testHandle() { - $scriptUri = 'http://localhost/my_script.php'; + $scriptUri = 'http://localhost/MyService.php'; - $server = $this->createAutodiscoverService(); - $server->setClass('\ZendTest\Soap\TestAsset\Test'); + $this->server->setClass('\ZendTest\Soap\TestAsset\Test'); ob_start(); - $server->handle(); + $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."); } - public function assertWsdlPathExists($xml, $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) { - $doc = new \DOMDocument('UTF-8'); - $doc->loadXML($xml); - $xpath = new \DOMXPath($doc); - $xpath->registerNamespace('wsdl', 'http://schemas.xmlsoap.org/wsdl/'); + $c = count($attributes); - $nodes = $xpath->query($path); + $keys = array_keys($attributes); - $this->assertTrue($nodes->length >= 1, "Could not assert that XML Document contains a node that matches the XPath Expression: " . $path); + 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/ClientTest.php b/test/ClientTest.php index 2c3a7222..c4305336 100644 --- a/test/ClientTest.php +++ b/test/ClientTest.php @@ -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/ServerTest.php b/test/ServerTest.php index db828304..1a127b6b 100644 --- a/test/ServerTest.php +++ b/test/ServerTest.php @@ -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); @@ -648,81 +648,116 @@ public function testHandle() $this->assertEquals($expectedResponse, $server1->handle($request)); } - public function testRegisterFaultException() + /** + * @dataProvider dataProviderForRegisterFaultException + * + * @param string|array $exception + */ + 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()); + } + } } - public function testDeregisterFaultException() + /** + * @dataProvider dataProviderForRegisterFaultException + * + * @param string|array $exception + */ + 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()); } - public function testGetFaultExceptions() + /** + * @dataProvider dataProviderForRegisterFaultException + * + * @param string|array $exception + */ + public function testIsRegisteredAsFaultException($exception) { + $server = new Server(); + $server->registerFaultException($exception); + - $this->assertEquals(array(), $server->getFaultExceptions()); - $server->registerFaultException("Exception"); - $this->assertEquals(array("Exception"), $server->getFaultExceptions()); + if (!is_array($exception)) { + $this->assertTrue($server->isRegisteredAsFaultException($exception)); + } else { + foreach($exception as $row) { + $this->assertTrue($server->isRegisteredAsFaultException($row)); + } + } + } + + /** + * @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()); } /** @@ -731,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() @@ -874,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 index 6e7275df..11af2921 100644 --- a/test/TestAsset/MockCallUserFunc.php +++ b/test/TestAsset/MockCallUserFunc.php @@ -18,7 +18,7 @@ class MockCallUserFunc /** * Whether to mock the call_user_func function. * - * @var boolean + * @var bool */ public static $mock = false; diff --git a/test/TestAsset/commontypes.php b/test/TestAsset/commontypes.php index 0317eb33..1e96fbb2 100644 --- a/test/TestAsset/commontypes.php +++ b/test/TestAsset/commontypes.php @@ -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/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 72405fb1..289951f0 100644 --- a/test/Wsdl/ArrayOfTypeComplexStrategyTest.php +++ b/test/Wsdl/ArrayOfTypeComplexStrategyTest.php @@ -10,10 +10,11 @@ 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 69898f91..3a45834c 100644 --- a/test/Wsdl/ArrayOfTypeSequenceStrategyTest.php +++ b/test/Wsdl/ArrayOfTypeSequenceStrategyTest.php @@ -10,6 +10,8 @@ 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 e9d71476..f239d080 100644 --- a/test/Wsdl/CompositeStrategyTest.php +++ b/test/Wsdl/CompositeStrategyTest.php @@ -8,13 +8,14 @@ * @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,24 +94,29 @@ 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() @@ -108,22 +126,16 @@ public function testCompositeRequiresContextForAddingComplexTypesOtherwiseThrows $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 39479d76..1914ba14 100644 --- a/test/Wsdl/DefaultComplexTypeTest.php +++ b/test/Wsdl/DefaultComplexTypeTest.php @@ -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 2d752a06..d50e0d3e 100644 --- a/test/WsdlTest.php +++ b/test/WsdlTest.php @@ -9,394 +9,467 @@ */ 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() + { + 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) { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); + $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')); + } /** - * @dataProvider ampersandInUrlDataProvider() + * @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) + { + $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')); + + $operationNodes = $this->xpath->query('wsdl:operation[@name="'.$operationName.'"]', $bindingNodes->item(0)); + $this->assertEquals(1, $operationNodes->length, 'Missing operation node in definition.'); + + 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->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 dataProviderForAddBindingOperation() + { + + $enc = 'http://schemas.xmlsoap.org/soap/encoding/'; + + 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'), + + ); + } + + /** + * @dataProvider dataProviderForSoapBindingStyle + */ + public function testAddSoapBinding($style) + { + $this->wsdl->addPortType('myPortType'); + $binding = $this->wsdl->addBinding('MyServiceBinding', 'myPortType'); + + $this->wsdl->addSoapBinding($binding, $style); + + $this->testDocumentNodes(); + + $nodes = $this->xpath->query('//soap:binding'); + + $this->assertGreaterThan(0, $nodes->length); + $this->assertEquals($style, $nodes->item(0)->getAttribute('style')); + } + + public function dataProviderForSoapBindingStyle() + { + return array( + array('document'), + array('rpc'), + ); + } + + /** + * @dataProvider dataProviderForAddSoapOperation + */ + public function testAddSoapOperation($operationUrl) + { + $this->wsdl->addPortType('myPortType'); + $binding = $this->wsdl->addBinding('MyServiceBinding', 'myPortType'); + + $this->wsdl->addSoapOperation($binding, $operationUrl); + + $this->testDocumentNodes(); + + $node = $this->xpath->query('//soap:operation'); + $this->assertGreaterThan(0, $node->length); + $this->assertEquals($operationUrl, $node->item(0)->getAttribute('soapAction')); + } + + public function dataProviderForAddSoapOperation() + { + 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) { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); - $wsdl->addPortType('myPortType'); - $binding = $wsdl->addBinding('MyServiceBinding', 'myPortType'); + $this->wsdl->addPortType('myPortType'); + $binding = $this->wsdl->addBinding('MyServiceBinding', 'myPortType'); - $wsdl->addBindingOperation( + $this->wsdl->addBindingOperation( $binding, 'operation1', array('use' => 'encoded', 'encodingStyle' => $actualUrl), @@ -404,18 +477,10 @@ public function testAddBindingOperationWithAmpersandInUrl($actualUrl, $expectedU array('name' => 'MyFault','use' => 'encoded', 'encodingStyle' => $actualUrl) ); - $expectedXml = '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . ''; - $this->assertContains($expectedXml, $wsdl->toXML()); + $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')); } /** @@ -423,15 +488,16 @@ public function testAddBindingOperationWithAmpersandInUrl($actualUrl, $expectedU */ public function testAddSoapOperationWithAmpersandInUrl($actualUrl, $expectedUrl) { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); + $this->wsdl->addPortType('myPortType'); + $binding = $this->wsdl->addBinding('MyServiceBinding', 'myPortType'); - $wsdl->addPortType('myPortType'); - $binding = $wsdl->addBinding('MyServiceBinding', 'myPortType'); + $this->wsdl->addSoapOperation($binding, $actualUrl); - $wsdl->addSoapOperation($binding, $actualUrl); + $this->testDocumentNodes(); - $expectedXml = ''; - $this->assertContains($expectedXml, $wsdl->toXML()); + $nodes = $this->xpath->query('//wsdl:binding/soap:operation'); + $this->assertGreaterThanOrEqual(1, $nodes->length); + $this->assertEquals($expectedUrl, $nodes->item(0)->getAttribute('soapAction')); } /** @@ -439,19 +505,16 @@ public function testAddSoapOperationWithAmpersandInUrl($actualUrl, $expectedUrl) */ public function testAddServiceWithAmpersandInUrl($actualUrl, $expectedUrl) { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); + $this->wsdl->addPortType('myPortType'); + $this->wsdl->addBinding('MyServiceBinding', 'myPortType'); - $wsdl->addPortType('myPortType'); - $wsdl->addBinding('MyServiceBinding', 'myPortType'); + $this->wsdl->addService('Service1', 'myPortType', 'MyServiceBinding', $actualUrl); - $wsdl->addService('Service1', 'myPortType', 'MyServiceBinding', $actualUrl); + $this->testDocumentNodes(); - $expectedXml = '' - . '' - . '' - . '' - . ''; - $this->assertContains($expectedXml, $wsdl->toXML()); + $nodes = $this->xpath->query('//wsdl:port/soap:address'); + $this->assertGreaterThanOrEqual(1, $nodes->length); + $this->assertEquals($expectedUrl, $nodes->item(0)->getAttribute('location')); } public function ampersandInUrlDataProvider() @@ -472,250 +535,260 @@ public function ampersandInUrlDataProvider() ); } + + /** + * + */ public function testAddDocumentation() { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); + $doc = 'This is a description for Port Type node.'; + $this->wsdl->addDocumentation($this->wsdl, $doc); - $portType = $wsdl->addPortType('myPortType'); + $this->testDocumentNodes(); - $wsdl->addDocumentation($portType, 'This is a description for Port Type node.'); + $nodes = $this->wsdl->toDomDocument()->childNodes; + $this->assertEquals(1, $nodes->length); + $this->assertEquals($doc, $nodes->item(0)->nodeValue); - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($wsdl->toXml()), - '' . - '' - . '' - . 'This is a description for Port Type node.' - . '' - . '' ); } - public function testAddDocumentationToSetInsertsBefore() + public function testAddDocumentationToSomeElmenet() { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); + $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'] = $wsdl->getType('int'); - $messageParts['parameter2'] = $wsdl->getType('string'); - $messageParts['parameter3'] = $wsdl->getType('mixed'); + $messageParts['parameter1'] = $this->wsdl->getType('int'); + $messageParts['parameter2'] = $this->wsdl->getType('string'); + $messageParts['parameter3'] = $this->wsdl->getType('mixed'); - $message = $wsdl->addMessage('myMessage', $messageParts); - $wsdl->addDocumentation($message, "foo"); + $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); - $this->assertEquals( - '' . - '' - . '' - . 'foo' - . '' - . '' - . '' - . '' - . '', - $this->sanitizeWsdlXmlOutputForOsCompability($wsdl->toXml()) - ); } - public function testToXml() + public function testDumpToFile() { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); + $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->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($wsdl->toXml()), - '' . - '' ); + $this->checkXMLContent($fileContent); } - public function testToDomDocument() + public function testDumpToOutput() { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); - $dom = $wsdl->toDomDocument(); + ob_start(); + $dumpStatus = $this->wsdl->dump(); + $screenContent = ob_get_clean(); - $this->assertTrue($dom instanceOf \DOMDocument); + $this->assertTrue($dumpStatus, 'Dump to output failed'); - $this->assertEquals($this->sanitizeWsdlXmlOutputForOsCompability($dom->saveXML()), - '' . - '' ); + $this->checkXMLContent($screenContent); } - public function testDump() + 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); - 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'); + $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 testGetType() { - $wsdl = new Wsdl('MyService', 'http://localhost/MyService.php'); - - $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.'); + $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.'); } public function testGetComplexTypeBasedOnStrategiesBackwardsCompabilityBoolean() { - $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); - -// $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); + $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")); } /** @@ -723,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()); - $wsdl->addComplexType("string[]"); - $wsdl->addComplexType("int[]"); - $wsdl->addComplexType("string[]"); + $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."); + + $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%3Db%26b%3Dc"; - 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_WITHOUT_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_WITHOUT_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/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 +