Added the zend framework 2 library, the path is specified in line no.26 in zend_modul...
[openemr.git] / interface / modules / zend_modules / library / Zend / Soap / AutoDiscover.php
blob82852f84ac48d9e00ac71c311e01a0fb087134bd
1 <?php
2 /**
3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
8 */
10 namespace Zend\Soap;
12 use Zend\Server\Reflection;
13 use Zend\Soap\AutoDiscover\DiscoveryStrategy\DiscoveryStrategyInterface as DiscoveryStrategy;
14 use Zend\Soap\AutoDiscover\DiscoveryStrategy\ReflectionDiscovery;
15 use Zend\Soap\Exception;
16 use Zend\Soap\Wsdl;
17 use Zend\Soap\Wsdl\ComplexTypeStrategy\ComplexTypeStrategyInterface as ComplexTypeStrategy;
18 use Zend\Uri;
20 class AutoDiscover
22 /**
23 * @var string
25 protected $serviceName;
27 /**
28 * @var Reflection
30 protected $reflection = null;
32 /**
33 * Service function names
34 * @var array
36 protected $functions = array();
38 /**
39 * Service class name
40 * @var string
42 protected $class;
44 /**
45 * @var bool
47 protected $strategy;
49 /**
50 * Url where the WSDL file will be available at.
51 * @var WSDL Uri
53 protected $uri;
55 /**
56 * soap:body operation style options
57 * @var array
59 protected $operationBodyStyle = array(
60 'use' => 'encoded',
61 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/"
64 /**
65 * soap:operation style
66 * @var array
68 protected $bindingStyle = array(
69 'style' => 'rpc',
70 'transport' => 'http://schemas.xmlsoap.org/soap/http'
73 /**
74 * Name of the class to handle the WSDL creation.
75 * @var string
77 protected $wsdlClass = 'Zend\Soap\Wsdl';
79 /**
80 * Class Map of PHP to WSDL types.
81 * @var array
83 protected $classMap = array();
85 /**
86 * Discovery strategy for types and other method details.
87 * @var DiscoveryStrategy
89 protected $discoveryStrategy;
91 /**
92 * Constructor
94 * @param null|ComplexTypeStrategy $strategy
95 * @param null|string|Uri\Uri $endpointUri
96 * @param null|string $wsdlClass
97 * @param null|array $classMap
99 public function __construct(
100 ComplexTypeStrategy $strategy = null,
101 $endpointUri = null,
102 $wsdlClass = null,
103 array $classMap = array()
105 $this->reflection = new Reflection();
106 $this->setDiscoveryStrategy(new ReflectionDiscovery());
108 if (null !== $strategy) {
109 $this->setComplexTypeStrategy($strategy);
111 if (null !== $endpointUri) {
112 $this->setUri($endpointUri);
114 if (null !== $wsdlClass) {
115 $this->setWsdlClass($wsdlClass);
117 $this->setClassMap($classMap);
121 * Set the discovery strategy for method type and other information.
123 * @param DiscoveryStrategy $discoveryStrategy
124 * @return self
126 public function setDiscoveryStrategy(DiscoveryStrategy $discoveryStrategy)
128 $this->discoveryStrategy = $discoveryStrategy;
129 return $this;
133 * Get the discovery strategy.
135 * @return DiscoveryStrategy
137 public function getDiscoveryStrategy()
139 return $this->discoveryStrategy;
143 * Get the class map of php to wsdl mappings.
145 * @return array
147 public function getClassMap()
149 return $this->classMap;
153 * Set the class map of php to wsdl mappings.
155 * @param array $classmap
156 * @return self
157 * @throws Exception\InvalidArgumentException
159 public function setClassMap($classMap)
161 if (!is_array($classMap)) {
162 throw new Exception\InvalidArgumentException(sprintf(
163 '%s expects an array; received "%s"',
164 __METHOD__,
165 (is_object($classMap) ? get_class($classMap) : gettype($classMap))
169 $this->classMap = $classMap;
170 return $this;
174 * Set service name
176 * @param string $serviceName
177 * @return self
178 * @throws Exception\InvalidArgumentException
180 public function setServiceName($serviceName)
182 $matches = array();
184 // first character must be letter or underscore {@see http://www.w3.org/TR/wsdl#_document-n}
185 $i = preg_match('/^[a-z\_]/ims', $serviceName, $matches);
186 if ($i != 1) {
187 throw new Exception\InvalidArgumentException('Service Name must start with letter or _');
190 $this->serviceName = $serviceName;
191 return $this;
195 * Get service name
197 * @return string
198 * @throws Exception\RuntimeException
200 public function getServiceName()
202 if (!$this->serviceName) {
203 if ($this->class) {
204 return $this->reflection->reflectClass($this->class)->getShortName();
205 } else {
206 throw new Exception\RuntimeException('No service name given. Call AutoDiscover::setServiceName().');
209 return $this->serviceName;
214 * Set the location at which the WSDL file will be available.
216 * @param Uri\Uri|string $uri
217 * @return self
218 * @throws Exception\InvalidArgumentException
220 public function setUri($uri)
222 if (!is_string($uri) && !($uri instanceof Uri\Uri)) {
223 throw new Exception\InvalidArgumentException(
224 'Argument to \Zend\Soap\AutoDiscover::setUri should be string or \Zend\Uri\Uri instance.'
228 $uri = trim($uri);
229 $uri = htmlspecialchars($uri, ENT_QUOTES, 'UTF-8', false);
231 if (empty($uri)) {
232 throw new Exception\InvalidArgumentException('Uri contains invalid characters or is empty');
235 $this->uri = $uri;
236 return $this;
240 * Return the current Uri that the SOAP WSDL Service will be located at.
242 * @return Uri\Uri
243 * @throws Exception\RuntimeException
245 public function getUri()
247 if ($this->uri === null) {
248 throw new Exception\RuntimeException(
249 'Missing uri. You have to explicitly configure the Endpoint Uri by calling AutoDiscover::setUri().'
252 if (is_string($this->uri)) {
253 $this->uri = Uri\UriFactory::factory($this->uri);
255 return $this->uri;
259 * Set the name of the WSDL handling class.
261 * @param string $wsdlClass
262 * @return self
263 * @throws Exception\InvalidArgumentException
265 public function setWsdlClass($wsdlClass)
267 if (!is_string($wsdlClass) && !is_subclass_of($wsdlClass, '\Zend\Soap\Wsdl')) {
268 throw new Exception\InvalidArgumentException(
269 'No \Zend\Soap\Wsdl subclass given to Zend\Soap\AutoDiscover::setWsdlClass as string.'
273 $this->wsdlClass = $wsdlClass;
274 return $this;
278 * Return the name of the WSDL handling class.
280 * @return string
282 public function getWsdlClass()
284 return $this->wsdlClass;
288 * Set options for all the binding operations soap:body elements.
290 * By default the options are set to 'use' => 'encoded' and
291 * 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/".
293 * @param array $operationStyle
294 * @return self
295 * @throws Exception\InvalidArgumentException
297 public function setOperationBodyStyle(array $operationStyle = array())
299 if (!isset($operationStyle['use'])) {
300 throw new Exception\InvalidArgumentException('Key "use" is required in Operation soap:body style.');
302 $this->operationBodyStyle = $operationStyle;
303 return $this;
307 * Set Binding soap:binding style.
309 * By default 'style' is 'rpc' and 'transport' is 'http://schemas.xmlsoap.org/soap/http'.
311 * @param array $bindingStyle
312 * @return self
314 public function setBindingStyle(array $bindingStyle = array())
316 if (isset($bindingStyle['style'])) {
317 $this->bindingStyle['style'] = $bindingStyle['style'];
319 if (isset($bindingStyle['transport'])) {
320 $this->bindingStyle['transport'] = $bindingStyle['transport'];
322 return $this;
326 * Set the strategy that handles functions and classes that are added AFTER this call.
328 * @param ComplexTypeStrategy $strategy
329 * @return self
331 public function setComplexTypeStrategy(ComplexTypeStrategy $strategy)
333 $this->strategy = $strategy;
334 return $this;
338 * Set the Class the SOAP server will use
340 * @param string $class Class Name
341 * @return self
343 public function setClass($class)
345 $this->class = $class;
346 return $this;
350 * Add a Single or Multiple Functions to the WSDL
352 * @param string $function Function Name
353 * @return self
354 * @throws Exception\InvalidArgumentException
356 public function addFunction($function)
358 if (is_array($function)) {
359 foreach($function as $row) {
360 $this->addFunction($row);
362 } elseif (is_string($function)) {
363 if (function_exists($function)) {
364 $this->functions[] = $function;
365 } else {
366 throw new Exception\InvalidArgumentException(
367 'Argument to Zend\Soap\AutoDiscover::addFunction should be a valid function name.'
371 } else {
372 throw new Exception\InvalidArgumentException(
373 'Argument to Zend\Soap\AutoDiscover::addFunction should be string or array of strings.'
376 return $this;
380 * Generate the WSDL for a service class.
382 * @return Wsdl
384 protected function _generateClass()
386 return $this->_generateWsdl($this->reflection->reflectClass($this->class)->getMethods());
390 * Generate the WSDL for a set of functions.
392 * @return Wsdl
394 protected function _generateFunctions()
396 $methods = array();
397 foreach (array_unique($this->functions) as $func) {
398 $methods[] = $this->reflection->reflectFunction($func);
400 return $this->_generateWsdl($methods);
404 * Generate the WSDL for a set of reflection method instances.
406 * @param array $reflectionMethods
407 * @return Wsdl
409 protected function _generateWsdl(array $reflectionMethods)
411 $uri = $this->getUri();
413 $serviceName = $this->getServiceName();
415 $wsdl = new $this->wsdlClass($serviceName, $uri, $this->strategy, $this->classMap);
417 // The wsdl:types element must precede all other elements (WS-I Basic Profile 1.1 R2023)
418 $wsdl->addSchemaTypeSection();
420 $port = $wsdl->addPortType($serviceName . 'Port');
421 $binding = $wsdl->addBinding($serviceName . 'Binding', Wsdl::TYPES_NS . ':' . $serviceName . 'Port');
423 $wsdl->addSoapBinding($binding, $this->bindingStyle['style'], $this->bindingStyle['transport']);
424 $wsdl->addService($serviceName . 'Service', $serviceName . 'Port', Wsdl::TYPES_NS . ':' . $serviceName . 'Binding', $uri);
426 foreach ($reflectionMethods as $method) {
427 $this->_addFunctionToWsdl($method, $wsdl, $port, $binding);
430 return $wsdl;
434 * Add a function to the WSDL document.
436 * @param $function Reflection\AbstractFunction function to add
437 * @param $wsdl Wsdl WSDL document
438 * @param $port \DOMElement wsdl:portType
439 * @param $binding \DOMElement wsdl:binding
440 * @throws Exception\InvalidArgumentException
442 protected function _addFunctionToWsdl($function, $wsdl, $port, $binding)
444 $uri = $this->getUri();
446 // We only support one prototype: the one with the maximum number of arguments
447 $prototype = null;
448 $maxNumArgumentsOfPrototype = -1;
449 foreach ($function->getPrototypes() as $tmpPrototype) {
450 $numParams = count($tmpPrototype->getParameters());
451 if ($numParams > $maxNumArgumentsOfPrototype) {
452 $maxNumArgumentsOfPrototype = $numParams;
453 $prototype = $tmpPrototype;
456 if ($prototype === null) {
457 throw new Exception\InvalidArgumentException(sprintf(
458 'No prototypes could be found for the "%s" function',
459 $function->getName()
463 $functionName = $wsdl->translateType($function->getName());
465 // Add the input message (parameters)
466 $args = array();
467 if ($this->bindingStyle['style'] == 'document') {
468 // Document style: wrap all parameters in a sequence element
469 $sequence = array();
470 foreach ($prototype->getParameters() as $param) {
471 $sequenceElement = array(
472 'name' => $param->getName(),
473 'type' => $wsdl->getType($this->discoveryStrategy->getFunctionParameterType($param))
475 if ($param->isOptional()) {
476 $sequenceElement['nillable'] = 'true';
478 $sequence[] = $sequenceElement;
481 $element = array(
482 'name' => $functionName,
483 'sequence' => $sequence
486 // Add the wrapper element part, which must be named 'parameters'
487 $args['parameters'] = array('element' => $wsdl->addElement($element));
489 } else {
490 // RPC style: add each parameter as a typed part
491 foreach ($prototype->getParameters() as $param) {
492 $args[$param->getName()] = array(
493 'type' => $wsdl->getType($this->discoveryStrategy->getFunctionParameterType($param))
497 $wsdl->addMessage($functionName . 'In', $args);
499 $isOneWayMessage = $this->discoveryStrategy->isFunctionOneWay($function, $prototype);
501 if ($isOneWayMessage == false) {
502 // Add the output message (return value)
503 $args = array();
504 if ($this->bindingStyle['style'] == 'document') {
505 // Document style: wrap the return value in a sequence element
506 $sequence = array();
507 if ($prototype->getReturnType() != "void") {
508 $sequence[] = array(
509 'name' => $functionName . 'Result',
510 'type' => $wsdl->getType($this->discoveryStrategy->getFunctionReturnType($function, $prototype))
514 $element = array(
515 'name' => $functionName . 'Response',
516 'sequence' => $sequence
519 // Add the wrapper element part, which must be named 'parameters'
520 $args['parameters'] = array('element' => $wsdl->addElement($element));
522 } elseif ($prototype->getReturnType() != "void") {
523 // RPC style: add the return value as a typed part
524 $args['return'] = array(
525 'type' => $wsdl->getType($this->discoveryStrategy->getFunctionReturnType($function, $prototype))
529 $wsdl->addMessage($functionName . 'Out', $args);
532 // Add the portType operation
533 if ($isOneWayMessage == false) {
534 $portOperation = $wsdl->addPortOperation(
535 $port,
536 $functionName,
537 Wsdl::TYPES_NS . ':' . $functionName . 'In', Wsdl::TYPES_NS . ':' . $functionName . 'Out'
539 } else {
540 $portOperation = $wsdl->addPortOperation(
541 $port,
542 $functionName,
543 Wsdl::TYPES_NS . ':' . $functionName . 'In', false
546 $desc = $this->discoveryStrategy->getFunctionDocumentation($function);
548 if (strlen($desc) > 0) {
549 $wsdl->addDocumentation($portOperation, $desc);
552 // When using the RPC style, make sure the operation style includes a 'namespace'
553 // attribute (WS-I Basic Profile 1.1 R2717)
554 $operationBodyStyle = $this->operationBodyStyle;
555 if ($this->bindingStyle['style'] == 'rpc' && !isset($operationBodyStyle['namespace'])) {
556 $operationBodyStyle['namespace'] = '' . $uri;
559 // Add the binding operation
560 if ($isOneWayMessage == false) {
561 $operation = $wsdl->addBindingOperation($binding, $functionName, $operationBodyStyle, $operationBodyStyle);
562 } else {
563 $operation = $wsdl->addBindingOperation($binding, $functionName, $operationBodyStyle);
565 $wsdl->addSoapOperation($operation, $uri . '#' . $functionName);
569 * Generate the WSDL file from the configured input.
571 * @return Wsdl
572 * @throws Exception\RuntimeException
574 public function generate()
576 if ($this->class && $this->functions) {
577 throw new Exception\RuntimeException('Can either dump functions or a class as a service, not both.');
580 if ($this->class) {
581 $wsdl = $this->_generateClass();
582 } else {
583 $wsdl = $this->_generateFunctions();
586 return $wsdl;
590 * Proxy to WSDL dump function
592 * @param string $filename
593 * @return bool
594 * @throws Exception\RuntimeException
596 public function dump($filename)
598 return $this->generate()->dump($filename);
602 * Proxy to WSDL toXml() function
604 * @return string
605 * @throws Exception\RuntimeException
607 public function toXml()
609 return $this->generate()->toXml();
613 * Handle WSDL document.
615 public function handle()
617 header('Content-Type: text/xml');
618 echo $this->toXml();