SOAP
[ class tree: SOAP ] [ index: SOAP ] [ all elements ]

Source for file WSDL.php

Documentation is available at WSDL.php

  1. <?php
  2. /**
  3.  * This file contains the code for dealing with WSDL access and services.
  4.  *
  5.  * PHP versions 4 and 5
  6.  *
  7.  * LICENSE: This source file is subject to version 2.02 of the PHP license,
  8.  * that is bundled with this package in the file LICENSE, and is available at
  9.  * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
  10.  * did not receive a copy of the PHP license and are unable to obtain it
  11.  * through the world-wide-web, please send a note to license@php.net so we can
  12.  * mail you a copy immediately.
  13.  *
  14.  * @category   Web Services
  15.  * @package    SOAP
  16.  * @author     Dietrich Ayala <dietrich@ganx4.com> Original Author
  17.  * @author     Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more
  18.  * @author     Chuck Hagenbuch <chuck@horde.org>   Maintenance
  19.  * @author     Jan Schneider <jan@horde.org>       Maintenance
  20.  * @copyright  2003-2005 The PHP Group
  21.  * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
  22.  * @link       http://pear.php.net/package/SOAP
  23.  */
  24.  
  25. require_once 'SOAP/Base.php';
  26. require_once 'SOAP/Fault.php';
  27. require_once 'HTTP/Request.php';
  28.  
  29. define('WSDL_CACHE_MAX_AGE'43200);
  30.  
  31. /**
  32.  * This class parses WSDL files, and can be used by SOAP::Client to properly
  33.  * register soap values for services.
  34.  *
  35.  * Originally based on SOAPx4 by Dietrich Ayala
  36.  * http://dietrich.ganx4.com/soapx4
  37.  *
  38.  * @todo
  39.  *  - refactor namespace handling ($namespace/$ns)
  40.  *  - implement IDL type syntax declaration so we can generate WSDL
  41.  *
  42.  * @access public
  43.  * @package SOAP
  44.  * @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
  45.  * @author Dietrich Ayala <dietrich@ganx4.com> Original Author
  46.  */
  47. class SOAP_WSDL extends SOAP_Base
  48. {
  49.     var $tns = null;
  50.     var $definition = array();
  51.     var $namespaces = array();
  52.     var $ns = array();
  53.     var $xsd = SOAP_XML_SCHEMA_VERSION;
  54.     var $complexTypes = array();
  55.     var $elements = array();
  56.     var $messages = array();
  57.     var $portTypes = array();
  58.     var $bindings = array();
  59.     var $imports = array();
  60.     var $services = array();
  61.     var $service = '';
  62.  
  63.     /**
  64.      * URL to WSDL file.
  65.      *
  66.      * @var string 
  67.      */
  68.     var $uri;
  69.  
  70.     /**
  71.      * Parse documentation in the WSDL?
  72.      *
  73.      * @var boolean 
  74.      */
  75.     var $docs;
  76.  
  77.     /**
  78.      * Proxy parameters.
  79.      *
  80.      * @var array 
  81.      */
  82.     var $proxy;
  83.  
  84.     /**
  85.      * Enable tracing in the generated proxy class?
  86.      *
  87.      * @var boolean 
  88.      */
  89.     var $trace = false;
  90.  
  91.     /**
  92.      * Use WSDL cache?
  93.      *
  94.      * @var boolean 
  95.      */
  96.     var $cacheUse;
  97.  
  98.     /**
  99.      * WSDL cache directory.
  100.      *
  101.      * @var string 
  102.      */
  103.     var $cacheDir;
  104.  
  105.     /**
  106.      * Cache maximum lifetime (in seconds).
  107.      *
  108.      * @var integer 
  109.      */
  110.     var $cacheMaxAge;
  111.  
  112.     /**
  113.      * Class to use for WSDL parsing. Can be overridden for special cases,
  114.      * subclasses, etc.
  115.      *
  116.      * @var string 
  117.      */
  118.     var $wsdlParserClass = 'SOAP_WSDL_Parser';
  119.  
  120.     /**
  121.      * Reserved PHP keywords.
  122.      *
  123.      * @link http://www.php.net/manual/en/reserved.php
  124.      *
  125.      * @var array 
  126.      */
  127.     var $_reserved = array('abstract''and''array''as''break''case',
  128.                            'catch''cfunction''class''clone''const',
  129.                            'continue''declare''default''die''do',
  130.                            'echo''else''elseif''empty''enddeclare',
  131.                            'endfor''endforeach''endif''endswitch',
  132.                            'endwhile''eval''exception''exit''extends',
  133.                            'final''for''foreach''function''global',
  134.                            'if''implements''include''include_once',
  135.                            'interface''isset''list''new''old_function',
  136.                            'or''php_user_filter''print''private',
  137.                            'protected''public''require''require_once',
  138.                            'return''static''switch''this''throw',
  139.                            'try''unset''use''var''while''xor');
  140.  
  141.     /**
  142.      * Regular expressions for invalid PHP labels.
  143.      *
  144.      * @link http://www.php.net/manual/en/language.variables.php.
  145.      *
  146.      * @var string 
  147.      */
  148.     var $_invalid = array('/^[^a-zA-Z_\x7f-\xff]/''/[^a-zA-Z0-9_\x7f-\xff]/');
  149.  
  150.     /**
  151.      * SOAP_WSDL constructor.
  152.      *
  153.      * @param string $wsdl_uri          URL to WSDL file.
  154.      * @param array $proxy              Options for HTTP_Request class
  155.      *                                  @see HTTP_Request.
  156.      * @param boolean|string$cacheUse  Use WSDL caching? The cache directory
  157.      *                                   if a string.
  158.      * @param integer $cacheMaxAge      Cache maximum lifetime (in seconds).
  159.      * @param boolean $docs             Parse documentation in the WSDL?
  160.      *
  161.      * @access public
  162.      */
  163.     function SOAP_WSDL($wsdl_uri    = false,
  164.                        $proxy       = array(),
  165.                        $cacheUse    = false,
  166.                        $cacheMaxAge = WSDL_CACHE_MAX_AGE,
  167.                        $docs        = false)
  168.     {
  169.         parent::SOAP_Base('WSDL');
  170.         $this->uri         = $wsdl_uri;
  171.         $this->proxy       = $proxy;
  172.         $this->cacheUse    = !empty($cacheUse);
  173.         $this->cacheMaxAge = $cacheMaxAge;
  174.         $this->docs        = $docs;
  175.         if (is_string($cacheUse)) {
  176.             $this->cacheDir = $cacheUse;
  177.         }
  178.  
  179.         if ($wsdl_uri{
  180.             if (!PEAR::isError($this->parseURL($wsdl_uri))) {
  181.                 reset($this->services);
  182.                 $this->service = key($this->services);
  183.             }
  184.         }
  185.     }
  186.  
  187.     /**
  188.      * @deprecated  Use setService().
  189.      */
  190.     function set_service($service)
  191.     {
  192.         $this->setService($service);
  193.     }
  194.  
  195.     /**
  196.      * Sets the service currently to be used.
  197.      *
  198.      * @param string $service  An (existing) service name.
  199.      */
  200.     function setService($service)
  201.     {
  202.         if (array_key_exists($service$this->services)) {
  203.             $this->service = $service;
  204.         }
  205.     }
  206.  
  207.     /**
  208.      * Fills the WSDL array tree with data from a WSDL file.
  209.      *
  210.      * @param string $wsdl_uri  URL to WSDL file.
  211.      */
  212.     function parseURL($wsdl_uri)
  213.     {
  214.         $parser =new $this->wsdlParserClass($wsdl_uri$this$this->docs);
  215.  
  216.         if ($parser->fault{
  217.             $this->_raiseSoapFault($parser->fault);
  218.         }
  219.     }
  220.  
  221.     /**
  222.      * Fills the WSDL array tree with data from one or more PHP class objects.
  223.      *
  224.      * @param mixed $wsdl_obj          An object or array of objects to add to
  225.      *                                  the internal WSDL tree.
  226.      * @param string $targetNamespace  The target namespace of schema types
  227.      *                                  etc.
  228.      * @param string $service_name     Name of the WSDL service.
  229.      * @param string $service_desc     Optional description of the WSDL
  230.      *                                  service.
  231.      */
  232.     function parseObject($wsdl_obj$targetNamespace$service_name,
  233.                          $service_desc '')
  234.     {
  235.         $parser = new SOAP_WSDL_ObjectParser($wsdl_obj$this,
  236.                                              $targetNamespace$service_name,
  237.                                              $service_desc);
  238.  
  239.         if ($parser->fault{
  240.             $this->_raiseSoapFault($parser->fault);
  241.         }
  242.     }
  243.  
  244.     function getEndpoint($portName)
  245.     {
  246.         if ($this->_isfault()) {
  247.             return $this->_getfault();
  248.         }
  249.  
  250.         return (isset($this->services[$this->service]['ports'][$portName]['address']['location']))
  251.                 ? $this->services[$this->service]['ports'][$portName]['address']['location']
  252.                 : $this->_raiseSoapFault("No endpoint for port for $portName"$this->uri);
  253.     }
  254.  
  255.     function _getPortName($operation$service)
  256.     {
  257.         if (isset($this->services[$service]['ports'])) {
  258.             $ports $this->services[$service]['ports'];
  259.             foreach ($ports as $port => $portAttrs{
  260.                 $type $ports[$port]['type'];
  261.                 if ($type == 'soap' &&
  262.                     isset($this->bindings[$portAttrs['binding']]['operations'][$operation])) {
  263.                     return $port;
  264.                 }
  265.             }
  266.         }
  267.         return null;
  268.     }
  269.  
  270.     /**
  271.      * Finds the name of the first port that contains an operation of name
  272.      * $operation. Always returns a SOAP portName.
  273.      */
  274.     function getPortName($operation$service = null)
  275.     {
  276.         if ($this->_isfault()) {
  277.             return $this->_getfault();
  278.         }
  279.  
  280.         if (!$service{
  281.             $service $this->service;
  282.         }
  283.         if (isset($this->services[$service]['ports'])) {
  284.             if ($portName $this->_getPortName($operation$service)) {
  285.                 return $portName;
  286.             }
  287.         }
  288.         // Try any service in the WSDL.
  289.         foreach ($this->services as $serviceName => $service{
  290.             if (isset($this->services[$serviceName]['ports'])) {
  291.                 if ($portName $this->_getPortName($operation$serviceName)) {
  292.                     $this->service = $serviceName;
  293.                     return $portName;
  294.                 }
  295.             }
  296.         }
  297.         return $this->_raiseSoapFault("No operation $operation in WSDL."$this->uri);
  298.     }
  299.  
  300.     function getOperationData($portName$operation)
  301.     {
  302.         if ($this->_isfault()) {
  303.             return $this->_getfault();
  304.         }
  305.  
  306.         if (!isset($this->services[$this->service]['ports'][$portName]['binding']||
  307.             !($binding $this->services[$this->service]['ports'][$portName]['binding'])) {
  308.             return $this->_raiseSoapFault("No binding for port $portName in WSDL."$this->uri);
  309.         }
  310.  
  311.         // Get operation data from binding.
  312.         if (is_array($this->bindings[$binding]['operations'][$operation])) {
  313.             $opData $this->bindings[$binding]['operations'][$operation];
  314.         }
  315.         // Get operation data from porttype.
  316.         $portType $this->bindings[$binding]['type'];
  317.         if (!$portType{
  318.             return $this->_raiseSoapFault("No port type for binding $binding in WSDL."$this->uri);
  319.         }
  320.         if (is_array($type $this->portTypes[$portType][$operation])) {
  321.             if (isset($type['parameterOrder'])) {
  322.                 $opData['parameterOrder'$type['parameterOrder'];
  323.             }
  324.             $opData['input'array_merge($opData['input']$type['input']);
  325.             $opData['output'array_merge($opData['output']$type['output']);
  326.         }
  327.         if (!$opData)
  328.             return $this->_raiseSoapFault("No operation $operation for port $portName in WSDL."$this->uri);
  329.         $opData['parameters'= false;
  330.         if (isset($this->bindings[$binding]['operations'][$operation]['input']['namespace']))
  331.             $opData['namespace'$this->bindings[$binding]['operations'][$operation]['input']['namespace'];
  332.         // Message data from messages.
  333.         $inputMsg $opData['input']['message'];
  334.         if (is_array($this->messages[$inputMsg])) {
  335.             foreach ($this->messages[$inputMsgas $pname => $pattrs{
  336.                 if ($opData['style'== 'document' &&
  337.                     $opData['input']['use'== 'literal' &&
  338.                     $pname == 'parameters'{
  339.                     $opData['parameters'= true;
  340.                     $opData['namespace'$this->namespaces[$pattrs['namespace']];
  341.                     $el $this->elements[$pattrs['namespace']][$pattrs['type']];
  342.                     if (isset($el['elements'])) {
  343.                         foreach ($el['elements'as $elname => $elattrs{
  344.                             $opData['input']['parts'][$elname$elattrs;
  345.                         }
  346.                     }
  347.                 else {
  348.                     $opData['input']['parts'][$pname$pattrs;
  349.                 }
  350.             }
  351.         }
  352.         $outputMsg $opData['output']['message'];
  353.         if (is_array($this->messages[$outputMsg])) {
  354.             foreach ($this->messages[$outputMsgas $pname => $pattrs{
  355.                 if ($opData['style'== 'document' &&
  356.                     $opData['output']['use'== 'literal' &&
  357.                     $pname == 'parameters'{
  358.  
  359.                     $el $this->elements[$pattrs['namespace']][$pattrs['type']];
  360.                     if (isset($el['elements'])) {
  361.                         foreach ($el['elements'as $elname => $elattrs{
  362.                             $opData['output']['parts'][$elname$elattrs;
  363.                         }
  364.                     }
  365.                 else {
  366.                     $opData['output']['parts'][$pname$pattrs;
  367.                 }
  368.             }
  369.         }
  370.         return $opData;
  371.     }
  372.  
  373.     function matchMethod(&$operation)
  374.     {
  375.         if ($this->_isfault()) {
  376.             return $this->_getfault();
  377.         }
  378.  
  379.         // Overloading lowercases function names :(
  380.         foreach ($this->services[$this->service]['ports'as $portAttrs{
  381.             foreach (array_keys($this->bindings[$portAttrs['binding']]['operations']as $op{
  382.                 if (strcasecmp($op$operation== 0{
  383.                     $operation $op;
  384.                 }
  385.             }
  386.         }
  387.     }
  388.  
  389.     /**
  390.      * Given a datatype, what function handles the processing?
  391.      *
  392.      * This is used for doc/literal requests where we receive a datatype, and
  393.      * we need to pass it to a method in out server class.
  394.      *
  395.      * @param string $datatype 
  396.      * @param string $namespace 
  397.      * @return string 
  398.      * @access public
  399.      */
  400.     function getDataHandler($datatype$namespace)
  401.     {
  402.         // See if we have an element by this name.
  403.         if (isset($this->namespaces[$namespace])) {
  404.             $namespace $this->namespaces[$namespace];
  405.         }
  406.  
  407.         if (!isset($this->ns[$namespace])) {
  408.             return null;
  409.         }
  410.  
  411.         $nsp $this->ns[$namespace];
  412.         //if (!isset($this->elements[$nsp]))
  413.         //    $nsp = $this->namespaces[$nsp];
  414.         if (!isset($this->elements[$nsp][$datatype])) {
  415.             return null;
  416.         }
  417.  
  418.         $checkmessages = array();
  419.         // Find what messages use this datatype.
  420.         foreach ($this->messages as $messagename => $message{
  421.             foreach ($message as $part{
  422.                 if ($part['type'== $datatype{
  423.                     $checkmessages[$messagename;
  424.                     break;
  425.                 }
  426.             }
  427.         }
  428.         // Find the operation that uses this message.
  429.         foreach($this->portTypes as $porttype{
  430.             foreach ($porttype as $opname => $opinfo{
  431.                 foreach ($checkmessages as $messagename{
  432.                     if ($opinfo['input']['message'== $messagename{
  433.                         return $opname;
  434.                     }
  435.                 }
  436.             }
  437.         }
  438.  
  439.         return null;
  440.     }
  441.  
  442.     function getSoapAction($portName$operation)
  443.     {
  444.         if ($this->_isfault()) {
  445.             return $this->_getfault();
  446.         }
  447.  
  448.         if (!empty($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction'])) {
  449.             return $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['soapAction'];
  450.         }
  451.  
  452.         return false;
  453.     }
  454.  
  455.     function getNamespace($portName$operation)
  456.     {
  457.         if ($this->_isfault()) {
  458.             return $this->_getfault();
  459.         }
  460.  
  461.         if (!empty($this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace'])) {
  462.             return $this->bindings[$this->services[$this->service]['ports'][$portName]['binding']]['operations'][$operation]['input']['namespace'];
  463.         }
  464.  
  465.         return false;
  466.     }
  467.  
  468.     function getNamespaceAttributeName($namespace)
  469.     {
  470.         /* If it doesn't exist at first, flip the array and check again. */
  471.         if (empty($this->ns[$namespace])) {
  472.             $this->ns = array_flip($this->namespaces);
  473.         }
  474.  
  475.         /* If it doesn't exist now, add it. */
  476.         if (empty($this->ns[$namespace])) {
  477.             return $this->addNamespace($namespace);
  478.         }
  479.  
  480.         return $this->ns[$namespace];
  481.     }
  482.  
  483.     function addNamespace($namespace)
  484.     {
  485.         if (!empty($this->ns[$namespace])) {
  486.             return $this->ns[$namespace];
  487.         }
  488.  
  489.         $n count($this->ns);
  490.         $attr 'ns' $n;
  491.         $this->namespaces['ns' $n$namespace;
  492.         $this->ns[$namespace$attr;
  493.  
  494.         return $attr;
  495.     }
  496.  
  497.     function _validateString($string)
  498.     {
  499.         return preg_match('/^[\w_:#\/]+$/'$string);
  500.     }
  501.  
  502.     function _addArg(&$args&$argarray$argname)
  503.     {
  504.         if ($args{
  505.             $args .= ', ';
  506.         }
  507.         $args .= '$' $argname;
  508.         if (!$this->_validateString($argname)) {
  509.             return;
  510.         }
  511.         if ($argarray{
  512.             $argarray .= ', ';
  513.         }
  514.         $argarray .= "'$argname' => $" . $argname;
  515.     }
  516.  
  517.     function _elementArg(&$args&$argarray&$_argtype$_argname)
  518.     {
  519.         $comments '';
  520.         $el $this->elements[$_argtype['namespace']][$_argtype['type']];
  521.         $tns = isset($this->ns[$el['namespace']])
  522.             ? $this->ns[$el['namespace']]
  523.             : $_argtype['namespace'];
  524.  
  525.         if (!empty($el['complex']||
  526.             (isset($el['type']&&
  527.              isset($this->complexTypes[$tns][$el['type']]))) {
  528.             // The element is a complex type.
  529.             $comments .= "        // {$_argtype['type']} is a ComplexType, refer to the WSDL for more info.\n";
  530.             $attrname = "{$_argtype['type']}_attr";
  531.             if (isset($el['type']&&
  532.                 isset($this->complexTypes[$tns][$el['type']]['attribute'])) {
  533.                 $comments .= "        // {$_argtype['type']} may require attributes, refer to the WSDL for more info.\n";
  534.             }
  535.             $comments .= "        \${$attrname}['xmlns'] = '{$this->namespaces[$_argtype['namespace']]}';\n";
  536.             $comments .= "        \${$_argtype['type']} = new SOAP_Value('{$_argtype['type']}', false, \${$_argtype['type']}, \$$attrname);\n";
  537.             $this->_addArg($args$argarray$_argtype['type']);
  538.             if (isset($el['type']&&
  539.                 isset($this->complexTypes[$tns][$el['type']]['attribute'])) {
  540.                 if ($args{
  541.                     $args .= ', ';
  542.                 }
  543.                 $args .= '$' $attrname;
  544.             }
  545.         elseif (isset($el['elements'])) {
  546.             foreach ($el['elements'as $ename => $element{
  547.                 $comments .= "        \$$ename = new SOAP_Value('{{$this->namespaces[$element['namespace']]}}$ename', '" .
  548.                     (isset($element['type']$element['type': false.
  549.                     "', \$$ename);\n";
  550.                 $this->_addArg($args$argarray$ename);
  551.             }
  552.         else {
  553.             $comments .= "        \$$_argname = new SOAP_Value('{{$this->namespaces[$tns]}}$_argname', '{$el['type']}', \$$_argname);\n";
  554.             $this->_addArg($args$argarray$_argname);
  555.         }
  556.  
  557.         return $comments;
  558.     }
  559.  
  560.     function _complexTypeArg(&$args&$argarray&$_argtype$_argname)
  561.     {
  562.         $comments '';
  563.         if (isset($this->complexTypes[$_argtype['namespace']][$_argtype['type']])) {
  564.             $comments  = "        // $_argname is a ComplexType {$_argtype['type']},\n" .
  565.                 "        // refer to wsdl for more info\n";
  566.             if (isset($this->complexTypes[$_argtype['namespace']][$_argtype['type']]['attribute'])) {
  567.                 $comments .= "        // $_argname may require attributes, refer to wsdl for more info\n";
  568.             }
  569.             $wrapname '{' $this->namespaces[$_argtype['namespace']].'}' $_argtype['type'];
  570.             $comments .= "        \$$_argname = new SOAP_Value('$_argname', '$wrapname', \$$_argname);\n";
  571.         }
  572.  
  573.         $this->_addArg($args$argarray$_argname);
  574.  
  575.         return $comments;
  576.     }
  577.  
  578.     /**
  579.      * Generates stub code from the WSDL that can be saved to a file or eval'd
  580.      * into existence.
  581.      */
  582.     function generateProxyCode($port ''$classname '')
  583.     {
  584.         if ($this->_isfault()) {
  585.             return $this->_getfault();
  586.         }
  587.  
  588.         $multiport count($this->services[$this->service]['ports']> 1;
  589.         if (!$port{
  590.             reset($this->services[$this->service]['ports']);
  591.             $port current($this->services[$this->service]['ports']);
  592.         }
  593.         // XXX currently do not support HTTP ports
  594.         if ($port['type'!= 'soap'{
  595.             return null;
  596.         }
  597.  
  598.         // XXX currentPort is BAD
  599.         $clienturl $port['address']['location'];
  600.         if (!$classname{
  601.             if ($multiport || $port{
  602.                 $classname 'WebService_' $this->service . '_' $port['name'];
  603.             else {
  604.                 $classname 'WebService_' $this->service;
  605.             }
  606.             $classname $this->_sanitize($classname);
  607.         }
  608.  
  609.         if (!$this->_validateString($classname)) {
  610.             return null;
  611.         }
  612.  
  613.         if (is_array($this->proxy&& count($this->proxy)) {
  614.             $class = "class $classname extends SOAP_Client\n{\n" .
  615.             "    function $classname(\$path = '$clienturl')\n    {\n" .
  616.             "        \$this->SOAP_Client(\$path, 0, 0,\n" .
  617.             '                           array(';
  618.  
  619.             foreach ($this->proxy as $key => $val{
  620.                 if (is_array($val)) {
  621.                     $class .= "'$key' => array(";
  622.                     foreach ($val as $key2 => $val2{
  623.                         $class .= "'$key2' => '$val2', ";
  624.                     }
  625.                     $class .= ')';
  626.                 else {
  627.                     $class .= "'$key' => '$val', ";
  628.                 }
  629.             }
  630.             $class .= "));\n    }\n";
  631.             $class str_replace(', ))''))'$class);
  632.         else {
  633.             $class = "class $classname extends SOAP_Client\n{\n" .
  634.             "    function $classname(\$path = '$clienturl')\n    {\n" .
  635.             "        \$this->SOAP_Client(\$path, 0);\n" .
  636.             "    }\n";
  637.         }
  638.  
  639.         // Get the binding, from that get the port type.
  640.         $primaryBinding $port['binding'];
  641.         $primaryBinding preg_replace("/^(.*:)/"''$primaryBinding);
  642.         $portType $this->bindings[$primaryBinding]['type'];
  643.         $portType preg_replace("/^(.*:)/"''$portType);
  644.         $style $this->bindings[$primaryBinding]['style'];
  645.  
  646.         // XXX currentPortType is BAD
  647.         foreach ($this->portTypes[$portTypeas $opname => $operation{
  648.             $binding $this->bindings[$primaryBinding]['operations'][$opname];
  649.             if (isset($binding['soapAction'])) {
  650.                 $soapaction $binding['soapAction'];
  651.             else {
  652.                 $soapaction = null;
  653.             }
  654.             if (isset($binding['style'])) {
  655.                 $opstyle $binding['style'];
  656.             else {
  657.                 $opstyle $style;
  658.             }
  659.             $use $binding['input']['use'];
  660.             if ($use == 'encoded'{
  661.                 $namespace $binding['input']['namespace'];
  662.             else {
  663.                 $bindingType $this->bindings[$primaryBinding]['type'];
  664.                 $ns $this->portTypes[$bindingType][$opname]['input']['namespace'];
  665.                 $namespace $this->namespaces[$ns];
  666.             }
  667.  
  668.             $args '';
  669.             $argarray '';
  670.             $comments '';
  671.             $wrappers '';
  672.             foreach ($operation['input'as $argname => $argtype{
  673.                 if ($argname == 'message'{
  674.                     foreach ($this->messages[$argtypeas $_argname => $_argtype{
  675.                         $_argname $this->_sanitize($_argname);
  676.                         if ($opstyle == 'document' && $use == 'literal' &&
  677.                             $_argtype['name'== 'parameters'{
  678.                             // The type or element refered to is used for
  679.                             // parameters.
  680.                             $elattrs = null;
  681.                             $el $this->elements[$_argtype['namespace']][$_argtype['type']];
  682.  
  683.                             if ($el['complex']{
  684.                                 $namespace $this->namespaces[$_argtype['namespace']];
  685.                                 // XXX need to wrap the parameters in a
  686.                                 // SOAP_Value.
  687.                             }
  688.                             if (isset($el['elements'])) {
  689.                                 foreach ($el['elements'as $elname => $elattrs{
  690.                                     $elname $this->_sanitize($elname);
  691.                                     // Is the element a complex type?
  692.                                     if (isset($this->complexTypes[$elattrs['namespace']][$elname])) {
  693.                                         $comments .= $this->_complexTypeArg($args$argarray$_argtype$_argname);
  694.                                     else {
  695.                                         $this->_addArg($args$argarray$elname);
  696.                                     }
  697.                                 }
  698.                             }
  699.                             if ($el['complex'&& $argarray{
  700.                                 $wrapname '{' $this->namespaces[$_argtype['namespace']].'}' $el['name'];
  701.                                 $comments .= "        \${$el['name']} = new SOAP_Value('$wrapname', false, \$v = array($argarray));\n";
  702.                                 $argarray = "'{$el['name']}' => \${$el['name']}";
  703.                             }
  704.                         else {
  705.                             if (isset($_argtype['element'])) {
  706.                                 // Element argument.
  707.                                 $comments .= $this->_elementArg($args$argarray$_argtype$_argtype['type']);
  708.                             else {
  709.                                 // Complex type argument.
  710.                                 $comments .= $this->_complexTypeArg($args$argarray$_argtype$_argname);
  711.                             }
  712.                         }
  713.                     }
  714.                 }
  715.             }
  716.  
  717.             // Validate entries.
  718.  
  719.             // Operation names are function names, so try to make sure it's
  720.             // legal. This could potentially cause collisions, but let's try
  721.             // to make everything callable and see how many problems that
  722.             // causes.
  723.             $opname_php $this->_sanitize($opname);
  724.             if (!$this->_validateString($opname_php)) {
  725.                 return null;
  726.             }
  727.  
  728.             if ($argarray{
  729.                 $argarray = "array($argarray)";
  730.             else {
  731.                 $argarray 'null';
  732.             }
  733.  
  734.             $class .= "    function &$opname_php($args)\n    {\n$comments$wrappers" .
  735.                 "        \$result = \$this->call('$opname',\n" .
  736.                 "                              \$v = $argarray,\n" .
  737.                 "                              array('namespace' => '$namespace',\n" .
  738.                 "                                    'soapaction' => '$soapaction',\n" .
  739.                 "                                    'style' => '$opstyle',\n" .
  740.                 "                                    'use' => '$use'" .
  741.                 ($this->trace ? ",\n                                    'trace' => true" ''"));\n" .
  742.                 "        return \$result;\n" .
  743.                 "    }\n";
  744.         }
  745.  
  746.         $class .= "}\n";
  747.  
  748.         return $class;
  749.     }
  750.  
  751.     function generateAllProxies()
  752.     {
  753.         $proxycode '';
  754.         foreach (array_keys($this->services[$this->service]['ports']as $key{
  755.             $port =$this->services[$this->service]['ports'][$key];
  756.             $proxycode .= $this->generateProxyCode($port);
  757.         }
  758.         return $proxycode;
  759.     }
  760.  
  761.     function &getProxy($port ''$name '')
  762.     {
  763.         if ($this->_isfault()) {
  764.             $fault =$this->_getfault();
  765.             return $fault;
  766.         }
  767.  
  768.         $multiport count($this->services[$this->service]['ports']> 1;
  769.  
  770.         if (!$port{
  771.             reset($this->services[$this->service]['ports']);
  772.             $port current($this->services[$this->service]['ports']);
  773.         }
  774.  
  775.         if ($multiport || $port{
  776.             $classname 'WebService_' $this->service . '_' $port['name'];
  777.         else {
  778.             $classname 'WebService_' $this->service;
  779.         }
  780.  
  781.         if ($name{
  782.             $classname $name '_' $classname;
  783.         }
  784.  
  785.         $classname $this->_sanitize($classname);
  786.         if (!class_exists($classname)) {
  787.             $proxy $this->generateProxyCode($port$classname);
  788.             require_once 'SOAP/Client.php';
  789.             eval($proxy);
  790.         }
  791.         $proxy =new $classname;
  792.  
  793.         return $proxy;
  794.     }
  795.  
  796.     /**
  797.      * Sanitizes a SOAP value, method or class name so that it can be used as
  798.      * a valid PHP identifier. Invalid characters are converted into
  799.      * underscores and reserved words are prefixed with an underscore.
  800.      *
  801.      * @param string $name  The identifier to sanitize.
  802.      *
  803.      * @return string  The sanitized identifier.
  804.      */
  805.     function _sanitize($name)
  806.     {
  807.         $name preg_replace($this->_invalid'_'$name);
  808.         if (in_array($name$this->_reserved)) {
  809.             $name '_' $name;
  810.         }
  811.         return $name;
  812.     }
  813.  
  814.     function &_getComplexTypeForElement($name$namespace)
  815.     {
  816.         $t = null;
  817.         if (isset($this->ns[$namespace]&&
  818.             isset($this->elements[$this->ns[$namespace]][$name]['type'])) {
  819.  
  820.             $type $this->elements[$this->ns[$namespace]][$name]['type'];
  821.             $ns $this->elements[$this->ns[$namespace]][$name]['namespace'];
  822.  
  823.             if (isset($this->complexTypes[$ns][$type])) {
  824.                 $t $this->complexTypes[$ns][$type];
  825.             }
  826.         }
  827.         return $t;
  828.     }
  829.  
  830.     function getComplexTypeNameForElement($name$namespace)
  831.     {
  832.         $t $this->_getComplexTypeForElement($name$namespace);
  833.         if ($t{
  834.             return $t['name'];
  835.         }
  836.         return null;
  837.     }
  838.  
  839.     function getComplexTypeChildType($ns$name$child_ns$child_name)
  840.     {
  841.         // Is the type an element?
  842.         $t $this->_getComplexTypeForElement($name$ns);
  843.         if ($t{
  844.             // No, get it from complex types directly.
  845.             if (isset($t['elements'][$child_name]['type']))
  846.                 return $t['elements'][$child_name]['type'];
  847.         elseif (isset($this->ns[$ns]&&
  848.                   isset($this->elements[$this->ns[$ns]][$name]['complex']&&
  849.                   $this->elements[$this->ns[$ns]][$name]['complex']{
  850.             // Type is not an element but complex.
  851.             return $this->elements[$this->ns[$ns]][$name]['elements'][$child_name]['type'];
  852.         }
  853.         return null;
  854.     }
  855.  
  856.     /**
  857.      * @param QName $name  A parameter name.
  858.      * @param QName $type  A parameter type.
  859.      *
  860.      * @return array  A list of [type, array element type, array element
  861.      *                 namespace, array length].
  862.      */
  863.     function getSchemaType($type$name)
  864.     {
  865.         // see if it's a complex type so we can deal properly with
  866.         // SOAPENC:arrayType.
  867.         if ($name && $type{
  868.             // XXX TODO:
  869.             // look up the name in the wsdl and validate the type.
  870.             foreach ($this->complexTypes as $types{
  871.                 if (isset($types[$type->name])) {
  872.                     if (isset($types[$type->name]['type'])) {
  873.                         list($arraytype_ns$arraytype$array_depth= isset($types[$type->name]['arrayType'])
  874.                             ? $this->_getDeepestArrayType($types[$type->name]['namespace']$types[$type->name]['arrayType'])
  875.                             : array($this->namespaces[$types[$type->name]['namespace']]null0);
  876.                         return array($types[$type->name]['type']$arraytype$arraytype_ns$array_depth);
  877.                     }
  878.                     if (isset($types[$type->name]['arrayType'])) {
  879.                         list($arraytype_ns$arraytype$array_depth=
  880.                             $this->_getDeepestArrayType($types[$type->name]['namespace']$types[$type->name]['arrayType']);
  881.                         return array('Array'$arraytype$arraytype_ns$array_depth);
  882.                     }
  883.                     if (!empty($types[$type->name]['elements'][$name->name])) {
  884.                         $type->name = $types[$type->name]['elements']['type'];
  885.                         return array($type->namenull$this->namespaces[$types[$type->name]['namespace']]null);
  886.                     }
  887.                     break;
  888.                 }
  889.             }
  890.         }
  891.         if ($type && $type->namespace{
  892.             $arrayType = null;
  893.             // XXX TODO:
  894.             // this code currently handles only one way of encoding array
  895.             // types in wsdl need to do a generalized function to figure out
  896.             // complex types
  897.             $p $this->ns[$type->namespace];
  898.             if ($p && !empty($this->complexTypes[$p][$type->name])) {
  899.                 if ($arrayType $this->complexTypes[$p][$type->name]['arrayType']{
  900.                     $type->name = 'Array';
  901.                 elseif ($this->complexTypes[$p][$type->name]['order'== 'sequence' &&
  902.                           array_key_exists('elements'$this->complexTypes[$p][$type->name])) {
  903.                     reset($this->complexTypes[$p][$type->name]['elements']);
  904.                     // assume an array
  905.                     if (count($this->complexTypes[$p][$type->name]['elements']== 1{
  906.                         $arg current($this->complexTypes[$p][$type->name]['elements']);
  907.                         $arrayType $arg['type'];
  908.                         $type->name = 'Array';
  909.                     else {
  910.                         foreach ($this->complexTypes[$p][$type->name]['elements'as $element{
  911.                             if ($element['name'== $type->name{
  912.                                 $arrayType $element['type'];
  913.                                 $type->name = $element['type'];
  914.                             }
  915.                         }
  916.                     }
  917.                 else {
  918.                     $type->name = 'Struct';
  919.                 }
  920.                 return array($type->name$arrayType$type->namespacenull);
  921.             }
  922.         }
  923.         return array(nullnullnullnull);
  924.     }
  925.  
  926.     /**
  927.      * Recurse through the WSDL structure looking for the innermost array type
  928.      * of multi-dimensional arrays.
  929.      *
  930.      * Takes a namespace prefix and a type, which can be in the form 'type' or
  931.      * 'type[]', and returns the full namespace URI, the type of the most
  932.      * deeply nested array type found, and the number of levels of nesting.
  933.      *
  934.      * @access private
  935.      * @return mixed array or nothing
  936.      */
  937.     function _getDeepestArrayType($nsPrefix$arrayType)
  938.     {
  939.         static $trail = array();
  940.  
  941.         $arrayType = ereg_replace('\[\]$'''$arrayType);
  942.  
  943.         // Protect against circular references XXX We really need to remove
  944.         // trail from this altogether (it's very inefficient and in the wrong
  945.         // place!) and put circular reference checking in when the WSDL info
  946.         // is generated in the first place
  947.         if (array_search($nsPrefix ':' $arrayType$trail)) {
  948.             return array(nullnull-count($trail));
  949.         }
  950.  
  951.         if (array_key_exists($nsPrefix$this->complexTypes&&
  952.             array_key_exists($arrayType$this->complexTypes[$nsPrefix]&&
  953.             array_key_exists('arrayType'$this->complexTypes[$nsPrefix][$arrayType])) {
  954.             $trail[$nsPrefix ':' $arrayType;
  955.             $result $this->_getDeepestArrayType($this->complexTypes[$nsPrefix][$arrayType]['namespace'],
  956.                                                   $this->complexTypes[$nsPrefix][$arrayType]['arrayType']);
  957.             return array($result[0]$result[1]$result[2+ 1);
  958.         }
  959.         return array($this->namespaces[$nsPrefix]$arrayType0);
  960.     }
  961.  
  962. }
  963.  
  964. class SOAP_WSDL_Cache extends SOAP_Base
  965. {
  966.     /**
  967.      * Use WSDL cache?
  968.      *
  969.      * @var boolean 
  970.      */
  971.     var $_cacheUse;
  972.  
  973.     /**
  974.      * WSDL cache directory.
  975.      *
  976.      * @var string 
  977.      */
  978.     var $_cacheDir;
  979.  
  980.     /**
  981.      * Cache maximum lifetime (in seconds)
  982.      *
  983.      * @var integer 
  984.      */
  985.     var $_cacheMaxAge;
  986.  
  987.     /**
  988.      * Constructor.
  989.      *
  990.      * @param boolean $cashUse      Use caching?
  991.      * @param integer $cacheMaxAge  Cache maximum lifetime (in seconds)
  992.      */
  993.     function SOAP_WSDL_Cache($cacheUse = false,
  994.                              $cacheMaxAge = WSDL_CACHE_MAX_AGE,
  995.                              $cacheDir = null)
  996.     {
  997.         parent::SOAP_Base('WSDLCACHE');
  998.         $this->_cacheUse $cacheUse;
  999.         $this->_cacheDir $cacheDir;
  1000.         $this->_cacheMaxAge $cacheMaxAge;
  1001.     }
  1002.  
  1003.     /**
  1004.      * Returns the path to the cache and creates it, if it doesn't exist.
  1005.      *
  1006.      * @private
  1007.      *
  1008.      * @return string  The directory to use for the cache.
  1009.      */
  1010.     function _cacheDir()
  1011.     {
  1012.         if (!empty($this->_cacheDir)) {
  1013.             $dir $this->_cacheDir;
  1014.         else {
  1015.             $dir getenv('WSDLCACHE');
  1016.             if (empty($dir)) {
  1017.                 $dir './wsdlcache';
  1018.             }
  1019.         }
  1020.         @mkdir($dir0700);
  1021.         return $dir;
  1022.     }
  1023.  
  1024.     /**
  1025.      * Retrieves a file from cache if it exists, otherwise retreive from net,
  1026.      * add to cache, and return from cache.
  1027.      *
  1028.      * @param  string   URL to WSDL
  1029.      * @param  array    proxy parameters
  1030.      * @param  int      expected MD5 of WSDL URL
  1031.      * @access public
  1032.      * @return string  data
  1033.      */
  1034.     function get($wsdl_fname$proxy_params = array()$cache = 0)
  1035.     {
  1036.         $cachename $md5_wsdl $file_data '';
  1037.         if ($this->_cacheUse{
  1038.             // Try to retrieve WSDL from cache
  1039.             $cachename $this->_cacheDir('/' md5($wsdl_fname)' .wsdl';
  1040.             if (file_exists($cachename&&
  1041.                 $file_data file_get_contents($cachename)) {
  1042.                 $md5_wsdl md5($file_data);
  1043.                 if ($cache{
  1044.                     if ($cache != $md5_wsdl{
  1045.                         return $this->_raiseSoapFault('WSDL Checksum error!'$wsdl_fname);
  1046.                     }
  1047.                 else {
  1048.                     $fi stat($cachename);
  1049.                     $cache_mtime $fi[8];
  1050.                     if ($cache_mtime $this->_cacheMaxAge time()) {
  1051.                         // Expired, refetch.
  1052.                         $md5_wsdl '';
  1053.                     }
  1054.                 }
  1055.             }
  1056.         }
  1057.  
  1058.         // Not cached or not using cache. Retrieve WSDL from URL
  1059.         if (!$md5_wsdl{
  1060.             // Is it a local file?
  1061.             if (strpos($wsdl_fname'file://'=== 0{
  1062.                 $wsdl_fname substr($wsdl_fname7);
  1063.                 if (!file_exists($wsdl_fname)) {
  1064.                     return $this->_raiseSoapFault('Unable to read local WSDL file'$wsdl_fname);
  1065.                 }
  1066.                 $file_data file_get_contents($wsdl_fname);
  1067.             elseif (!preg_match('|^https?://|'$wsdl_fname)) {
  1068.                 return $this->_raiseSoapFault('Unknown schema of WSDL URL'$wsdl_fname);
  1069.             else {
  1070.                 $uri explode('?'$wsdl_fname);
  1071.                 $rq = new HTTP_Request($uri[0]$proxy_params);
  1072.                 // the user agent HTTP_Request uses fouls things up
  1073.                 if (isset($uri[1])) {
  1074.                     $rq->addRawQueryString($uri[1]);
  1075.                 }
  1076.  
  1077.                 if (isset($proxy_params['proxy_host']&&
  1078.                     isset($proxy_params['proxy_port']&&
  1079.                     isset($proxy_params['proxy_user']&&
  1080.                     isset($proxy_params['proxy_pass'])) {
  1081.                     $rq->setProxy($proxy_params['proxy_host'],
  1082.                                   $proxy_params['proxy_port'],
  1083.                                   $proxy_params['proxy_user'],
  1084.                                   $proxy_params['proxy_pass']);
  1085.                 elseif (isset($proxy_params['proxy_host']&&
  1086.                           isset($proxy_params['proxy_port'])) {
  1087.                     $rq->setProxy($proxy_params['proxy_host'],
  1088.                                   $proxy_params['proxy_port']);
  1089.                 }
  1090.  
  1091.                 $result $rq->sendRequest();
  1092.                 if (PEAR::isError($result)) {
  1093.                     return $this->_raiseSoapFault("Unable to retrieve WSDL $wsdl_fname," . $rq->getResponseCode()$wsdl_fname);
  1094.                 }
  1095.                 $file_data $rq->getResponseBody();
  1096.                 if (!$file_data{
  1097.                     return $this->_raiseSoapFault("Unable to retrieve WSDL $wsdl_fname, no http body"$wsdl_fname);
  1098.                 }
  1099.             }
  1100.  
  1101.             $md5_wsdl md5($file_data);
  1102.  
  1103.             if ($this->_cacheUse{
  1104.                 $fp fopen($cachename"wb");
  1105.                 fwrite($fp$file_data);
  1106.                 fclose($fp);
  1107.             }
  1108.         }
  1109.  
  1110.         if ($this->_cacheUse && $cache && $cache != $md5_wsdl{
  1111.             return $this->_raiseSoapFault('WSDL Checksum error!'$wsdl_fname);
  1112.         }
  1113.  
  1114.         return $file_data;
  1115.     }
  1116.  
  1117. }
  1118.  
  1119. class SOAP_WSDL_Parser extends SOAP_Base
  1120. {
  1121.  
  1122.     /**
  1123.      * Define internal arrays of bindings, ports, operations,
  1124.      * messages, etc.
  1125.      */
  1126.     var $currentMessage;
  1127.     var $currentOperation;
  1128.     var $currentPortType;
  1129.     var $currentBinding;
  1130.     var $currentPort;
  1131.  
  1132.     /**
  1133.      * Parser vars.
  1134.      */
  1135.     var $cache;
  1136.  
  1137.     var $tns = null;
  1138.     var $soapns = array('soap');
  1139.     var $uri = '';
  1140.     var $wsdl = null;
  1141.  
  1142.     var $status = '';
  1143.     var $element_stack = array();
  1144.     var $parentElement = '';
  1145.  
  1146.     var $schema = '';
  1147.     var $schemaStatus = '';
  1148.     var $schema_stack = array();
  1149.     var $currentComplexType;
  1150.     var $schema_element_stack = array();
  1151.     var $currentElement;
  1152.  
  1153.     /**
  1154.      * Constructor.
  1155.      */
  1156.     function SOAP_WSDL_Parser($uri&$wsdl$docs = false)
  1157.     {
  1158.         parent::SOAP_Base('WSDLPARSER');
  1159.         $this->cache =new SOAP_WSDL_Cache($wsdl->cacheUse,
  1160.                                             $wsdl->cacheMaxAge,
  1161.                                             $wsdl->cacheDir);
  1162.         $this->uri = $uri;
  1163.         $this->wsdl = &$wsdl;
  1164.         $this->docs $docs;
  1165.         $this->parse($uri);
  1166.     }
  1167.  
  1168.     function parse($uri)
  1169.     {
  1170.         // Check whether content has been read.
  1171.         $fd $this->cache->get($uri$this->wsdl->proxy);
  1172.         if (PEAR::isError($fd)) {
  1173.             return $this->_raiseSoapFault($fd);
  1174.         }
  1175.  
  1176.         // Create an XML parser.
  1177.         $parser xml_parser_create();
  1178.         xml_parser_set_option($parserXML_OPTION_CASE_FOLDING0);
  1179.         xml_set_object($parser$this);
  1180.         xml_set_element_handler($parser'startElement''endElement');
  1181.         if ($this->docs{
  1182.             xml_set_character_data_handler($parser'characterData');
  1183.         }
  1184.  
  1185.         if (!xml_parse($parser$fdtrue)) {
  1186.             $detail sprintf('XML error on line %d: %s',
  1187.                               xml_get_current_line_number($parser),
  1188.                               xml_error_string(xml_get_error_code($parser)));
  1189.             return $this->_raiseSoapFault("Unable to parse WSDL file $uri\n$detail");
  1190.         }
  1191.         xml_parser_free($parser);
  1192.         return true;
  1193.     }
  1194.  
  1195.     /**
  1196.      * start-element handler
  1197.      */
  1198.     function startElement($parser$name$attrs)
  1199.     {
  1200.         // Get element prefix.
  1201.         $qname = new QName($name);
  1202.         if ($qname->ns{
  1203.             $ns $qname->ns;
  1204.             if ($ns && ((!$this->tns && strcasecmp($qname->name'definitions'== 0|| $ns == $this->tns)) {
  1205.                 $name $qname->name;
  1206.             }
  1207.         }
  1208.         $this->currentTag $qname->name;
  1209.         $this->parentElement = '';
  1210.         $stack_size count($this->element_stack);
  1211.         if ($stack_size{
  1212.             $this->parentElement = $this->element_stack[$stack_size - 1];
  1213.         }
  1214.         $this->element_stack[$this->currentTag;
  1215.  
  1216.         // Find status, register data.
  1217.         switch ($this->status{
  1218.         case 'types':
  1219.             // sect 2.2 wsdl:types
  1220.             // children: xsd:schema
  1221.             $parent_tag '';
  1222.             $stack_size count($this->schema_stack);
  1223.             if ($stack_size{
  1224.                 $parent_tag $this->schema_stack[$stack_size - 1];
  1225.             }
  1226.  
  1227.             switch ($qname->name{
  1228.             case 'schema':
  1229.                 // No parent should be in the stack.
  1230.                 if (!$parent_tag || $parent_tag == 'types'{
  1231.                     if (array_key_exists('targetNamespace'$attrs)) {
  1232.                         $this->schema = $this->wsdl->getNamespaceAttributeName($attrs['targetNamespace']);
  1233.                     else {
  1234.                         $this->schema = $this->wsdl->getNamespaceAttributeName($this->wsdl->tns);
  1235.                     }
  1236.                     $this->wsdl->complexTypes[$this->schema= array();
  1237.                     $this->wsdl->elements[$this->schema= array();
  1238.                 }
  1239.                 break;
  1240.  
  1241.             case 'complexType':
  1242.                 if ($parent_tag == 'schema'{
  1243.                     $this->currentComplexType = $attrs['name'];
  1244.                     if (!isset($attrs['namespace'])) {
  1245.                         $attrs['namespace'$this->schema;
  1246.                     }
  1247.                     $this->wsdl->complexTypes[$this->schema][$this->currentComplexType$attrs;
  1248.                     if (array_key_exists('base'$attrs)) {
  1249.                         $qn = new QName($attrs['base']);
  1250.                         $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'$qn->name;
  1251.                         $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'$qn->ns;
  1252.                     else {
  1253.                         $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type''Struct';
  1254.                     }
  1255.                     $this->schemaStatus = 'complexType';
  1256.                 else {
  1257.                     $this->wsdl->elements[$this->schema][$this->currentElement]['complex'= true;
  1258.                 }
  1259.                 break;
  1260.  
  1261.             case 'element':
  1262.                 if (isset($attrs['type'])) {
  1263.                     $qn = new QName($attrs['type']);
  1264.                     $attrs['type'$qn->name;
  1265.                     if ($qn->ns && array_key_exists($qn->ns$this->wsdl->namespaces)) {
  1266.                         $attrs['namespace'$qn->ns;
  1267.                     }
  1268.                 }
  1269.  
  1270.                 $parentElement '';
  1271.                 $stack_size count($this->schema_element_stack);
  1272.                 if ($stack_size > 0{
  1273.                     $parentElement $this->schema_element_stack[$stack_size - 1];
  1274.                 }
  1275.  
  1276.                 if (isset($attrs['ref'])) {
  1277.                     $qn = new QName($attrs['ref']);
  1278.                     $this->currentElement = $qn->name;
  1279.                 else {
  1280.                     $this->currentElement = $attrs['name'];
  1281.                 }
  1282.                 $this->schema_element_stack[$this->currentElement;
  1283.                 if (!isset($attrs['namespace'])) {
  1284.                     $attrs['namespace'$this->schema;
  1285.                 }
  1286.  
  1287.                 if ($parent_tag == 'schema'{
  1288.                     $this->wsdl->elements[$this->schema][$this->currentElement$attrs;
  1289.                     $this->wsdl->elements[$this->schema][$this->currentElement]['complex'= false;
  1290.                     $this->schemaStatus = 'element';
  1291.                 elseif ($this->currentComplexType{
  1292.                     // we're inside a complexType
  1293.                     if ((isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order']&&
  1294.                          $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'== 'sequence')
  1295.                         && $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'== 'Array'{
  1296.                         $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['arrayType'= isset($attrs['type']$attrs['type': null;
  1297.                     }
  1298.                     $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements'][$this->currentElement$attrs;
  1299.                 else {
  1300.                     $this->wsdl->elements[$this->schema][$parentElement]['elements'][$this->currentElement$attrs;
  1301.                 }
  1302.                 break;
  1303.  
  1304.             case 'complexContent':
  1305.             case 'simpleContent':
  1306.                 break;
  1307.  
  1308.             case 'extension':
  1309.             case 'restriction':
  1310.                 if ($this->schemaStatus == 'complexType'{
  1311.                     if (!empty($attrs['base'])) {
  1312.                         $qn = new QName($attrs['base']);
  1313.                         $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'$qn->name;
  1314.  
  1315.                         // Types that extend from other types aren't
  1316.                         // *of* those types. Reflect this by denoting
  1317.                         // which type they extend. I'm leaving the
  1318.                         // 'type' setting here since I'm not sure what
  1319.                         // removing it might break at the moment.
  1320.                         if ($qname->name == 'extension'{
  1321.                             $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['extends'$qn->name;
  1322.                         }
  1323.                     else {
  1324.                         $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type''Struct';
  1325.                     }
  1326.                 }
  1327.                 break;
  1328.  
  1329.             case 'sequence':
  1330.                 if ($this->schemaStatus == 'complexType'{
  1331.                     $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'$qname->name;
  1332.                     if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) {
  1333.                         $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type''Array';
  1334.                     }
  1335.                 }
  1336.                 break;
  1337.  
  1338.             case 'all':
  1339.                 $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'$qname->name;
  1340.                 if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) {
  1341.                     $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type''Struct';
  1342.                 }
  1343.                 break;
  1344.  
  1345.             case 'choice':
  1346.                 $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['order'$qname->name;
  1347.                 if (!isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])) {
  1348.                     $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type''Array';
  1349.                 }
  1350.  
  1351.             case 'attribute':
  1352.                 if ($this->schemaStatus == 'complexType'{
  1353.                     if (isset($attrs['name'])) {
  1354.                         $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['attribute'][$attrs['name']] $attrs;
  1355.                     else {
  1356.                         if (isset($attrs['ref'])) {
  1357.                             $q = new QName($attrs['ref']);
  1358.                             foreach ($attrs as $k => $v{
  1359.                                 if ($k != 'ref' && strstr($k$q->name)) {
  1360.                                     $vq = new QName($v);
  1361.                                     if ($q->name == 'arrayType'{
  1362.                                         $this->wsdl->complexTypes[$this->schema][$this->currentComplexType][$q->name$vq->name. $vq->arrayInfo;
  1363.                                         $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type''Array';
  1364.                                         $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['namespace'$vq->ns;
  1365.                                     else {
  1366.                                         $this->wsdl->complexTypes[$this->schema][$this->currentComplexType][$q->name$vq->name;
  1367.                                     }
  1368.                                 }
  1369.                             }
  1370.                         }
  1371.                     }
  1372.                 }
  1373.                 break;
  1374.             }
  1375.  
  1376.             $this->schema_stack[$qname->name;
  1377.             break;
  1378.  
  1379.         case 'message':
  1380.             // sect 2.3 wsdl:message child wsdl:part
  1381.             switch ($qname->name{
  1382.             case 'part':
  1383.                 $qn = null;
  1384.                 if (isset($attrs['type'])) {
  1385.                     $qn = new QName($attrs['type']);
  1386.                 elseif (isset($attrs['element'])) {
  1387.                     $qn = new QName($attrs['element']);
  1388.                 }
  1389.                 if ($qn{
  1390.                     $attrs['type'$qn->name;
  1391.                     $attrs['namespace'$qn->ns;
  1392.                 }
  1393.                 $this->wsdl->messages[$this->currentMessage][$attrs['name']] $attrs;
  1394.                 // error in wsdl
  1395.  
  1396.             case 'documentation':
  1397.                 break;
  1398.  
  1399.             default:
  1400.                 break;
  1401.             }
  1402.             break;
  1403.  
  1404.         case 'portType':
  1405.             // sect 2.4
  1406.             switch ($qname->name{
  1407.             case 'operation':
  1408.                 // attributes: name
  1409.                 // children: wsdl:input wsdl:output wsdl:fault
  1410.                 $this->currentOperation = $attrs['name'];
  1411.                 $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation$attrs;
  1412.                 break;
  1413.  
  1414.             case 'input':
  1415.             case 'output':
  1416.             case 'fault':
  1417.                 // wsdl:input wsdl:output wsdl:fault
  1418.                 // attributes: name message parameterOrder(optional)
  1419.                 if ($this->currentOperation{
  1420.                     if (isset($this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name])) {
  1421.                         $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$namearray_merge($this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]$attrs);
  1422.                     else {
  1423.                         $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name$attrs;
  1424.                     }
  1425.                     if (array_key_exists('message'$attrs)) {
  1426.                         $qn = new QName($attrs['message']);
  1427.                         $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]['message'$qn->name;
  1428.                         $this->wsdl->portTypes[$this->currentPortType][$this->currentOperation][$name]['namespace'$qn->ns;
  1429.                     }
  1430.                 }
  1431.                 break;
  1432.  
  1433.             case 'documentation':
  1434.                 break;
  1435.  
  1436.             default:
  1437.                 break;
  1438.             }
  1439.             break;
  1440.  
  1441.         case 'binding':
  1442.             $ns $qname->ns ? $this->wsdl->namespaces[$qname->nsSCHEMA_WSDL;
  1443.             switch ($ns{
  1444.             case SCHEMA_SOAP:
  1445.             case SCHEMA_SOAP12:
  1446.                 // this deals with wsdl section 3 soap binding
  1447.                 switch ($qname->name{
  1448.                 case 'binding':
  1449.                     // sect 3.3
  1450.                     // soap:binding, attributes: transport(required), style(optional, default = document)
  1451.                     // if style is missing, it is assumed to be 'document'
  1452.                     if (!isset($attrs['style'])) {
  1453.                         $attrs['style''document';
  1454.                     }
  1455.                     $this->wsdl->bindings[$this->currentBindingarray_merge($this->wsdl->bindings[$this->currentBinding]$attrs);
  1456.                     break;
  1457.  
  1458.                 case 'operation':
  1459.                     // sect 3.4
  1460.                     // soap:operation, attributes: soapAction(required), style(optional, default = soap:binding:style)
  1461.                     if (!isset($attrs['style'])) {
  1462.                         $attrs['style'$this->wsdl->bindings[$this->currentBinding]['style'];
  1463.                     }
  1464.                     if (isset($this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation])) {
  1465.                         $this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperationarray_merge($this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation]$attrs);
  1466.                     else {
  1467.                         $this->wsdl->bindings[$this->currentBinding]['operations'][$this->currentOperation$attrs;
  1468.                     }
  1469.                     break;
  1470.  
  1471.                 case 'body':
  1472.                     // sect 3.5
  1473.                     // soap:body attributes:
  1474.                     // part - optional.  listed parts must appear in body, missing means all parts appear in body
  1475.                     // use - required. encoded|literal
  1476.                     // encodingStyle - optional.  space seperated list of encodings (uri's)
  1477.                     $this->wsdl->bindings[$this->currentBinding]
  1478.                                     ['operations'][$this->currentOperation][$this->opStatus$attrs;
  1479.                     break;
  1480.  
  1481.                 case 'fault':
  1482.                     // sect 3.6
  1483.                     // soap:fault attributes: name use  encodingStyle namespace
  1484.                     $this->wsdl->bindings[$this->currentBinding]
  1485.                                     ['operations'][$this->currentOperation][$this->opStatus$attrs;
  1486.                     break;
  1487.  
  1488.                 case 'header':
  1489.                     // sect 3.7
  1490.                     // soap:header attributes: message part use encodingStyle namespace
  1491.                     $this->wsdl->bindings[$this->currentBinding]
  1492.                                     ['operations'][$this->currentOperation][$this->opStatus]['headers'][$attrs;
  1493.                     break;
  1494.  
  1495.                 case 'headerfault':
  1496.                     // sect 3.7
  1497.                     // soap:header attributes: message part use encodingStyle namespace
  1498.                     $header count($this->wsdl->bindings[$this->currentBinding]
  1499.                                     ['operations'][$this->currentOperation][$this->opStatus]['headers'])-1;
  1500.                     $this->wsdl->bindings[$this->currentBinding]
  1501.                                     ['operations'][$this->currentOperation][$this->opStatus]['headers'][$header]['fault'$attrs;
  1502.                     break;
  1503.  
  1504.                 case 'documentation':
  1505.                     break;
  1506.  
  1507.                 default:
  1508.                     // error!  not a valid element inside binding
  1509.                     break;
  1510.                 }
  1511.                 break;
  1512.  
  1513.             case SCHEMA_WSDL:
  1514.                 // XXX verify correct namespace
  1515.                 // for now, default is the 'wsdl' namespace
  1516.                 // other possible namespaces include smtp, http, etc. for alternate bindings
  1517.                 switch ($qname->name{
  1518.                 case 'operation':
  1519.                     // sect 2.5
  1520.                     // wsdl:operation attributes: name
  1521.                     $this->currentOperation = $attrs['name'];
  1522.                     break;
  1523.  
  1524.                 case 'output':
  1525.                 case 'input':
  1526.                 case 'fault':
  1527.                     // sect 2.5
  1528.                     // wsdl:input attributes: name
  1529.                     $this->opStatus $qname->name;
  1530.                     break;
  1531.  
  1532.                 case 'documentation':
  1533.                     break;
  1534.  
  1535.                 default:
  1536.                     break;
  1537.                 }
  1538.                 break;
  1539.  
  1540.             case SCHEMA_WSDL_HTTP:
  1541.                 switch ($qname->name{
  1542.                 case 'binding':
  1543.                     // sect 4.4
  1544.                     // http:binding attributes: verb
  1545.                     // parent: wsdl:binding
  1546.                     $this->wsdl->bindings[$this->currentBindingarray_merge($this->wsdl->bindings[$this->currentBinding]$attrs);
  1547.                     break;
  1548.  
  1549.                 case 'operation':
  1550.                     // sect 4.5
  1551.                     // http:operation attributes: location
  1552.                     // parent: wsdl:operation
  1553.                     $this->wsdl->bindings[$this->currentBinding]['operations']
  1554.                                                         [$this->currentOperation$attrs;
  1555.                     break;
  1556.  
  1557.                 case 'urlEncoded':
  1558.                     // sect 4.6
  1559.                     // http:urlEncoded attributes: location
  1560.                     // parent: wsdl:input wsdl:output etc.
  1561.                     $this->wsdl->bindings[$this->currentBinding]['operations'][$this->opStatus]
  1562.                                                         [$this->currentOperation]['uri''urlEncoded';
  1563.                     break;
  1564.  
  1565.                 case 'urlReplacement':
  1566.                     // sect 4.7
  1567.                     // http:urlReplacement attributes: location
  1568.                     // parent: wsdl:input wsdl:output etc.
  1569.                     $this->wsdl->bindings[$this->currentBinding]['operations'][$this->opStatus]
  1570.                                                         [$this->currentOperation]['uri''urlReplacement';
  1571.                     break;
  1572.  
  1573.                 case 'documentation':
  1574.                     break;
  1575.  
  1576.                 default:
  1577.                     // error
  1578.                     break;
  1579.                 }
  1580.  
  1581.             case SCHEMA_MIME:
  1582.                 // sect 5
  1583.                 // all mime parts are children of wsdl:input, wsdl:output, etc.
  1584.                 // unsuported as of yet
  1585.                 switch ($qname->name{
  1586.                 case 'content':
  1587.                     // sect 5.3 mime:content
  1588.                     // <mime:content part="nmtoken"? type="string"?/>
  1589.                     // part attribute only required if content is child of multipart related,
  1590.                     //        it contains the name of the part
  1591.                     // type attribute contains the mime type
  1592.                 case 'multipartRelated':
  1593.                     // sect 5.4 mime:multipartRelated
  1594.                 case 'part':
  1595.                 case 'mimeXml':
  1596.                     // sect 5.6 mime:mimeXml
  1597.                     // <mime:mimeXml part="nmtoken"?/>
  1598.                     //
  1599.                 case 'documentation':
  1600.                     break;
  1601.  
  1602.                 default:
  1603.                     // error
  1604.                     break;
  1605.                 }
  1606.  
  1607.             case SCHEMA_DIME:
  1608.                 // DIME is defined in:
  1609.                 // http://gotdotnet.com/team/xml_wsspecs/dime/WSDL-Extension-for-DIME.htm
  1610.                 // all DIME parts are children of wsdl:input, wsdl:output, etc.
  1611.                 // unsuported as of yet
  1612.                 switch ($qname->name{
  1613.                 case 'message':
  1614.                     // sect 4.1 dime:message
  1615.                     // appears in binding section
  1616.                     $this->wsdl->bindings[$this->currentBinding]['dime'$attrs;
  1617.                     break;
  1618.  
  1619.                 default:
  1620.                     break;
  1621.                 }
  1622.  
  1623.             default:
  1624.                 break;
  1625.             }
  1626.             break;
  1627.  
  1628.         case 'service':
  1629.             $ns $qname->ns ? $this->wsdl->namespaces[$qname->nsSCHEMA_WSDL;
  1630.  
  1631.             switch ($qname->name{
  1632.             case 'port':
  1633.                 // sect 2.6 wsdl:port attributes: name binding
  1634.                 $this->currentPort = $attrs['name'];
  1635.                 $this->wsdl->services[$this->currentService]['ports'][$this->currentPort$attrs;
  1636.                 // XXX hack to deal with binding namespaces
  1637.                 $qn = new QName($attrs['binding']);
  1638.                 $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['binding'$qn->name;
  1639.                 $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['namespace'$qn->ns;
  1640.                 break;
  1641.  
  1642.             case 'address':
  1643.                 $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['address'$attrs;
  1644.                 // what TYPE of port is it?  SOAP or HTTP?
  1645.                 $ns $qname->ns ? $this->wsdl->namespaces[$qname->nsSCHEMA_WSDL;
  1646.                 switch ($ns{
  1647.                 case SCHEMA_WSDL_HTTP:
  1648.                     $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='http';
  1649.                     break;
  1650.  
  1651.                 case SCHEMA_SOAP:
  1652.                     $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='soap';
  1653.                     break;
  1654.  
  1655.                 default:
  1656.                     // Shouldn't happen, we'll assume SOAP.
  1657.                     $this->wsdl->services[$this->currentService]['ports'][$this->currentPort]['type']='soap';
  1658.                 }
  1659.  
  1660.                 break;
  1661.  
  1662.             case 'documentation':
  1663.                 break;
  1664.  
  1665.             default:
  1666.                 break;
  1667.             }
  1668.         }
  1669.  
  1670.         // Top level elements found under wsdl:definitions.
  1671.         switch ($qname->name{
  1672.         case 'import':
  1673.             // sect 2.1.1 wsdl:import attributes: namespace location
  1674.             if ((isset($attrs['location']|| isset($attrs['schemaLocation'])) &&
  1675.                 !isset($this->wsdl->imports[$attrs['namespace']])) {
  1676.                 $uri = isset($attrs['location']$attrs['location'$attrs['schemaLocation'];
  1677.                 $location @parse_url($uri);
  1678.                 if (!isset($location['scheme'])) {
  1679.                     $base @parse_url($this->uri);
  1680.                     $uri $this->mergeUrl($base$uri);
  1681.                 }
  1682.  
  1683.                 $this->wsdl->imports[$attrs['namespace']] $attrs;
  1684.                 $import_parser_class get_class($this);
  1685.                 $import_parser =new $import_parser_class($uri$this->wsdl$this->docs);
  1686.                 if ($import_parser->fault{
  1687.                     unset($this->wsdl->imports[$attrs['namespace']]);
  1688.                     return false;
  1689.                 }
  1690.                 $this->currentImport $attrs['namespace'];
  1691.             }
  1692.             // Continue on to the 'types' case - lack of break; is
  1693.             // intentional.
  1694.  
  1695.         case 'types':
  1696.             // sect 2.2 wsdl:types
  1697.             $this->status = 'types';
  1698.             break;
  1699.  
  1700.         case 'schema':
  1701.             // We can hit this at the top level if we've been asked to
  1702.             // import an XSD file.
  1703.             if (!empty($attrs['targetNamespace'])) {
  1704.                 $this->schema = $this->wsdl->getNamespaceAttributeName($attrs['targetNamespace']);
  1705.             else {
  1706.                 $this->schema = $this->wsdl->getNamespaceAttributeName($this->wsdl->tns);
  1707.             }
  1708.             $this->wsdl->complexTypes[$this->schema= array();
  1709.             $this->wsdl->elements[$this->schema= array();
  1710.             $this->schema_stack[$qname->name;
  1711.             $this->status = 'types';
  1712.             break;
  1713.  
  1714.         case 'message':
  1715.             // sect 2.3 wsdl:message attributes: name children:wsdl:part
  1716.             $this->status = 'message';
  1717.             if (isset($attrs['name'])) {
  1718.                 $this->currentMessage = $attrs['name'];
  1719.                 $this->wsdl->messages[$this->currentMessage= array();
  1720.             }
  1721.             break;
  1722.  
  1723.         case 'portType':
  1724.             // sect 2.4 wsdl:portType
  1725.             // attributes: name
  1726.             // children: wsdl:operation
  1727.             $this->status = 'portType';
  1728.             $this->currentPortType = $attrs['name'];
  1729.             $this->wsdl->portTypes[$this->currentPortType= array();
  1730.             break;
  1731.  
  1732.         case 'binding':
  1733.             // sect 2.5 wsdl:binding attributes: name type
  1734.             // children: wsdl:operation soap:binding http:binding
  1735.             if ($qname->ns && $qname->ns != $this->tns{
  1736.                 break;
  1737.             }
  1738.             $this->status = 'binding';
  1739.             $this->currentBinding = $attrs['name'];
  1740.             $qn = new QName($attrs['type']);
  1741.             $this->wsdl->bindings[$this->currentBinding]['type'$qn->name;
  1742.             $this->wsdl->bindings[$this->currentBinding]['namespace'$qn->ns;
  1743.             break;
  1744.  
  1745.         case 'service':
  1746.             // sect 2.7 wsdl:service attributes: name children: ports
  1747.             $this->currentService $attrs['name'];
  1748.             $this->wsdl->services[$this->currentService]['ports'= array();
  1749.             $this->status = 'service';
  1750.             break;
  1751.  
  1752.         case 'definitions':
  1753.             // sec 2.1 wsdl:definitions
  1754.             // attributes: name targetNamespace xmlns:*
  1755.             // children: wsdl:import wsdl:types wsdl:message wsdl:portType wsdl:binding wsdl:service
  1756.             $this->wsdl->definition = $attrs;
  1757.             foreach ($attrs as $key => $value{
  1758.                 if (strstr($key'xmlns:'!== false{
  1759.                     $qn = new QName($key);
  1760.                     // XXX need to refactor ns handling.
  1761.                     $this->wsdl->namespaces[$qn->name$value;
  1762.                     $this->wsdl->ns[$value$qn->name;
  1763.                     if ($key == 'targetNamespace' ||
  1764.                         strcasecmp($value,SOAP_SCHEMA== 0{
  1765.                         $this->soapns[$qn->name;
  1766.                     else {
  1767.                         if (in_array($value$this->_XMLSchema)) {
  1768.                             $this->wsdl->xsd = $value;
  1769.                         }
  1770.                     }
  1771.                 }
  1772.             }
  1773.             if (isset($ns&& $ns{
  1774.                 $namespace 'xmlns:' $ns;
  1775.                 if (!$this->wsdl->definition[$namespace]{
  1776.                     return $this->_raiseSoapFault("parse error, no namespace for $namespace"$this->uri);
  1777.                 }
  1778.                 $this->tns = $ns;
  1779.             }
  1780.             break;
  1781.         }
  1782.     }
  1783.  
  1784.     /**
  1785.      * end-element handler.
  1786.      */
  1787.     function endElement($parser$name)
  1788.     {
  1789.         $stacksize count($this->element_stack);
  1790.         if ($stacksize{
  1791.             if ($this->element_stack[$stacksize - 1== 'definitions'{
  1792.                 $this->status = '';
  1793.             }
  1794.             array_pop($this->element_stack);
  1795.         }
  1796.  
  1797.         if (stristr($name'schema')) {
  1798.             array_pop($this->schema_stack);
  1799.             $this->schema = '';
  1800.         }
  1801.  
  1802.         if ($this->schema{
  1803.             array_pop($this->schema_stack);
  1804.             if (count($this->schema_stack<= 1{
  1805.                 /* Correct the type for sequences with multiple
  1806.                  * elements. */
  1807.                 if (isset($this->currentComplexType&& isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'])
  1808.                     && $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type'== 'Array'
  1809.                     && array_key_exists('elements'$this->wsdl->complexTypes[$this->schema][$this->currentComplexType])
  1810.                     && count($this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements']> 1{
  1811.                         $this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['type''Struct';
  1812.                 }
  1813.             }
  1814.             if (stristr($name'complexType')) {
  1815.                 $this->currentComplexType = '';
  1816.                 if (count($this->schema_element_stack)) {
  1817.                     $this->currentElement = array_pop($this->schema_element_stack);
  1818.                 else {
  1819.                     $this->currentElement = '';
  1820.                 }
  1821.             elseif (stristr($name'element')) {
  1822.                 if (count($this->schema_element_stack)) {
  1823.                     $this->currentElement = array_pop($this->schema_element_stack);
  1824.                 else {
  1825.                     $this->currentElement = '';
  1826.                 }
  1827.             }
  1828.         }
  1829.     }
  1830.  
  1831.     /**
  1832.      * Element content handler.
  1833.      */
  1834.     function characterData($parser$data)
  1835.     {
  1836.         // Store the documentation in the WSDL file.
  1837.         if ($this->currentTag == 'documentation'{
  1838.             $data trim(preg_replace('/\s+/'' '$data));
  1839.             if (!strlen($data)) {
  1840.                 return;
  1841.             }
  1842.  
  1843.             switch ($this->status{
  1844.             case 'service':
  1845.                 $ptr =$this->wsdl->services[$this->currentService];
  1846.                 break;
  1847.  
  1848.             case 'portType':
  1849.                 $ptr =$this->wsdl->portTypes[$this->currentPortType][$this->currentOperation];
  1850.                 break;
  1851.  
  1852.             case 'binding':
  1853.                 $ptr =$this->wsdl->bindings[$this->currentBinding];
  1854.                 break;
  1855.  
  1856.             case 'message':
  1857.                 $ptr =$this->wsdl->messages[$this->currentMessage];
  1858.                 break;
  1859.  
  1860.             case 'operation':
  1861.                 break;
  1862.  
  1863.             case 'types':
  1864.                 if (isset($this->currentComplexType&&
  1865.                     isset($this->wsdl->complexTypes[$this->schema][$this->currentComplexType])) {
  1866.                     if ($this->currentElement{
  1867.                         $ptr =$this->wsdl->complexTypes[$this->schema][$this->currentComplexType]['elements'][$this->currentElement];
  1868.                     else {
  1869.                         $ptr =$this->wsdl->complexTypes[$this->schema][$this->currentComplexType];
  1870.                     }
  1871.                 }
  1872.                 break;
  1873.             }
  1874.  
  1875.             if (isset($ptr)) {
  1876.                 if (!isset($ptr['documentation'])) {
  1877.                     $ptr['documentation''';
  1878.                 else {
  1879.                     $ptr['documentation'.= ' ';
  1880.                 }
  1881.                 $ptr['documentation'.= $data;
  1882.             }
  1883.         }
  1884.     }
  1885.  
  1886.     /**
  1887.      * $parsed is an array returned by parse_url().
  1888.      *
  1889.      * @access private
  1890.      */
  1891.     function mergeUrl($parsed$path)
  1892.     {
  1893.         if (!is_array($parsed)) {
  1894.             return false;
  1895.         }
  1896.  
  1897.         $uri '';
  1898.         if (!empty($parsed['scheme'])) {
  1899.             $sep (strtolower($parsed['scheme']== 'mailto' ':' '://');
  1900.             $uri $parsed['scheme'$sep;
  1901.         }
  1902.  
  1903.         if (isset($parsed['pass'])) {
  1904.             $uri .= "$parsed[user]:$parsed[pass]@";
  1905.         elseif (isset($parsed['user'])) {
  1906.             $uri .= "$parsed[user]@";
  1907.         }
  1908.  
  1909.         if (isset($parsed['host'])) {
  1910.             $uri .= $parsed['host'];
  1911.         }
  1912.         if (isset($parsed['port'])) {
  1913.             $uri .= ":$parsed[port]";
  1914.         }
  1915.         if ($path[0!= '/' && isset($parsed['path'])) {
  1916.             if ($parsed['path'][strlen($parsed['path']- 1!= '/'{
  1917.                 $path dirname($parsed['path']'/' $path;
  1918.             else {
  1919.                 $path $parsed['path'$path;
  1920.             }
  1921.             $path $this->_normalize($path);
  1922.         }
  1923.         $sep $path[0== '/' '' '/';
  1924.         $uri .= $sep $path;
  1925.  
  1926.         return $uri;
  1927.     }
  1928.  
  1929.     function _normalize($path_str)
  1930.     {
  1931.         $pwd '';
  1932.         $strArr preg_split('/(\/)/'$path_str-1PREG_SPLIT_NO_EMPTY);
  1933.         $pwdArr '';
  1934.         $j = 0;
  1935.         for ($i = 0; $i count($strArr)$i++{
  1936.             if ($strArr[$i!= ' ..'{
  1937.                 if ($strArr[$i!= ' .'{
  1938.                     $pwdArr[$j$strArr[$i];
  1939.                     $j++;
  1940.                 }
  1941.             else {
  1942.                 array_pop($pwdArr);
  1943.                 $j--;
  1944.             }
  1945.         }
  1946.         $pStr implode('/'$pwdArr);
  1947.         $pwd (strlen($pStr> 0('/' $pStr'/';
  1948.         return $pwd;
  1949.     }
  1950.  
  1951. }
  1952.  
  1953. /**
  1954.  * Parses the types and methods used in web service objects into the internal
  1955.  * data structures used by SOAP_WSDL.
  1956.  *
  1957.  * Assumes the SOAP_WSDL class is unpopulated to start with.</