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

Source for file WSDL.php

Documentation is available at WSDL.php

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

Documentation generated on Mon, 11 Mar 2019 14:20:06 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.