Spaces:
Sleeping
Sleeping
| /** | |
| * This file contains the code for dealing with WSDL access and services. | |
| * | |
| * PHP versions 4 and 5 | |
| * | |
| * LICENSE: This source file is subject to version 2.02 of the PHP license, | |
| * that is bundled with this package in the file LICENSE, and is available at | |
| * through the world-wide-web at http://www.php.net/license/2_02.txt. If you | |
| * did not receive a copy of the PHP license and are unable to obtain it | |
| * through the world-wide-web, please send a note to license@php.net so we can | |
| * mail you a copy immediately. | |
| * | |
| * @category Web Services | |
| * @package SOAP | |
| * @author Dietrich Ayala <dietrich@ganx4.com> Original Author | |
| * @author Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more | |
| * @author Chuck Hagenbuch <chuck@horde.org> Maintenance | |
| * @author Jan Schneider <jan@horde.org> Maintenance | |
| * @copyright 2003-2005 The PHP Group | |
| * @license http://www.php.net/license/2_02.txt PHP License 2.02 | |
| * @link http://pear.php.net/package/SOAP | |
| */ | |
| require_once 'SOAP/Base.php'; | |
| require_once 'SOAP/Fault.php'; | |
| require_once 'HTTP/Request.php'; | |
| define('WSDL_CACHE_MAX_AGE', 43200); | |
| /** | |
| * This class parses WSDL files, and can be used by SOAP::Client to properly | |
| * register soap values for services. | |
| * | |
| * Originally based on SOAPx4 by Dietrich Ayala | |
| * http://dietrich.ganx4.com/soapx4 | |
| * | |
| * @todo | |
| * - add wsdl caching | |
| * - refactor namespace handling ($namespace/$ns) | |
| * - implement IDL type syntax declaration so we can generate WSDL | |
| * | |
| * @access public | |
| * @package SOAP | |
| * @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates | |
| * @author Dietrich Ayala <dietrich@ganx4.com> Original Author | |
| */ | |
| class SOAP_WSDL extends SOAP_Base | |
| { | |
| var $tns = null; | |
| var $definition = array(); | |
| var $namespaces = array(); | |
| var $ns = array(); | |
| var $xsd = SOAP_XML_SCHEMA_VERSION; | |
| var $complexTypes = array(); | |
| var $elements = array(); | |
| var $messages = array(); | |
| var $portTypes = array(); | |
| var $bindings = array(); | |
| var $imports = array(); | |
| var $services = array(); | |
| var $service = ''; | |
| /** | |
| * URL to WSDL file. | |
| * | |
| * @var string | |
| */ | |
| var $uri; | |
| /** | |
| * Parse documentation in the WSDL? | |
| * | |
| * @var boolean | |
| */ | |
| var $docs; | |
| /** | |
| * Proxy parameters. | |
| * | |
| * @var array | |
| */ | |
| var $proxy; | |
| /** | |
| * Enable tracing in the generated proxy class? | |
| * | |
| * @var boolean | |
| */ | |
| var $trace = false; | |
| /** | |
| * Use WSDL cache? | |
| * | |
| * @var boolean | |
| */ | |
| var $cacheUse; | |
| /** | |
| * WSDL cache directory. | |
| * | |
| * @var string | |
| */ | |
| var $cacheDir; | |
| /** | |
| * Cache maximum lifetime (in seconds). | |
| * | |
| * @var integer | |
| */ | |
| var $cacheMaxAge; | |
| /** | |
| * Class to use for WSDL parsing. Can be overridden for special cases, | |
| * subclasses, etc. | |
| * | |
| * @var string | |
| */ | |
| var $wsdlParserClass = 'SOAP_WSDL_Parser'; | |
| /** | |
| * Reserved PHP keywords. | |
| * | |
| * @link http://www.php.net/manual/en/reserved.php | |
| * | |
| * @var array | |
| */ | |
| var $_reserved = array('abstract', 'and', 'array', 'as', 'break', 'case', | |
| 'catch', 'cfunction', 'class', 'clone', 'const', | |
| 'continue', 'declare', 'default', 'die', 'do', | |
| 'echo', 'else', 'elseif', 'empty', 'enddeclare', | |
| 'endfor', 'endforeach', 'endif', 'endswitch', | |
| 'endwhile', 'eval', 'exception', 'exit', 'extends', | |
| 'final', 'for', 'foreach', 'function', 'global', | |
| 'if', 'implements', 'include', 'include_once', | |
| 'interface', 'isset', 'list', 'new', 'old_function', | |
| 'or', 'php_user_filter', 'print', 'private', | |
| 'protected', 'public', 'require', 'require_once', | |
| 'return', 'static', 'switch', 'this', 'throw', | |
| 'try', 'unset', 'use', 'var', 'while', 'xor'); | |
| /** | |
| * Regular expressions for invalid PHP labels. | |
| * | |
| * @link http://www.php.net/manual/en/language.variables.php. | |
| * | |
| * @var string | |
| */ | |
| var $_invalid = array('/^[^a-zA-Z_\x7f-\xff]/', '/[^a-zA-Z0-9_\x7f-\xff]/'); | |
| /** | |
| * SOAP_WSDL constructor. | |
| * | |
| * @param string $wsdl_uri URL to WSDL file. | |
| * @param array $proxy Options for HTTP_Request class | |
| * @see HTTP_Request. | |
| * @param boolean|string $cacheUse Use WSDL caching? The cache directory | |
| * if a string. | |
| * @param integer $cacheMaxAge Cache maximum lifetime (in seconds). | |
| * @param boolean $docs Parse documentation in the WSDL? | |
| * | |
| * @access public | |
| */ | |
| function SOAP_WSDL($wsdl_uri = false, | |
| $proxy = array(), | |
| $cacheUse = false, | |
| $cacheMaxAge = WSDL_CACHE_MAX_AGE, | |
| $docs = false) | |
| { | |
| parent::SOAP_Base('WSDL'); | |
| $this->uri = $wsdl_uri; | |
| $this->proxy = $proxy; | |
| $this->cacheUse = !empty($cacheUse); | |
| $this->cacheMaxAge = $cacheMaxAge; | |
| $this->docs = $docs; | |
| if (is_string($cacheUse)) { | |
| $this->cacheDir = $cacheUse; | |
| } | |
| if ($wsdl_uri) { | |
| if (!PEAR::isError($this->parseURL($wsdl_uri))) { | |
| reset($this->services); | |
| $this->service = key($this->services); | |
| } | |
| } | |
| } | |
| /** | |
| * @deprecated Use setService(). | |
| */ | |
| function set_service($service) | |
| { | |
| $this->setService($service); | |
| } | |
| /** | |
| * Sets the service currently to be used. | |
| * | |
| * @param string $service An (existing) service name. | |
| */ | |
| function setService($service) | |
| { | |
| if (array_key_exists($service, $this->services)) { | |
| $this->service = $service; | |
| } | |
| } | |
| /** | |
| * Fills the WSDL array tree with data from a WSDL file. | |
| * | |
| * @param string $wsdl_uri URL to WSDL file. | |
| */ | |
| function parseURL($wsdl_uri) | |
| { | |
| $parser = new $this->wsdlParserClass($wsdl_uri, $this, $this->docs); | |
| if ($parser->fault) { | |
| $this->_raiseSoapFault($parser->fault); | |
| } | |
| } | |
| /** | |
| * Fills the WSDL array tree with data from one or more PHP class objects. | |
| * | |
| * @param mixed $wsdl_obj An object or array of objects to add to | |
| * the internal WSDL tree. | |
| * @param string $targetNamespace The target namespace of schema types | |
| * etc. | |
| * @param string $service_name Name of the WSDL service. | |
| * @param string $service_desc Optional description of the WSDL | |
| * service. | |
| */ | |
| function parseObject(&$wsdl_obj, $targetNamespace, $service_name, | |
| $service_desc = '') | |
| { | |
| $parser = new SOAP_WSDL_ObjectParser($wsdl_obj, $this, | |
| $targetNamespace, $service_name, | |
| $service_desc); | |
| if ($parser->fault) { | |
| $this->_raiseSoapFault($parser->fault); | |
| } | |
| } | |
| function getEndpoint($portName) | |
| { | |
| if ($this->_isfault()) { | |
| return $this->_getfault(); | |
| } | |
| return (isset($this->services[$this->service]['ports'][$portName]['address']['location'])) | |
| ? $this->services[$this->service]['ports'][$portName]['address']['location'] | |
| : $this->_raiseSoapFault("No endpoint for port for $portName", $this->uri); | |
| } | |
| function _getPortName($operation, $service) | |
| { | |
| if (isset($this->services[$service]['ports'])) { | |
| $ports = $this->services[$service]['ports']; | |
| foreach ($ports as $port => $portAttrs) { | |
| $type = $ports[$port]['type']; | |
| if ($type == 'soap' && | |
| isset($this->bindings[$portAttrs['binding']]['operations'][$operation])) { | |
| return $port; | |
| } | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * Finds the name of the first port that contains an operation of name | |
| * $operation. Always returns a SOAP portName. | |
| */ | |
| function getPortName($operation, $service = null) | |
| { | |
| if ($this->_isfault()) { | |
| return $this->_getfault(); | |
| } | |
| if (!$service) { | |
| $service = $this->service; | |
| } | |
| if (isset($this->services[$service]['ports'])) { | |
| if ($portName = $this->_getPortName($operation, $service)) { | |
| return $portName; | |
| } | |
| } | |
| // Try any service in the WSDL. | |
| foreach ($this->services as $serviceName => $service) { | |
| if (isset($this->services[$serviceName]['ports'])) { | |
| if ($portName = $this->_getPortName($operation, $serviceName)) { | |
| $this->service = $serviceName; | |
| return $portName; | |
| } | |
| } | |
| } | |
| return $this->_raiseSoapFault("No operation $operation in WSDL.", $this->uri); | |
| } | |
| function getOperationData($portName, $operation) | |
| { | |
| if ($this->_isfault()) { | |
| return $this->_getfault(); | |
| } | |
| if (!isset($this->services[$this->service]['ports'][$portName]['binding']) || | |
| !($binding = $this->services[$this->service]['ports'][$portName]['binding'])) { | |
| return $this->_raiseSoapFault("No binding for port $portName in WSDL.", $this->uri); | |
| } | |
| // Get operation data from binding. | |
| if (is_array($this->bindings[$binding]['operations'][$operation])) { | |
| $opData = $this->bindings[$binding]['operations'][$operation]; | |
| } | |
| // get operation data from porttype | |
| $portType = $this->bindings[$binding]['type']; | |
| if (!$portType) { | |
| return $this->_raiseSoapFault("No port type for binding $binding in WSDL.", $this->uri); | |
| } | |
| if (is_array($type = $this->portTypes[$portType][$operation])) { | |
| if (isset($type['parameterOrder'])) { | |
| $opData['parameterOrder'] = $type['parameterOrder']; | |
| } | |
| $opData['input'] = array_merge($opData['input'], $type['input']); | |
| $opData['output'] = array_merge($opData['output'], $type['output']); | |
| } | |
| if (!$opData) | |
| return $this->_raiseSoapFault("No operation $operation for port $portName in WSDL.", $this->uri); | |
| $opData['parameters'] = false; | |
| if (isset($this->bindings[$binding]['operations'][$operation]['input']['namespace'])) | |
| $opData['namespace'] = $this->bindings[$binding]['operations'][$operation]['input']['namespace']; | |
| // Message data from messages. | |
| $inputMsg = $opData['input']['message']; | |
| if (is_array($this->messages[$inputMsg])) { | |
| foreach ($this->messages[$inputMsg] as $pname => $pattrs) { | |
| if ($opData['style'] == 'document' && | |
| $opData['input']['use'] == 'literal' && | |
| $pname == 'parameters') { | |
| $opData['parameters'] = true; | |
| $opData['namespace'] = $this->namespaces[$pattrs['namespace']]; | |
| $el = $this->elements[$pattrs['namespace']][$pattrs['type']]; | |
| if (isset($el['elements'])) { | |
| foreach ($el['elements'] as $elname => $elattrs) { | |
| $opData['input']['parts'][$elname] = $elattrs; | |
| } | |
| } | |
| } else { | |
| $opData['input']['parts'][$pname] = $pattrs; | |
| } | |
| } | |
| } | |
| $outputMsg = $opData['output']['message']; | |
| if (is_array($this->messages[$outputMsg])) { | |
| foreach ($this->messages[$outputMsg] as $pname => $pattrs) { | |
| if ($opData['style'] == 'document' && | |
| $opData['output']['use'] == 'literal' && | |
| $pname == 'parameters') { | |
| $el = $this->elements[$pattrs['namespace']][$pattrs['type']]; | |
| if (isset($el['elements'])) { | |
| foreach ($el['elements'] as $elname => $elattrs) { | |
| $opData['output']['parts'][$elname] = $elattrs; | |
| } | |
| } | |
| } else { | |
| $opData['output']['parts'][$pname] = $pattrs; | |
| } | |
| } | |
| } | |
| return $opData; | |
| } | |
| function matchMethod(&$operation) | |
| { | |
| if ($this->_isfault()) { | |
| return $this->_getfault(); | |
| } | |
| // Overloading lowercases function names :( | |
| foreach ($this->services[$this->service]['ports'] as $portAttrs) { | |
| foreach (array_keys($this->bindings[$portAttrs['binding']]['operations']) as $op) { | |
| if (strcasecmp($op, $operation) == 0) { | |
| $operation = $op; | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Given a datatype, what function handles the processing? | |
| * | |
| * This is used for doc/literal requests where we receive a datatype, and | |
| * we need to pass it to a method in out server class. | |
| * | |
| * @param string $datatype | |
| * @param string $namespace | |
| * @return string | |
| * @access public | |
| */ | |
| function getDataHandler($datatype, $namespace) | |
| { | |
| // See if we have an element by this name. | |
| if (isset($this->namespaces[$namespace])) { | |
| $namespace = $this->namespaces[$namespace]; | |
| } | |
| if (isset($this->ns[$namespace])) { | |
| $nsp = $this->ns[$namespace]; | |
| //if (!isset($this->elements[$nsp])) | |
| // $nsp = $this->namespaces[$nsp]; | |
| if (isset($this->elements[$nsp][$datatype])) { | |
| $checkmessages = array(); | |
| // Find what messages use this datatype. | |
| foreach ($this->messages as $messagename => $message) { | |
| foreach ($message as $part) { | |
| if ($part['type'] == $datatype) { | |
| $checkmessages[] = $messagename; | |
| break; | |
| } | |
| } | |
| } | |
| // Find the operation that uses this message. | |
| foreach($this->portTypes as $porttype) { | |
| foreach ($porttype as $opname => $opinfo) { | |
| foreach ($checkmessages as $messagename) { | |
| if ($opinfo['input']['message'] == $messagename) { | |
| return $opname; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return null; | |
| } | |
| function getSoapAction($portName, $operation) | |
| { | |
| if ($this->_isfault()) { | |
| return $this->_getfault(); | |
| } | |
| if (!empty($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction'])) { | |
| return $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction']; | |
| } | |
| return false; | |
| } | |
| function getNamespace($portName, $operation) | |
| { | |
| if ($this->_isfault()) { | |
| return $this->_getfault(); | |
| } | |
| if (!empty($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace'])) { | |
| return $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace']; | |
| } | |
| return false; | |
| } | |
| function getNamespaceAttributeName($namespace) | |
| { | |
| /* If it doesn't exist at first, flip the array and check again. */ | |
| if (empty($this->ns[$namespace])) { | |
| $this->ns = array_flip($this->namespaces); | |
| } | |
| /* If it doesn't exist now, add it. */ | |
| if (empty($this->ns[$namespace])) { | |
| return $this->addNamespace($namespace); | |
| } | |
| return $this->ns[$namespace]; | |
| } | |
| function addNamespace($namespace) | |
| { | |
| if (!empty($this->ns[$namespace])) { | |
| return $this->ns[$namespace]; | |
| } | |
| $n = count($this->ns); | |
| $attr = 'ns' . $n; | |
| $this->namespaces['ns' . $n] = $namespace; | |
| $this->ns[$namespace] = $attr; | |
| return $attr; | |
| } | |
| function _validateString($string) | |
| { | |
| return preg_match('/^[\w_:#\/]+$/', $string); | |
| } | |
| function _addArg(&$args, &$argarray, $argname) | |
| { | |
| if ($args) { | |
| $args .= ', '; | |
| } | |
| $args .= '$' . $argname; | |
| if (!$this->_validateString($argname)) { | |
| return; | |
| } | |
| if ($argarray) { | |
| $argarray .= ', '; | |
| } | |
| $argarray .= "'$argname' => $" . $argname; | |
| } | |
| function _elementArg(&$args, &$argarray, &$_argtype, $_argname) | |
| { | |
| $comments = ''; | |
| $el = $this->elements[$_argtype['namespace']][$_argtype['type']]; | |
| $tns = isset($this->ns[$el['namespace']]) | |
| ? $this->ns[$el['namespace']] | |
| : $_argtype['namespace']; | |
| if (!empty($el['complex']) || | |
| (isset($el['type']) && | |
| isset($this->complexTypes[$tns][$el['type']]))) { | |
| // The element is a complex type. | |
| $comments .= " // {$_argtype['type']} is a ComplexType, refer to the WSDL for more info.\n"; | |
| $attrname = "{$_argtype['type']}_attr"; | |
| if (isset($el['type']) && | |
| isset($this->complexTypes[$tns][$el['type']]['attribute'])) { | |
| $comments .= " // {$_argtype['type']} may require attributes, refer to the WSDL for more info.\n"; | |
| } | |
| $comments .= " \${$attrname}['xmlns'] = '{$this->namespaces[$_argtype['namespace']]}';\n"; | |
| $comments .= " \${$_argtype['type']} = new SOAP_Value('{$_argtype['type']}', false, \${$_argtype['type']}, \$$attrname);\n"; | |
| $this->_addArg($args, $argarray, $_argtype['type']); | |
| if (isset($el['type']) && | |
| isset($this->complexTypes[$tns][$el['type']]['attribute'])) { | |
| if ($args) { | |
| $args .= ', '; | |
| } | |
| $args .= '$' . $attrname; | |
| } | |
| } elseif (isset($el['elements'])) { | |
| foreach ($el['elements'] as $ename => $element) { | |
| $comments .= " \$$ename = new SOAP_Value('{{$this->namespaces[$element['namespace']]}}$ename', '" . | |
| (isset($element['type']) ? $element['type'] : false) . | |
| "', \$$ename);\n"; | |
| $this->_addArg($args, $argarray, $ename); | |
| } | |
| } else { | |
| $comments .= " \$$_argname = new SOAP_Value('{{$this->namespaces[$tns]}}$_argname', '{$el['type']}', \$$_argname);\n"; | |
| $this->_addArg($args, $argarray, $_argname); | |
| } | |
| return $comments; | |
| } | |
| function _complexTypeArg(&$args, &$argarray, &$_argtype, $_argname) | |
| { | |
| $comments = ''; | |
| if (isset($this->complexTypes[$_argtype['namespace']][$_argtype['type']])) { | |
| $comments = " // $_argname is a ComplexType {$_argtype['type']},\n" . | |
| " // refer to wsdl for more info\n"; | |
| if (isset($this->complexTypes[$_argtype['namespace']][$_argtype['type']]['attribute'])) { | |
| $comments .= " // $_argname may require attributes, refer to wsdl for more info\n"; | |
| } | |
| $wrapname = '{' . $this->namespaces[$_argtype['namespace']].'}' . $_argtype['type']; | |
| $comments .= " \$$_argname = new SOAP_Value('$_argname', '$wrapname', \$$_argname);\n"; | |
| } | |
| $this->_addArg($args, $argarray, $_argname); | |
| return $comments; | |
| } | |
| /** | |
| * Generates stub code from the WSDL that can be saved to a file or eval'd | |
| * into existence. | |
| */ | |
| function generateProxyCode($port = '', $classname = '') | |
| { | |
| if ($this->_isfault()) { | |
| return $this->_getfault(); | |
| } | |
| $multiport = count($this->services[$this->service]['ports']) > 1; | |
| if (!$port) { | |
| reset($this->services[$this->service]['ports']); | |
| $port = current($this->services[$this->service]['ports']); | |
| } | |
| // XXX currently do not support HTTP ports | |
| if ($port['type'] != 'soap') { | |
| return null; | |
| } | |
| // XXX currentPort is BAD | |
| $clienturl = $port['address']['location']; | |
| if (!$classname) { | |
| if ($multiport || $port) { | |
| $classname = 'WebService_' . $this->service . '_' . $port['name']; | |
| } else { | |
| $classname = 'WebService_' . $this->service; | |
| } | |
| $classname = $this->_sanitize($classname); | |
| } | |
| if (!$this->_validateString($classname)) { | |
| return null; | |
| } | |
| if (is_array($this->proxy) && count($this->proxy)) { | |
| $class = "class $classname extends SOAP_Client\n{\n" . | |
| " function $classname(\$path = '$clienturl')\n {\n" . | |
| " \$this->SOAP_Client(\$path, 0, 0,\n" . | |
| ' array('; | |
| foreach ($this->proxy as $key => $val) { | |
| if (is_array($val)) { | |
| $class .= "'$key' => array("; | |
| foreach ($val as $key2 => $val2) { | |
| $class .= "'$key2' => '$val2', "; | |
| } | |
| $class .= ')'; | |
| } else { | |
| $class .= "'$key' => '$val', "; | |
| } | |
| } | |
| $class .= "));\n }\n"; | |
| $class = str_replace(', ))', '))', $class); | |
| } else { | |
| $class = "class $classname extends SOAP_Client\n{\n" . | |
| " function $classname(\$path = '$clienturl')\n {\n" . | |
| " \$this->SOAP_Client(\$path, 0);\n" . | |
| " }\n"; | |
| } | |
| // Get the binding, from that get the port type. | |
| $primaryBinding = $port['binding']; | |
| $primaryBinding = preg_replace("/^(.*:)/", '', $primaryBinding); | |
| $portType = $this->bindings[$primaryBinding]['type']; | |
| $portType = preg_replace("/^(.*:)/", '', $portType); | |
| $style = $this->bindings[$primaryBinding]['style']; | |
| // XXX currentPortType is BAD | |
| foreach ($this->portTypes[$portType] as $opname => $operation) { | |
| $binding = $this->bindings[$primaryBinding]['operations'][$opname]; | |
| if (isset($binding['soapAction'])) { | |
| $soapaction = $binding['soapAction']; | |
| } else { | |
| $soapaction = null; | |
| } | |
| if (isset($binding['style'])) { | |
| $opstyle = $binding['style']; | |
| } else { | |
| $opstyle = $style; | |
| } | |
| $use = $binding['input']['use']; | |
| if ($use == 'encoded') { | |
| $namespace = $binding['input']['namespace']; | |
| } else { | |
| $bindingType = $this->bindings[$primaryBinding]['type']; | |
| $ns = $this->portTypes[$bindingType][$opname]['input']['namespace']; | |
| $namespace = $this->namespaces[$ns]; | |
| } | |
| $args = ''; | |
| $argarray = ''; | |
| $comments = ''; | |
| $wrappers = ''; | |
| foreach ($operation['input'] as $argname => $argtype) { | |
| if ($argname == 'message') { | |
| foreach ($this->messages[$argtype] as $_argname => $_argtype) { | |
| $_argname = $this->_sanitize($_argname); | |
| if ($opstyle == 'document' && $use == 'literal' && | |
| $_argtype['name'] == 'parameters') { | |
| // The type or element refered to is used for | |
| // parameters. | |
| $elattrs = null; | |
| $el = $this->elements[$_argtype['namespace']][$_argtype['type']]; | |
| if ($el['complex']) { | |
| $namespace = $this->namespaces[$_argtype['namespace']]; | |
| // XXX need to wrap the parameters in a | |
| // SOAP_Value. | |
| } | |
| if (isset($el['elements'])) { | |
| foreach ($el['elements'] as $elname => $elattrs) { | |
| $elname = $this->_sanitize($elname); | |
| // Is the element a complex type? | |
| if (isset($this->complexTypes[$elattrs['namespace']][$elname])) { | |
| $comments .= $this->_complexTypeArg($args, $argarray, $_argtype, $_argname); | |
| } else { | |
| $this->_addArg($args, $argarray, $elname); | |
| } | |
| } | |
| } | |
| if ($el['complex'] && $argarray) { | |
| $wrapname = '{' . $this->namespaces[$_argtype['namespace']].'}' . $el['name']; | |
| $comments .= " \${$el['name']} = new SOAP_Value('$wrapname', false, \$v = array($argarray));\n"; | |
| $argarray = "'{$el['name']}' => \${$el['name']}"; | |
| } | |
| } else { | |
| if (isset($_argtype['element'])) { | |
| // Element argument. | |
| $comments .= $this->_elementArg($args, $argarray, $_argtype, $_argtype['type']); | |
| } else { | |
| // Complex type argument. | |
| $comments .= $this->_complexTypeArg($args, $argarray, $_argtype, $_argname); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // Validate entries. | |
| // Operation names are function names, so try to make sure it's | |
| // legal. This could potentially cause collisions, but let's try | |
| // to make everything callable and see how many problems that | |
| // causes. | |
| $opname_php = $this->_sanitize($opname); | |
| if (!$this->_validateString($opname_php)) { | |
| return null; | |
| } | |
| if ($argarray) { | |
| $argarray = "array($argarray)"; | |
| } else { | |
| $argarray = 'null'; | |
| } | |
| $class .= " function &$opname_php($args)\n {\n$comments$wrappers" . | |
| " \$result = \$this->call('$opname',\n" . | |
| " \$v = $argarray,\n" . | |
| " array('namespace' => '$namespace',\n" . | |
| " 'soapaction' => '$soapaction',\n" . | |
| " 'style' => '$opstyle',\n" . | |
| " 'use' => '$use'" . | |
| ($this->trace ? ",\n 'trace' => true" : '') . "));\n" . | |
| " return \$result;\n" . | |
| " }\n"; | |
| } | |
| $class .= "}\n"; | |
| return $class; | |
| } | |
| function generateAllProxies() | |
| { | |
| $proxycode = ''; | |
| foreach (array_keys($this->services[$this->service]['ports']) as $key) { | |
| $port =& $this->services[$this->service]['ports'][$key]; | |
| $proxycode .= $this->generateProxyCode($port); | |
| } | |
| return $proxycode; | |
| } | |
| function &getProxy($port = '', $name = '') | |
| { | |
| if ($this->_isfault()) { | |
| $fault =& $this->_getfault(); | |
| return $fault; | |
| } | |
| $multiport = count($this->services[$this->service]['ports']) > 1; | |
| if (!$port) { | |
| reset($this->services[$this->service]['ports']); | |
| $port = current($this->services[$this->service]['ports']); | |
| } | |
| if ($multiport || $port) { | |
| $classname = 'WebService_' . $this->service . '_' . $port['name']; | |
| } else { | |
| $classname = 'WebService_' . $this->service; | |
| } | |
| if ($name) { | |
| $classname = $name . '_' . $classname; | |
| } | |
| $classname = $this->_sanitize($classname); | |
| if (!class_exists($classname)) { | |
| $proxy = $this->generateProxyCode($port, $classname); | |
| require_once 'SOAP/Client.php'; | |
| eval($proxy); | |
| } | |
| $proxy = new $classname; | |
| return $proxy; | |
| } | |
| /** | |
| * Sanitizes a SOAP value, method or class name so that it can be used as | |
| * a valid PHP identifier. Invalid characters are converted into | |
| * underscores and reserved words are prefixed with an underscore. | |
| * | |
| * @param string $name The identifier to sanitize. | |
| * | |
| * @return string The sanitized identifier. | |
| */ | |
| function _sanitize($name) | |
| { | |
| $name = preg_replace($this->_invalid, '_', $name); | |
| if (in_array($name, $this->_reserved)) { | |
| $name = '_' . $name; | |
| } | |
| return $name; | |
| } | |
| function &_getComplexTypeForElement($name, $namespace) | |
| { | |
| $t = null; | |
| if (isset($this->ns[$namespace]) && | |
| isset($this->elements[$this->ns[$namespace]][$name]['type'])) { | |
| $type = $this->elements[$this->ns[$namespace]][$name]['type']; | |
| $ns = $this->elements[$this->ns[$namespace]][$name]['namespace']; | |
| if (isset($this->complexTypes[$ns][$type])) { | |
| $t = $this->complexTypes[$ns][$type]; | |
| } | |
| } | |
| return $t; | |
| } | |
| function getComplexTypeNameForElement($name, $namespace) | |
| { | |
| $t = $this->_getComplexTypeForElement($name, $namespace); | |
| if ($t) { | |
| return $t['name']; | |
| } | |
| return null; | |
| } | |
| function getComplexTypeChildType($ns, $name, $child_ns, $child_name) | |
| { | |
| // Is the type an element? | |
| $t = $this->_getComplexTypeForElement($name, $ns); | |
| if ($t) { | |
| // No, get it from complex types directly. | |
| if (isset($t['elements'][$child_name]['type'])) | |
| return $t['elements'][$child_name]['type']; | |
| } elseif (isset($this->ns[$ns]) && | |
| isset($this->elements[$this->ns[$ns]][$name]['complex']) && | |
| $this->elements[$this->ns[$ns]][$name]['complex']) { | |
| // Type is not an element but complex. | |
| return $this->elements[$this->ns[$ns]][$name]['elements'][$child_name]['type']; | |
| } | |
| return null; | |
| } | |
| function getSchemaType($type, $name, $type_namespace) | |
| { | |
| // see if it's a complex type so we can deal properly with | |
| // SOAPENC:arrayType. | |
| if ($name && $type) { | |
| // XXX TODO: | |
| // look up the name in the wsdl and validate the type. | |
| foreach ($this->complexTypes as $types) { | |
| if (isset($types[$type])) { | |
| if (isset($types[$type]['type'])) { | |
| list($arraytype_ns, $arraytype, $array_depth) = isset($types[$type]['arrayType']) | |
| ? $this->_getDeepestArrayType($types[$type]['namespace'], $types[$type]['arrayType']) | |
| : array($this->namespaces[$types[$type]['namespace']], null, 0); | |
| return array($types[$type]['type'], $arraytype, $arraytype_ns, $array_depth); | |
| } | |
| if (isset($types[$type]['arrayType'])) { | |
| list($arraytype_ns, $arraytype, $array_depth) = | |
| $this->_getDeepestArrayType($types[$type]['namespace'], $types[$type]['arrayType']); | |
| return array('Array', $arraytype, $arraytype_ns, $array_depth); | |
| } | |
| if (!empty($types[$type]['elements'][$name])) { | |
| $type = $types[$type]['elements']['type']; | |
| return array($type, null, $this->namespaces[$types[$type]['namespace']], null); | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| if ($type && $type_namespace) { | |
| $arrayType = null; | |
| // XXX TODO: | |
| // this code currently handles only one way of encoding array | |
| // types in wsdl need to do a generalized function to figure out | |
| // complex types | |
| $p = $this->ns[$type_namespace]; | |
| if ($p && !empty($this->complexTypes[$p][$type])) { | |
| if ($arrayType = $this->complexTypes[$p][$type]['arrayType']) { | |
| $type = 'Array'; | |
| } elseif ($this->complexTypes[$p][$type]['order'] == 'sequence' && | |
| array_key_exists('elements', $this->complexTypes[$p][$type])) { | |
| reset($this->complexTypes[$p][$type]['elements']); | |
| // assume an array | |
| if (count($this->complexTypes[$p][$type]['elements']) == 1) { | |
| $arg = current($this->complexTypes[$p][$type]['elements']); | |
| $arrayType = $arg['type']; | |
| $type = 'Array'; | |
| } else { | |
| foreach ($this->complexTypes[$p][$type]['elements'] as $element) { | |
| if ($element['name'] == $type) { | |
| $arrayType = $element['type']; | |
| $type = $element['type']; | |
| } | |
| } | |
| } | |
| } else { | |
| $type = 'Struct'; | |
| } | |
| return array($type, $arrayType, $type_namespace, null); | |
| } | |
| } | |
| return array(null, null, null, null); | |
| } | |
| /** | |
| * Recurse through the WSDL structure looking for the innermost array type | |
| * of multi-dimensional arrays. | |
| * | |
| * Takes a namespace prefix and a type, which can be in the form 'type' or | |
| * 'type[]', and returns the full namespace URI, the type of the most | |
| * deeply nested array type found, and the number of levels of nesting. | |
| * | |
| * @access private | |
| * @return mixed array or nothing | |
| */ | |
| function _getDeepestArrayType($nsPrefix, $arrayType) | |
| { | |
| static $trail = array(); | |
| $arrayType = ereg_replace('\[\]$', '', $arrayType); | |
| // Protect against circular references XXX We really need to remove | |
| // trail from this altogether (it's very inefficient and in the wrong | |
| // place!) and put circular reference checking in when the WSDL info | |
| // is generated in the first place | |
| if (array_search($nsPrefix . ':' . $arrayType, $trail)) { | |
| return array(null, null, -count($trail)); | |
| } | |
| if (array_key_exists($nsPrefix, $this->complexTypes) && | |
| array_key_exists($arrayType, $this->complexTypes[$nsPrefix]) && | |
| array_key_exists('arrayType', $this->complexTypes[$nsPrefix][$arrayType])) { | |
| $trail[] = $nsPrefix . ':' . $arrayType; | |
| $result = $this->_getDeepestArrayType($this->complexTypes[$nsPrefix][$arrayType]['namespace'], | |
| $this->complexTypes[$nsPrefix][$arrayType]['arrayType']); | |
| return array($result[0], $result[1], $result[2] + 1); | |
| } | |
| return array($this->namespaces[$nsPrefix], $arrayType, 0); | |
| } | |
| } | |
| class SOAP_WSDL_Cache extends SOAP_Base | |
| { | |
| /** | |
| * Use WSDL cache? | |
| * | |
| * @var boolean | |
| */ | |
| var $_cacheUse; | |
| /** | |
| * WSDL cache directory. | |
| * | |
| * @var string | |
| */ | |
| var $_cacheDir; | |
| /** | |
| * Cache maximum lifetime (in seconds) | |
| * | |
| * @var integer | |
| */ | |
| var $_cacheMaxAge; | |
| /** | |
| * Constructor. | |
| * | |
| * @param boolean $cashUse Use caching? | |
| * @param integer $cacheMaxAge Cache maximum lifetime (in seconds) | |
| */ | |
| function SOAP_WSDL_Cache($cacheUse = false, | |
| $cacheMaxAge = WSDL_CACHE_MAX_AGE, | |
| $cacheDir = null) | |
| { | |
| parent::SOAP_Base('WSDLCACHE'); | |
| $this->_cacheUse = $cacheUse; | |
| $this->_cacheDir = $cacheDir; | |
| $this->_cacheMaxAge = $cacheMaxAge; | |
| } | |
| /** | |
| * Returns the path to the cache and creates it, if it doesn't exist. | |
| * | |
| * @private | |
| * | |
| * @return string The directory to use for the cache. | |
| */ | |
| function _cacheDir() | |
| { | |
| if (!empty($this->_cacheDir)) { | |
| $dir = $this->_cacheDir; | |
| } else { | |
| $dir = getenv('WSDLCACHE'); | |
| if (empty($dir)) { | |
| $dir = './wsdlcache'; | |
| } | |
| } | |
| @mkdir($dir, 0700); | |
| return $dir; | |
| } | |
| /** | |
| * Retrieves a file from cache if it exists, otherwise retreive from net, | |
| * add to cache, and return from cache. | |
| * | |
| * @param string URL to WSDL | |
| * @param array proxy parameters | |
| * @param int expected MD5 of WSDL URL | |
| * @access public | |
| * @return string data | |
| */ | |
| function get($wsdl_fname, $proxy_params = array(), $cache = 0) | |
| { | |
| $cachename = $md5_wsdl = $file_data = ''; | |
| if ($this->_cacheUse) { | |
| // Try to retrieve WSDL from cache | |
| $cachename = $this->_cacheDir() . '/' . $this->encryptOld($wsdl_fname). ' .wsdl'; | |
| if (file_exists($cachename)) { | |
| $wf = fopen($cachename, 'rb'); | |
| if ($wf) { | |
| // Reading cached file | |
| $file_data = fread($wf, filesize($cachename)); | |
| $md5_wsdl = $this->encryptOld($file_data); | |
| fclose($wf); | |
| } | |
| if ($cache) { | |
| if ($cache != $md5_wsdl) { | |
| return $this->_raiseSoapFault('WSDL Checksum error!', $wsdl_fname); | |
| } | |
| } else { | |
| $fi = stat($cachename); | |
| $cache_mtime = $fi[8]; | |
| //print cache_mtime, time() | |
| if ($cache_mtime + $this->_cacheMaxAge < time()) { | |
| // expired | |
| $md5_wsdl = ''; // refetch | |
| } | |
| } | |
| } | |
| } | |
| if (!$md5_wsdl) { | |
| // Not cached or not using cache. Retrieve WSDL from URL | |
| // is it a local file? | |
| // this section should be replace by curl at some point | |
| if (!preg_match('/^(https?|file):\/\//', $wsdl_fname)) { | |
| if (!file_exists($wsdl_fname)) { | |
| return $this->_raiseSoapFault("Unable to read local WSDL $wsdl_fname", $wsdl_fname); | |
| } | |
| $file_data = file_get_contents($wsdl_fname); | |
| } else { | |
| $uri = explode('?', $wsdl_fname); | |
| $rq = new HTTP_Request($uri[0], $proxy_params); | |
| // the user agent HTTP_Request uses fouls things up | |
| if (isset($uri[1])) { | |
| $rq->addRawQueryString($uri[1]); | |
| } | |
| if (isset($proxy_params['proxy_host']) && | |
| isset($proxy_params['proxy_port']) && | |
| isset($proxy_params['proxy_user']) && | |
| isset($proxy_params['proxy_pass'])) { | |
| $rq->setProxy($proxy_params['proxy_host'], $proxy_params['proxy_port'], | |
| $proxy_params['proxy_user'], $proxy_params['proxy_pass']); | |
| } elseif (isset($proxy_params['proxy_host']) && | |
| isset($proxy_params['proxy_port'])) { | |
| $rq->setProxy($proxy_params['proxy_host'], $proxy_params['proxy_port']); | |
| } | |
| $result = $rq->sendRequest(); | |
| if (PEAR::isError($result)) { | |
| return $this->_raiseSoapFault("Unable to retrieve WSDL $wsdl_fname," . $rq->getResponseCode(), $wsdl_fname); | |
| } | |
| $file_data = $rq->getResponseBody(); | |
| if (!$file_data) { | |
| return $this->_raiseSoapFault("Unable to retrieve WSDL $wsdl_fname, no http body", $wsdl_fname); | |
| } | |
| } | |
| $md5_wsdl = $this->encryptOld($file_data); | |
| if ($this->_cacheUse) { | |
| $fp = fopen($cachename, "wb"); | |
| fwrite($fp, $file_data); | |
| fclose($fp); | |
| } | |
| } | |
| if ($this->_cacheUse && $cache && $cache != $md5_wsdl) { | |
| return $this->_raiseSoapFault("WSDL Checksum error!", $wsdl_fname); | |
| } | |
| return $file_data; | |
| } | |
| public function encryptOld($string) | |
| { | |
| if (!class_exists('G')) { | |
| $realdocuroot = str_replace( '\\', '/', $_SERVER['DOCUMENT_ROOT'] ); | |
| $docuroot = explode( '/', $realdocuroot ); | |
| array_pop( $docuroot ); | |
| $pathhome = implode( '/', $docuroot ) . '/'; | |
| array_pop( $docuroot ); | |
| $pathTrunk = implode( '/', $docuroot ) . '/'; | |
| require_once($pathTrunk.'gulliver/system/class.g.php'); | |
| } | |
| return G::encryptOld($string); | |
| } | |
| } | |
| class SOAP_WSDL_Parser extends SOAP_Base | |
| { | |
| /** | |
| * Define internal arrays of bindings, ports, operations, | |
| * messages, etc. | |
| */ | |
| var $currentMessage; | |
| var $currentOperation; | |
| var $currentPortType; | |
| var $currentBinding; | |
| var $currentPort; | |
| /** | |
| * Parser vars. | |
| */ | |
| var $cache; | |
| var $tns = null; | |
| var $soapns = array('soap'); | |
| var $uri = ''; | |
| var $wsdl = null; | |
| var $status = ''; | |
| var $element_stack = array(); | |
| var $parentElement = ''; | |
| var $schema = ''; | |
| var $schemaStatus = ''; | |
| var $schema_stack = array(); | |
| var $currentComplexType; | |
| var $schema_element_stack = array(); | |
| var $currentElement; | |
| /** | |
| * Constructor. | |
| */ | |
| function SOAP_WSDL_Parser($uri, &$wsdl, $docs = false) | |
| { | |
| parent::SOAP_Base('WSDLPARSER'); | |
| $this->cache = new SOAP_WSDL_Cache($wsdl->cacheUse, | |
| $wsdl->cacheMaxAge, | |
| $wsdl->cacheDir); | |
| $this->uri = $uri; | |
| $this->wsdl = &$wsdl; | |
| $this->docs = $docs; | |
| $this->parse($uri); | |
| } | |
| function parse($uri) | |
| { | |
| // Check whether content has been read. | |
| $fd = $this->cache->get($uri, $this->wsdl->proxy); | |
| if (PEAR::isError($fd)) { | |
| return $this->_raiseSoapFault($fd); | |
| } | |
| // Create an XML parser. | |
| $parser = xml_parser_create(); | |
| xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); | |
| xml_set_object($parser, $this); | |
| xml_set_element_handler($parser, 'startElement', 'endElement'); | |
| if ($this->docs) { | |
| xml_set_character_data_handler($parser, 'characterData'); | |
| } | |
| if (!xml_parse($parser, $fd, true)) { | |
| $detail = sprintf('XML error on line %d: %s', | |
| xml_get_current_line_number($parser), | |
| xml_error_string(xml_get_error_code($parser))); | |
| return $this->_raiseSoapFault("Unable to parse WSDL file $uri\n$detail"); | |
| } | |
| xml_parser_free($parser); | |
| return true; | |
| } | |
| /** | |
| * start-element handler | |
| */ | |
| function startElement($parser, $name, $attrs) | |
| { | |
| // Get element prefix. | |
| $qname = new QName($name); | |
| if ($qname->ns) { | |
| $ns = $qname->ns; | |
| if ($ns && ((!$this->tns && strcasecmp($qname->name, 'definitions') == 0) || $ns == $this->tns)) { | |
| $name = $qname->name; | |
| } | |
| } | |
| $this->currentTag = $qname->name; | |
| $this->parentElement = ''; | |
| $stack_size = count($this->element_stack); | |
| if ($stack_size) { | |
| $this->parentElement = $this->element_stack[$stack_size - 1]; | |
| } | |
| $this->element_stack[] = $this->currentTag; | |
| // Find status, register data. | |
| switch ($this->status) { | |
| case 'types': | |
| // sect 2.2 wsdl:types | |
| // children: xsd:schema | |
| $parent_tag = ''; | |
| $stack_size = count($this->schema_stack); | |
| if ($stack_size) { | |
| $parent_tag = $this->schema_stack[$stack_size - 1]; | |
| } | |
| switch ($qname->name) { | |
| case 'schema': | |
| // No parent should be in the stack. | |
| if (!$parent_tag || $parent_tag == 'types') { | |
| if (array_key_exists('targetNamespace', $attrs)) { | |
| $this->schema = $this->wsdl->getNamespaceAttributeName($attrs['targetNamespace']); | |
| } else { | |
| $this->schema = $this->wsdl->getNamespaceAttributeName($this->wsdl->tns); | |
| } | |
| $this->wsdl->complexTypes[$this->schema] = array(); | |
| $this->wsdl->elements[$this->schema] = array(); | |
| } | |
| break; | |
| case 'complexType': | |
| if ($parent_tag == 'schema') { | |
| $this->currentComplexType = $attrs['name']; | |
| if (!isset($attrs['namespace'])) { | |
| $attrs['namespace'] = $this->schema; | |
| } | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType] = $attrs; | |
| if (array_key_exists('base', $attrs)) { | |
| $qn = new QName($attrs['base']); | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = $qn->name; | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'] = $qn->ns; | |
| } else { | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct'; | |
| } | |
| $this->schemaStatus = 'complexType'; | |
| } else { | |
| $this->wsdl->elements[$this->schema][$this->currentElement]['complex'] = true; | |
| } | |
| break; | |
| case 'element': | |
| if (isset($attrs['type'])) { | |
| $qn = new QName($attrs['type']); | |
| $attrs['type'] = $qn->name; | |
| if ($qn->ns && array_key_exists($qn->ns, $this->wsdl->namespaces)) { | |
| $attrs['namespace'] = $qn->ns; | |
| } | |
| } | |
| $parentElement = ''; | |
| $stack_size = count($this->schema_element_stack); | |
| if ($stack_size > 0) { | |
| $parentElement = $this->schema_element_stack[$stack_size - 1]; | |
| } | |
| if (isset($attrs['ref'])) { | |
| $qn = new QName($attrs['ref']); | |
| $this->currentElement = $qn->name; | |
| } else { | |
| $this->currentElement = $attrs['name']; | |
| } | |
| $this->schema_element_stack[] = $this->currentElement; | |
| if (!isset($attrs['namespace'])) { | |
| $attrs['namespace'] = $this->schema; | |
| } | |
| if ($parent_tag == 'schema') { | |
| $this->wsdl->elements[$this->schema][$this->currentElement] = $attrs; | |
| $this->wsdl->elements[$this->schema][$this->currentElement]['complex'] = false; | |
| $this->schemaStatus = 'element'; | |
| } elseif ($this->currentComplexType) { | |
| // we're inside a complexType | |
| if ((isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order']) && | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] == 'sequence') | |
| && $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] == 'Array') { | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['arrayType'] = isset($attrs['type']) ? $attrs['type'] : null; | |
| } | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements'][$this->currentElement] = $attrs; | |
| } else { | |
| $this->wsdl->elements[$this->schema][$parentElement]['elements'][$this->currentElement] = $attrs; | |
| } | |
| break; | |
| case 'complexContent': | |
| case 'simpleContent': | |
| break; | |
| case 'extension': | |
| case 'restriction': | |
| if ($this->schemaStatus == 'complexType') { | |
| if (!empty($attrs['base'])) { | |
| $qn = new QName($attrs['base']); | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = $qn->name; | |
| // Types that extend from other types aren't | |
| // *of* those types. Reflect this by denoting | |
| // which type they extend. I'm leaving the | |
| // 'type' setting here since I'm not sure what | |
| // removing it might break at the moment. | |
| if ($qname->name == 'extension') { | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['extends'] = $qn->name; | |
| } | |
| } else { | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct'; | |
| } | |
| } | |
| break; | |
| case 'sequence': | |
| if ($this->schemaStatus == 'complexType') { | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name; | |
| if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) { | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array'; | |
| } | |
| } | |
| break; | |
| case 'all': | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name; | |
| if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) { | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct'; | |
| } | |
| break; | |
| case 'choice': | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'] = $qname->name; | |
| if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) { | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array'; | |
| } | |
| case 'attribute': | |
| if ($this->schemaStatus == 'complexType') { | |
| if (isset($attrs['name'])) { | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['attribute'][$attrs['name']] = $attrs; | |
| } else { | |
| if (isset($attrs['ref'])) { | |
| $q = new QName($attrs['ref']); | |
| foreach ($attrs as $k => $v) { | |
| if ($k != 'ref' && strstr($k, $q->name)) { | |
| $vq = new QName($v); | |
| if ($q->name == 'arrayType') { | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType][$q->name] = $vq->name. $vq->arrayInfo; | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Array'; | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'] = $vq->ns; | |
| } else { | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType][$q->name] = $vq->name; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| break; | |
| } | |
| $this->schema_stack[] = $qname->name; | |
| break; | |
| case 'message': | |
| // sect 2.3 wsdl:message child wsdl:part | |
| switch ($qname->name) { | |
| case 'part': | |
| $qn = null; | |
| if (isset($attrs['type'])) { | |
| $qn = new QName($attrs['type']); | |
| } elseif (isset($attrs['element'])) { | |
| $qn = new QName($attrs['element']); | |
| } | |
| if ($qn) { | |
| $attrs['type'] = $qn->name; | |
| $attrs['namespace'] = $qn->ns; | |
| } | |
| $this->wsdl->messages[$this->currentMessage][$attrs['name']] = $attrs; | |
| // error in wsdl | |
| case 'documentation': | |
| break; | |
| default: | |
| break; | |
| } | |
| break; | |
| case 'portType': | |
| // sect 2.4 | |
| switch ($qname->name) { | |
| case 'operation': | |
| // attributes: name | |
| // children: wsdl:input wsdl:output wsdl:fault | |
| $this->currentOperation = $attrs['name']; | |
| $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation] = $attrs; | |
| break; | |
| case 'input': | |
| case 'output': | |
| case 'fault': | |
| // wsdl:input wsdl:output wsdl:fault | |
| // attributes: name message parameterOrder(optional) | |
| if ($this->currentOperation) { | |
| if (isset($this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name])) { | |
| $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name] = array_merge($this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name], $attrs); | |
| } else { | |
| $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name] = $attrs; | |
| } | |
| if (array_key_exists('message', $attrs)) { | |
| $qn = new QName($attrs['message']); | |
| $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]['message'] = $qn->name; | |
| $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]['namespace'] = $qn->ns; | |
| } | |
| } | |
| break; | |
| case 'documentation': | |
| break; | |
| default: | |
| break; | |
| } | |
| break; | |
| case 'binding': | |
| $ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL; | |
| switch ($ns) { | |
| case SCHEMA_SOAP: | |
| case SCHEMA_SOAP12: | |
| // this deals with wsdl section 3 soap binding | |
| switch ($qname->name) { | |
| case 'binding': | |
| // sect 3.3 | |
| // soap:binding, attributes: transport(required), style(optional, default = document) | |
| // if style is missing, it is assumed to be 'document' | |
| if (!isset($attrs['style'])) { | |
| $attrs['style'] = 'document'; | |
| } | |
| $this->wsdl->bindings[$this->currentBinding] = array_merge($this->wsdl->bindings[$this->currentBinding], $attrs); | |
| break; | |
| case 'operation': | |
| // sect 3.4 | |
| // soap:operation, attributes: soapAction(required), style(optional, default = soap:binding:style) | |
| if (!isset($attrs['style'])) { | |
| $attrs['style'] = $this->wsdl->bindings[$this->currentBinding]['style']; | |
| } | |
| if (isset($this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation])) { | |
| $this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation] = array_merge($this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation], $attrs); | |
| } else { | |
| $this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation] = $attrs; | |
| } | |
| break; | |
| case 'body': | |
| // sect 3.5 | |
| // soap:body attributes: | |
| // part - optional. listed parts must appear in body, missing means all parts appear in body | |
| // use - required. encoded|literal | |
| // encodingStyle - optional. space seperated list of encodings (uri's) | |
| $this->wsdl->bindings[$this->currentBinding] | |
| ['operations'][$this->currentOperation][$this->opStatus] = $attrs; | |
| break; | |
| case 'fault': | |
| // sect 3.6 | |
| // soap:fault attributes: name use encodingStyle namespace | |
| $this->wsdl->bindings[$this->currentBinding] | |
| ['operations'][$this->currentOperation][$this->opStatus] = $attrs; | |
| break; | |
| case 'header': | |
| // sect 3.7 | |
| // soap:header attributes: message part use encodingStyle namespace | |
| $this->wsdl->bindings[$this->currentBinding] | |
| ['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs; | |
| break; | |
| case 'headerfault': | |
| // sect 3.7 | |
| // soap:header attributes: message part use encodingStyle namespace | |
| $header = count($this->wsdl->bindings[$this->currentBinding] | |
| ['operations'][$this->currentOperation][$this->opStatus]['headers'])-1; | |
| $this->wsdl->bindings[$this->currentBinding] | |
| ['operations'][$this->currentOperation][$this->opStatus]['headers'][$header]['fault'] = $attrs; | |
| break; | |
| case 'documentation': | |
| break; | |
| default: | |
| // error! not a valid element inside binding | |
| break; | |
| } | |
| break; | |
| case SCHEMA_WSDL: | |
| // XXX verify correct namespace | |
| // for now, default is the 'wsdl' namespace | |
| // other possible namespaces include smtp, http, etc. for alternate bindings | |
| switch ($qname->name) { | |
| case 'operation': | |
| // sect 2.5 | |
| // wsdl:operation attributes: name | |
| $this->currentOperation = $attrs['name']; | |
| break; | |
| case 'output': | |
| case 'input': | |
| case 'fault': | |
| // sect 2.5 | |
| // wsdl:input attributes: name | |
| $this->opStatus = $qname->name; | |
| break; | |
| case 'documentation': | |
| break; | |
| default: | |
| break; | |
| } | |
| break; | |
| case SCHEMA_WSDL_HTTP: | |
| switch ($qname->name) { | |
| case 'binding': | |
| // sect 4.4 | |
| // http:binding attributes: verb | |
| // parent: wsdl:binding | |
| $this->wsdl->bindings[$this->currentBinding] = array_merge($this->wsdl->bindings[$this->currentBinding], $attrs); | |
| break; | |
| case 'operation': | |
| // sect 4.5 | |
| // http:operation attributes: location | |
| // parent: wsdl:operation | |
| $this->wsdl->bindings[$this->currentBinding]['operations'] | |
| [$this->currentOperation] = $attrs; | |
| break; | |
| case 'urlEncoded': | |
| // sect 4.6 | |
| // http:urlEncoded attributes: location | |
| // parent: wsdl:input wsdl:output etc. | |
| $this->wsdl->bindings[$this->currentBinding]['operations'][$this->opStatus] | |
| [$this->currentOperation]['uri'] = 'urlEncoded'; | |
| break; | |
| case 'urlReplacement': | |
| // sect 4.7 | |
| // http:urlReplacement attributes: location | |
| // parent: wsdl:input wsdl:output etc. | |
| $this->wsdl->bindings[$this->currentBinding]['operations'][$this->opStatus] | |
| [$this->currentOperation]['uri'] = 'urlReplacement'; | |
| break; | |
| case 'documentation': | |
| break; | |
| default: | |
| // error | |
| break; | |
| } | |
| case SCHEMA_MIME: | |
| // sect 5 | |
| // all mime parts are children of wsdl:input, wsdl:output, etc. | |
| // unsuported as of yet | |
| switch ($qname->name) { | |
| case 'content': | |
| // sect 5.3 mime:content | |
| // <mime:content part="nmtoken"? type="string"?/> | |
| // part attribute only required if content is child of multipart related, | |
| // it contains the name of the part | |
| // type attribute contains the mime type | |
| case 'multipartRelated': | |
| // sect 5.4 mime:multipartRelated | |
| case 'part': | |
| case 'mimeXml': | |
| // sect 5.6 mime:mimeXml | |
| // <mime:mimeXml part="nmtoken"?/> | |
| // | |
| case 'documentation': | |
| break; | |
| default: | |
| // error | |
| break; | |
| } | |
| case SCHEMA_DIME: | |
| // DIME is defined in: | |
| // http://gotdotnet.com/team/xml_wsspecs/dime/WSDL-Extension-for-DIME.htm | |
| // all DIME parts are children of wsdl:input, wsdl:output, etc. | |
| // unsuported as of yet | |
| switch ($qname->name) { | |
| case 'message': | |
| // sect 4.1 dime:message | |
| // appears in binding section | |
| $this->wsdl->bindings[$this->currentBinding]['dime'] = $attrs; | |
| break; | |
| default: | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| break; | |
| case 'service': | |
| $ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL; | |
| switch ($qname->name) { | |
| case 'port': | |
| // sect 2.6 wsdl:port attributes: name binding | |
| $this->currentPort = $attrs['name']; | |
| $this->wsdl->services[$this->currentService]['ports'][$this->currentPort] = $attrs; | |
| // XXX hack to deal with binding namespaces | |
| $qn = new QName($attrs['binding']); | |
| $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['binding'] = $qn->name; | |
| $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['namespace'] = $qn->ns; | |
| break; | |
| case 'address': | |
| $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['address'] = $attrs; | |
| // what TYPE of port is it? SOAP or HTTP? | |
| $ns = $qname->ns ? $this->wsdl->namespaces[$qname->ns] : SCHEMA_WSDL; | |
| switch ($ns) { | |
| case SCHEMA_WSDL_HTTP: | |
| $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='http'; | |
| break; | |
| case SCHEMA_SOAP: | |
| $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='soap'; | |
| break; | |
| default: | |
| // Shouldn't happen, we'll assume SOAP. | |
| $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='soap'; | |
| } | |
| break; | |
| case 'documentation': | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| // Top level elements found under wsdl:definitions. | |
| switch ($qname->name) { | |
| case 'import': | |
| // sect 2.1.1 wsdl:import attributes: namespace location | |
| if ((isset($attrs['location']) || isset($attrs['schemaLocation'])) && | |
| !isset($this->wsdl->imports[$attrs['namespace']])) { | |
| $uri = isset($attrs['location']) ? $attrs['location'] : $attrs['schemaLocation']; | |
| $location = @parse_url($uri); | |
| if (!isset($location['scheme'])) { | |
| $base = @parse_url($this->uri); | |
| $uri = $this->mergeUrl($base, $uri); | |
| } | |
| $this->wsdl->imports[$attrs['namespace']] = $attrs; | |
| $import_parser_class = get_class($this); | |
| $import_parser = new $import_parser_class($uri, $this->wsdl, $this->docs); | |
| if ($import_parser->fault) { | |
| unset($this->wsdl->imports[$attrs['namespace']]); | |
| return false; | |
| } | |
| $this->currentImport = $attrs['namespace']; | |
| } | |
| // Continue on to the 'types' case - lack of break; is | |
| // intentional. | |
| case 'types': | |
| // sect 2.2 wsdl:types | |
| $this->status = 'types'; | |
| break; | |
| case 'schema': | |
| // We can hit this at the top level if we've been asked to | |
| // import an XSD file. | |
| if (!empty($attrs['targetNamespace'])) { | |
| $this->schema = $this->wsdl->getNamespaceAttributeName($attrs['targetNamespace']); | |
| } else { | |
| $this->schema = $this->wsdl->getNamespaceAttributeName($this->wsdl->tns); | |
| } | |
| $this->wsdl->complexTypes[$this->schema] = array(); | |
| $this->wsdl->elements[$this->schema] = array(); | |
| $this->schema_stack[] = $qname->name; | |
| $this->status = 'types'; | |
| break; | |
| case 'message': | |
| // sect 2.3 wsdl:message attributes: name children:wsdl:part | |
| $this->status = 'message'; | |
| if (isset($attrs['name'])) { | |
| $this->currentMessage = $attrs['name']; | |
| $this->wsdl->messages[$this->currentMessage] = array(); | |
| } | |
| break; | |
| case 'portType': | |
| // sect 2.4 wsdl:portType | |
| // attributes: name | |
| // children: wsdl:operation | |
| $this->status = 'portType'; | |
| $this->currentPortType = $attrs['name']; | |
| $this->wsdl->portTypes[$this->currentPortType] = array(); | |
| break; | |
| case 'binding': | |
| // sect 2.5 wsdl:binding attributes: name type | |
| // children: wsdl:operation soap:binding http:binding | |
| if ($qname->ns && $qname->ns != $this->tns) { | |
| break; | |
| } | |
| $this->status = 'binding'; | |
| $this->currentBinding = $attrs['name']; | |
| $qn = new QName($attrs['type']); | |
| $this->wsdl->bindings[$this->currentBinding]['type'] = $qn->name; | |
| $this->wsdl->bindings[$this->currentBinding]['namespace'] = $qn->ns; | |
| break; | |
| case 'service': | |
| // sect 2.7 wsdl:service attributes: name children: ports | |
| $this->currentService = $attrs['name']; | |
| $this->wsdl->services[$this->currentService]['ports'] = array(); | |
| $this->status = 'service'; | |
| break; | |
| case 'definitions': | |
| // sec 2.1 wsdl:definitions | |
| // attributes: name targetNamespace xmlns:* | |
| // children: wsdl:import wsdl:types wsdl:message wsdl:portType wsdl:binding wsdl:service | |
| $this->wsdl->definition = $attrs; | |
| foreach ($attrs as $key => $value) { | |
| if (strstr($key, 'xmlns:') !== false) { | |
| $qn = new QName($key); | |
| // XXX need to refactor ns handling. | |
| $this->wsdl->namespaces[$qn->name] = $value; | |
| $this->wsdl->ns[$value] = $qn->name; | |
| if ($key == 'targetNamespace' || | |
| strcasecmp($value,SOAP_SCHEMA) == 0) { | |
| $this->soapns[] = $qn->name; | |
| } else { | |
| if (in_array($value, $this->_XMLSchema)) { | |
| $this->wsdl->xsd = $value; | |
| } | |
| } | |
| } | |
| } | |
| if (isset($ns) && $ns) { | |
| $namespace = 'xmlns:' . $ns; | |
| if (!$this->wsdl->definition[$namespace]) { | |
| return $this->_raiseSoapFault("parse error, no namespace for $namespace", $this->uri); | |
| } | |
| $this->tns = $ns; | |
| } | |
| break; | |
| } | |
| } | |
| /** | |
| * end-element handler. | |
| */ | |
| function endElement($parser, $name) | |
| { | |
| $stacksize = count($this->element_stack); | |
| if ($stacksize) { | |
| if ($this->element_stack[$stacksize - 1] == 'definitions') { | |
| $this->status = ''; | |
| } | |
| array_pop($this->element_stack); | |
| } | |
| if (stristr($name, 'schema')) { | |
| array_pop($this->schema_stack); | |
| $this->schema = ''; | |
| } | |
| if ($this->schema) { | |
| array_pop($this->schema_stack); | |
| if (count($this->schema_stack) <= 1) { | |
| /* Correct the type for sequences with multiple | |
| * elements. */ | |
| if (isset($this->currentComplexType) && isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type']) | |
| && $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] == 'Array' | |
| && array_key_exists('elements', $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]) | |
| && count($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements']) > 1) { | |
| $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'] = 'Struct'; | |
| } | |
| } | |
| if (stristr($name, 'complexType')) { | |
| $this->currentComplexType = ''; | |
| if (count($this->schema_element_stack)) { | |
| $this->currentElement = array_pop($this->schema_element_stack); | |
| } else { | |
| $this->currentElement = ''; | |
| } | |
| } elseif (stristr($name, 'element')) { | |
| if (count($this->schema_element_stack)) { | |
| $this->currentElement = array_pop($this->schema_element_stack); | |
| } else { | |
| $this->currentElement = ''; | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Element content handler. | |
| */ | |
| function characterData($parser, $data) | |
| { | |
| // Store the documentation in the WSDL file. | |
| if ($this->currentTag == 'documentation') { | |
| $data = trim(preg_replace('/\s+/', ' ', $data)); | |
| if (!strlen($data)) { | |
| return; | |
| } | |
| switch ($this->status) { | |
| case 'service': | |
| $ptr =& $this->wsdl->services[$this->currentService]; | |
| break; | |
| case 'portType': | |
| $ptr =& $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation]; | |
| break; | |
| case 'binding': | |
| $ptr =& $this->wsdl->bindings[$this->currentBinding]; | |
| break; | |
| case 'message': | |
| $ptr =& $this->wsdl->messages[$this->currentMessage]; | |
| break; | |
| case 'operation': | |
| break; | |
| case 'types': | |
| if (isset($this->currentComplexType) && | |
| isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType])) { | |
| if ($this->currentElement) { | |
| $ptr =& $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements'][$this->currentElement]; | |
| } else { | |
| $ptr =& $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]; | |
| } | |
| } | |
| break; | |
| } | |
| if (isset($ptr)) { | |
| if (!isset($ptr['documentation'])) { | |
| $ptr['documentation'] = ''; | |
| } else { | |
| $ptr['documentation'] .= ' '; | |
| } | |
| $ptr['documentation'] .= $data; | |
| } | |
| } | |
| } | |
| /** | |
| * $parsed is an array returned by parse_url(). | |
| * | |
| * @access private | |
| */ | |
| function mergeUrl($parsed, $path) | |
| { | |
| if (!is_array($parsed)) { | |
| return false; | |
| } | |
| $uri = ''; | |
| if (!empty($parsed['scheme'])) { | |
| $sep = (strtolower($parsed['scheme']) == 'mailto' ? ':' : '://'); | |
| $uri = $parsed['scheme'] . $sep; | |
| } | |
| if (isset($parsed['pass'])) { | |
| $uri .= "$parsed[user]:$parsed[pass]@"; | |
| } elseif (isset($parsed['user'])) { | |
| $uri .= "$parsed[user]@"; | |
| } | |
| if (isset($parsed['host'])) { | |
| $uri .= $parsed['host']; | |
| } | |
| if (isset($parsed['port'])) { | |
| $uri .= ":$parsed[port]"; | |
| } | |
| if ($path[0] != '/' && isset($parsed['path'])) { | |
| if ($parsed['path'][strlen($parsed['path']) - 1] != '/') { | |
| $path = dirname($parsed['path']) . '/' . $path; | |
| } else { | |
| $path = $parsed['path'] . $path; | |
| } | |
| $path = $this->_normalize($path); | |
| } | |
| $sep = $path[0] == '/' ? '' : '/'; | |
| $uri .= $sep . $path; | |
| return $uri; | |
| } | |
| function _normalize($path_str) | |
| { | |
| $pwd = ''; | |
| $strArr = preg_split('/(\/)/', $path_str, -1, PREG_SPLIT_NO_EMPTY); | |
| $pwdArr = ''; | |
| $j = 0; | |
| for ($i = 0; $i < count($strArr); $i++) { | |
| if ($strArr[$i] != ' ..') { | |
| if ($strArr[$i] != ' .') { | |
| $pwdArr[$j] = $strArr[$i]; | |
| $j++; | |
| } | |
| } else { | |
| array_pop($pwdArr); | |
| $j--; | |
| } | |
| } | |
| $pStr = implode('/', $pwdArr); | |
| $pwd = (strlen($pStr) > 0) ? ('/' . $pStr) : '/'; | |
| return $pwd; | |
| } | |
| } | |
| /** | |
| * Parses the types and methods used in web service objects into the internal | |
| * data structures used by SOAP_WSDL. | |
| * | |
| * Assumes the SOAP_WSDL class is unpopulated to start with. | |
| * | |
| * @author Chris Coe <info@intelligentstreaming.com> | |
| */ | |
| class SOAP_WSDL_ObjectParser extends SOAP_Base | |
| { | |
| /** | |
| * Target namespace for the WSDL document will have the following | |
| * prefix. | |
| */ | |
| var $tnsPrefix = 'tns'; | |
| /** | |
| * Reference to the SOAP_WSDL object to populate. | |
| */ | |
| var $wsdl = null; | |
| /** | |
| * Constructor. | |
| * | |
| * @param object|array $objects Reference to the object or array of | |
| * objects to parse. | |
| * @param SOAP_WSDL $wsdl Reference to the SOAP_WSDL object to | |
| * populate. | |
| * @param string $targetNamespace The target namespace of schema types | |
| * etc. | |
| * @param string $service_name Name of the WSDL <service>. | |
| * @param string $service_desc Optional description of the WSDL | |
| * <service>. | |
| */ | |
| function SOAP_WSDL_ObjectParser(&$objects, &$wsdl, $targetNamespace, | |
| $service_name, $service_desc = '') | |
| { | |
| parent::SOAP_Base('WSDLOBJECTPARSER'); | |
| $this->wsdl = &$wsdl; | |
| // Set up the SOAP_WSDL object | |
| $this->_initialise($service_name); | |
| // Parse each web service object | |
| $wsdl_ref = (is_array($objects)? $objects : array(&$objects)); | |
| foreach ($wsdl_ref as $ref_item) { | |
| if (!is_object($ref_item)) { | |
| $this->_raiseSoapFault('Invalid web service object passed to object parser'); | |
| continue; | |
| } | |
| if (!$this->_parse($ref_item, $targetNamespace, $service_name)) { | |
| break; | |
| } | |
| } | |
| // Build bindings from abstract data. | |
| if ($this->fault == null) { | |
| $this->_generateBindingsAndServices($targetNamespace, $service_name, $service_desc); | |
| } | |
| } | |
| /** | |
| * Initialise the SOAP_WSDL tree (destructive). | |
| * | |
| * If the object has already been initialised, the only effect | |
| * will be to change the tns namespace to the new service name. | |
| * | |
| * @param $service_name Name of the WSDL <service> | |
| * @access private | |
| */ | |
| function _initialise($service_name) | |
| { | |
| // Set up the basic namespaces that all WSDL definitions use. | |
| $this->wsdl->namespaces['wsdl'] = SCHEMA_WSDL; // WSDL language | |
| $this->wsdl->namespaces['soap'] = SCHEMA_SOAP; // WSDL SOAP bindings | |
| $this->wsdl->namespaces[$this->tnsPrefix] = 'urn:' . $service_name; // Target namespace | |
| $this->wsdl->namespaces['xsd'] = array_search('xsd', $this->_namespaces); // XML Schema | |
| $this->wsdl->namespaces['SOAP-ENC'] = array_search('SOAP-ENC', $this->_namespaces); // SOAP types | |
| // XXX Refactor $namespace/$ns for Shane :-) | |
| unset($this->wsdl->ns['urn:' . $service_name]); | |
| $this->wsdl->ns += array_flip($this->wsdl->namespaces); | |
| // Imports are not implemented in WSDL generation from classes. | |
| // *** <wsdl:import> *** | |
| } | |
| /** | |
| * Parser - takes a single object to add to tree (non-destructive). | |
| * | |
| * @access private | |
| * | |
| * @param object $object Reference to the object to parse. | |
| * @param string $schemaNamespace | |
| * @param string $service_name Name of the WSDL <service>. | |
| */ | |
| function _parse(&$object, $schemaNamespace, $service_name) | |
| { | |
| // Create namespace prefix for the schema | |
| list($schPrefix,) = $this->_getTypeNs('{' . $schemaNamespace . '}'); | |
| // Parse all the types defined by the object in whatever | |
| // schema language we are using (currently __typedef arrays) | |
| // *** <wsdl:types> *** | |
| foreach ($object->__typedef as $typeName => $typeValue) { | |
| // Get/create namespace definition | |
| list($nsPrefix, $typeName) = $this->_getTypeNs($typeName); | |
| // Create type definition | |
| $this->wsdl->complexTypes[$schPrefix][$typeName] = array('name' => $typeName); | |
| $thisType =& $this->wsdl->complexTypes[$schPrefix][$typeName]; | |
| // According to Dmitri's documentation, __typedef comes in two | |
| // flavors: | |
| // Array = array(array("item" => "value")) | |
| // Struct = array("item1" => "value1", "item2" => "value2", ...) | |
| if (is_array($typeValue)) { | |
| if (is_array(current($typeValue)) && count($typeValue) == 1 | |
| && count(current($typeValue)) == 1) { | |
| // It's an array | |
| $thisType['type'] = 'Array'; | |
| $nsType = current(current($typeValue)); | |
| list($nsPrefix, $typeName) = $this->_getTypeNs($nsType); | |
| $thisType['namespace'] = $nsPrefix; | |
| $thisType['arrayType'] = $typeName . '[]'; | |
| } elseif (!is_array(current($typeValue))) { | |
| // It's a struct | |
| $thisType['type'] = 'Struct'; | |
| $thisType['order'] = 'all'; | |
| $thisType['namespace'] = $nsPrefix; | |
| $thisType['elements'] = array(); | |
| foreach ($typeValue as $elementName => $elementType) { | |
| list($nsPrefix, $typeName) = $this->_getTypeNs($elementType); | |
| $thisType['elements'][$elementName]['name'] = $elementName; | |
| $thisType['elements'][$elementName]['type'] = $typeName; | |
| $thisType['elements'][$elementName]['namespace'] = $nsPrefix; | |
| } | |
| } else { | |
| // It's erroneous | |
| return $this->_raiseSoapFault("The type definition for $nsPrefix:$typeName is invalid.", 'urn:' . get_class($object)); | |
| } | |
| } else { | |
| // It's erroneous | |
| return $this->_raiseSoapFault("The type definition for $nsPrefix:$typeName is invalid.", 'urn:' . get_class($object)); | |
| } | |
| } | |
| // Create an empty element array with the target namespace | |
| // prefix, to match the results of WSDL parsing. | |
| $this->wsdl->elements[$schPrefix] = array(); | |
| // Populate tree with message information | |
| // *** <wsdl:message> *** | |
| foreach ($object->__dispatch_map as $operationName => $messages) { | |
| foreach ($messages as $messageType => $messageParts) { | |
| unset($thisMessage); | |
| switch ($messageType) { | |
| case 'in': | |
| $this->wsdl->messages[$operationName . 'Request'] = array(); | |
| $thisMessage =& $this->wsdl->messages[$operationName . 'Request']; | |
| break; | |
| case 'out': | |
| $this->wsdl->messages[$operationName . 'Response'] = array(); | |
| $thisMessage =& $this->wsdl->messages[$operationName . 'Response']; | |
| break; | |
| case 'alias': | |
| // Do nothing | |
| break; | |
| default: | |
| // Error condition | |
| break; | |
| } | |
| if (isset($thisMessage)) { | |
| foreach ($messageParts as $partName => $partType) { | |
| list ($nsPrefix, $typeName) = $this->_getTypeNs($partType); | |
| $thisMessage[$partName] = array( | |
| 'name' => $partName, | |
| 'type' => $typeName, | |
| 'namespace' => $nsPrefix | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| // Populate tree with portType information | |
| // XXX Current implementation only supports one portType that | |
| // encompasses all of the operations available. | |
| // *** <wsdl:portType> *** | |
| if (!isset($this->wsdl->portTypes[$service_name . 'Port'])) { | |
| $this->wsdl->portTypes[$service_name . 'Port'] = array(); | |
| } | |
| $thisPortType =& $this->wsdl->portTypes[$service_name . 'Port']; | |
| foreach ($object->__dispatch_map as $operationName => $messages) { | |
| $thisPortType[$operationName] = array('name' => $operationName); | |
| foreach ($messages as $messageType => $messageParts) { | |
| switch ($messageType) { | |
| case 'in': | |
| $thisPortType[$operationName]['input'] = array( | |
| 'message' => $operationName . 'Request', | |
| 'namespace' => $this->tnsPrefix); | |
| break; | |
| case 'out': | |
| $thisPortType[$operationName]['output'] = array( | |
| 'message' => $operationName . 'Response', | |
| 'namespace' => $this->tnsPrefix); | |
| break; | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| /** | |
| * Takes all the abstract WSDL data and builds concrete bindings and | |
| * services (destructive). | |
| * | |
| * @access private | |
| * @todo Current implementation discards $service_desc. | |
| * | |
| * @param string $schemaNamespace Namespace for types etc. | |
| * @param string $service_name Name of the WSDL <service>. | |
| * @param string $service_desc Optional description of the WSDL | |
| * <service>. | |
| */ | |
| function _generateBindingsAndServices($schemaNamespace, $service_name, | |
| $service_desc = '') | |
| { | |
| // Populate tree with bindings information | |
| // XXX Current implementation only supports one binding that | |
| // matches the single portType and all of its operations. | |
| // XXX Is this the correct use of $schemaNamespace here? | |
| // *** <wsdl:binding> *** | |
| $this->wsdl->bindings[$service_name . 'Binding'] = array( | |
| 'type' => $service_name . 'Port', | |
| 'namespace' => $this->tnsPrefix, | |
| 'style' => 'rpc', | |
| 'transport' => SCHEMA_SOAP_HTTP, | |
| 'operations' => array()); | |
| $thisBinding =& $this->wsdl->bindings[$service_name . 'Binding']; | |
| foreach ($this->wsdl->portTypes[$service_name . 'Port'] as $operationName => $operationData) { | |
| $thisBinding['operations'][$operationName] = array( | |
| 'soapAction' => $schemaNamespace . '#' . $operationName, | |
| 'style' => $thisBinding['style']); | |
| foreach (array('input', 'output') as $messageType) | |
| if (isset($operationData[$messageType])) { | |
| $thisBinding['operations'][$operationName][$messageType] = array( | |
| 'use' => 'encoded', | |
| 'namespace' => $schemaNamespace, | |
| 'encodingStyle' => SOAP_SCHEMA_ENCODING); | |
| } | |
| } | |
| // Populate tree with service information | |
| // XXX Current implementation supports one service which groups | |
| // all of the ports together, one port per binding | |
| // *** <wsdl:service> *** | |
| $this->wsdl->services[$service_name . 'Service'] = array('ports' => array()); | |
| $thisService =& $this->wsdl->services[$service_name . 'Service']['ports']; | |
| $https = (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on')) || | |
| getenv('SSL_PROTOCOL_VERSION'); | |
| foreach ($this->wsdl->bindings as $bindingName => $bindingData) { | |
| $thisService[$bindingData['type']] = array( | |
| 'name' => $bindingData['type'], | |
| 'binding' => $bindingName, | |
| 'namespace' => $this->tnsPrefix, | |
| 'address' => array('location' => | |
| ($https ? 'https://' : 'http://') . | |
| $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'] . | |
| (isset($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '')), | |
| 'type' => 'soap'); | |
| } | |
| // Set service | |
| $this->wsdl->set_service($service_name . 'Service'); | |
| $this->wsdl->uri = $this->wsdl->namespaces[$this->tnsPrefix]; | |
| // Create WSDL definition | |
| // *** <wsdl:definitions> *** | |
| $this->wsdl->definition = array( | |
| 'name' => $service_name, | |
| 'targetNamespace' => $this->wsdl->namespaces[$this->tnsPrefix], | |
| 'xmlns' => SCHEMA_WSDL); | |
| foreach ($this->wsdl->namespaces as $nsPrefix => $namespace) { | |
| $this->wsdl->definition['xmlns:' . $nsPrefix] = $namespace; | |
| } | |
| } | |
| /** | |
| * This function is adapted from Dmitri V's implementation of | |
| * DISCO/WSDL generation. It separates namespace from type name in | |
| * a __typedef key and creates a new namespace entry in the WSDL | |
| * structure if the namespace has not been used before. The | |
| * namespace prefix and type name are returned. If no namespace is | |
| * specified, xsd is assumed. | |
| * | |
| * We will not need this function anymore once __typedef is | |
| * eliminated. | |
| */ | |
| function _getTypeNs($type) | |
| { | |
| preg_match_all('/\{(.*)\}/sm', $type, $m); | |
| if (!empty($m[1][0])) { | |
| if (!isset($this->wsdl->ns[$m[1][0]])) { | |
| $ns_pref = 'ns' . count($this->wsdl->namespaces); | |
| $this->wsdl->ns[$m[1][0]] = $ns_pref; | |
| $this->wsdl->namespaces[$ns_pref] = $m[1][0]; | |
| } | |
| $typens = $this->wsdl->ns[$m[1][0]]; | |
| $type = str_replace($m[0][0], '', $type); | |
| } else { | |
| $typens = 'xsd'; | |
| } | |
| return array($typens, $type); | |
| } | |
| } | |