fix calendar css, take 2. (#213)
[openemr.git] / interface / modules / zend_modules / library / Zend / Soap / Server.php
blobd5e22fb2179ff79dde8851ec314ad11b80b5f959
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-2015 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 SoapServer;
13 use SoapFault;
14 use Traversable;
15 use DOMDocument;
16 use DOMNode;
17 use SimpleXMLElement;
18 use ReflectionClass;
19 use Zend\Server\Server as ZendServerServer;
20 use Zend\Stdlib\ArrayUtils;
22 class Server implements ZendServerServer
24 /**
25 * Actor URI
26 * @var string URI
28 protected $actor;
30 /**
31 * Class registered with this server
32 * @var string
34 protected $class;
36 /**
37 * Server instance
38 * @var SoapServer
40 protected $server = null;
41 /**
42 * Arguments to pass to {@link $class} constructor
43 * @var array
45 protected $classArgs = array();
47 /**
48 * Array of SOAP type => PHP class pairings for handling return/incoming values
49 * @var array
51 protected $classmap;
53 /**
54 * Encoding
55 * @var string
57 protected $encoding;
59 /**
60 * Registered fault exceptions
61 * @var array
63 protected $faultExceptions = array();
65 /**
66 * Container for caught exception during business code execution
67 * @var \Exception
69 protected $caughtException = null;
71 /**
72 * SOAP Server Features
73 * @var int
75 protected $features;
77 /**
78 * Functions registered with this server; may be either an array or the SOAP_FUNCTIONS_ALL constant
79 * @var array|int
81 protected $functions = array();
83 /**
84 * Object registered with this server
86 protected $object;
88 /**
89 * Informs if the soap server is in debug mode
90 * @var bool
92 protected $debug = false;
94 /**
95 * Persistence mode; should be one of the SOAP persistence constants
96 * @var int
98 protected $persistence;
101 * Request XML
102 * @var string
104 protected $request;
107 * Response XML
108 * @var string
110 protected $response;
113 * Flag: whether or not {@link handle()} should return a response instead of automatically emitting it.
114 * @var bool
116 protected $returnResponse = false;
119 * SOAP version to use; SOAP_1_2 by default, to allow processing of headers
120 * @var int
122 protected $soapVersion = SOAP_1_2;
125 * Array of type mappings
126 * @var array
128 protected $typemap;
131 * URI namespace for SOAP server
132 * @var string URI
134 protected $uri;
137 * URI or path to WSDL
138 * @var string
140 protected $wsdl;
143 * WSDL Caching Options of SOAP Server
144 * @var mixed
146 protected $wsdlCache;
149 * Constructor
151 * Sets display_errors INI setting to off (prevent client errors due to bad
152 * XML in response). Registers {@link handlePhpErrors()} as error handler
153 * for E_USER_ERROR.
155 * If $wsdl is provided, it is passed on to {@link setWSDL()}; if any
156 * options are specified, they are passed on to {@link setOptions()}.
158 * @param string $wsdl
159 * @param array $options
160 * @throws Exception\ExtensionNotLoadedException
162 public function __construct($wsdl = null, array $options = null)
164 if (!extension_loaded('soap')) {
165 throw new Exception\ExtensionNotLoadedException('SOAP extension is not loaded.');
168 if (null !== $wsdl) {
169 $this->setWSDL($wsdl);
172 if (null !== $options) {
173 $this->setOptions($options);
178 * Set Options
180 * Allows setting options as an associative array of option => value pairs.
182 * @param array|\Traversable $options
183 * @return self
185 public function setOptions($options)
187 if ($options instanceof Traversable) {
188 $options = ArrayUtils::iteratorToArray($options);
191 foreach ($options as $key => $value) {
192 switch (strtolower($key)) {
193 case 'actor':
194 $this->setActor($value);
195 break;
197 case 'classmap':
198 case 'class_map':
199 $this->setClassmap($value);
200 break;
202 case 'typemap':
203 case 'type_map':
204 $this->setTypemap($value);
205 break;
207 case 'encoding':
208 $this->setEncoding($value);
209 break;
211 case 'soapversion':
212 case 'soap_version':
213 $this->setSoapVersion($value);
214 break;
216 case 'uri':
217 $this->setUri($value);
218 break;
220 case 'wsdl':
221 $this->setWSDL($value);
222 break;
224 case 'cache_wsdl':
225 $this->setWSDLCache($value);
226 break;
228 case 'features':
229 $this->setSoapFeatures($value);
230 break;
232 default:
233 break;
237 return $this;
241 * Return array of options suitable for using with SoapServer constructor
243 * @return array
245 public function getOptions()
247 $options = array();
248 if (null !== $this->actor) {
249 $options['actor'] = $this->getActor();
252 if (null !== $this->classmap) {
253 $options['classmap'] = $this->getClassmap();
256 if (null !== $this->typemap) {
257 $options['typemap'] = $this->getTypemap();
260 if (null !== $this->encoding) {
261 $options['encoding'] = $this->getEncoding();
264 if (null !== $this->soapVersion) {
265 $options['soap_version'] = $this->getSoapVersion();
268 if (null !== $this->uri) {
269 $options['uri'] = $this->getUri();
272 if (null !== $this->features) {
273 $options['features'] = $this->getSoapFeatures();
276 if (null !== $this->wsdlCache) {
277 $options['cache_wsdl'] = $this->getWSDLCache();
280 return $options;
284 * Set encoding
286 * @param string $encoding
287 * @return self
288 * @throws Exception\InvalidArgumentException with invalid encoding argument
290 public function setEncoding($encoding)
292 if (!is_string($encoding)) {
293 throw new Exception\InvalidArgumentException('Invalid encoding specified');
296 $this->encoding = $encoding;
297 return $this;
301 * Get encoding
303 * @return string
305 public function getEncoding()
307 return $this->encoding;
311 * Set SOAP version
313 * @param int $version One of the SOAP_1_1 or SOAP_1_2 constants
314 * @return self
315 * @throws Exception\InvalidArgumentException with invalid soap version argument
317 public function setSoapVersion($version)
319 if (!in_array($version, array(SOAP_1_1, SOAP_1_2))) {
320 throw new Exception\InvalidArgumentException('Invalid soap version specified');
323 $this->soapVersion = $version;
324 return $this;
328 * Get SOAP version
330 * @return int
332 public function getSoapVersion()
334 return $this->soapVersion;
338 * Check for valid URN
340 * @param string $urn
341 * @return true
342 * @throws Exception\InvalidArgumentException on invalid URN
344 public function validateUrn($urn)
346 $scheme = parse_url($urn, PHP_URL_SCHEME);
347 if ($scheme === false || $scheme === null) {
348 throw new Exception\InvalidArgumentException('Invalid URN');
351 return true;
355 * Set actor
357 * Actor is the actor URI for the server.
359 * @param string $actor
360 * @return self
362 public function setActor($actor)
364 $this->validateUrn($actor);
365 $this->actor = $actor;
366 return $this;
370 * Retrieve actor
372 * @return string
374 public function getActor()
376 return $this->actor;
380 * Set URI
382 * URI in SoapServer is actually the target namespace, not a URI; $uri must begin with 'urn:'.
384 * @param string $uri
385 * @return self
387 public function setUri($uri)
389 $this->validateUrn($uri);
390 $this->uri = $uri;
391 return $this;
395 * Retrieve URI
397 * @return string
399 public function getUri()
401 return $this->uri;
405 * Set classmap
407 * @param array $classmap
408 * @return self
409 * @throws Exception\InvalidArgumentException for any invalid class in the class map
411 public function setClassmap($classmap)
413 if (!is_array($classmap)) {
414 throw new Exception\InvalidArgumentException('Classmap must be an array');
416 foreach ($classmap as $class) {
417 if (!class_exists($class)) {
418 throw new Exception\InvalidArgumentException('Invalid class in class map');
422 $this->classmap = $classmap;
423 return $this;
427 * Retrieve classmap
429 * @return mixed
431 public function getClassmap()
433 return $this->classmap;
437 * Set typemap with xml to php type mappings with appropriate validation.
439 * @param array $typeMap
440 * @return self
441 * @throws Exception\InvalidArgumentException
443 public function setTypemap($typeMap)
445 if (!is_array($typeMap)) {
446 throw new Exception\InvalidArgumentException('Typemap must be an array');
449 foreach ($typeMap as $type) {
450 if (!is_callable($type['from_xml'])) {
451 throw new Exception\InvalidArgumentException(sprintf(
452 'Invalid from_xml callback for type: %s',
453 $type['type_name']
456 if (!is_callable($type['to_xml'])) {
457 throw new Exception\InvalidArgumentException('Invalid to_xml callback for type: ' . $type['type_name']);
461 $this->typemap = $typeMap;
462 return $this;
466 * Retrieve typemap
468 * @return array
470 public function getTypemap()
472 return $this->typemap;
476 * Set wsdl
478 * @param string $wsdl URI or path to a WSDL
479 * @return self
481 public function setWSDL($wsdl)
483 $this->wsdl = $wsdl;
484 return $this;
488 * Retrieve wsdl
490 * @return string
492 public function getWSDL()
494 return $this->wsdl;
498 * Set the SOAP Feature options.
500 * @param string|int $feature
501 * @return self
503 public function setSoapFeatures($feature)
505 $this->features = $feature;
506 return $this;
510 * Return current SOAP Features options
512 * @return int
514 public function getSoapFeatures()
516 return $this->features;
520 * Set the SOAP WSDL Caching Options
522 * @param string|int|bool $options
523 * @return self
525 public function setWSDLCache($options)
527 $this->wsdlCache = $options;
528 return $this;
532 * Get current SOAP WSDL Caching option
534 public function getWSDLCache()
536 return $this->wsdlCache;
540 * Attach a function as a server method
542 * @param array|string $function Function name, array of function names to attach,
543 * or SOAP_FUNCTIONS_ALL to attach all functions
544 * @param string $namespace Ignored
545 * @return self
546 * @throws Exception\InvalidArgumentException on invalid functions
548 public function addFunction($function, $namespace = '')
550 // Bail early if set to SOAP_FUNCTIONS_ALL
551 if ($this->functions == SOAP_FUNCTIONS_ALL) {
552 return $this;
555 if (is_array($function)) {
556 foreach ($function as $func) {
557 if (is_string($func) && function_exists($func)) {
558 $this->functions[] = $func;
559 } else {
560 throw new Exception\InvalidArgumentException('One or more invalid functions specified in array');
563 } elseif (is_string($function) && function_exists($function)) {
564 $this->functions[] = $function;
565 } elseif ($function == SOAP_FUNCTIONS_ALL) {
566 $this->functions = SOAP_FUNCTIONS_ALL;
567 } else {
568 throw new Exception\InvalidArgumentException('Invalid function specified');
571 if (is_array($this->functions)) {
572 $this->functions = array_unique($this->functions);
575 return $this;
579 * Attach a class to a server
581 * Accepts a class name to use when handling requests. Any additional
582 * arguments will be passed to that class' constructor when instantiated.
584 * See {@link setObject()} to set pre-configured object instances as request handlers.
586 * @param string|object $class Class name or object instance which executes
587 * SOAP Requests at endpoint.
588 * @param string $namespace
589 * @param null|array $argv
590 * @return self
591 * @throws Exception\InvalidArgumentException if called more than once, or if class does not exist
593 public function setClass($class, $namespace = '', $argv = null)
595 if (isset($this->class)) {
596 throw new Exception\InvalidArgumentException(
597 'A class has already been registered with this soap server instance'
601 if (is_object($class)) {
602 return $this->setObject($class);
605 if (!is_string($class)) {
606 throw new Exception\InvalidArgumentException(sprintf(
607 'Invalid class argument (%s)',
608 gettype($class)
612 if (!class_exists($class)) {
613 throw new Exception\InvalidArgumentException(sprintf(
614 'Class "%s" does not exist',
615 $class
619 $this->class = $class;
620 if (2 < func_num_args()) {
621 $argv = func_get_args();
622 $this->classArgs = array_slice($argv, 2);
625 return $this;
629 * Attach an object to a server
631 * Accepts an instantiated object to use when handling requests.
633 * @param object $object
634 * @return self
635 * @throws Exception\InvalidArgumentException
637 public function setObject($object)
639 if (!is_object($object)) {
640 throw new Exception\InvalidArgumentException(sprintf(
641 'Invalid object argument (%s)',
642 gettype($object)
646 if (isset($this->object)) {
647 throw new Exception\InvalidArgumentException(
648 'An object has already been registered with this soap server instance'
652 $this->object = $object;
653 return $this;
657 * Return a server definition array
659 * Returns a list of all functions registered with {@link addFunction()},
660 * merged with all public methods of the class set with {@link setClass()}
661 * (if any).
663 * @return array
665 public function getFunctions()
667 $functions = array();
668 if (null !== $this->class) {
669 $functions = get_class_methods($this->class);
670 } elseif (null !== $this->object) {
671 $functions = get_class_methods($this->object);
674 return array_merge((array) $this->functions, $functions);
678 * Unimplemented: Load server definition
680 * @param array $definition
681 * @throws Exception\RuntimeException Unimplemented
683 public function loadFunctions($definition)
685 throw new Exception\RuntimeException('Unimplemented method.');
689 * Set server persistence
691 * @param int $mode SOAP_PERSISTENCE_SESSION or SOAP_PERSISTENCE_REQUEST constants
692 * @return self
693 * @throws Exception\InvalidArgumentException
695 public function setPersistence($mode)
697 if (!in_array($mode, array(SOAP_PERSISTENCE_SESSION, SOAP_PERSISTENCE_REQUEST))) {
698 throw new Exception\InvalidArgumentException('Invalid persistence mode specified');
701 $this->persistence = $mode;
702 return $this;
706 * Get server persistence
708 * @return int
710 public function getPersistence()
712 return $this->persistence;
716 * Set request
718 * $request may be any of:
719 * - DOMDocument; if so, then cast to XML
720 * - DOMNode; if so, then grab owner document and cast to XML
721 * - SimpleXMLElement; if so, then cast to XML
722 * - stdClass; if so, calls __toString() and verifies XML
723 * - string; if so, verifies XML
725 * @param DOMDocument|DOMNode|SimpleXMLElement|\stdClass|string $request
726 * @return self
727 * @throws Exception\InvalidArgumentException
729 protected function _setRequest($request)
731 $xml = null;
733 if ($request instanceof DOMDocument) {
734 $xml = $request->saveXML();
735 } elseif ($request instanceof DOMNode) {
736 $xml = $request->ownerDocument->saveXML();
737 } elseif ($request instanceof SimpleXMLElement) {
738 $xml = $request->asXML();
739 } elseif (is_object($request) || is_string($request)) {
740 if (is_object($request)) {
741 $xml = $request->__toString();
742 } else {
743 $xml = $request;
745 $xml = trim($xml);
747 $loadEntities = libxml_disable_entity_loader(true);
749 $dom = new DOMDocument();
750 $loadStatus = $dom->loadXML($xml);
752 libxml_disable_entity_loader($loadEntities);
754 // @todo check libxml errors ? validate document ?
755 if (strlen($xml) == 0 || !$loadStatus) {
756 throw new Exception\InvalidArgumentException('Invalid XML');
759 foreach ($dom->childNodes as $child) {
760 if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
761 throw new Exception\InvalidArgumentException('Invalid XML: Detected use of illegal DOCTYPE');
766 $this->request = $xml;
767 return $this;
771 * Retrieve request XML
773 * @return string
775 public function getLastRequest()
777 return $this->request;
781 * Set return response flag
783 * If true, {@link handle()} will return the response instead of
784 * automatically sending it back to the requesting client.
786 * The response is always available via {@link getResponse()}.
788 * @param bool $flag
789 * @return self
791 public function setReturnResponse($flag = true)
793 $this->returnResponse = (bool) $flag;
794 return $this;
798 * Retrieve return response flag
800 * @return bool
802 public function getReturnResponse()
804 return $this->returnResponse;
808 * Get response XML
810 * @return string
812 public function getResponse()
814 return $this->response;
818 * Get SoapServer object
820 * Uses {@link $wsdl} and return value of {@link getOptions()} to instantiate
821 * SoapServer object, and then registers any functions or class with it, as
822 * well as persistence.
824 * @return SoapServer
826 public function getSoap()
828 if ($this->server instanceof SoapServer) {
829 return $this->server;
832 $options = $this->getOptions();
833 $server = new SoapServer($this->wsdl, $options);
835 if (!empty($this->functions)) {
836 $server->addFunction($this->functions);
839 if (!empty($this->class)) {
840 $args = $this->classArgs;
841 array_unshift($args, $this->class);
842 call_user_func_array(array($server, 'setClass'), $args);
845 if (!empty($this->object)) {
846 $server->setObject($this->object);
849 if (null !== $this->persistence) {
850 $server->setPersistence($this->persistence);
853 $this->server = $server;
854 return $this->server;
858 * Proxy for _getSoap method
859 * @see _getSoap
860 * @return SoapServer the soapServer instance
861 public function getSoap()
863 return $this->_getSoap();
868 * Handle a request
870 * Instantiates SoapServer object with options set in object, and
871 * dispatches its handle() method.
873 * $request may be any of:
874 * - DOMDocument; if so, then cast to XML
875 * - DOMNode; if so, then grab owner document and cast to XML
876 * - SimpleXMLElement; if so, then cast to XML
877 * - stdClass; if so, calls __toString() and verifies XML
878 * - string; if so, verifies XML
880 * If no request is passed, pulls request using php:://input (for
881 * cross-platform compatibility purposes).
883 * @param DOMDocument|DOMNode|SimpleXMLElement|\stdClass|string $request Optional request
884 * @return void|string
886 public function handle($request = null)
888 if (null === $request) {
889 $request = file_get_contents('php://input');
892 // Set Server error handler
893 $displayErrorsOriginalState = $this->_initializeSoapErrorContext();
895 $setRequestException = null;
896 try {
897 $this->_setRequest($request);
898 } catch (\Exception $e) {
899 $setRequestException = $e;
902 $soap = $this->getSoap();
904 $fault = false;
905 $this->response = '';
907 if ($setRequestException instanceof \Exception) {
908 // Create SOAP fault message if we've caught a request exception
909 $fault = $this->fault($setRequestException->getMessage(), 'Sender');
910 } else {
911 ob_start();
912 try {
913 $soap->handle($this->request);
914 } catch (\Exception $e) {
915 $fault = $this->fault($e);
917 $this->response = ob_get_clean();
920 // Restore original error handler
921 restore_error_handler();
922 ini_set('display_errors', (string) $displayErrorsOriginalState);
924 // Send a fault, if we have one
925 if ($fault instanceof SoapFault && !$this->returnResponse) {
926 $soap->fault($fault->faultcode, $fault->getMessage());
928 return;
931 // Echo the response, if we're not returning it
932 if (!$this->returnResponse) {
933 echo $this->response;
935 return;
938 // Return a fault, if we have it
939 if ($fault instanceof SoapFault) {
940 return $fault;
943 // Return the response
944 return $this->response;
948 * Method initializes the error context that the SOAPServer environment will run in.
950 * @return bool display_errors original value
952 protected function _initializeSoapErrorContext()
954 $displayErrorsOriginalState = ini_get('display_errors');
955 ini_set('display_errors', '0');
956 set_error_handler(array($this, 'handlePhpErrors'), E_USER_ERROR);
957 return $displayErrorsOriginalState;
961 * Set the debug mode.
962 * In debug mode, all exceptions are send to the client.
964 * @param bool $debug
965 * @return self
967 public function setDebugMode($debug)
969 $this->debug = $debug;
970 return $this;
974 * Validate and register fault exception
976 * @param string|array $class Exception class or array of exception classes
977 * @return self
978 * @throws Exception\InvalidArgumentException
980 public function registerFaultException($class)
982 if (is_array($class)) {
983 foreach ($class as $row) {
984 $this->registerFaultException($row);
986 } elseif (is_string($class)
987 && class_exists($class)
988 && (is_subclass_of($class, 'Exception') || 'Exception' === $class)
990 $ref = new ReflectionClass($class);
992 $this->faultExceptions[] = $ref->getName();
993 $this->faultExceptions = array_unique($this->faultExceptions);
994 } else {
995 throw new Exception\InvalidArgumentException(
996 'Argument for Zend\Soap\Server::registerFaultException should be'
997 . ' string or array of strings with valid exception names'
1001 return $this;
1005 * Checks if provided fault name is registered as valid in this server.
1007 * @param string $fault Name of a fault class
1008 * @return bool
1010 public function isRegisteredAsFaultException($fault)
1012 if ($this->debug) {
1013 return true;
1016 $ref = new ReflectionClass($fault);
1017 $classNames = $ref->getName();
1018 return in_array($classNames, $this->faultExceptions);
1022 * Deregister a fault exception from the fault exception stack
1024 * @param string $class
1025 * @return bool
1027 public function deregisterFaultException($class)
1029 if (in_array($class, $this->faultExceptions, true)) {
1030 $index = array_search($class, $this->faultExceptions);
1031 unset($this->faultExceptions[$index]);
1032 return true;
1035 return false;
1039 * Return fault exceptions list
1041 * @return array
1043 public function getFaultExceptions()
1045 return $this->faultExceptions;
1049 * Return caught exception during business code execution
1050 * @return null|\Exception caught exception
1052 public function getException()
1054 return $this->caughtException;
1058 * Generate a server fault
1060 * Note that the arguments are reverse to those of SoapFault.
1062 * If an exception is passed as the first argument, its message and code
1063 * will be used to create the fault object if it has been registered via
1064 * {@Link registerFaultException()}.
1066 * @link http://www.w3.org/TR/soap12-part1/#faultcodes
1067 * @param string|\Exception $fault
1068 * @param string $code SOAP Fault Codes
1069 * @return SoapFault
1071 public function fault($fault = null, $code = 'Receiver')
1073 $this->caughtException = (is_string($fault)) ? new \Exception($fault) : $fault;
1075 if ($fault instanceof \Exception) {
1076 if ($this->isRegisteredAsFaultException($fault)) {
1077 $message = $fault->getMessage();
1078 $eCode = $fault->getCode();
1079 $code = empty($eCode) ? $code : $eCode;
1080 } else {
1081 $message = 'Unknown error';
1083 } elseif (is_string($fault)) {
1084 $message = $fault;
1085 } else {
1086 $message = 'Unknown error';
1089 $allowedFaultModes = array(
1090 'VersionMismatch',
1091 'MustUnderstand',
1092 'DataEncodingUnknown',
1093 'Sender',
1094 'Receiver',
1095 'Server'
1097 if (!in_array($code, $allowedFaultModes)) {
1098 $code = 'Receiver';
1101 return new SoapFault($code, $message);
1105 * Throw PHP errors as SoapFaults
1107 * @param int $errno
1108 * @param string $errstr
1109 * @param string $errfile
1110 * @param int $errline
1111 * @param array $errcontext
1112 * @throws SoapFault
1114 public function handlePhpErrors($errno, $errstr, $errfile = null, $errline = null, array $errcontext = null)
1116 throw $this->fault($errstr, 'Receiver');