root/trunk/lib/nusoap.php

Revision 64, 255.4 kB (checked in by yann, 5 years ago)

wsdl test send data...
encoding : utf-8

Line 
1 <?php
2
3 /*
4 $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
5
6 NuSOAP - Web Services Toolkit for PHP
7
8 Copyright (c) 2002 NuSphere Corporation
9
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2.1 of the License, or (at your option) any later version.
14
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 Lesser General Public License for more details.
19
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
24 If you have any questions or comments, please email:
25
26 Dietrich Ayala
27 dietrich@ganx4.com
28 http://dietrich.ganx4.com/nusoap
29
30 NuSphere Corporation
31 http://www.nusphere.com
32
33 */
34
35 /* load classes
36
37 // necessary classes
38 require_once('class.soapclient.php');
39 require_once('class.soap_val.php');
40 require_once('class.soap_parser.php');
41 require_once('class.soap_fault.php');
42
43 // transport classes
44 require_once('class.soap_transport_http.php');
45
46 // optional add-on classes
47 require_once('class.xmlschema.php');
48 require_once('class.wsdl.php');
49
50 // server class
51 require_once('class.soap_server.php');*/
52
53 // class variable emulation
54 // cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
55 $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = 9;
56
57 /**
58 *
59 * nusoap_base
60 *
61 * @author   Dietrich Ayala <dietrich@ganx4.com>
62 * @version  $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
63 * @access   public
64 */
65 class nusoap_base {
66     /**
67      * Identification for HTTP headers.
68      *
69      * @var string
70      * @access private
71      */
72     var $title = 'NuSOAP';
73     /**
74      * Version for HTTP headers.
75      *
76      * @var string
77      * @access private
78      */
79     var $version = '0.7.2';
80     /**
81      * CVS revision for HTTP headers.
82      *
83      * @var string
84      * @access private
85      */
86     var $revision = '$Revision: 1.94 $';
87     /**
88      * Current error string (manipulated by getError/setError)
89      *
90      * @var string
91      * @access private
92      */
93     var $error_str = '';
94     /**
95      * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
96      *
97      * @var string
98      * @access private
99      */
100     var $debug_str = '';
101     /**
102      * toggles automatic encoding of special characters as entities
103      * (should always be true, I think)
104      *
105      * @var boolean
106      * @access private
107      */
108     var $charencoding = true;
109     /**
110      * the debug level for this instance
111      *
112      * @var    integer
113      * @access private
114      */
115     var $debugLevel;
116
117     /**
118     * set schema version
119     *
120     * @var      string
121     * @access   public
122     */
123     var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
124     
125     /**
126     * charset encoding for outgoing messages
127     *
128     * @var      string
129     * @access   public
130     */
131     //var $soap_defencoding = 'ISO-8859-1';
132     var $soap_defencoding = 'UTF-8';
133
134     /**
135     * namespaces in an array of prefix => uri
136     *
137     * this is "seeded" by a set of constants, but it may be altered by code
138     *
139     * @var      array
140     * @access   public
141     */
142     var $namespaces = array(
143         'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
144         'xsd' => 'http://www.w3.org/2001/XMLSchema',
145         'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
146         'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/'
147         );
148
149     /**
150     * namespaces used in the current context, e.g. during serialization
151     *
152     * @var      array
153     * @access   private
154     */
155     var $usedNamespaces = array();
156
157     /**
158     * XML Schema types in an array of uri => (array of xml type => php type)
159     * is this legacy yet?
160     * no, this is used by the xmlschema class to verify type => namespace mappings.
161     * @var      array
162     * @access   public
163     */
164     var $typemap = array(
165     'http://www.w3.org/2001/XMLSchema' => array(
166         'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
167         'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
168         'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
169         // abstract "any" types
170         'anyType'=>'string','anySimpleType'=>'string',
171         // derived datatypes
172         'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
173         'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
174         'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
175         'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
176     'http://www.w3.org/2000/10/XMLSchema' => array(
177         'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
178         'float'=>'double','dateTime'=>'string',
179         'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
180     'http://www.w3.org/1999/XMLSchema' => array(
181         'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
182         'float'=>'double','dateTime'=>'string',
183         'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
184     'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
185     'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
186     'http://xml.apache.org/xml-soap' => array('Map')
187     );
188
189     /**
190     * XML entities to convert
191     *
192     * @var      array
193     * @access   public
194     * @deprecated
195     * @see    expandEntities
196     */
197     var $xmlEntities = array('quot' => '"','amp' => '&',
198         'lt' => '<','gt' => '>','apos' => "'");
199
200     /**
201     * constructor
202     *
203     * @access    public
204     */
205     function nusoap_base() {
206         $this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel;
207     }
208
209     /**
210     * gets the global debug level, which applies to future instances
211     *
212     * @return    integer    Debug level 0-9, where 0 turns off
213     * @access    public
214     */
215     function getGlobalDebugLevel() {
216         return $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel;
217     }
218
219     /**
220     * sets the global debug level, which applies to future instances
221     *
222     * @param    int    $level    Debug level 0-9, where 0 turns off
223     * @access    public
224     */
225     function setGlobalDebugLevel($level) {
226         $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = $level;
227     }
228
229     /**
230     * gets the debug level for this instance
231     *
232     * @return    int    Debug level 0-9, where 0 turns off
233     * @access    public
234     */
235     function getDebugLevel() {
236         return $this->debugLevel;
237     }
238
239     /**
240     * sets the debug level for this instance
241     *
242     * @param    int    $level    Debug level 0-9, where 0 turns off
243     * @access    public
244     */
245     function setDebugLevel($level) {
246         $this->debugLevel = $level;
247     }
248
249     /**
250     * adds debug data to the instance debug string with formatting
251     *
252     * @param    string $string debug data
253     * @access   private
254     */
255     function debug($string){
256         if ($this->debugLevel > 0) {
257             $this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n");
258         }
259     }
260
261     /**
262     * adds debug data to the instance debug string without formatting
263     *
264     * @param    string $string debug data
265     * @access   public
266     */
267     function appendDebug($string){
268         if ($this->debugLevel > 0) {
269             // it would be nice to use a memory stream here to use
270             // memory more efficiently
271             $this->debug_str .= $string;
272         }
273     }
274
275     /**
276     * clears the current debug data for this instance
277     *
278     * @access   public
279     */
280     function clearDebug() {
281         // it would be nice to use a memory stream here to use
282         // memory more efficiently
283         $this->debug_str = '';
284     }
285
286     /**
287     * gets the current debug data for this instance
288     *
289     * @return   debug data
290     * @access   public
291     */
292     function &getDebug() {
293         // it would be nice to use a memory stream here to use
294         // memory more efficiently
295         return $this->debug_str;
296     }
297
298     /**
299     * gets the current debug data for this instance as an XML comment
300     * this may change the contents of the debug data
301     *
302     * @return   debug data as an XML comment
303     * @access   public
304     */
305     function &getDebugAsXMLComment() {
306         // it would be nice to use a memory stream here to use
307         // memory more efficiently
308         while (strpos($this->debug_str, '--')) {
309             $this->debug_str = str_replace('--', '- -', $this->debug_str);
310         }
311         return "<!--\n" . $this->debug_str . "\n-->";
312     }
313
314     /**
315     * expands entities, e.g. changes '<' to '&lt;'.
316     *
317     * @param    string    $val    The string in which to expand entities.
318     * @access    private
319     */
320     function expandEntities($val) {
321         if ($this->charencoding) {
322             $val = str_replace('&', '&amp;', $val);
323             $val = str_replace("'", '&apos;', $val);
324             $val = str_replace('"', '&quot;', $val);
325             $val = str_replace('<', '&lt;', $val);
326             $val = str_replace('>', '&gt;', $val);
327         }
328         return $val;
329     }
330
331     /**
332     * returns error string if present
333     *
334     * @return   mixed error string or false
335     * @access   public
336     */
337     function getError(){
338         if($this->error_str != ''){
339             return $this->error_str;
340         }
341         return false;
342     }
343
344     /**
345     * sets error string
346     *
347     * @return   boolean $string error string
348     * @access   private
349     */
350     function setError($str){
351         $this->error_str = $str;
352     }
353
354     /**
355     * detect if array is a simple array or a struct (associative array)
356     *
357     * @param    mixed    $val    The PHP array
358     * @return    string    (arraySimple|arrayStruct)
359     * @access    private
360     */
361     function isArraySimpleOrStruct($val) {
362         $keyList = array_keys($val);
363         foreach ($keyList as $keyListValue) {
364             if (!is_int($keyListValue)) {
365                 return 'arrayStruct';
366             }
367         }
368         return 'arraySimple';
369     }
370
371     /**
372     * serializes PHP values in accordance w/ section 5. Type information is
373     * not serialized if $use == 'literal'.
374     *
375     * @param    mixed    $val    The value to serialize
376     * @param    string    $name    The name (local part) of the XML element
377     * @param    string    $type    The XML schema type (local part) for the element
378     * @param    string    $name_ns    The namespace for the name of the XML element
379     * @param    string    $type_ns    The namespace for the type of the element
380     * @param    array    $attributes    The attributes to serialize as name=>value pairs
381     * @param    string    $use    The WSDL "use" (encoded|literal)
382     * @return    string    The serialized element, possibly with child elements
383     * @access    public
384     */
385     function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded'){
386         $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use");
387         $this->appendDebug('value=' . $this->varDump($val));
388         $this->appendDebug('attributes=' . $this->varDump($attributes));
389         
390         if(is_object($val) && get_class($val) == 'soapval'){
391             return $val->serialize($use);
392         }
393         // force valid name if necessary
394         if (is_numeric($name)) {
395             $name = '__numeric_' . $name;
396         } elseif (! $name) {
397             $name = 'noname';
398         }
399         // if name has ns, add ns prefix to name
400         $xmlns = '';
401         if($name_ns){
402             $prefix = 'nu'.rand(1000,9999);
403             $name = $prefix.':'.$name;
404             $xmlns .= " xmlns:$prefix=\"$name_ns\"";
405         }
406         // if type is prefixed, create type prefix
407         if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
408             // need to fix this. shouldn't default to xsd if no ns specified
409             // w/o checking against typemap
410             $type_prefix = 'xsd';
411         } elseif($type_ns){
412             $type_prefix = 'ns'.rand(1000,9999);
413             $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
414         }
415         // serialize attributes if present
416         $atts = '';
417         if($attributes){
418             foreach($attributes as $k => $v){
419                 $atts .= " $k=\"".$this->expandEntities($v).'"';
420             }
421         }
422         // serialize null value
423         if (is_null($val)) {
424             if ($use == 'literal') {
425                 // TODO: depends on minOccurs
426                 return "<$name$xmlns $atts/>";
427             } else {
428                 if (isset($type) && isset($type_prefix)) {
429                     $type_str = " xsi:type=\"$type_prefix:$type\"";
430                 } else {
431                     $type_str = '';
432                 }
433                 return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>";
434             }
435         }
436         // serialize if an xsd built-in primitive type
437         if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
438             if (is_bool($val)) {
439                 if ($type == 'boolean') {
440                     $val = $val ? 'true' : 'false';
441                 } elseif (! $val) {
442                     $val = 0;
443                 }
444             } else if (is_string($val)) {
445                 $val = $this->expandEntities($val);
446             }
447             if ($use == 'literal') {
448                 return "<$name$xmlns $atts>$val</$name>";
449             } else {
450                 return "<$name$xmlns $atts xsi:type=\"xsd:$type\">$val</$name>";
451             }
452         }
453         // detect type and serialize
454         $xml = '';
455         switch(true) {
456             case (is_bool($val) || $type == 'boolean'):
457                 if ($type == 'boolean') {
458                     $val = $val ? 'true' : 'false';
459                 } elseif (! $val) {
460                     $val = 0;
461                 }
462                 if ($use == 'literal') {
463                     $xml .= "<$name$xmlns $atts>$val</$name>";
464                 } else {
465                     $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
466                 }
467                 break;
468             case (is_int($val) || is_long($val) || $type == 'int'):
469                 if ($use == 'literal') {
470                     $xml .= "<$name$xmlns $atts>$val</$name>";
471                 } else {
472                     $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
473                 }
474                 break;
475             case (is_float($val)|| is_double($val) || $type == 'float'):
476                 if ($use == 'literal') {
477                     $xml .= "<$name$xmlns $atts>$val</$name>";
478                 } else {
479                     $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
480                 }
481                 break;
482             case (is_string($val) || $type == 'string'):
483                 $val = $this->expandEntities($val);
484                 if ($use == 'literal') {
485                     $xml .= "<$name$xmlns $atts>$val</$name>";
486                 } else {
487                     $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
488                 }
489                 break;
490             case is_object($val):
491                 if (! $name) {
492                     $name = get_class($val);
493                     $this->debug("In serialize_val, used class name $name as element name");
494                 } else {
495                     $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
496                 }
497                 foreach(get_object_vars($val) as $k => $v){
498                     $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
499                 }
500                 $xml .= '<'.$name.'>'.$pXml.'</'.$name.'>';
501                 break;
502             break;
503             case (is_array($val) || $type):
504                 // detect if struct or array
505                 $valueType = $this->isArraySimpleOrStruct($val);
506                 if($valueType=='arraySimple' || ereg('^ArrayOf',$type)){
507                     $i = 0;
508                     if(is_array($val) && count($val)> 0){
509                         foreach($val as $v){
510                             if(is_object($v) && get_class($v) ==  'soapval'){
511                                 $tt_ns = $v->type_ns;
512                                 $tt = $v->type;
513                             } elseif (is_array($v)) {
514                                 $tt = $this->isArraySimpleOrStruct($v);
515                             } else {
516                                 $tt = gettype($v);
517                             }
518                             $array_types[$tt] = 1;
519                             // TODO: for literal, the name should be $name
520                             $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
521                             ++$i;
522                         }
523                         if(count($array_types) > 1){
524                             $array_typename = 'xsd:anyType';
525                         } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
526                             if ($tt == 'integer') {
527                                 $tt = 'int';
528                             }
529                             $array_typename = 'xsd:'.$tt;
530                         } elseif(isset($tt) && $tt == 'arraySimple'){
531                             $array_typename = 'SOAP-ENC:Array';
532                         } elseif(isset($tt) && $tt == 'arrayStruct'){
533                             $array_typename = 'unnamed_struct_use_soapval';
534                         } else {
535                             // if type is prefixed, create type prefix
536                             if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){
537                                  $array_typename = 'xsd:' . $tt;
538                             } elseif ($tt_ns) {
539                                 $tt_prefix = 'ns' . rand(1000, 9999);
540                                 $array_typename = "$tt_prefix:$tt";
541                                 $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
542                             } else {
543                                 $array_typename = $tt;
544                             }
545                         }
546                         $array_type = $i;
547                         if ($use == 'literal') {
548                             $type_str = '';
549                         } else if (isset($type) && isset($type_prefix)) {
550                             $type_str = " xsi:type=\"$type_prefix:$type\"";
551                         } else {
552                             $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
553                         }
554                     // empty array
555                     } else {
556                         if ($use == 'literal') {
557                             $type_str = '';
558                         } else if (isset($type) && isset($type_prefix)) {
559                             $type_str = " xsi:type=\"$type_prefix:$type\"";
560                         } else {
561                             $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\"";
562                         }
563                     }
564                     // TODO: for array in literal, there is no wrapper here
565                     $xml = "<$name$xmlns$type_str$atts>".$xml."</$name>";
566                 } else {
567                     // got a struct
568                     if(isset($type) && isset($type_prefix)){
569                         $type_str = " xsi:type=\"$type_prefix:$type\"";
570                     } else {
571                         $type_str = '';
572                     }
573                     if ($use == 'literal') {
574                         $xml .= "<$name$xmlns $atts>";
575                     } else {
576                         $xml .= "<$name$xmlns$type_str$atts>";
577                     }
578                     foreach($val as $k => $v){
579                         // Apache Map
580                         if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
581                             $xml .= '<item>';
582                             $xml .= $this->serialize_val($k,'key',false,false,false,false,$use);
583                             $xml .= $this->serialize_val($v,'value',false,false,false,false,$use);
584                             $xml .= '</item>';
585                         } else {
586                             $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
587                         }
588                     }
589                     $xml .= "</$name>";
590                 }
591                 break;
592             default:
593                 $xml .= 'not detected, got '.gettype($val).' for '.$val;
594                 break;
595         }
596         return $xml;
597     }
598
599     /**
600     * serializes a message
601     *
602     * @param string $body the XML of the SOAP body
603     * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers
604     * @param array $namespaces optional the namespaces used in generating the body and headers
605     * @param string $style optional (rpc|document)
606     * @param string $use optional (encoded|literal)
607     * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
608     * @return string the message
609     * @access public
610     */
611     function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded',$encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'){
612     // TODO: add an option to automatically run utf8_encode on $body and $headers
613     // if $this->soap_defencoding is UTF-8.  Not doing this automatically allows
614     // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
615
616     $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
617     $this->debug("headers:");
618     $this->appendDebug($this->varDump($headers));
619     $this->debug("namespaces:");
620     $this->appendDebug($this->varDump($namespaces));
621
622     // serialize namespaces
623     $ns_string = '';
624     foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
625         $ns_string .= " xmlns:$k=\"$v\"";
626     }
627     if($encodingStyle) {
628         $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
629     }
630
631     // serialize headers
632     if($headers){
633         if (is_array($headers)) {
634             $xml = '';
635             foreach ($headers as $header) {
636                 $xml .= $this->serialize_val($header, false, false, false, false, false, $use);
637             }
638             $headers = $xml;
639             $this->debug("In serializeEnvelope, serialzied array of headers to $headers");
640         }
641         $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
642     }
643     // serialize envelope
644     return
645     '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
646     '<SOAP-ENV:Envelope'.$ns_string.">".
647     $headers.
648     "<SOAP-ENV:Body>".
649         $body.
650     "</SOAP-ENV:Body>".
651     "</SOAP-ENV:Envelope>";
652     }
653
654     /**
655      * formats a string to be inserted into an HTML stream
656      *
657      * @param string $str The string to format
658      * @return string The formatted string
659      * @access public
660      * @deprecated
661      */
662     function formatDump($str){
663         $str = htmlspecialchars($str);
664         return nl2br($str);
665     }
666
667     /**
668     * contracts (changes namespace to prefix) a qualified name
669     *
670     * @param    string $qname qname
671     * @return    string contracted qname
672     * @access   private
673     */
674     function contractQname($qname){
675         // get element namespace
676         //$this->xdebug("Contract $qname");
677         if (strrpos($qname, ':')) {
678             // get unqualified name
679             $name = substr($qname, strrpos($qname, ':') + 1);
680             // get ns
681             $ns = substr($qname, 0, strrpos($qname, ':'));
682             $p = $this->getPrefixFromNamespace($ns);
683             if ($p) {
684                 return $p . ':' . $name;
685             }
686             return $qname;
687         } else {
688             return $qname;
689         }
690     }
691
692     /**
693     * expands (changes prefix to namespace) a qualified name
694     *
695     * @param    string $string qname
696     * @return    string expanded qname
697     * @access   private
698     */
699     function expandQname($qname){
700         // get element prefix
701         if(strpos($qname,':') && !ereg('^http://',$qname)){
702             // get unqualified name
703             $name = substr(strstr($qname,':'),1);
704             // get ns prefix
705             $prefix = substr($qname,0,strpos($qname,':'));
706             if(isset($this->namespaces[$prefix])){
707                 return $this->namespaces[$prefix].':'.$name;
708             } else {
709                 return $qname;
710             }
711         } else {
712             return $qname;
713         }
714     }
715
716     /**
717     * returns the local part of a prefixed string
718     * returns the original string, if not prefixed
719     *
720     * @param string $str The prefixed string
721     * @return string The local part
722     * @access public
723     */
724     function getLocalPart($str){
725         if($sstr = strrchr($str,':')){
726             // get unqualified name
727             return substr( $sstr, 1 );
728         } else {
729             return $str;
730         }
731     }
732
733     /**
734     * returns the prefix part of a prefixed string
735     * returns false, if not prefixed
736     *
737     * @param string $str The prefixed string
738     * @return mixed The prefix or false if there is no prefix
739     * @access public
740     */
741     function getPrefix($str){
742         if($pos = strrpos($str,':')){
743             // get prefix
744             return substr($str,0,$pos);
745         }
746         return false;
747     }
748
749     /**
750     * pass it a prefix, it returns a namespace
751     *
752     * @param string $prefix The prefix
753     * @return mixed The namespace, false if no namespace has the specified prefix
754     * @access public
755     */
756     function getNamespaceFromPrefix($prefix){
757         if (isset($this->namespaces[$prefix])) {
758             return $this->namespaces[$prefix];
759         }
760         //$this->setError("No namespace registered for prefix '$prefix'");
761         return false;
762     }
763
764     /**
765     * returns the prefix for a given namespace (or prefix)
766     * or false if no prefixes registered for the given namespace
767     *
768     * @param string $ns The namespace
769     * @return mixed The prefix, false if the namespace has no prefixes
770     * @access public
771     */
772     function getPrefixFromNamespace($ns) {
773         foreach ($this->namespaces as $p => $n) {
774             if ($ns == $n || $ns == $p) {
775                 $this->usedNamespaces[$p] = $n;
776                 return $p;
777             }
778         }
779         return false;
780     }
781
782     /**
783     * returns the time in ODBC canonical form with microseconds
784     *
785     * @return string The time in ODBC canonical form with microseconds
786     * @access public
787     */
788     function getmicrotime() {
789         if (function_exists('gettimeofday')) {
790             $tod = gettimeofday();
791             $sec = $tod['sec'];
792             $usec = $tod['usec'];
793         } else {
794             $sec = time();
795             $usec = 0;
796         }
797         return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
798     }
799
800     /**
801      * Returns a string with the output of var_dump
802      *
803      * @param mixed $data The variable to var_dump
804      * @return string The output of var_dump
805      * @access public
806      */
807     function varDump($data) {
808         ob_start();
809         var_dump($data);
810         $ret_val = ob_get_contents();
811         ob_end_clean();
812         return $ret_val;
813     }
814 }
815
816 // XML Schema Datatype Helper Functions
817
818 //xsd:dateTime helpers
819
820 /**
821 * convert unix timestamp to ISO 8601 compliant date string
822 *
823 * @param    string $timestamp Unix time stamp
824 * @access   public
825 */
826 function timestamp_to_iso8601($timestamp,$utc=true){
827     $datestr = date('Y-m-d\TH:i:sO',$timestamp);
828     if($utc){
829         $eregStr =
830         '([0-9]{4})-'.    // centuries & years CCYY-
831         '([0-9]{2})-'.    // months MM-
832         '([0-9]{2})'.    // days DD
833         'T'.            // separator T
834         '([0-9]{2}):'.    // hours hh:
835         '([0-9]{2}):'.    // minutes mm:
836         '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
837         '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
838
839         if(ereg($eregStr,$datestr,$regs)){
840             return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
841         }
842         return false;
843     } else {
844         return $datestr;
845     }
846 }
847
848 /**
849 * convert ISO 8601 compliant date string to unix timestamp
850 *
851 * @param    string $datestr ISO 8601 compliant date string
852 * @access   public
853 */
854 function iso8601_to_timestamp($datestr){
855     $eregStr =
856     '([0-9]{4})-'.    // centuries & years CCYY-
857     '([0-9]{2})-'.    // months MM-
858     '([0-9]{2})'.    // days DD
859     'T'.            // separator T
860     '([0-9]{2}):'.    // hours hh:
861     '([0-9]{2}):'.    // minutes mm:
862     '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
863     '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
864     if(ereg($eregStr,$datestr,$regs)){
865         // not utc
866         if($regs[8] != 'Z'){
867             $op = substr($regs[8],0,1);
868             $h = substr($regs[8],1,2);
869             $m = substr($regs[8],strlen($regs[8])-2,2);
870             if($op == '-'){
871                 $regs[4] = $regs[4] + $h;
872                 $regs[5] = $regs[5] + $m;
873             } elseif($op == '+'){
874                 $regs[4] = $regs[4] - $h;
875                 $regs[5] = $regs[5] - $m;
876             }
877         }
878         return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
879     } else {
880         return false;
881     }
882 }
883
884 /**
885 * sleeps some number of microseconds
886 *
887 * @param    string $usec the number of microseconds to sleep
888 * @access   public
889 * @deprecated
890 */
891 function usleepWindows($usec)
892 {
893     $start = gettimeofday();
894     
895     do
896     {
897         $stop = gettimeofday();
898         $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
899         + $stop['usec'] - $start['usec'];
900     }
901     while ($timePassed < $usec);
902 }
903
904 ?><?php
905
906
907
908 /**
909 * Contains information for a SOAP fault.
910 * Mainly used for returning faults from deployed functions
911 * in a server instance.
912 * @author   Dietrich Ayala <dietrich@ganx4.com>
913 * @version  $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
914 * @access public
915 */
916 class soap_fault extends nusoap_base {
917     /**
918      * The fault code (client|server)
919      * @var string
920      * @access private
921      */
922     var $faultcode;
923     /**
924      * The fault actor
925      * @var string
926      * @access private
927      */
928     var $faultactor;
929     /**
930      * The fault string, a description of the fault
931      * @var string
932      * @access private
933      */
934     var $faultstring;
935     /**
936      * The fault detail, typically a string or array of string
937      * @var mixed
938      * @access private
939      */
940     var $faultdetail;
941
942     /**
943     * constructor
944     *
945     * @param string $faultcode (client | server)
946     * @param string $faultactor only used when msg routed between multiple actors
947     * @param string $faultstring human readable error message
948     * @param mixed $faultdetail detail, typically a string or array of string
949     */
950     function soap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
951         parent::nusoap_base();
952         $this->faultcode = $faultcode;
953         $this->faultactor = $faultactor;
954         $this->faultstring = $faultstring;
955         $this->faultdetail = $faultdetail;
956     }
957
958     /**
959     * serialize a fault
960     *
961     * @return    string    The serialization of the fault instance.
962     * @access   public
963     */
964     function serialize(){
965         $ns_string = '';
966         foreach($this->namespaces as $k => $v){
967             $ns_string .= "\n  xmlns:$k=\"$v\"";
968         }
969         $return_msg =
970             '<?xml version="1.0" encoding="'.$this->soap_defencoding.'"?>'.
971             '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
972                 '<SOAP-ENV:Body>'.
973                 '<SOAP-ENV:Fault>'.
974                     $this->serialize_val($this->faultcode, 'faultcode').
975                     $this->serialize_val($this->faultactor, 'faultactor').
976                     $this->serialize_val($this->faultstring, 'faultstring').
977                     $this->serialize_val($this->faultdetail, 'detail').
978                 '</SOAP-ENV:Fault>'.
979                 '</SOAP-ENV:Body>'.
980             '</SOAP-ENV:Envelope>';
981         return $return_msg;
982     }
983 }
984
985
986
987 ?><?php
988
989
990
991 /**
992 * parses an XML Schema, allows access to it's data, other utility methods
993 * no validation... yet.
994 * very experimental and limited. As is discussed on XML-DEV, I'm one of the people
995 * that just doesn't have time to read the spec(s) thoroughly, and just have a couple of trusty
996 * tutorials I refer to :)
997 *
998 * @author   Dietrich Ayala <dietrich@ganx4.com>
999 * @version  $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
1000 * @access   public
1001 */
1002 class XMLSchema extends nusoap_base  {
1003     
1004     // files
1005     var $schema = '';
1006     var $xml = '';
1007     // namespaces
1008     var $enclosingNamespaces;
1009     // schema info
1010     var $schemaInfo = array();
1011     var $schemaTargetNamespace = '';
1012     // types, elements, attributes defined by the schema
1013     var $attributes = array();
1014     var $complexTypes = array();
1015     var $complexTypeStack = array();
1016     var $currentComplexType = null;
1017     var $elements = array();
1018     var $elementStack = array();
1019     var $currentElement = null;
1020     var $simpleTypes = array();
1021     var $simpleTypeStack = array();
1022     var $currentSimpleType = null;
1023     // imports
1024     var $imports = array();
1025     // parser vars
1026     var $parser;
1027     var $position = 0;
1028     var $depth = 0;
1029     var $depth_array = array();
1030     var $message = array();
1031     var $defaultNamespace = array();
1032     
1033     /**
1034     * constructor
1035     *
1036     * @param    string $schema schema document URI
1037     * @param    string $xml xml document URI
1038     * @param    string $namespaces namespaces defined in enclosing XML
1039     * @access   public
1040     */
1041     function XMLSchema($schema='',$xml='',$namespaces=array()){
1042         parent::nusoap_base();
1043         $this->debug('xmlschema class instantiated, inside constructor');
1044         // files
1045         $this->schema = $schema;
1046         $this->xml = $xml;
1047
1048         // namespaces
1049         $this->enclosingNamespaces = $namespaces;
1050         $this->namespaces = array_merge($this->namespaces, $namespaces);
1051
1052         // parse schema file
1053         if($schema != ''){
1054             $this->debug('initial schema file: '.$schema);
1055             $this->parseFile($schema, 'schema');
1056         }
1057
1058         // parse xml file
1059         if($xml != ''){
1060             $this->debug('initial xml file: '.$xml);
1061             $this->parseFile($xml, 'xml');
1062         }
1063
1064     }
1065
1066     /**
1067     * parse an XML file
1068     *
1069     * @param string $xml, path/URL to XML file
1070     * @param string $type, (schema | xml)
1071     * @return boolean
1072     * @access public
1073     */
1074     function parseFile($xml,$type){
1075         // parse xml file
1076         if($xml != ""){
1077             $xmlStr = @join("",@file($xml));
1078             if($xmlStr == ""){
1079                 $msg = 'Error reading XML from '.$xml;
1080                 $this->setError($msg);
1081                 $this->debug($msg);
1082             return false;
1083             } else {
1084                 $this->debug("parsing $xml");
1085                 $this->parseString($xmlStr,$type);
1086                 $this->debug("done parsing $xml");
1087             return true;
1088             }
1089         }
1090         return false;
1091     }
1092
1093     /**
1094     * parse an XML string
1095     *
1096     * @param    string $xml path or URL
1097     * @param string $type, (schema|xml)
1098     * @access   private
1099     */
1100     function parseString($xml,$type){
1101         // parse xml string
1102         if($xml != ""){
1103
1104             // Create an XML parser.
1105             $this->parser = xml_parser_create();
1106             // Set the options for parsing the XML data.
1107             xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
1108
1109             // Set the object for the parser.
1110             xml_set_object($this->parser, $this);
1111
1112             // Set the element handlers for the parser.
1113             if($type == "schema"){
1114                 xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
1115                 xml_set_character_data_handler($this->parser,'schemaCharacterData');
1116             } elseif($type == "xml"){
1117                 xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
1118                 xml_set_character_data_handler($this->parser,'xmlCharacterData');
1119             }
1120
1121             // Parse the XML file.
1122             if(!xml_parse($this->parser,$xml,true)){
1123             // Display an error message.
1124                 $errstr = sprintf('XML error parsing XML schema on line %d: %s',
1125                 xml_get_current_line_number($this->parser),
1126                 xml_error_string(xml_get_error_code($this->parser))
1127                 );
1128                 $this->debug($errstr);
1129                 $this->debug("XML payload:\n" . $xml);
1130                 $this->setError($errstr);
1131             }
1132             
1133             xml_parser_free($this->parser);
1134         } else{
1135             $this->debug('no xml passed to parseString()!!');
1136             $this->setError('no xml passed to parseString()!!');
1137         }
1138     }
1139
1140     /**
1141     * start-element handler
1142     *
1143     * @param    string $parser XML parser object
1144     * @param    string $name element name
1145     * @param    string $attrs associative array of attributes
1146     * @access   private
1147     */
1148     function schemaStartElement($parser, $name, $attrs) {
1149         
1150         // position in the total number of elements, starting from 0
1151         $pos = $this->position++;
1152         $depth = $this->depth++;
1153         // set self as current value for this depth
1154         $this->depth_array[$depth] = $pos;
1155         $this->message[$pos] = array('cdata' => '');
1156         if ($depth > 0) {
1157             $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
1158         } else {
1159             $this->defaultNamespace[$pos] = false;
1160         }
1161
1162         // get element prefix
1163         if($prefix = $this->getPrefix($name)){
1164             // get unqualified name
1165             $name = $this->getLocalPart($name);
1166         } else {
1167             $prefix = '';
1168         }
1169         
1170         // loop thru attributes, expanding, and registering namespace declarations
1171         if(count($attrs) > 0){
1172             foreach($attrs as $k => $v){
1173                 // if ns declarations, add to class level array of valid namespaces
1174                 if(ereg("^xmlns",$k)){
1175                     //$this->xdebug("$k: $v");
1176                     //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
1177                     if($ns_prefix = substr(strrchr($k,':'),1)){
1178                         //$this->xdebug("Add namespace[$ns_prefix] = $v");
1179                         $this->namespaces[$ns_prefix] = $v;
1180                     } else {
1181                         $this->defaultNamespace[$pos] = $v;
1182                         if (! $this->getPrefixFromNamespace($v)) {
1183                             $this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
1184                         }
1185                     }
1186                     if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema'){
1187                         $this->XMLSchemaVersion = $v;
1188                         $this->namespaces['xsi'] = $v.'-instance';
1189                     }
1190                 }
1191             }
1192             foreach($attrs as $k => $v){
1193                 // expand each attribute
1194                 $k = strpos($k,':') ? $this->expandQname($k) : $k;
1195                 $v = strpos($v,':') ? $this->expandQname($v) : $v;
1196                 $eAttrs[$k] = $v;
1197             }
1198             $attrs = $eAttrs;
1199         } else {
1200             $attrs = array();
1201         }
1202         // find status, register data
1203         switch($name){
1204             case 'all':            // (optional) compositor content for a complexType
1205             case 'choice':
1206             case 'group':
1207             case 'sequence':
1208                 //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
1209                 $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
1210                 //if($name == 'all' || $name == 'sequence'){
1211                 //    $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1212                 //}
1213             break;
1214             case 'attribute':    // complexType attribute
1215                 //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
1216                 $this->xdebug("parsing attribute:");
1217                 $this->appendDebug($this->varDump($attrs));
1218                 if (!isset($attrs['form'])) {
1219                     $attrs['form'] = $this->schemaInfo['attributeFormDefault'];
1220                 }
1221                 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1222                     $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1223                     if (!strpos($v, ':')) {
1224                         // no namespace in arrayType attribute value...
1225                         if ($this->defaultNamespace[$pos]) {
1226                             // ...so use the default
1227                             $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1228                         }
1229                     }
1230                 }
1231                 if(isset($attrs['name'])){
1232                     $this->attributes[$attrs['name']] = $attrs;
1233                     $aname = $attrs['name'];
1234                 } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
1235                     if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1236                         $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1237                     } else {
1238                         $aname = '';
1239                     }
1240                 } elseif(isset($attrs['ref'])){
1241                     $aname = $attrs['ref'];
1242                     $this->attributes[$attrs['ref']] = $attrs;
1243                 }
1244                 
1245                 if($this->currentComplexType){    // This should *always* be
1246                     $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
1247                 }
1248                 // arrayType attribute
1249                 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
1250                     $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1251                     $prefix = $this->getPrefix($aname);
1252                     if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
1253                         $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1254                     } else {
1255                         $v = '';
1256                     }
1257                     if(strpos($v,'[,]')){
1258                         $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
1259                     }
1260                     $v = substr($v,0,strpos($v,'[')); // clip the []
1261                     if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
1262                         $v = $this->XMLSchemaVersion.':'.$v;
1263                     }
1264                     $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
1265                 }
1266             break;
1267             case 'complexContent':    // (optional) content for a complexType
1268             break;
1269             case 'complexType':
1270                 array_push($this->complexTypeStack, $this->currentComplexType);
1271                 if(isset($attrs['name'])){
1272                     $this->xdebug('processing named complexType '.$attrs['name']);
1273                     //$this->currentElement = false;
1274                     $this->currentComplexType = $attrs['name'];
1275                     $this->complexTypes[$this->currentComplexType] = $attrs;
1276                     $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1277                     // This is for constructs like
1278                     //           <complexType name="ListOfString" base="soap:Array">
1279                     //                <sequence>
1280                     //                    <element name="string" type="xsd:string"
1281                     //                        minOccurs="0" maxOccurs="unbounded" />
1282                     //                </sequence>
1283                     //            </complexType>
1284                     if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
1285                         $this->xdebug('complexType is unusual array');
1286                         $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1287                     } else {
1288                         $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1289                     }
1290                 }else{
1291                     $this->xdebug('processing unnamed complexType for element '.$this->currentElement);
1292                     $this->currentComplexType = $this->currentElement . '_ContainedType';
1293                     //$this->currentElement = false;
1294                     $this->complexTypes[$this->currentComplexType] = $attrs;
1295                     $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1296                     // This is for constructs like
1297                     //           <complexType name="ListOfString" base="soap:Array">
1298                     //                <sequence>
1299                     //                    <element name="string" type="xsd:string"
1300                     //                        minOccurs="0" maxOccurs="unbounded" />
1301                     //                </sequence>
1302                     //            </complexType>
1303                     if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
1304                         $this->xdebug('complexType is unusual array');
1305                         $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1306                     } else {
1307                         $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1308                     }
1309                 }
1310             break;
1311             case 'element':
1312                 array_push($this->elementStack, $this->currentElement);
1313                 // elements defined as part of a complex type should
1314                 // not really be added to $this->elements, but for some
1315                 // reason, they are
1316                 if (!isset($attrs['form'])) {
1317                     $attrs['form'] = $this->schemaInfo['elementFormDefault'];
1318                 }
1319                 if(isset($attrs['type'])){
1320                     $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']);
1321                     if (! $this->getPrefix($attrs['type'])) {
1322                         if ($this->defaultNamespace[$pos]) {
1323                             $attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type'];
1324                             $this->xdebug('used default namespace to make type ' . $attrs['type']);
1325                         }
1326                     }
1327                     // This is for constructs like
1328                     //           <complexType name="ListOfString" base="soap:Array">
1329                     //                <sequence>
1330                     //                    <element name="string" type="xsd:string"
1331                     //                        minOccurs="0" maxOccurs="unbounded" />
1332                     //                </sequence>
1333                     //            </complexType>
1334                     if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') {
1335                         $this->xdebug('arrayType for unusual array is ' . $attrs['type']);
1336                         $this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type'];
1337                     }
1338                     $this->currentElement = $attrs['name'];
1339                     $this->elements[ $attrs['name'] ] = $attrs;
1340                     $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1341                     $ename = $attrs['name'];
1342                 } elseif(isset($attrs['ref'])){
1343                     $this->xdebug("processing element as ref to ".$attrs['ref']);
1344                     $this->currentElement = "ref to ".$attrs['ref'];
1345                     $ename = $this->getLocalPart($attrs['ref']);
1346                 } else {
1347                     $this->xdebug("processing untyped element ".$attrs['name']);
1348                     $this->currentElement = $attrs['name'];
1349                     $this->elements[ $attrs['name'] ] = $attrs;
1350                     $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1351                     $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['name'] . '_ContainedType';
1352                     $this->elements[ $attrs['name'] ]['type'] = $attrs['type'];
1353                     $ename = $attrs['name'];
1354                 }
1355                 if(isset($ename) && $this->currentComplexType){
1356                     $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
1357                 }
1358             break;
1359             case 'enumeration':    //    restriction value list member
1360                 $this->xdebug('enumeration ' . $attrs['value']);
1361                 if ($this->currentSimpleType) {
1362                     $this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value'];
1363                 } elseif ($this->currentComplexType) {
1364                     $this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value'];
1365                 }
1366             break;
1367             case 'extension':    // simpleContent or complexContent type extension
1368                 $this->xdebug('extension ' . $attrs['base']);
1369                 if ($this->currentComplexType) {
1370                     $this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base'];
1371                 }
1372             break;
1373             case 'import':
1374                 if (isset($attrs['schemaLocation'])) {
1375                     //$this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
1376                     $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
1377                 } else {
1378                     //$this->xdebug('import namespace ' . $attrs['namespace']);
1379                     $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
1380                     if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
1381                         $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
1382                     }
1383                 }
1384             break;
1385             case 'list':    // simpleType value list
1386             break;
1387             case 'restriction':    // simpleType, simpleContent or complexContent value restriction
1388                 $this->xdebug('restriction ' . $attrs['base']);
1389                 if($this->currentSimpleType){
1390                     $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
1391                 } elseif($this->currentComplexType){
1392                     $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
1393                     if(strstr($attrs['base'],':') == ':Array'){
1394                         $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1395                     }
1396                 }
1397             break;
1398             case 'schema':
1399                 $this->schemaInfo = $attrs;
1400                 $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
1401                 if (isset($attrs['targetNamespace'])) {
1402                     $this->schemaTargetNamespace = $attrs['targetNamespace'];
1403                 }
1404                 if (!isset($attrs['elementFormDefault'])) {
1405                     $this->schemaInfo['elementFormDefault'] = 'unqualified';
1406                 }
1407                 if (!isset($attrs['attributeFormDefault'])) {
1408                     $this->schemaInfo['attributeFormDefault'] = 'unqualified';
1409                 }
1410             break;
1411             case 'simpleContent':    // (optional) content for a complexType
1412             break;
1413             case 'simpleType':
1414                 array_push($this->simpleTypeStack, $this->currentSimpleType);
1415                 if(isset($attrs['name'])){
1416                     $this->xdebug("processing simpleType for name " . $attrs['name']);
1417                     $this->currentSimpleType = $attrs['name'];
1418                     $this->simpleTypes[ $attrs['name'] ] = $attrs;
1419                     $this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType';
1420                     $this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar';
1421                 } else {
1422                     $this->xdebug('processing unnamed simpleType for element '.$this->currentElement);
1423                     $this->currentSimpleType = $this->currentElement . '_ContainedType';
1424                     //$this->currentElement = false;
1425                     $this->simpleTypes[$this->currentSimpleType] = $attrs;
1426                     $this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar';
1427                 }
1428             break;
1429             case 'union':    // simpleType type list
1430             break;
1431             default:
1432                 //$this->xdebug("do not have anything to do for element $name");
1433         }
1434     }
1435
1436     /**
1437     * end-element handler
1438     *
1439     * @param    string $parser XML parser object
1440     * @param    string $name element name
1441     * @access   private
1442     */
1443     function schemaEndElement($parser, $name) {
1444         // bring depth down a notch
1445         $this->depth--;
1446         // position of current element is equal to the last value left in depth_array for my depth
1447         if(isset($this->depth_array[$this->depth])){
1448             $pos = $this->depth_array[$this->depth];
1449         }
1450         // get element prefix
1451         if ($prefix = $this->getPrefix($name)){
1452             // get unqualified name
1453             $name = $this->getLocalPart($name);
1454         } else {
1455             $prefix = '';
1456         }
1457         // move on...
1458         if($name == 'complexType'){
1459             $this->xdebug('done processing complexType ' . ($this->currentComplexType ? $this->currentComplexType : '(unknown)'));
1460             $this->currentComplexType = array_pop($this->complexTypeStack);
1461             //$this->currentElement = false;
1462         }
1463         if($name == 'element'){
1464             $this->xdebug('done processing element ' . ($this->currentElement ? $this->currentElement : '(unknown)'));
1465             $this->currentElement = array_pop($this->elementStack);
1466         }
1467         if($name == 'simpleType'){
1468             $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ? $this->currentSimpleType : '(unknown)'));
1469             $this->currentSimpleType = array_pop($this->simpleTypeStack);
1470         }
1471     }
1472
1473     /**
1474     * element content handler
1475     *
1476     * @param    string $parser XML parser object
1477     * @param    string $data element content
1478     * @access   private
1479     */
1480     function schemaCharacterData($parser, $data){
1481         $pos = $this->depth_array[$this->depth - 1];
1482         $this->message[$pos]['cdata'] .= $data;
1483     }
1484
1485     /**
1486     * serialize the schema
1487     *
1488     * @access   public
1489     */
1490     function serializeSchema(){
1491
1492         $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
1493         $xml = '';
1494         // imports
1495         if (sizeof($this->imports) > 0) {
1496             foreach($this->imports as $ns => $list) {
1497                 foreach ($list as $ii) {
1498                     if ($ii['location'] != '') {
1499                         $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
1500                     } else {
1501                         $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
1502                     }
1503                 }
1504             }
1505         }
1506         // complex types
1507         foreach($this->complexTypes as $typeName => $attrs){
1508             $contentStr = '';
1509             // serialize child elements
1510             if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){
1511                 foreach($attrs['elements'] as $element => $eParts){
1512                     if(isset($eParts['ref'])){
1513                         $contentStr .= "   <$schemaPrefix:element ref=\"$element\"/>\n";
1514                     } else {
1515                         $contentStr .= "   <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"";
1516                         foreach ($eParts as $aName => $aValue) {
1517                             // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
1518                             if ($aName != 'name' && $aName != 'type') {
1519                                 $contentStr .= " $aName=\"$aValue\"";
1520                             }
1521                         }
1522                         $contentStr .= "/>\n";
1523                     }
1524                 }
1525                 // compositor wraps elements
1526                 if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) {
1527                     $contentStr = "  <$schemaPrefix:$attrs[compositor]>\n".$contentStr."  </$schemaPrefix:$attrs[compositor]>\n";
1528                 }
1529             }
1530             // attributes
1531             if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){
1532                 foreach($attrs['attrs'] as $attr => $aParts){
1533                     $contentStr .= "    <$schemaPrefix:attribute";
1534                     foreach ($aParts as $a => $v) {
1535                         if ($a == 'ref' || $a == 'type') {
1536                             $contentStr .= " $a=\"".$this->contractQName($v).'"';
1537                         } elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') {
1538                             $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
1539                             $contentStr .= ' wsdl:arrayType="'.$this->contractQName($v).'"';
1540                         } else {
1541                             $contentStr .= " $a=\"$v\"";
1542                         }
1543                     }
1544                     $contentStr .= "/>\n";
1545                 }
1546             }
1547             // if restriction
1548             if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
1549                 $contentStr = "   <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr."   </$schemaPrefix:restriction>\n";
1550                 // complex or simple content
1551                 if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){
1552                     $contentStr = "  <$schemaPrefix:complexContent>\n".$contentStr."  </$schemaPrefix:complexContent>\n";
1553                 }
1554             }
1555             // finalize complex type
1556             if($contentStr != ''){
1557                 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n";
1558             } else {
1559                 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
1560             }
1561             $xml .= $contentStr;
1562         }
1563         // simple types
1564         if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){
1565             foreach($this->simpleTypes as $typeName => $eParts){
1566                 $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n  <$schemaPrefix:restriction base=\"".$this->contractQName($eParts['type'])."\"/>\n";
1567                 if (isset($eParts['enumeration'])) {
1568                     foreach ($eParts['enumeration'] as $e) {
1569                         $xml .= "  <$schemaPrefix:enumeration value=\"$e\"/>\n";
1570                     }
1571                 }
1572                 $xml .= " </$schemaPrefix:simpleType>";
1573             }
1574         }
1575         // elements
1576         if(isset($this->elements) && count($this->elements) > 0){
1577             foreach($this->elements as $element => $eParts){
1578                 $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n";
1579             }
1580         }
1581         // attributes
1582         if(isset($this->attributes) && count($this->attributes) > 0){
1583             foreach($this->attributes as $attr => $aParts){
1584                 $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>";
1585             }
1586         }
1587         // finish 'er up
1588         $el = "<$schemaPrefix:schema targetNamespace=\"$this->schemaTargetNamespace\"\n";
1589         foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
1590             $el .= " xmlns:$nsp=\"$ns\"\n";
1591         }
1592         $xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n";
1593         return $xml;
1594     }
1595
1596     /**
1597     * adds debug data to the clas level debug string
1598     *
1599     * @param    string $string debug data
1600     * @access   private
1601     */
1602     function xdebug($string){
1603         $this->debug('<' . $this->schemaTargetNamespace . '> '.$string);
1604     }
1605
1606     /**
1607     * get the PHP type of a user defined type in the schema
1608     * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1609     * returns false if no type exists, or not w/ the given namespace
1610     * else returns a string that is either a native php type, or 'struct'
1611     *
1612     * @param string $type, name of defined type
1613     * @param string $ns, namespace of type
1614     * @return mixed
1615     * @access public
1616     * @deprecated
1617     */
1618     function getPHPType($type,$ns){
1619         if(isset($this->typemap[$ns][$type])){
1620             //print "found type '$type' and ns $ns in typemap<br>";
1621             return $this->typemap[$ns][$type];
1622         } elseif(isset($this->complexTypes[$type])){
1623             //print "getting type '$type' and ns $ns from complexTypes array<br>";
1624             return $this->complexTypes[$type]['phpType'];
1625         }
1626         return false;
1627     }
1628
1629     /**
1630     * returns an associative array of information about a given type
1631     * returns false if no type exists by the given name
1632     *
1633     *    For a complexType typeDef = array(
1634     *    'restrictionBase' => '',
1635     *    'phpType' => '',
1636     *    'compositor' => '(sequence|all)',
1637     *    'elements' => array(), // refs to elements array
1638     *    'attrs' => array() // refs to attributes array
1639     *    ... and so on (see addComplexType)
1640     *    )
1641     *
1642     *   For simpleType or element, the array has different keys.
1643     *
1644     * @param string
1645     * @return mixed
1646     * @access public
1647     * @see addComplexType
1648     * @see addSimpleType
1649     * @see addElement
1650     */
1651     function getTypeDef($type){
1652         //$this->debug("in getTypeDef for type $type");
1653         if(isset($this->complexTypes[$type])){
1654             $this->xdebug("in getTypeDef, found complexType $type");
1655             return $this->complexTypes[$type];
1656         } elseif(isset($this->simpleTypes[$type])){
1657             $this->xdebug("in getTypeDef, found simpleType $type");
1658             if (!isset($this->simpleTypes[$type]['phpType'])) {
1659                 // get info for type to tack onto the simple type
1660                 // TODO: can this ever really apply (i.e. what is a simpleType really?)
1661                 $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1);
1662                 $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':'));
1663                 $etype = $this->getTypeDef($uqType);
1664                 if ($etype) {
1665                     $this->xdebug("in getTypeDef, found type for simpleType $type:");
1666                     $this->xdebug($this->varDump($etype));
1667                     if (isset($etype['phpType'])) {
1668                         $this->simpleTypes[$type]['phpType'] = $etype['phpType'];
1669                     }
1670                     if (isset($etype['elements'])) {
1671                         $this->simpleTypes[$type]['elements'] = $etype['elements'];
1672                     }
1673                 }
1674             }
1675             return $this->simpleTypes[$type];
1676         } elseif(isset($this->elements[$type])){
1677             $this->xdebug("in getTypeDef, found element $type");
1678             if (!isset($this->elements[$type]['phpType'])) {
1679                 // get info for type to tack onto the element
1680                 $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1);
1681                 $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':'));
1682                 $etype = $this->getTypeDef($uqType);
1683                 if ($etype) {
1684                     $this->xdebug("in getTypeDef, found type for element $type:");
1685                     $this->xdebug($this->varDump($etype));
1686                     if (isset($etype['phpType'])) {
1687                         $this->elements[$type]['phpType'] = $etype['phpType'];
1688                     }
1689                     if (isset($etype['elements'])) {
1690                         $this->elements[$type]['elements'] = $etype['elements'];
1691                     }
1692                 } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
1693                     $this->xdebug("in getTypeDef, element $type is an XSD type");
1694                     $this->elements[$type]['phpType'] = 'scalar';
1695                 }
1696             }
1697             return $this->elements[$type];
1698         } elseif(isset($this->attributes[$type])){
1699             $this->xdebug("in getTypeDef, found attribute $type");
1700             return $this->attributes[$type];
1701         } elseif (ereg('_ContainedType$', $type)) {
1702             $this->xdebug("in getTypeDef, have an untyped element $type");
1703             $typeDef['typeClass'] = 'simpleType';
1704             $typeDef['phpType'] = 'scalar';
1705             $typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string';
1706             return $typeDef;
1707         }
1708         $this->xdebug("in getTypeDef, did not find $type");
1709         return false;
1710     }
1711
1712     /**
1713     * returns a sample serialization of a given type, or false if no type by the given name
1714     *
1715     * @param string $type, name of type
1716     * @return mixed
1717     * @access public
1718     * @deprecated
1719     */
1720     function serializeTypeDef($type){
1721         //print "in sTD() for type $type<br>";
1722     if($typeDef = $this->getTypeDef($type)){
1723         $str .= '<'.$type;
1724         if(is_array($typeDef['attrs'])){
1725         foreach($attrs as $attName => $data){
1726             $str .= " $attName=\"{type = ".$data['type']."}\"";
1727         }
1728         }
1729         $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
1730         if(count($typeDef['elements']) > 0){
1731         $str .= ">";
1732         foreach($typeDef['elements'] as $element => $eData){
1733             $str .= $this->serializeTypeDef($element);
1734         }
1735         $str .= "</$type>";
1736         } elseif($typeDef['typeClass'] == 'element') {
1737         $str .= "></$type>";
1738         } else {
1739         $str .= "/>";
1740         }
1741             return $str;
1742     }
1743         return false;
1744     }
1745
1746     /**
1747     * returns HTML form elements that allow a user
1748     * to enter values for creating an instance of the given type.
1749     *
1750     * @param string $name, name for type instance
1751     * @param string $type, name of type
1752     * @return string
1753     * @access public
1754     * @deprecated
1755     */
1756     function typeToForm($name,$type){
1757         // get typedef
1758         if($typeDef = $this->getTypeDef($type)){
1759             // if struct
1760             if($typeDef['phpType'] == 'struct'){
1761                 $buffer .= '<table>';
1762                 foreach($typeDef['elements'] as $child => $childDef){
1763                     $buffer .= "
1764                     <tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>
1765                     <td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
1766                 }
1767                 $buffer .= '</table>';
1768             // if array
1769             } elseif($typeDef['phpType'] == 'array'){
1770                 $buffer .= '<table>';
1771                 for($i=0;$i < 3; $i++){
1772                     $buffer .= "
1773                     <tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
1774                     <td><input type='text' name='parameters[".$name."][]'></td></tr>";
1775                 }
1776                 $buffer .= '</table>';
1777             // if scalar
1778             } else {
1779                 $buffer .= "<input type='text' name='parameters[$name]'>";
1780             }
1781         } else {
1782             $buffer .= "<input type='text' name='parameters[$name]'>";
1783         }
1784         return $buffer;
1785     }
1786     
1787     /**
1788     * adds a complex type to the schema
1789     *
1790     * example: array
1791     *
1792     * addType(
1793     *     'ArrayOfstring',
1794     *     'complexType',
1795     *     'array',
1796     *     '',
1797     *     'SOAP-ENC:Array',
1798     *     array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1799     *     'xsd:string'
1800     * );
1801     *
1802     * example: PHP associative array ( SOAP Struct )
1803     *
1804     * addType(
1805     *     'SOAPStruct',
1806     *     'complexType',
1807     *     'struct',
1808     *     'all',
1809     *     array('myVar'=> array('name'=>'myVar','type'=>'string')
1810     * );
1811     *
1812     * @param name
1813     * @param typeClass (complexType|simpleType|attribute)
1814     * @param phpType: currently supported are array and struct (php assoc array)
1815     * @param compositor (all|sequence|choice)
1816     * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1817     * @param elements = array ( name = array(name=>'',type=>'') )
1818     * @param attrs = array(
1819     *     array(
1820     *        'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1821     *        "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1822     *     )
1823     * )
1824     * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1825     * @access public
1826     * @see getTypeDef
1827     */
1828     function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
1829         $this->complexTypes[$name] = array(
1830         'name'        => $name,
1831         'typeClass'    => $typeClass,
1832         'phpType'    => $phpType,
1833         'compositor'=> $compositor,
1834         'restrictionBase' => $restrictionBase,
1835         'elements'    => $elements,
1836         'attrs'        => $attrs,
1837         'arrayType'    => $arrayType
1838         );
1839         
1840         $this->xdebug("addComplexType $name:");
1841         $this->appendDebug($this->varDump($this->complexTypes[$name]));
1842     }
1843     
1844     /**
1845     * adds a simple type to the schema
1846     *
1847     * @param string $name
1848     * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1849     * @param string $typeClass (should always be simpleType)
1850     * @param string $phpType (should always be scalar)
1851     * @param array $enumeration array of values
1852     * @access public
1853     * @see xmlschema
1854     * @see getTypeDef
1855     */
1856     function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
1857         $this->simpleTypes[$name] = array(
1858         'name'            => $name,
1859         'typeClass'        => $typeClass,
1860         'phpType'        => $phpType,
1861         'type'            => $restrictionBase,
1862         'enumeration'    => $enumeration
1863         );
1864         
1865         $this->xdebug("addSimpleType $name:");
1866         $this->appendDebug($this->varDump($this->simpleTypes[$name]));
1867     }
1868
1869     /**
1870     * adds an element to the schema
1871     *
1872     * @param array $attrs attributes that must include name and type
1873     * @see xmlschema
1874     * @access public
1875     */
1876     function addElement($attrs) {
1877         if (! $this->getPrefix($attrs['type'])) {
1878             $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type'];
1879         }
1880         $this->elements[ $attrs['name'] ] = $attrs;
1881         $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1882         
1883         $this->xdebug("addElement " . $attrs['name']);
1884         $this->appendDebug($this->varDump($this->elements[ $attrs['name'] ]));
1885     }
1886 }
1887
1888
1889
1890 ?><?php
1891
1892
1893
1894 /**
1895 * For creating serializable abstractions of native PHP types.  This class
1896 * allows element name/namespace, XSD type, and XML attributes to be
1897 * associated with a value.  This is extremely useful when WSDL is not
1898 * used, but is also useful when WSDL is used with polymorphic types, including
1899 * xsd:anyType and user-defined types.
1900 *
1901 * @author   Dietrich Ayala <dietrich@ganx4.com>
1902 * @version  $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
1903 * @access   public
1904 */
1905 class soapval extends nusoap_base {
1906     /**
1907      * The XML element name
1908      *
1909      * @var string
1910      * @access private
1911      */
1912     var $name;
1913     /**
1914      * The XML type name (string or false)
1915      *
1916      * @var mixed
1917      * @access private
1918      */
1919     var $type;
1920     /**
1921      * The PHP value
1922      *
1923      * @var mixed
1924      * @access private
1925      */
1926     var $value;
1927     /**
1928      * The XML element namespace (string or false)
1929      *
1930      * @var mixed
1931      * @access private
1932      */
1933     var $element_ns;
1934     /**
1935      * The XML type namespace (string or false)
1936      *
1937      * @var mixed
1938      * @access private
1939      */
1940     var $type_ns;
1941     /**
1942      * The XML element attributes (array or false)
1943      *
1944      * @var mixed
1945      * @access private
1946      */
1947     var $attributes;
1948
1949     /**
1950     * constructor
1951     *
1952     * @param    string $name optional name
1953     * @param    mixed $type optional type name
1954     * @param    mixed $value optional value
1955     * @param    mixed $element_ns optional namespace of value
1956     * @param    mixed $type_ns optional namespace of type
1957     * @param    mixed $attributes associative array of attributes to add to element serialization
1958     * @access   public
1959     */
1960       function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
1961         parent::nusoap_base();
1962         $this->name = $name;
1963         $this->type = $type;
1964         $this->value = $value;
1965         $this->element_ns = $element_ns;
1966         $this->type_ns = $type_ns;
1967         $this->attributes = $attributes;
1968     }
1969
1970     /**
1971     * return serialized value
1972     *
1973     * @param    string $use The WSDL use value (encoded|literal)
1974     * @return    string XML data
1975     * @access   public
1976     */
1977     function serialize($use='encoded') {
1978         return $this->serialize_val($this->value,$this->name,$this->type,$this->element_ns,$this->type_ns,$this->attributes,$use);
1979     }
1980
1981     /**
1982     * decodes a soapval object into a PHP native type
1983     *
1984     * @return    mixed
1985     * @access   public
1986     */
1987     function decode(){
1988         return $this->value;
1989     }
1990 }
1991
1992
1993
1994 ?><?php
1995
1996
1997
1998 /**
1999 * transport class for sending/receiving data via HTTP and HTTPS
2000 * NOTE: PHP must be compiled with the CURL extension for HTTPS support
2001 *
2002 * @author   Dietrich Ayala <dietrich@ganx4.com>
2003 * @version  $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
2004 * @access public
2005 */
2006 class soap_transport_http extends nusoap_base {
2007
2008     var $url = '';
2009     var $uri = '';
2010     var $digest_uri = '';
2011     var $scheme = '';
2012     var $host = '';
2013     var $port = '';
2014     var $path = '';
2015     var $request_method = 'POST';
2016     var $protocol_version = '1.0';
2017     var $encoding = '';
2018     var $outgoing_headers = array();
2019     var $incoming_headers = array();
2020     var $incoming_cookies = array();
2021     var $outgoing_payload = '';
2022     var $incoming_payload = '';
2023     var $useSOAPAction = true;
2024     var $persistentConnection = false;
2025     var $ch = false;    // cURL handle
2026     var $username = '';
2027     var $password = '';
2028     var $authtype = '';
2029     var $digestRequest = array();
2030     var $certRequest = array();    // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional)
2031                                 // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem'
2032                                 // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem'
2033                                 // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem'
2034                                 // passphrase: SSL key password/passphrase
2035                                 // verifypeer: default is 1
2036                                 // verifyhost: default is 1
2037
2038     /**
2039     * constructor
2040     */
2041     function soap_transport_http($url){
2042         parent::nusoap_base();
2043         $this->setURL($url);
2044         ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
2045         $this->outgoing_headers['User-Agent'] = $this->title.'/'.$this->version.' ('.$rev[1].')';
2046         $this->debug('set User-Agent: ' . $this->outgoing_headers['User-Agent']);
2047     }
2048
2049     function setURL($url) {
2050         $this->url = $url;
2051
2052         $u = parse_url($url);
2053         foreach($u as $k => $v){
2054             $this->debug("$k = $v");
2055             $this->$k = $v;
2056         }
2057         
2058         // add any GET params to path
2059         if(isset($u['query']) && $u['query'] != ''){
2060             $this->path .= '?' . $u['query'];
2061         }
2062         
2063         // set default port
2064         if(!isset($u['port'])){
2065             if($u['scheme'] == 'https'){
2066                 $this->port = 443;
2067             } else {
2068                 $this->port = 80;
2069             }
2070         }
2071         
2072         $this->uri = $this->path;
2073         $this->digest_uri = $this->uri;
2074         
2075         // build headers
2076         if (!isset($u['port'])) {
2077             $this->outgoing_headers['Host'] = $this->host;
2078         } else {
2079             $this->outgoing_headers['Host'] = $this->host.':'.$this->port;
2080         }
2081         $this->debug('set Host: ' . $this->outgoing_headers['Host']);
2082
2083         if (isset($u['user']) && $u['user'] != '') {
2084             $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
2085         }
2086     }
2087     
2088     function connect($connection_timeout=0,$response_timeout=30){
2089           // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
2090           // "regular" socket.
2091           // TODO: disabled for now because OpenSSL must be *compiled* in (not just
2092           //       loaded), and until PHP5 stream_get_wrappers is not available.
2093 //          if ($this->scheme == 'https') {
2094 //              if (version_compare(phpversion(), '4.3.0') >= 0) {
2095 //                  if (extension_loaded('openssl')) {
2096 //                      $this->scheme = 'ssl';
2097 //                      $this->debug('Using SSL over OpenSSL');
2098 //                  }
2099 //              }
2100 //        }
2101         $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
2102       if ($this->scheme == 'http' || $this->scheme == 'ssl') {
2103         // use persistent connection
2104         if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){
2105             if (!feof($this->fp)) {
2106                 $this->debug('Re-use persistent connection');
2107                 return true;
2108             }
2109             fclose($this->fp);
2110             $this->debug('Closed persistent connection at EOF');
2111         }
2112
2113         // munge host if using OpenSSL
2114         if ($this->scheme == 'ssl') {
2115             $host = 'ssl://' . $this->host;
2116         } else {
2117             $host = $this->host;
2118         }
2119         $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
2120
2121         // open socket
2122         if($connection_timeout > 0){
2123             $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout);
2124         } else {
2125             $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str);
2126         }
2127         
2128         // test pointer
2129         if(!$this->fp) {
2130             $msg = 'Couldn\'t open socket connection to server ' . $this->url;
2131             if ($this->errno) {
2132                 $msg .= ', Error ('.$this->errno.'): '.$this->error_str;
2133             } else {
2134                 $msg .= ' prior to connect().  This is often a problem looking up the host name.';
2135             }
2136             $this->debug($msg);
2137             $this->setError($msg);
2138             return false;
2139         }
2140         
2141         // set response timeout
2142         $this->debug('set response timeout to ' . $response_timeout);
2143         socket_set_timeout( $this->fp, $response_timeout);
2144
2145         $this->debug('socket connected');
2146         return true;
2147       } else if ($this->scheme == 'https') {
2148         if (!extension_loaded('curl')) {
2149             $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
2150             return false;
2151         }
2152         $this->debug('connect using https');
2153         // init CURL
2154         $this->ch = curl_init();
2155         // set url
2156         $hostURL = ($this->port != '') ? "https://$this->host:$this->port" : "https://$this->host";
2157         // add path
2158         $hostURL .= $this->path;
2159         curl_setopt($this->ch, CURLOPT_URL, $hostURL);
2160         // follow location headers (re-directs)
2161         curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, 1);
2162         // ask for headers in the response output
2163         curl_setopt($this->ch, CURLOPT_HEADER, 1);
2164         // ask for the response output as the return value
2165         curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
2166         // encode
2167         // We manage this ourselves through headers and encoding
2168 //        if(function_exists('gzuncompress')){
2169 //            curl_setopt($this->ch, CURLOPT_ENCODING, 'deflate');
2170 //        }
2171         // persistent connection
2172         if ($this->persistentConnection) {
2173             // The way we send data, we cannot use persistent connections, since
2174             // there will be some "junk" at the end of our request.
2175             //curl_setopt($this->ch, CURL_HTTP_VERSION_1_1, true);
2176             $this->persistentConnection = false;
2177             $this->outgoing_headers['Connection'] = 'close';
2178             $this->debug('set Connection: ' . $this->outgoing_headers['Connection']);
2179         }
2180         // set timeout
2181         if ($connection_timeout != 0) {
2182             curl_setopt($this->ch, CURLOPT_TIMEOUT, $connection_timeout);
2183         }
2184         // TODO: cURL has added a connection timeout separate from the response timeout
2185         //if ($connection_timeout != 0) {
2186         //    curl_setopt($this->ch, CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
2187         //}
2188         //if ($response_timeout != 0) {
2189         //    curl_setopt($this->ch, CURLOPT_TIMEOUT, $response_timeout);
2190         //}
2191
2192         // recent versions of cURL turn on peer/host checking by default,
2193         // while PHP binaries are not compiled with a default location for the
2194         // CA cert bundle, so disable peer/host checking.
2195 //curl_setopt($this->ch, CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');       
2196         curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 0);
2197         curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 0);
2198
2199         // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
2200         if ($this->authtype == 'certificate') {
2201             if (isset($this->certRequest['cainfofile'])) {
2202                 curl_setopt($this->ch, CURLOPT_CAINFO, $this->certRequest['cainfofile']);
2203             }
2204             if (isset($this->certRequest['verifypeer'])) {
2205                 curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
2206             } else {
2207                 curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 1);
2208             }
2209             if (isset($this->certRequest['verifyhost'])) {
2210                 curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
2211             } else {
2212                 curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 1);
2213             }
2214             if (isset($this->certRequest['sslcertfile'])) {
2215                 curl_setopt($this->ch, CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
2216             }
2217             if (isset($this->certRequest['sslkeyfile'])) {
2218                 curl_setopt($this->ch, CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
2219             }
2220             if (isset($this->certRequest['passphrase'])) {
2221                 curl_setopt($this->ch, CURLOPT_SSLKEYPASSWD , $this->certRequest['passphrase']);
2222             }
2223         }
2224         $this->debug('cURL connection set up');
2225         return true;
2226       } else {
2227         $this->setError('Unknown scheme ' . $this->scheme);
2228         $this->debug('Unknown scheme ' . $this->scheme);
2229         return false;
2230       }
2231     }
2232     
2233     /**
2234     * send the SOAP message via HTTP
2235     *
2236     * @param    string $data message data
2237     * @param    integer $timeout set connection timeout in seconds
2238     * @param    integer $response_timeout set response timeout in seconds
2239     * @param    array $cookies cookies to send
2240     * @return    string data
2241     * @access   public
2242     */
2243     function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) {
2244         
2245         $this->debug('entered send() with data of length: '.strlen($data));
2246
2247         $this->tryagain = true;
2248         $tries = 0;
2249         while ($this->tryagain) {
2250             $this->tryagain = false;
2251             if ($tries++ < 2) {
2252                 // make connnection
2253                 if (!$this->connect($timeout, $response_timeout)){
2254                     return false;
2255                 }
2256                 
2257                 // send request
2258                 if (!$this->sendRequest($data, $cookies)){
2259                     return false;
2260                 }
2261                 
2262                 // get response
2263                 $respdata = $this->getResponse();
2264             } else {
2265                 $this->setError('Too many tries to get an OK response');
2266             }
2267         }       
2268         $this->debug('end of send()');
2269         return $respdata;
2270     }
2271
2272
2273     /**
2274     * send the SOAP message via HTTPS 1.0 using CURL
2275     *
2276     * @param    string $msg message data
2277     * @param    integer $timeout set connection timeout in seconds
2278     * @param    integer $response_timeout set response timeout in seconds
2279     * @param    array $cookies cookies to send
2280     * @return    string data
2281     * @access   public
2282     */
2283     function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) {
2284         return $this->send($data, $timeout, $response_timeout, $cookies);
2285     }
2286     
2287     /**
2288     * if authenticating, set user credentials here
2289     *
2290     * @param    string $username
2291     * @param    string $password
2292     * @param    string $authtype (basic, digest, certificate)
2293     * @param    array $digestRequest (keys must be nonce, nc, realm, qop)
2294     * @param    array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
2295     * @access   public
2296     */
2297     function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) {
2298         $this->debug("Set credentials for authtype $authtype");
2299         // cf. RFC 2617
2300         if ($authtype == 'basic') {
2301             $this->outgoing_headers['Authorization'] = 'Basic '.base64_encode(str_replace(':','',$username).':'.$password);
2302         } elseif ($authtype == 'digest') {
2303             if (isset($digestRequest['nonce'])) {
2304                 $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
2305                 
2306                 // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
2307     
2308                 // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
2309                 $A1 = $username. ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;
2310     
2311                 // H(A1) = MD5(A1)
2312                 $HA1 = md5($A1);
2313     
2314                 // A2 = Method ":" digest-uri-value
2315                 $A2 = 'POST:' . $this->digest_uri;
2316     
2317                 // H(A2)
2318                 $HA2 md5($A2);
2319     
2320                 // KD(secret, data) = H(concat(secret, ":", data))
2321                 // if qop == auth:
2322                 // request-digest  = <"> < KD ( H(A1),     unq(nonce-value)
2323                 //                              ":" nc-value
2324                 //                              ":" unq(cnonce-value)
2325                 //                              ":" unq(qop-value)
2326                 //                              ":" H(A2)
2327                 //                            ) <">
2328                 // if qop is missing,
2329                 // request-digest  = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
2330     
2331                 $unhashedDigest = '';
2332                 $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
2333                 $cnonce = $nonce;
2334                 if ($digestRequest['qop'] != '') {
2335                     $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
2336                 } else {
2337                     $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
2338                 }
2339     
2340                 $hashedDigest = md5($unhashedDigest);
2341     
2342                 $this->outgoing_headers['Authorization'] = 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"';
2343             }
2344         } elseif ($authtype == 'certificate') {
2345             $this->certRequest = $certRequest;
2346         }
2347         $this->username = $username;
2348         $this->password = $password;
2349         $this->authtype = $authtype;
2350         $this->digestRequest = $digestRequest;
2351         
2352         if (isset($this->outgoing_headers['Authorization'])) {
2353             $this->debug('set Authorization: ' . substr($this->outgoing_headers['Authorization'], 0, 12) . '...');
2354         } else {
2355             $this->debug('Authorization header not set');
2356         }
2357     }
2358     
2359     /**
2360     * set the soapaction value
2361     *
2362     * @param    string $soapaction
2363     * @access   public
2364     */
2365     function setSOAPAction($soapaction) {
2366         $this->outgoing_headers['SOAPAction'] = '"' . $soapaction . '"';
2367         $this->debug('set SOAPAction: ' . $this->outgoing_headers['SOAPAction']);
2368     }
2369     
2370     /**
2371     * use http encoding
2372     *
2373     * @param    string $enc encoding style. supported values: gzip, deflate, or both
2374     * @access   public
2375     */
2376     function setEncoding($enc='gzip, deflate') {
2377         if (function_exists('gzdeflate')) {
2378             $this->protocol_version = '1.1';
2379             $this->outgoing_headers['Accept-Encoding'] = $enc;
2380             $this->debug('set Accept-Encoding: ' . $this->outgoing_headers['Accept-Encoding']);
2381             if (!isset($this->outgoing_headers['Connection'])) {
2382                 $this->outgoing_headers['Connection'] = 'close';
2383                 $this->persistentConnection = false;
2384                 $this->debug('set Connection: ' . $this->outgoing_headers['Connection']);
2385             }
2386             set_magic_quotes_runtime(0);
2387             // deprecated
2388             $this->encoding = $enc;
2389         }
2390     }
2391     
2392     /**
2393     * set proxy info here
2394     *
2395     * @param    string $proxyhost
2396     * @param    string $proxyport
2397     * @param    string $proxyusername
2398     * @param    string $proxypassword
2399     * @access   public
2400     */
2401     function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
2402         $this->uri = $this->url;
2403         $this->host = $proxyhost;
2404         $this->port = $proxyport;
2405         if ($proxyusername != '' && $proxypassword != '') {
2406             $this->outgoing_headers['Proxy-Authorization'] = ' Basic '.base64_encode($proxyusername.':'.$proxypassword);
2407             $this->debug('set Proxy-Authorization: ' . $this->outgoing_headers['Proxy-Authorization']);
2408         }
2409     }
2410     
2411     /**
2412     * decode a string that is encoded w/ "chunked' transfer encoding
2413      * as defined in RFC2068 19.4.6
2414     *
2415     * @param    string $buffer
2416     * @param    string $lb
2417     * @returns    string
2418     * @access   public
2419     * @deprecated
2420     */
2421     function decodeChunked($buffer, $lb){
2422         // length := 0
2423         $length = 0;
2424         $new = '';
2425         
2426         // read chunk-size, chunk-extension (if any) and CRLF
2427         // get the position of the linebreak
2428         $chunkend = strpos($buffer, $lb);
2429         if ($chunkend == FALSE) {
2430             $this->debug('no linebreak found in decodeChunked');
2431             return $new;
2432         }
2433         $temp = substr($buffer,0,$chunkend);
2434         $chunk_size = hexdec( trim($temp) );
2435         $chunkstart = $chunkend + strlen($lb);
2436         // while (chunk-size > 0) {
2437         while ($chunk_size > 0) {
2438             $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
2439             $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size);
2440               
2441             // Just in case we got a broken connection
2442               if ($chunkend == FALSE) {
2443                   $chunk = substr($buffer,$chunkstart);
2444                 // append chunk-data to entity-body
2445                 $new .= $chunk;
2446                   $length += strlen($chunk);
2447                   break;
2448             }
2449             
2450               // read chunk-data and CRLF
2451               $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2452               // append chunk-data to entity-body
2453               $new .= $chunk;
2454               // length := length + chunk-size
2455               $length += strlen($chunk);
2456               // read chunk-size and CRLF
2457               $chunkstart = $chunkend + strlen($lb);
2458             
2459               $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb);
2460             if ($chunkend == FALSE) {
2461                 break; //Just in case we got a broken connection
2462             }
2463             $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2464             $chunk_size = hexdec( trim($temp) );
2465             $chunkstart = $chunkend;
2466         }
2467         return $new;
2468     }
2469     
2470     /*
2471      *    Writes payload, including HTTP headers, to $this->outgoing_payload.
2472      */
2473     function buildPayload($data, $cookie_str = '') {
2474         // add content-length header
2475         $this->outgoing_headers['Content-Length'] = strlen($data);
2476         $this->debug('set Content-Length: ' . $this->outgoing_headers['Content-Length']);
2477
2478         // start building outgoing payload:
2479         $req = "$this->request_method $this->uri HTTP/$this->protocol_version";
2480         $this->debug("HTTP request: $req");
2481         $this->outgoing_payload = "$req\r\n";
2482
2483         // loop thru headers, serializing
2484         foreach($this->outgoing_headers as $k => $v){
2485             $hdr = $k.': '.$v;
2486             $this->debug("HTTP header: $hdr");
2487             $this->outgoing_payload .= "$hdr\r\n";
2488         }
2489
2490         // add any cookies
2491         if ($cookie_str != '') {
2492             $hdr = 'Cookie: '.$cookie_str;
2493             $this->debug("HTTP header: $hdr");
2494             $this->outgoing_payload .= "$hdr\r\n";
2495         }
2496
2497         // header/body separator
2498         $this->outgoing_payload .= "\r\n";
2499         
2500         // add data
2501         $this->outgoing_payload .= $data;
2502     }
2503
2504     function sendRequest($data, $cookies = NULL) {
2505         // build cookie string
2506         $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https')));
2507
2508         // build payload
2509         $this->buildPayload($data, $cookie_str);
2510
2511       if ($this->scheme == 'http' || $this->scheme == 'ssl') {
2512         // send payload
2513         if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
2514             $this->setError('couldn\'t write message data to socket');
2515             $this->debug('couldn\'t write message data to socket');
2516             return false;
2517         }
2518         $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
2519         return true;
2520       } else if ($this->scheme == 'https') {
2521         // set payload
2522         // TODO: cURL does say this should only be the verb, and in fact it
2523         // turns out that the URI and HTTP version are appended to this, which
2524         // some servers refuse to work with
2525         //curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
2526         foreach($this->outgoing_headers as $k => $v){
2527             $curl_headers[] = "$k: $v";
2528         }
2529         if ($cookie_str != '') {
2530             $curl_headers[] = 'Cookie: ' . $cookie_str;
2531         }
2532         curl_setopt($this->ch, CURLOPT_HTTPHEADER, $curl_headers);
2533         if ($this->request_method == "POST") {
2534               curl_setopt($this->ch, CURLOPT_POST, 1);
2535               curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data);
2536           } else {
2537           }
2538         $this->debug('set cURL payload');
2539         return true;
2540       }
2541     }
2542
2543     function getResponse(){
2544         $this->incoming_payload = '';
2545         
2546       if ($this->scheme == 'http' || $this->scheme == 'ssl') {
2547         // loop until headers have been retrieved
2548         $data = '';
2549         while (!isset($lb)){
2550
2551             // We might EOF during header read.
2552             if(feof($this->fp)) {
2553                 $this->incoming_payload = $data;
2554                 $this->debug('found no headers before EOF after length ' . strlen($data));
2555                 $this->debug("received before EOF:\n" . $data);
2556                 $this->setError('server failed to send headers');
2557                 return false;
2558             }
2559
2560             $tmp = fgets($this->fp, 256);
2561             $tmplen = strlen($tmp);
2562             $this->debug("read line of $tmplen bytes: " . trim($tmp));
2563
2564             if ($tmplen == 0) {
2565                 $this->incoming_payload = $data;
2566                 $this->debug('socket read of headers timed out after length ' . strlen($data));
2567                 $this->debug("read before timeout: " . $data);
2568                 $this->setError('socket read of headers timed out');
2569                 return false;
2570             }
2571
2572             $data .= $tmp;
2573             $pos = strpos($data,"\r\n\r\n");
2574             if($pos > 1){
2575                 $lb = "\r\n";
2576             } else {
2577                 $pos = strpos($data,"\n\n");
2578                 if($pos > 1){
2579                     $lb = "\n";
2580                 }
2581             }
2582             // remove 100 header
2583             if(isset($lb) && ereg('^HTTP/1.1 100',$data)){
2584                 unset($lb);
2585                 $data = '';
2586             }//
2587         }
2588         // store header data
2589         $this->incoming_payload .= $data;
2590         $this->debug('found end of headers after length ' . strlen($data));
2591         // process headers
2592         $header_data = trim(substr($data,0,$pos));
2593         $header_array = explode($lb,$header_data);
2594         $this->incoming_headers = array();
2595         $this->incoming_cookies = array();
2596         foreach($header_array as $header_line){
2597             $arr = explode(':',$header_line, 2);
2598             if(count($arr) > 1){
2599                 $header_name = strtolower(trim($arr[0]));
2600                 $this->incoming_headers[$header_name] = trim($arr[1]);
2601                 if ($header_name == 'set-cookie') {
2602                     // TODO: allow multiple cookies from parseCookie
2603                     $cookie = $this->parseCookie(trim($arr[1]));
2604                     if ($cookie) {
2605                         $this->incoming_cookies[] = $cookie;
2606                         $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
2607                     } else {
2608                         $this->debug('did not find cookie in ' . trim($arr[1]));
2609                     }
2610                 }
2611             } else if (isset($header_name)) {
2612                 // append continuation line to previous header
2613                 $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
2614             }
2615         }
2616         
2617         // loop until msg has been received
2618         if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
2619             $content_length 2147483647;    // ignore any content-length header
2620             $chunked = true;
2621             $this->debug("want to read chunked content");
2622         } elseif (isset($this->incoming_headers['content-length'])) {
2623             $content_length = $this->incoming_headers['content-length'];
2624             $chunked = false;
2625             $this->debug("want to read content of length $content_length");
2626         } else {
2627             $content_length 2147483647;
2628             $chunked = false;
2629             $this->debug("want to read content to EOF");
2630         }
2631         $data = '';
2632         do {
2633             if ($chunked) {
2634                 $tmp = fgets($this->fp, 256);
2635                 $tmplen = strlen($tmp);
2636                 $this->debug("read chunk line of $tmplen bytes");
2637                 if ($tmplen == 0) {
2638                     $this->incoming_payload = $data;
2639                     $this->debug('socket read of chunk length timed out after length ' . strlen($data));
2640                     $this->debug("read before timeout:\n" . $data);
2641                     $this->setError('socket read of chunk length timed out');
2642                     return false;
2643                 }
2644                 $content_length = hexdec(trim($tmp));
2645                 $this->debug("chunk length $content_length");
2646             }
2647             $strlen = 0;
2648             while (($strlen < $content_length) && (!feof($this->fp))) {
2649                 $readlen = min(8192, $content_length - $strlen);
2650                 $tmp = fread($this->fp, $readlen);
2651                 $tmplen = strlen($tmp);
2652                 $this->debug("read buffer of $tmplen bytes");
2653                 if (($tmplen == 0) && (!feof($this->fp))) {
2654                     $this->incoming_payload = $data;
2655                     $this->debug('socket read of body timed out after length ' . strlen($data));
2656                     $this->debug("read before timeout:\n" . $data);
2657                     $this->setError('socket read of body timed out');
2658                     return false;
2659                 }
2660                 $strlen += $tmplen;
2661                 $data .= $tmp;
2662             }
2663             if ($chunked && ($content_length > 0)) {
2664                 $tmp = fgets($this->fp, 256);
2665                 $tmplen = strlen($tmp);
2666                 $this->debug("read chunk terminator of $tmplen bytes");
2667                 if ($tmplen == 0) {
2668                     $this->incoming_payload = $data;
2669                     $this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
2670                     $this->debug("read before timeout:\n" . $data);
2671                     $this->setError('socket read of chunk terminator timed out');
2672                     return false;
2673                 }
2674             }
2675         } while ($chunked && ($content_length > 0) && (!feof($this->fp)));
2676         if (feof($this->fp)) {
2677             $this->debug('read to EOF');
2678         }
2679         $this->debug('read body of length ' . strlen($data));
2680         $this->incoming_payload .= $data;
2681         $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server');
2682         
2683         // close filepointer
2684         if(
2685             (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') ||
2686             (! $this->persistentConnection) || feof($this->fp)){
2687             fclose($this->fp);
2688             $this->fp = false;
2689             $this->debug('closed socket');
2690         }
2691         
2692         // connection was closed unexpectedly
2693         if($this->incoming_payload == ''){
2694             $this->setError('no response from server');
2695             return false;
2696         }
2697         
2698         // decode transfer-encoding
2699 //        if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
2700 //            if(!$data = $this->decodeChunked($data, $lb)){
2701 //                $this->setError('Decoding of chunked data failed');
2702 //                return false;
2703 //            }
2704             //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
2705             // set decoded payload
2706 //            $this->incoming_payload = $header_data.$lb.$lb.$data;
2707 //        }
2708     
2709       } else if ($this->scheme == 'https') {
2710         // send and receive
2711         $this->debug('send and receive with cURL');
2712         $this->incoming_payload = curl_exec($this->ch);
2713         $data = $this->incoming_payload;
2714
2715         $cErr = curl_error($this->ch);
2716         if ($cErr != '') {
2717             $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'<br>';
2718             // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
2719             foreach(curl_getinfo($this->ch) as $k => $v){
2720                 $err .= "$k: $v<br>";
2721             }
2722             $this->debug($err);
2723             $this->setError($err);
2724             curl_close($this->ch);
2725             return false;
2726         } else {
2727             //echo '<pre>';
2728             //var_dump(curl_getinfo($this->ch));
2729             //echo '</pre>';
2730         }
2731         // close curl
2732         $this->debug('No cURL error, closing cURL');
2733         curl_close($this->ch);
2734         
2735         // remove 100 header(s)
2736         while (ereg('^HTTP/1.1 100',$data)) {
2737             if ($pos = strpos($data,"\r\n\r\n")) {
2738                 $data = ltrim(substr($data,$pos));
2739             } elseif($pos = strpos($data,"\n\n") ) {
2740                 $data = ltrim(substr($data,$pos));
2741             }
2742         }
2743         
2744         // separate content from HTTP headers
2745         if ($pos = strpos($data,"\r\n\r\n")) {
2746             $lb = "\r\n";
2747         } elseif( $pos = strpos($data,"\n\n")) {
2748             $lb = "\n";
2749         } else {
2750             $this->debug('no proper separation of headers and document');
2751             $this->setError('no proper separation of headers and document');
2752             return false;
2753         }
2754         $header_data = trim(substr($data,0,$pos));
2755         $header_array = explode($lb,$header_data);
2756         $data = ltrim(substr($data,$pos));
2757         $this->debug('found proper separation of headers and document');
2758         $this->debug('cleaned data, stringlen: '.strlen($data));
2759         // clean headers
2760         foreach ($header_array as $header_line) {
2761             $arr = explode(':',$header_line,2);
2762             if(count($arr) > 1){
2763                 $header_name = strtolower(trim($arr[0]));
2764                 $this->incoming_headers[$header_name] = trim($arr[1]);
2765                 if ($header_name == 'set-cookie') {
2766                     // TODO: allow multiple cookies from parseCookie
2767                     $cookie = $this->parseCookie(trim($arr[1]));
2768                     if ($cookie) {
2769                         $this->incoming_cookies[] = $cookie;
2770                         $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
2771                     } else {
2772                         $this->debug('did not find cookie in ' . trim($arr[1]));
2773                     }
2774                 }
2775             } else if (isset($header_name)) {
2776                 // append continuation line to previous header
2777                 $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
2778             }
2779         }
2780       }
2781
2782         $arr = explode(' ', $header_array[0], 3);
2783         $http_version = $arr[0];
2784         $http_status = intval($arr[1]);
2785         $http_reason = count($arr) > 2 ? $arr[2] : '';
2786
2787          // see if we need to resend the request with http digest authentication
2788          if (isset($this->incoming_headers['location']) && $http_status == 301) {
2789              $this->debug("Got 301 $http_reason with Location: " . $this->incoming_headers['location']);
2790              $this->setURL($this->incoming_headers['location']);
2791             $this->tryagain = true;
2792             return false;
2793         }
2794
2795          // see if we need to resend the request with http digest authentication
2796          if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) {
2797              $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
2798              if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) {
2799                  $this->debug('Server wants digest authentication');
2800                  // remove "Digest " from our elements
2801                  $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
2802                 
2803                  // parse elements into array
2804                  $digestElements = explode(',', $digestString);
2805                  foreach ($digestElements as $val) {
2806                      $tempElement = explode('=', trim($val), 2);
2807                      $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
2808                  }
2809
2810                 // should have (at least) qop, realm, nonce
2811                  if (isset($digestRequest['nonce'])) {
2812                      $this->setCredentials($this->username, $this->password, 'digest', $digestRequest);
2813                      $this->tryagain = true;
2814                      return false;
2815                  }
2816              }
2817             $this->debug('HTTP authentication failed');
2818             $this->setError('HTTP authentication failed');
2819             return false;
2820          }
2821         
2822         if (
2823             ($http_status >= 300 && $http_status <= 307) ||
2824             ($http_status >= 400 && $http_status <= 417) ||
2825             ($http_status >= 501 && $http_status <= 505)
2826            ) {
2827             $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)");
2828             return false;
2829         }
2830
2831         // decode content-encoding
2832         if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){
2833             if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){
2834                 // if decoding works, use it. else assume data wasn't gzencoded
2835                 if(function_exists('gzinflate')){
2836                     //$timer->setMarker('starting decoding of gzip/deflated content');
2837                     // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
2838                     // this means there are no Zlib headers, although there should be
2839                     $this->debug('The gzinflate function exists');
2840                     $datalen = strlen($data);
2841                     if ($this->incoming_headers['content-encoding'] == 'deflate') {
2842                         if ($degzdata = @gzinflate($data)) {
2843                             $data = $degzdata;
2844                             $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
2845                             if (strlen($data) < $datalen) {
2846                                 // test for the case that the payload has been compressed twice
2847                                 $this->debug('The inflated payload is smaller than the gzipped one; try again');
2848                                 if ($degzdata = @gzinflate($data)) {
2849                                     $data = $degzdata;
2850                                     $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
2851                                 }
2852                             }
2853                         } else {
2854                             $this->debug('Error using gzinflate to inflate the payload');
2855                             $this->setError('Error using gzinflate to inflate the payload');
2856                         }
2857                     } elseif ($this->incoming_headers['content-encoding'] == 'gzip') {
2858                         if ($degzdata = @gzinflate(substr($data, 10))) {    // do our best
2859                             $data = $degzdata;
2860                             $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
2861                             if (strlen($data) < $datalen) {
2862                                 // test for the case that the payload has been compressed twice
2863                                 $this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
2864                                 if ($degzdata = @gzinflate(substr($data, 10))) {
2865                                     $data = $degzdata;
2866                                     $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
2867                                 }
2868                             }
2869                         } else {
2870                             $this->debug('Error using gzinflate to un-gzip the payload');
2871                             $this->setError('Error using gzinflate to un-gzip the payload');
2872                         }
2873                     }
2874                     //$timer->setMarker('finished decoding of gzip/deflated content');
2875                     //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
2876                     // set decoded payload
2877                     $this->incoming_payload = $header_data.$lb.$lb.$data;
2878                 } else {
2879                     $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
2880                     $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
2881                 }
2882             } else {
2883                 $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
2884                 $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
2885             }
2886         } else {
2887             $this->debug('No Content-Encoding header');
2888         }
2889         
2890         if(strlen($data) == 0){
2891             $this->debug('no data after headers!');
2892             $this->setError('no data present after HTTP headers');
2893             return false;
2894         }
2895         
2896         return $data;
2897     }
2898
2899     function setContentType($type, $charset = false) {
2900         $this->outgoing_headers['Content-Type'] = $type . ($charset ? '; charset=' . $charset : '');
2901         $this->debug('set Content-Type: ' . $this->outgoing_headers['Content-Type']);
2902     }
2903
2904     function usePersistentConnection(){
2905         if (isset($this->outgoing_headers['Accept-Encoding'])) {
2906             return false;
2907         }
2908         $this->protocol_version = '1.1';
2909         $this->persistentConnection = true;
2910         $this->outgoing_headers['Connection'] = 'Keep-Alive';
2911         $this->debug('set Connection: ' . $this->outgoing_headers['Connection']);
2912         return true;
2913     }
2914
2915     /**
2916      * parse an incoming Cookie into it's parts
2917      *
2918      * @param    string $cookie_str content of cookie
2919      * @return    array with data of that cookie
2920      * @access    private
2921      */
2922     /*
2923      * TODO: allow a Set-Cookie string to be parsed into multiple cookies
2924      */
2925     function parseCookie($cookie_str) {
2926         $cookie_str = str_replace('; ', ';', $cookie_str) . ';';
2927         $data = split(';', $cookie_str);
2928         $value_str = $data[0];
2929
2930         $cookie_param = 'domain=';
2931         $start = strpos($cookie_str, $cookie_param);
2932         if ($start > 0) {
2933             $domain = substr($cookie_str, $start + strlen($cookie_param));
2934             $domain = substr($domain, 0, strpos($domain, ';'));
2935         } else {
2936             $domain = '';
2937         }
2938
2939         $cookie_param = 'expires=';
2940         $start = strpos($cookie_str, $cookie_param);
2941         if ($start > 0) {
2942             $expires = substr($cookie_str, $start + strlen($cookie_param));
2943             $expires = substr($expires, 0, strpos($expires, ';'));
2944         } else {
2945             $expires = '';
2946         }
2947
2948         $cookie_param = 'path=';
2949         $start = strpos($cookie_str, $cookie_param);
2950         if ( $start > 0 ) {
2951             $path = substr($cookie_str, $start + strlen($cookie_param));
2952             $path = substr($path, 0, strpos($path, ';'));
2953         } else {
2954             $path = '/';
2955         }
2956                         
2957         $cookie_param = ';secure;';
2958         if (strpos($cookie_str, $cookie_param) !== FALSE) {
2959             $secure = true;
2960         } else {
2961             $secure = false;
2962         }
2963
2964         $sep_pos = strpos($value_str, '=');
2965
2966         if ($sep_pos) {
2967             $name = substr($value_str, 0, $sep_pos);
2968             $value = substr($value_str, $sep_pos + 1);
2969             $cookie= array(    'name' => $name,
2970                             'value' => $value,
2971                             'domain' => $domain,
2972                             'path' => $path,
2973                             'expires' => $expires,
2974                             'secure' => $secure
2975                             );       
2976             return $cookie;
2977         }
2978         return false;
2979     }
2980  
2981     /**
2982      * sort out cookies for the current request
2983      *
2984      * @param    array $cookies array with all cookies
2985      * @param    boolean $secure is the send-content secure or not?
2986      * @return    string for Cookie-HTTP-Header
2987      * @access    private
2988      */
2989     function getCookiesForRequest($cookies, $secure=false) {
2990         $cookie_str = '';
2991         if ((! is_null($cookies)) && (is_array($cookies))) {
2992             foreach ($cookies as $cookie) {
2993                 if (! is_array($cookie)) {
2994                     continue;
2995                 }
2996                 $this->debug("check cookie for validity: ".$cookie['name'].'='.$cookie['value']);
2997                 if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
2998                     if (strtotime($cookie['expires']) <= time()) {
2999                         $this->debug('cookie has expired');
3000                         continue;
3001                     }
3002                 }
3003                 if ((isset($cookie['domain'])) && (! empty($cookie['domain']))) {
3004                     $domain = preg_quote($cookie['domain']);
3005                     if (! preg_match("'.*$domain$'i", $this->host)) {
3006                         $this->debug('cookie has different domain');
3007                         continue;
3008                     }
3009                 }
3010                 if ((isset($cookie['path'])) && (! empty($cookie['path']))) {
3011                     $path = preg_quote($cookie['path']);
3012                     if (! preg_match("'^$path.*'i", $this->path)) {
3013                         $this->debug('cookie is for a different path');
3014                         continue;
3015                     }
3016                 }
3017                 if ((! $secure) && (isset($cookie['secure'])) && ($cookie['secure'])) {
3018                     $this->debug('cookie is secure, transport is not');
3019                     continue;
3020                 }
3021                 $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; ';
3022                 $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']);
3023             }
3024         }
3025         return $cookie_str;
3026   }
3027 }
3028
3029 ?><?php
3030
3031
3032
3033 /**
3034 *
3035 * soap_server allows the user to create a SOAP server
3036 * that is capable of receiving messages and returning responses
3037 *
3038 * NOTE: WSDL functionality is experimental
3039 *
3040 * @author   Dietrich Ayala <dietrich@ganx4.com>
3041 * @version  $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
3042 * @access   public
3043 */
3044 class soap_server extends nusoap_base {
3045     /**
3046      * HTTP headers of request
3047      * @var array
3048      * @access private
3049      */
3050     var $headers = array();
3051     /**
3052      * HTTP request
3053      * @var string
3054      * @access private
3055      */
3056     var $request = '';
3057     /**
3058      * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
3059      * @var string
3060      * @access public
3061      */
3062     var $requestHeaders = '';
3063     /**
3064      * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
3065      * @var string
3066      * @access public
3067      */
3068     var $document = '';
3069     /**
3070      * SOAP payload for request (text)
3071      * @var string
3072      * @access public
3073      */
3074     var $requestSOAP = '';
3075     /**
3076      * requested method namespace URI
3077      * @var string
3078      * @access private
3079      */
3080     var $methodURI = '';
3081     /**
3082      * name of method requested
3083      * @var string
3084      * @access private
3085      */
3086     var $methodname = '';
3087     /**
3088      * method parameters from request
3089      * @var array
3090      * @access private
3091      */
3092     var $methodparams = array();
3093     /**
3094      * SOAP Action from request
3095      * @var string
3096      * @access private
3097      */
3098     var $SOAPAction = '';
3099     /**
3100      * character set encoding of incoming (request) messages
3101      * @var string
3102      * @access public
3103      */
3104     var $xml_encoding = '';
3105     /**
3106      * toggles whether the parser decodes element content w/ utf8_decode()
3107      * @var boolean
3108      * @access public
3109      */
3110     var $decode_utf8 = true;
3111
3112     /**
3113      * HTTP headers of response
3114      * @var array
3115      * @access public
3116      */
3117     var $outgoing_headers = array();
3118     /**
3119      * HTTP response
3120      * @var string
3121      * @access private
3122      */
3123     var $response = '';
3124     /**
3125      * SOAP headers for response (text)
3126      * @var string
3127      * @access public
3128      */
3129     var $responseHeaders = '';
3130     /**
3131      * SOAP payload for response (text)
3132      * @var string
3133      * @access private
3134      */
3135     var $responseSOAP = '';
3136     /**
3137      * method return value to place in response
3138      * @var mixed
3139      * @access private
3140      */
3141     var $methodreturn = false;
3142     /**
3143      * whether $methodreturn is a string of literal XML
3144      * @var boolean
3145      * @access public
3146      */
3147     var $methodreturnisliteralxml = false;
3148     /**
3149      * SOAP fault for response (or false)
3150      * @var mixed
3151      * @access private
3152      */
3153     var $fault = false;
3154     /**
3155      * text indication of result (for debugging)
3156      * @var string
3157      * @access private
3158      */
3159     var $result = 'successful';
3160
3161     /**
3162      * assoc array of operations => opData; operations are added by the register()
3163      * method or by parsing an external WSDL definition
3164      * @var array
3165      * @access private
3166      */
3167     var $operations = array();
3168     /**
3169      * wsdl instance (if one)
3170      * @var mixed
3171      * @access private
3172      */
3173     var $wsdl = false;
3174     /**
3175      * URL for WSDL (if one)
3176      * @var mixed
3177      * @access private
3178      */
3179     var $externalWSDLURL = false;
3180     /**
3181      * whether to append debug to response as XML comment
3182      * @var boolean
3183      * @access public
3184      */
3185     var $debug_flag = false;
3186
3187
3188     /**
3189     * constructor
3190     * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
3191     *
3192     * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
3193     * @access   public
3194     */
3195     function soap_server($wsdl=false){
3196         parent::nusoap_base();
3197         // turn on debugging?
3198         global $debug;
3199         global $HTTP_SERVER_VARS;
3200
3201         if (isset($_SERVER)) {
3202             $this->debug("_SERVER is defined:");
3203             $this->appendDebug($this->varDump($_SERVER));
3204         } elseif (isset($HTTP_SERVER_VARS)) {
3205             $this->debug("HTTP_SERVER_VARS is defined:");
3206             $this->appendDebug($this->varDump($HTTP_SERVER_VARS));
3207         } else {
3208             $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
3209         }
3210
3211         if (isset($debug)) {
3212             $this->debug("In soap_server, set debug_flag=$debug based on global flag");
3213             $this->debug_flag = $debug;
3214         } elseif (isset($_SERVER['QUERY_STRING'])) {
3215             $qs = explode('&', $_SERVER['QUERY_STRING']);
3216             foreach ($qs as $v) {
3217                 if (substr($v, 0, 6) == 'debug=') {
3218                     $this->debug("In soap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
3219                     $this->debug_flag = substr($v, 6);
3220                 }
3221             }
3222         } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3223             $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
3224             foreach ($qs as $v) {
3225                 if (substr($v, 0, 6) == 'debug=') {
3226                     $this->debug("In soap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
3227                     $this->debug_flag = substr($v, 6);
3228                 }
3229             }
3230         }
3231
3232         // wsdl
3233         if($wsdl){
3234             $this->debug("In soap_server, WSDL is specified");
3235             if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) {
3236                 $this->wsdl = $wsdl;
3237                 $this->externalWSDLURL = $this->wsdl->wsdl;
3238                 $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
3239             } else {
3240                 $this->debug('Create wsdl from ' . $wsdl);
3241                 $this->wsdl = new wsdl($wsdl);
3242                 $this->externalWSDLURL = $wsdl;
3243             }
3244             $this->appendDebug($this->wsdl->getDebug());
3245             $this->wsdl->clearDebug();
3246             if($err = $this->wsdl->getError()){
3247                 die('WSDL ERROR: '.$err);
3248             }
3249         }
3250     }
3251
3252     /**
3253     * processes request and returns response
3254     *
3255     * @param    string $data usually is the value of $HTTP_RAW_POST_DATA
3256     * @access   public
3257     */
3258     function service($data){
3259         global $HTTP_SERVER_VARS;
3260
3261         if (isset($_SERVER['QUERY_STRING'])) {
3262             $qs = $_SERVER['QUERY_STRING'];
3263         } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3264             $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
3265         } else {
3266             $qs = '';
3267         }
3268         $this->debug("In service, query string=$qs");
3269
3270         if (ereg('wsdl', $qs) ){
3271             $this->debug("In service, this is a request for WSDL");
3272             if($this->externalWSDLURL){
3273               if (strpos($this->externalWSDLURL,"://")!==false) { // assume URL
3274                 header('Location: '.$this->externalWSDLURL);
3275               } else { // assume file
3276                 header("Content-Type: text/xml\r\n");
3277                 $fp = fopen($this->externalWSDLURL, 'r');
3278                 fpassthru($fp);
3279               }
3280             } elseif ($this->wsdl) {
3281                 header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
3282                 print $this->wsdl->serialize($this->debug_flag);
3283                 if ($this->debug_flag) {
3284                     $this->debug('wsdl:');
3285                     $this->appendDebug($this->varDump($this->wsdl));
3286                     print $this->getDebugAsXMLComment();
3287                 }
3288             } else {
3289                 header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3290                 print "This service does not provide WSDL";
3291             }
3292         } elseif ($data == '' && $this->wsdl) {
3293             $this->debug("In service, there is no data, so return Web description");
3294             print $this->wsdl->webDescription();
3295         } else {
3296             $this->debug("In service, invoke the request");
3297             $this->parse_request($data);
3298             if (! $this->fault) {
3299                 $this->invoke_method();
3300             }
3301             if (! $this->fault) {
3302                 $this->serialize_return();
3303             }
3304             $this->send_response();
3305         }
3306     }
3307
3308     /**
3309     * parses HTTP request headers.
3310     *
3311     * The following fields are set by this function (when successful)
3312     *
3313     * headers
3314     * request
3315     * xml_encoding
3316     * SOAPAction
3317     *
3318     * @access   private
3319     */
3320     function parse_http_headers() {
3321         global $HTTP_SERVER_VARS;
3322
3323         $this->request = '';
3324         $this->SOAPAction = '';
3325         if(function_exists('getallheaders')){
3326             $this->debug("In parse_http_headers, use getallheaders");
3327             $headers = getallheaders();
3328             foreach($headers as $k=>$v){
3329                 $k = strtolower($k);
3330                 $this->headers[$k] = $v;
3331                 $this->request .= "$k: $v\r\n";
3332                 $this->debug("$k: $v");
3333             }
3334             // get SOAPAction header
3335             if(isset($this->headers['soapaction'])){
3336                 $this->SOAPAction = str_replace('"','',$this->headers['soapaction']);
3337             }
3338             // get the character encoding of the incoming request
3339             if(isset($this->headers['content-type']) && strpos($this->headers['content-type'],'=')){
3340                 $enc = str_replace('"','',substr(strstr($this->headers["content-type"],'='),1));
3341                 if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
3342                     $this->xml_encoding = strtoupper($enc);
3343                 } else {
3344                     $this->xml_encoding = 'US-ASCII';
3345                 }
3346             } else {
3347                 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3348                 $this->xml_encoding = 'ISO-8859-1';
3349             }
3350         } elseif(isset($_SERVER) && is_array($_SERVER)){
3351             $this->debug("In parse_http_headers, use _SERVER");
3352             foreach ($_SERVER as $k => $v) {
3353                 if (substr($k, 0, 5) == 'HTTP_') {
3354                     $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));                                              $k = strtolower(substr($k, 5));
3355                 } else {
3356                     $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));                                              $k = strtolower($k);
3357                 }
3358                 if ($k == 'soapaction') {
3359                     // get SOAPAction header
3360                     $k = 'SOAPAction';
3361                     $v = str_replace('"', '', $v);
3362                     $v = str_replace('\\', '', $v);
3363                     $this->SOAPAction = $v;
3364                 } else if ($k == 'content-type') {
3365                     // get the character encoding of the incoming request
3366                     if (strpos($v, '=')) {
3367                         $enc = substr(strstr($v, '='), 1);
3368                         $enc = str_replace('"', '', $enc);
3369                         $enc = str_replace('\\', '', $enc);
3370                         if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
3371                             $this->xml_encoding = strtoupper($enc);
3372                         } else {
3373                             $this->xml_encoding = 'US-ASCII';
3374                         }
3375                     } else {
3376                         // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3377                         $this->xml_encoding = 'ISO-8859-1';
3378                     }
3379                 }
3380                 $this->headers[$k] = $v;
3381                 $this->request .= "$k: $v\r\n";
3382                 $this->debug("$k: $v");
3383             }
3384         } elseif (is_array($HTTP_SERVER_VARS)) {
3385             $this->debug("In parse_http_headers, use HTTP_SERVER_VARS");
3386             foreach ($HTTP_SERVER_VARS as $k => $v) {
3387                 if (substr($k, 0, 5) == 'HTTP_') {
3388                     $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));                                              $k = strtolower(substr($k, 5));
3389                 } else {
3390                     $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));                                              $k = strtolower($k);
3391                 }
3392                 if ($k == 'soapaction') {
3393                     // get SOAPAction header
3394                     $k = 'SOAPAction';
3395                     $v = str_replace('"', '', $v);
3396                     $v = str_replace('\\', '', $v);
3397                     $this->SOAPAction = $v;
3398                 } else if ($k == 'content-type') {
3399                     // get the character encoding of the incoming request
3400                     if (strpos($v, '=')) {
3401                         $enc = substr(strstr($v, '='), 1);
3402                         $enc = str_replace('"', '', $enc);
3403                         $enc = str_replace('\\', '', $enc);
3404                         if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
3405                             $this->xml_encoding = strtoupper($enc);
3406                         } else {
3407                             $this->xml_encoding = 'US-ASCII';
3408                         }
3409                     } else {
3410                         // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3411                         $this->xml_encoding = 'ISO-8859-1';
3412                     }
3413                 }
3414                 $this->headers[$k] = $v;
3415                 $this->request .= "$k: $v\r\n";
3416                 $this->debug("$k: $v");
3417             }
3418         } else {
3419             $this->debug("In parse_http_headers, HTTP headers not accessible");
3420             $this->setError("HTTP headers not accessible");
3421         }
3422     }
3423
3424     /**
3425     * parses a request
3426     *
3427     * The following fields are set by this function (when successful)
3428     *
3429     * headers
3430     * request
3431     * xml_encoding
3432     * SOAPAction
3433     * request
3434     * requestSOAP
3435     * methodURI
3436     * methodname
3437     * methodparams
3438     * requestHeaders
3439     * document
3440     *
3441     * This sets the fault field on error
3442     *
3443     * @param    string $data XML string
3444     * @access   private
3445     */
3446     function parse_request($data='') {
3447         $this->debug('entering parse_request()');
3448         $this->parse_http_headers();
3449         $this->debug('got character encoding: '.$this->xml_encoding);
3450         // uncompress if necessary
3451         if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') {
3452             $this->debug('got content encoding: ' . $this->headers['content-encoding']);
3453             if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') {
3454                 // if decoding works, use it. else assume data wasn't gzencoded
3455                 if (function_exists('gzuncompress')) {
3456                     if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) {
3457                         $data = $degzdata;
3458                     } elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
3459                         $data = $degzdata;
3460                     } else {
3461                         $this->fault('Client', 'Errors occurred when trying to decode the data');
3462                         return;
3463                     }
3464                 } else {
3465                     $this->fault('Client', 'This Server does not support compressed data');
3466                     return;
3467                 }
3468             }
3469         }
3470         $this->request .= "\r\n".$data;
3471         $data = $this->parseRequest($this->headers, $data);
3472         $this->requestSOAP = $data;
3473         $this->debug('leaving parse_request');
3474     }
3475
3476     /**
3477     * invokes a PHP function for the requested SOAP method
3478     *
3479     * The following fields are set by this function (when successful)
3480     *
3481     * methodreturn
3482     *
3483     * Note that the PHP function that is called may also set the following
3484     * fields to affect the response sent to the client
3485     *
3486     * responseHeaders
3487     * outgoing_headers
3488     *
3489     * This sets the fault field on error
3490     *
3491     * @access   private
3492     */
3493     function invoke_method() {
3494         $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
3495
3496         if ($this->wsdl) {
3497             if ($this->opData = $this->wsdl->getOperationData($this->methodname)) {
3498                 $this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
3499                 $this->appendDebug('opData=' . $this->varDump($this->opData));
3500             } elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) {
3501                 // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
3502                 $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
3503                 $this->appendDebug('opData=' . $this->varDump($this->opData));
3504                 $this->methodname = $this->opData['name'];
3505             } else {
3506                 $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
3507                 $this->fault('Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
3508                 return;
3509             }
3510         } else {
3511             $this->debug('in invoke_method, no WSDL to validate method');
3512         }
3513
3514         // if a . is present in $this->methodname, we see if there is a class in scope,
3515         // which could be referred to. We will also distinguish between two deliminators,
3516         // to allow methods to be called a the class or an instance
3517         $class = '';
3518         $method = '';
3519         if (strpos($this->methodname, '..') > 0) {
3520             $delim = '..';
3521         } else if (strpos($this->methodname, '.') > 0) {
3522             $delim = '.';
3523         } else {
3524             $delim = '';
3525         }
3526
3527         if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1 &&
3528             class_exists(substr($this->methodname, 0, strpos($this->methodname, $delim)))) {
3529             // get the class and method name
3530             $class = substr($this->methodname, 0, strpos($this->methodname, $delim));
3531             $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim));
3532             $this->debug("in invoke_method, class=$class method=$method delim=$delim");
3533         }
3534
3535         // does method exist?
3536         if ($class == '') {
3537             if (!function_exists($this->methodname)) {
3538                 $this->debug("in invoke_method, function '$this->methodname' not found!");
3539                 $this->result = 'fault: method not found';
3540                 $this->fault('Client',"method '$this->methodname' not defined in service");
3541                 return;
3542             }
3543         } else {
3544             $method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method;
3545             if (!in_array($method_to_compare, get_class_methods($class))) {
3546                 $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
3547                 $this->result = 'fault: method not found';
3548                 $this->fault('Client',"method '$this->methodname' not defined in service");
3549                 return;
3550             }
3551         }
3552
3553         // evaluate message, getting back parameters
3554         // verify that request parameters match the method's signature
3555         if(! $this->verify_method($this->methodname,$this->methodparams)){
3556             // debug
3557             $this->debug('ERROR: request not verified against method signature');
3558             $this->result = 'fault: request failed validation against method signature';
3559             // return fault
3560             $this->fault('Client',"Operation '$this->methodname' not defined in service.");
3561             return;
3562         }
3563
3564         // if there are parameters to pass
3565         $this->debug('in invoke_method, params:');
3566         $this->appendDebug($this->varDump($this->methodparams));
3567         $this->debug("in invoke_method, calling '$this->methodname'");
3568         if (!function_exists('call_user_func_array')) {
3569             if ($class == '') {
3570                 $this->debug('in invoke_method, calling function using eval()');
3571                 $funcCall = "\$this->methodreturn = $this->methodname(";
3572             } else {
3573                 if ($delim == '..') {
3574                     $this->debug('in invoke_method, calling class method using eval()');
3575                     $funcCall = "\$this->methodreturn = ".$class."::".$method."(";
3576                 } else {
3577                     $this->debug('in invoke_method, calling instance method using eval()');
3578                     // generate unique instance name
3579                     $instname = "\$inst_".time();
3580                     $funcCall = $instname." = new ".$class."(); ";
3581                     $funcCall .= "\$this->methodreturn = ".$instname."->".$method."(";
3582                 }
3583             }
3584             if ($this->methodparams) {
3585                 foreach ($this->methodparams as $param) {
3586                     if (is_array($param)) {
3587                         $this->fault('Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
3588                         return;
3589                     }
3590                     $funcCall .= "\"$param\",";
3591                 }
3592                 $funcCall = substr($funcCall, 0, -1);
3593             }
3594             $funcCall .= ');';
3595             $this->debug('in invoke_method, function call: '.$funcCall);
3596             @eval($funcCall);
3597         } else {
3598             if ($class == '') {
3599                 $this->debug('in invoke_method, calling function using call_user_func_array()');
3600                 $call_arg = "$this->methodname";    // straight assignment changes $this->methodname to lower case after call_user_func_array()
3601             } elseif ($delim == '..') {
3602                 $this->debug('in invoke_method, calling class method using call_user_func_array()');
3603                 $call_arg = array ($class, $method);
3604             } else {
3605                 $this->debug('in invoke_method, calling instance method using call_user_func_array()');
3606                 $instance = new $class ();
3607                 $call_arg = array(&$instance, $method);
3608             }
3609             $this->methodreturn = call_user_func_array($call_arg, $this->methodparams);
3610         }
3611         $this->debug('in invoke_method, methodreturn:');
3612         $this->appendDebug($this->varDump($this->methodreturn));
3613         $this->debug("in invoke_method, called method $this->methodname, received $this->methodreturn of type ".gettype($this->methodreturn));
3614     }
3615
3616     /**
3617     * serializes the return value from a PHP function into a full SOAP Envelope
3618     *
3619     * The following fields are set by this function (when successful)
3620     *
3621     * responseSOAP
3622     *
3623     * This sets the fault field on error
3624     *
3625     * @access   private
3626     */
3627     function serialize_return() {
3628         $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
3629         // if fault
3630         if (isset($this->methodreturn) && (get_class($this->methodreturn) == 'soap_fault')) {
3631             $this->debug('got a fault object from method');
3632             $this->fault = $this->methodreturn;
3633             return;
3634         } elseif ($this->methodreturnisliteralxml) {
3635             $return_val = $this->methodreturn;
3636         // returned value(s)
3637         } else {
3638             $this->debug('got a(n) '.gettype($this->methodreturn).' from method');
3639             $this->debug('serializing return value');
3640             if($this->wsdl){
3641                 // weak attempt at supporting multiple output params
3642                 if(sizeof($this->opData['output']['parts']) > 1){
3643                     $opParams = $this->methodreturn;
3644                 } else {
3645                     // TODO: is this really necessary?
3646                     $opParams = array($this->methodreturn);
3647                 }
3648                 $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
3649                 $this->appendDebug($this->wsdl->getDebug());
3650                 $this->wsdl->clearDebug();
3651                 if($errstr = $this->wsdl->getError()){
3652                     $this->debug('got wsdl error: '.$errstr);
3653                     $this->fault('Server', 'unable to serialize result');
3654                     return;
3655                 }
3656             } else {
3657                 if (isset($this->methodreturn)) {
3658                     $return_val = $this->serialize_val($this->methodreturn, 'return');
3659                 } else {
3660                     $return_val = '';
3661                     $this->debug('in absence of WSDL, assume void return for backward compatibility');
3662                 }
3663             }
3664         }
3665         $this->debug('return value:');
3666         $this->appendDebug($this->varDump($return_val));
3667
3668         $this->debug('serializing response');
3669         if ($this->wsdl) {
3670             $this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
3671             if ($this->opData['style'] == 'rpc') {
3672                 $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
3673                 if ($this->opData['output']['use'] == 'literal') {
3674                     $payload = '<'.$this->methodname.'Response xmlns="'.$this->methodURI.'">'.$return_val.'</'.$this->methodname."Response>";
3675                 } else {
3676                     $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
3677                 }
3678             } else {
3679                 $this->debug('style is not rpc for serialization: assume document');
3680                 $payload = $return_val;
3681             }
3682         } else {
3683             $this->debug('do not have WSDL for serialization: assume rpc/encoded');
3684             $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
3685         }
3686         $this->result = 'successful';
3687         if($this->wsdl){
3688             //if($this->debug_flag){
3689                 $this->appendDebug($this->wsdl->getDebug());
3690             //    }
3691             if (isset($opData['output']['encodingStyle'])) {
3692                 $encodingStyle = $opData['output']['encodingStyle'];
3693             } else {
3694                 $encodingStyle = '';
3695             }
3696             // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
3697             $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style'],$encodingStyle);
3698         } else {
3699             $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders);
3700         }
3701         $this->debug("Leaving serialize_return");
3702     }
3703
3704     /**
3705     * sends an HTTP response
3706     *
3707     * The following fields are set by this function (when successful)
3708     *
3709     * outgoing_headers
3710     * response
3711     *
3712     * @access   private
3713     */
3714     function send_response() {
3715         $this->debug('Enter send_response');
3716         if ($this->fault) {
3717             $payload = $this->fault->serialize();
3718             $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
3719             $this->outgoing_headers[] = "Status: 500 Internal Server Error";
3720         } else {
3721             $payload = $this->responseSOAP;
3722             // Some combinations of PHP+Web server allow the Status
3723             // to come through as a header.  Since OK is the default
3724             // just do nothing.
3725             // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
3726             // $this->outgoing_headers[] = "Status: 200 OK";
3727         }
3728         // add debug data if in debug mode
3729         if(isset($this->debug_flag) && $this->debug_flag){
3730             $payload .= $this->getDebugAsXMLComment();
3731         }
3732         $this->outgoing_headers[] = "Server: $this->title Server v$this->version";
3733         ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
3734         $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")";
3735         // Let the Web server decide about this
3736         //$this->outgoing_headers[] = "Connection: Close\r\n";
3737         $payload = $this->getHTTPBody($payload);
3738         $type = $this->getHTTPContentType();
3739         $charset = $this->getHTTPContentTypeCharset();
3740         $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
3741         //begin code to compress payload - by John
3742         // NOTE: there is no way to know whether the Web server will also compress
3743         // this data.
3744         if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {   
3745             if (strstr($this->headers['accept-encoding'], 'gzip')) {
3746                 if (function_exists('gzencode')) {
3747                     if (isset($this->debug_flag) && $this->debug_flag) {
3748                         $payload .= "<!-- Content being gzipped -->";
3749                     }
3750                     $this->outgoing_headers[] = "Content-Encoding: gzip";
3751                     $payload = gzencode($payload);
3752                 } else {
3753                     if (isset($this->debug_flag) && $this->debug_flag) {
3754                         $payload .= "<!-- Content will not be gzipped: no gzencode -->";
3755                     }
3756                 }
3757             } elseif (strstr($this->headers['accept-encoding'], 'deflate')) {
3758                 // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
3759                 // instead of gzcompress output,
3760                 // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
3761                 if (function_exists('gzdeflate')) {
3762                     if (isset($this->debug_flag) && $this->debug_flag) {
3763                         $payload .= "<!-- Content being deflated -->";
3764                     }
3765                     $this->outgoing_headers[] = "Content-Encoding: deflate";
3766                     $payload = gzdeflate($payload);
3767                 } else {
3768                     if (isset($this->debug_flag) && $this->debug_flag) {
3769                         $payload .= "<!-- Content will not be deflated: no gzcompress -->";
3770                     }
3771                 }
3772             }
3773         }
3774         //end code
3775         $this->outgoing_headers[] = "Content-Length: ".strlen($payload);
3776         reset($this->outgoing_headers);
3777         foreach($this->outgoing_headers as $hdr){
3778             header($hdr, false);
3779         }
3780         print $payload;
3781         $this->response = join("\r\n",$this->outgoing_headers)."\r\n\r\n".$payload;
3782     }
3783
3784     /**
3785     * takes the value that was created by parsing the request
3786     * and compares to the method's signature, if available.
3787     *
3788     * @param    string    $operation    The operation to be invoked
3789     * @param    array    $request    The array of parameter values
3790     * @return    boolean    Whether the operation was found
3791     * @access   private
3792     */
3793     function verify_method($operation,$request){
3794         if(isset($this->wsdl) && is_object($this->wsdl)){
3795             if($this->wsdl->getOperationData($operation)){
3796                 return true;
3797             }
3798         } elseif(isset($this->operations[$operation])){
3799             return true;
3800         }
3801         return false;
3802     }
3803
3804     /**
3805     * processes SOAP message received from client
3806     *
3807     * @param    array    $headers    The HTTP headers
3808     * @param    string    $data        unprocessed request data from client
3809     * @return    mixed    value of the message, decoded into a PHP type
3810     * @access   private
3811     */
3812     function parseRequest($headers, $data) {
3813         $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' and type ' . $headers['content-type']);
3814         if (!strstr($headers['content-type'], 'text/xml')) {
3815             $this->setError('Request not of type text/xml');
3816             return false;
3817         }
3818         if (strpos($headers['content-type'], '=')) {
3819             $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
3820             $this->debug('Got response encoding: ' . $enc);
3821             if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
3822                 $this->xml_encoding = strtoupper($enc);
3823             } else {
3824                 $this->xml_encoding = 'US-ASCII';
3825             }
3826         } else {
3827             // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3828             $this->xml_encoding = 'ISO-8859-1';
3829         }
3830         $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating soap_parser');
3831         // parse response, get soap parser obj
3832         $parser = new soap_parser($data,$this->xml_encoding,'',$this->decode_utf8);
3833         // parser debug
3834         $this->debug("parser debug: \n".$parser->getDebug());
3835         // if fault occurred during message parsing
3836         if($err = $parser->getError()){
3837             $this->result = 'fault: error in msg parsing: '.$err;
3838             $this->fault('Client',"error in msg parsing:\n".$err);
3839         // else successfully parsed request into soapval object
3840         } else {
3841             // get/set methodname
3842             $this->methodURI = $parser->root_struct_namespace;
3843             $this->methodname = $parser->root_struct_name;
3844             $this->debug('methodname: '.$this->methodname.' methodURI: '.$this->methodURI);
3845             $this->debug('calling parser->get_response()');
3846             $this->methodparams = $parser->get_response();
3847             // get SOAP headers
3848             $this->requestHeaders = $parser->getHeaders();
3849             // add document for doclit support
3850             $this->document = $parser->document;
3851         }
3852      }
3853
3854     /**
3855     * gets the HTTP body for the current response.
3856     *
3857     * @param string $soapmsg The SOAP payload
3858     * @return string The HTTP body, which includes the SOAP payload
3859     * @access private
3860     */
3861     function getHTTPBody($soapmsg) {
3862         return $soapmsg;
3863     }
3864     
3865     /**
3866     * gets the HTTP content type for the current response.
3867     *
3868     * Note: getHTTPBody must be called before this.
3869     *
3870     * @return string the HTTP content type for the current response.
3871     * @access private
3872     */
3873     function getHTTPContentType() {
3874         return 'text/xml';
3875     }
3876     
3877     /**
3878     * gets the HTTP content type charset for the current response.
3879     * returns false for non-text content types.
3880     *
3881     * Note: getHTTPBody must be called before this.
3882     *
3883     * @return string the HTTP content type charset for the current response.
3884     * @access private
3885     */
3886     function getHTTPContentTypeCharset() {
3887         return $this->soap_defencoding;
3888     }
3889
3890     /**
3891     * add a method to the dispatch map (this has been replaced by the register method)
3892     *
3893     * @param    string $methodname
3894     * @param    string $in array of input values
3895     * @param    string $out array of output values
3896     * @access   public
3897     * @deprecated
3898     */
3899     function add_to_map($methodname,$in,$out){
3900             $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
3901     }
3902
3903     /**
3904     * register a service function with the server
3905     *
3906     * @param    string $name the name of the PHP function, class.method or class..method
3907     * @param    array $in assoc array of input values: key = param name, value = param type
3908     * @param    array $out assoc array of output values: key = param name, value = param type
3909     * @param    mixed $namespace the element namespace for the method or false
3910     * @param    mixed $soapaction the soapaction for the method or false
3911     * @param    mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
3912     * @param    mixed $use optional (encoded|literal) or false
3913     * @param    string $documentation optional Description to include in WSDL
3914     * @param    string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
3915     * @access   public
3916     */
3917     function register($name,$in=array(),$out=array(),$namespace=false,$soapaction=false,$style=false,$use=false,$documentation='',$encodingStyle=''){
3918         global $HTTP_SERVER_VARS;
3919
3920         if($this->externalWSDLURL){
3921             die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
3922         }
3923         if (! $name) {
3924             die('You must specify a name when you register an operation');
3925         }
3926         if (!is_array($in)) {
3927             die('You must provide an array for operation inputs');
3928         }
3929         if (!is_array($out)) {
3930             die('You must provide an array for operation outputs');
3931         }
3932         if(false == $namespace) {
3933         }
3934         if(false == $soapaction) {
3935             if (isset($_SERVER)) {
3936                 $SERVER_NAME = $_SERVER['SERVER_NAME'];
3937                 $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
3938             } elseif (isset($HTTP_SERVER_VARS)) {
3939                 $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
3940                 $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
3941             } else {
3942                 $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
3943             }
3944             $soapaction = "http://$SERVER_NAME$SCRIPT_NAME/$name";
3945         }
3946         if(false == $style) {
3947             $style = "rpc";
3948         }
3949         if(false == $use) {
3950             $use = "encoded";
3951         }
3952         if ($use == 'encoded' && $encodingStyle = '') {
3953             $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
3954         }
3955
3956         $this->operations[$name] = array(
3957         'name' => $name,
3958         'in' => $in,
3959         'out' => $out,
3960         'namespace' => $namespace,
3961         'soapaction' => $soapaction,
3962         'style' => $style);
3963         if($this->wsdl){
3964             $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use,$documentation,$encodingStyle);
3965         }
3966         return true;
3967     }
3968
3969     /**
3970     * Specify a fault to be returned to the client.
3971     * This also acts as a flag to the server that a fault has occured.
3972     *
3973     * @param    string $faultcode
3974     * @param    string $faultstring
3975     * @param    string $faultactor
3976     * @param    string $faultdetail
3977     * @access   public
3978     */
3979     function fault($faultcode,$faultstring,$faultactor='',$faultdetail=''){
3980         if ($faultdetail == '' && $this->debug_flag) {
3981             $faultdetail = $this->getDebug();
3982         }
3983         $this->fault = new soap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
3984         $this->fault->soap_defencoding = $this->soap_defencoding;
3985     }
3986
3987     /**
3988     * Sets up wsdl object.
3989     * Acts as a flag to enable internal WSDL generation
3990     *
3991     * @param string $serviceName, name of the service
3992     * @param mixed $namespace optional 'tns' service namespace or false
3993     * @param mixed $endpoint optional URL of service endpoint or false
3994     * @param string $style optional (rpc|document) WSDL style (also specified by operation)
3995     * @param string $transport optional SOAP transport
3996     * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
3997     */
3998     function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
3999     {
4000         global $HTTP_SERVER_VARS;
4001
4002         if (isset($_SERVER)) {
4003             $SERVER_NAME = $_SERVER['SERVER_NAME'];
4004             $SERVER_PORT = $_SERVER['SERVER_PORT'];
4005             $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4006             $HTTPS = $_SERVER['HTTPS'];
4007         } elseif (isset($HTTP_SERVER_VARS)) {
4008             $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
4009             $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT'];
4010             $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
4011             $HTTPS = $HTTP_SERVER_VARS['HTTPS'];
4012         } else {
4013             $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
4014         }
4015         if ($SERVER_PORT == 80) {
4016             $SERVER_PORT = '';
4017         } else {
4018             $SERVER_PORT = ':' . $SERVER_PORT;
4019         }
4020         if(false == $namespace) {
4021             $namespace = "http://$SERVER_NAME/soap/$serviceName";
4022         }
4023         
4024         if(false == $endpoint) {
4025             if ($HTTPS == '1' || $HTTPS == 'on') {
4026                 $SCHEME = 'https';
4027             } else {
4028                 $SCHEME = 'http';
4029             }
4030             $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME";
4031         }
4032         
4033         if(false == $schemaTargetNamespace) {
4034             $schemaTargetNamespace = $namespace;
4035         }
4036         
4037         $this->wsdl = new wsdl;
4038         $this->wsdl->serviceName = $serviceName;
4039         $this->wsdl->endpoint = $endpoint;
4040         $this->wsdl->namespaces['tns'] = $namespace;
4041         $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
4042         $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
4043         if ($schemaTargetNamespace != $namespace) {
4044             $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
4045         }
4046         $this->wsdl->schemas[$schemaTargetNamespace][0] = new xmlschema('', '', $this->wsdl->namespaces);
4047         $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
4048         $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true);
4049         $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true);
4050         $this->wsdl->bindings[$serviceName.'Binding'] = array(
4051             'name'=>$serviceName.'Binding',
4052             'style'=>$style,
4053             'transport'=>$transport,
4054             'portType'=>$serviceName.'PortType');
4055         $this->wsdl->ports[$serviceName.'Port'] = array(
4056             'binding'=>$serviceName.'Binding',
4057             'location'=>$endpoint,
4058             'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');
4059     }
4060 }
4061
4062
4063
4064 ?><?php
4065
4066
4067
4068 /**
4069 * parses a WSDL file, allows access to it's data, other utility methods
4070 *
4071 * @author   Dietrich Ayala <dietrich@ganx4.com>
4072 * @version  $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
4073 * @access public
4074 */
4075 class wsdl extends nusoap_base {
4076     // URL or filename of the root of this WSDL
4077     var $wsdl;
4078     // define internal arrays of bindings, ports, operations, messages, etc.
4079     var $schemas = array();
4080     var $currentSchema;
4081     var $message = array();
4082     var $complexTypes = array();
4083     var $messages = array();
4084     var $currentMessage;
4085     var $currentOperation;
4086     var $portTypes = array();
4087     var $currentPortType;
4088     var $bindings = array();
4089     var $currentBinding;
4090     var $ports = array();
4091     var $currentPort;
4092     var $opData = array();
4093     var $status = '';
4094     var $documentation = false;
4095     var $endpoint = '';
4096     // array of wsdl docs to import
4097     var $import = array();
4098     // parser vars
4099     var $parser;
4100     var $position = 0;
4101     var $depth = 0;
4102     var $depth_array = array();
4103     // for getting wsdl
4104     var $proxyhost = '';
4105     var $proxyport = '';
4106     var $proxyusername = '';
4107     var $proxypassword = '';
4108     var $timeout = 0;
4109     var $response_timeout = 30;
4110
4111     /**
4112      * constructor
4113      *
4114      * @param string $wsdl WSDL document URL
4115      * @param string $proxyhost
4116      * @param string $proxyport
4117      * @param string $proxyusername
4118      * @param string $proxypassword
4119      * @param integer $timeout set the connection timeout
4120      * @param integer $response_timeout set the response timeout
4121      * @access public
4122      */
4123     function wsdl($wsdl = '',$proxyhost=false,$proxyport=false,$proxyusername=false,$proxypassword=false,$timeout=0,$response_timeout=30){
4124         parent::nusoap_base();
4125         $this->wsdl = $wsdl;
4126         $this->proxyhost = $proxyhost;
4127         $this->proxyport = $proxyport;
4128         $this->proxyusername = $proxyusername;
4129         $this->proxypassword = $proxypassword;
4130         $this->timeout = $timeout;
4131         $this->response_timeout = $response_timeout;
4132         
4133         // parse wsdl file
4134         if ($wsdl != "") {
4135             $this->debug('initial wsdl URL: ' . $wsdl);
4136             $this->parseWSDL($wsdl);
4137         }
4138         // imports
4139         // TODO: handle imports more properly, grabbing them in-line and nesting them
4140             $imported_urls = array();
4141             $imported = 1;
4142             while ($imported > 0) {
4143                 $imported = 0;
4144                 // Schema imports
4145                 foreach ($this->schemas as $ns => $list) {
4146                     foreach ($list as $xs) {
4147                         $wsdlparts = parse_url($this->wsdl);    // this is bogusly simple!
4148                         foreach ($xs->imports as $ns2 => $list2) {
4149                             for ($ii = 0; $ii < count($list2); $ii++) {
4150                                 if (! $list2[$ii]['loaded']) {
4151                                     $this->schemas[$ns]->imports[$ns2][$ii]['loaded'] = true;
4152                                     $url = $list2[$ii]['location'];
4153                                     if ($url != '') {
4154                                         $urlparts = parse_url($url);
4155                                         if (!isset($urlparts['host'])) {
4156                                             $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' .$wsdlparts['port'] : '') .
4157                                                     substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
4158                                         }
4159                                         if (! in_array($url, $imported_urls)) {
4160                                             $this->parseWSDL($url);
4161                                             $imported++;
4162                                             $imported_urls[] = $url;
4163                                         }
4164                                     } else {
4165                                         $this->debug("Unexpected scenario: empty URL for unloaded import");
4166                                     }
4167                                 }
4168                             }
4169                         }
4170                     }
4171                 }
4172                 // WSDL imports
4173                 $wsdlparts = parse_url($this->wsdl);    // this is bogusly simple!
4174                 foreach ($this->import as $ns => $list) {
4175                     for ($ii = 0; $ii < count($list); $ii++) {
4176                         if (! $list[$ii]['loaded']) {
4177                             $this->import[$ns][$ii]['loaded'] = true;
4178                             $url = $list[$ii]['location'];
4179                             if ($url != '') {
4180                                 $urlparts = parse_url($url);
4181                                 if (!isset($urlparts['host'])) {
4182                                     $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') .
4183                                             substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
4184                                 }
4185                                 if (! in_array($url, $imported_urls)) {
4186                                     $this->parseWSDL($url);
4187                                     $imported++;
4188                                     $imported_urls[] = $url;
4189                                 }
4190                             } else {
4191                                 $this->debug("Unexpected scenario: empty URL for unloaded import");
4192                             }
4193                         }
4194                     }
4195                 }
4196             }
4197         // add new data to operation data
4198         foreach($this->bindings as $binding => $bindingData) {
4199             if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
4200                 foreach($bindingData['operations'] as $operation => $data) {
4201                     $this->debug('post-parse data gathering for ' . $operation);
4202                     $this->bindings[$binding]['operations'][$operation]['input'] =
4203                         isset($this->bindings[$binding]['operations'][$operation]['input']) ?
4204                         array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) :
4205                         $this->portTypes[ $bindingData['portType'] ][$operation]['input'];
4206                     $this->bindings[$binding]['operations'][$operation]['output'] =
4207                         isset($this->bindings[$binding]['operations'][$operation]['output']) ?
4208                         array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) :
4209                         $this->portTypes[ $bindingData['portType'] ][$operation]['output'];
4210                     if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){
4211                         $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ];
4212                     }
4213                     if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){
4214                            $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ];
4215                     }
4216                     if (isset($bindingData['style'])) {
4217                         $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
4218                     }
4219                     $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
4220                     $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : '';
4221                     $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
4222                 }
4223             }
4224         }
4225     }
4226
4227     /**
4228      * parses the wsdl document
4229      *
4230      * @param string $wsdl path or URL
4231      * @access private
4232      */
4233     function parseWSDL($wsdl = '')
4234     {
4235         if ($wsdl == '') {
4236             $this->debug('no wsdl passed to parseWSDL()!!');
4237             $this->setError('no wsdl passed to parseWSDL()!!');
4238             return false;
4239         }
4240         
4241         // parse $wsdl for url format
4242         $wsdl_props = parse_url($wsdl);
4243
4244         if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' || $wsdl_props['scheme'] == 'https')) {
4245             $this->debug('getting WSDL http(s) URL ' . $wsdl);
4246             // get wsdl
4247             $tr = new soap_transport_http($wsdl);
4248             $tr->request_method = 'GET';
4249             $tr->useSOAPAction = false;
4250             if($this->proxyhost && $this->proxyport){
4251                 $tr->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
4252             }
4253             $tr->setEncoding('gzip, deflate');
4254             $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout);
4255             //$this->debug("WSDL request\n" . $tr->outgoing_payload);
4256             //$this->debug("WSDL response\n" . $tr->incoming_payload);
4257             $this->appendDebug($tr->getDebug());
4258             // catch errors
4259             if($err = $tr->getError() ){
4260                 $errstr = 'HTTP ERROR: '.$err;
4261                 $this->debug($errstr);
4262                 $this->setError($errstr);
4263                 unset($tr);
4264                 return false;
4265             }
4266             unset($tr);
4267             $this->debug("got WSDL URL");
4268         } else {
4269             // $wsdl is not http(s), so treat it as a file URL or plain file path
4270             if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'file') && isset($wsdl_props['path'])) {
4271                 $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
4272             } else {
4273                 $path = $wsdl;
4274             }
4275             $this->debug('getting WSDL file ' . $path);
4276             if ($fp = @fopen($path, 'r')) {
4277                 $wsdl_string = '';
4278                 while ($data = fread($fp, 32768)) {
4279                     $wsdl_string .= $data;
4280                 }
4281                 fclose($fp);
4282             } else {
4283                 $errstr = "Bad path to WSDL file $path";
4284                 $this->debug($errstr);
4285                 $this->setError($errstr);
4286                 return false;
4287             }
4288         }
4289         $this->debug('Parse WSDL');
4290         // end new code added
4291         // Create an XML parser.
4292         $this->parser = xml_parser_create();
4293         // Set the options for parsing the XML data.
4294         // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
4295         xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
4296         // Set the object for the parser.
4297         xml_set_object($this->parser, $this);
4298         // Set the element handlers for the parser.
4299         xml_set_element_handler($this->parser, 'start_element', 'end_element');
4300         xml_set_character_data_handler($this->parser, 'character_data');
4301         // Parse the XML file.
4302         if (!xml_parse($this->parser, $wsdl_string, true)) {
4303             // Display an error message.
4304             $errstr = sprintf(
4305                 'XML error parsing WSDL from %s on line %d: %s',
4306                 $wsdl,
4307                 xml_get_current_line_number($this->parser),
4308                 xml_error_string(xml_get_error_code($this->parser))
4309                 );
4310             $this->debug($errstr);
4311             $this->debug("XML payload:\n" . $wsdl_string);
4312             $this->setError($errstr);
4313             return false;
4314         }
4315         // free the parser
4316         xml_parser_free($this->parser);
4317         $this->debug('Parsing WSDL done');
4318         // catch wsdl parse errors
4319         if($this->getError()){
4320             return false;
4321         }
4322         return true;
4323     }
4324
4325     /**
4326      * start-element handler
4327      *
4328      * @param string $parser XML parser object
4329      * @param string $name element name
4330      * @param string $attrs associative array of attributes
4331      * @access private
4332      */
4333     function start_element($parser, $name, $attrs)
4334     {
4335         if ($this->status == 'schema') {
4336             $this->currentSchema->schemaStartElement($parser, $name, $attrs);
4337             $this->appendDebug($this->currentSchema->getDebug());
4338             $this->currentSchema->clearDebug();
4339         } elseif (ereg('schema$', $name)) {
4340             $this->debug('Parsing WSDL schema');
4341             // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
4342             $this->status = 'schema';
4343             $this->currentSchema = new xmlschema('', '', $this->namespaces);
4344             $this->currentSchema->schemaStartElement($parser, $name, $attrs);
4345             $this->appendDebug($this->currentSchema->getDebug());
4346             $this->currentSchema->clearDebug();
4347         } else {
4348             // position in the total number of elements, starting from 0
4349             $pos = $this->position++;
4350             $depth = $this->depth++;
4351             // set self as current value for this depth
4352             $this->depth_array[$depth] = $pos;
4353             $this->message[$pos] = array('cdata' => '');
4354             // process attributes
4355             if (count($attrs) > 0) {
4356                 // register namespace declarations
4357                 foreach($attrs as $k => $v) {
4358                     if (ereg("^xmlns", $k)) {
4359                         if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
4360                             $this->namespaces[$ns_prefix] = $v;
4361                         } else {
4362                             $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
4363                         }
4364                         if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema') {
4365                             $this->XMLSchemaVersion = $v;
4366                             $this->namespaces['xsi'] = $v . '-instance';
4367                         }
4368                     }
4369                 }
4370                 // expand each attribute prefix to its namespace
4371                 foreach($attrs as $k => $v) {
4372                     $k = strpos($k, ':') ? $this->expandQname($k) : $k;
4373                     if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
4374                         $v = strpos($v, ':') ? $this->expandQname($v) : $v;
4375                     }
4376                     $eAttrs[$k] = $v;
4377                 }
4378                 $attrs = $eAttrs;
4379             } else {
4380                 $attrs = array();
4381             }
4382             // get element prefix, namespace and name
4383             if (ereg(':', $name)) {
4384                 // get ns prefix
4385                 $prefix = substr($name, 0, strpos($name, ':'));
4386                 // get ns
4387                 $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
4388                 // get unqualified name
4389                 $name = substr(strstr($name, ':'), 1);
4390             }
4391             // process attributes, expanding any prefixes to namespaces
4392             // find status, register data
4393             switch ($this->status) {
4394                 case 'message':
4395                     if ($name == 'part') {
4396                         if (isset($attrs['type'])) {
4397                             $this->debug("msg " . $this->currentMessage . ": found part $attrs[name]: " . implode(',', $attrs));
4398                             $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
4399                         }
4400                         if (isset($attrs['element'])) {
4401                             $this->debug("msg " . $this->currentMessage . ": found part $attrs[name]: " . implode(',', $attrs));
4402                             $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'];
4403                         }
4404                     }
4405                     break;
4406                 case 'portType':
4407                     switch ($name) {
4408                         case 'operation':
4409                             $this->currentPortOperation = $attrs['name'];
4410                             $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
4411                             if (isset($attrs['parameterOrder'])) {
4412                                 $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
4413                             }
4414                             break;
4415                         case 'documentation':
4416                             $this->documentation = true;
4417                             break;
4418                         // merge input/output data
4419                         default:
4420                             $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
4421                             $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
4422                             break;
4423                     }
4424                     break;
4425                 case 'binding':
4426                     switch ($name) {
4427                         case 'binding':
4428                             // get ns prefix
4429                             if (isset($attrs['style'])) {
4430                             $this->bindings[$this->currentBinding]['prefix'] = $prefix;
4431                             }
4432                             $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
4433                             break;
4434                         case 'header':
4435                             $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
4436                             break;
4437                         case 'operation':
4438                             if (isset($attrs['soapAction'])) {
4439                                 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
4440                             }
4441                             if (isset($attrs['style'])) {
4442                                 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
4443                             }
4444                             if (isset($attrs['name'])) {
4445                                 $this->currentOperation = $attrs['name'];
4446                                 $this->debug("current binding operation: $this->currentOperation");
4447                                 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
4448                                 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
4449                                 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
4450                             }
4451                             break;
4452                         case 'input':
4453                             $this->opStatus = 'input';
4454                             break;
4455                         case 'output':
4456                             $this->opStatus = 'output';
4457                             break;
4458                         case 'body':
4459                             if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
4460                                 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
4461                             } else {
4462                                 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
4463                             }
4464                             break;
4465                     }
4466                     break;
4467                 case 'service':
4468                     switch ($name) {
4469                         case 'port':
4470                             $this->currentPort = $attrs['name'];
4471                             $this->debug('current port: ' . $this->currentPort);
4472                             $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
4473                     
4474                             break;
4475                         case 'address':
4476                             $this->ports[$this->currentPort]['location'] = $attrs['location'];
4477                             $this->ports[$this->currentPort]['bindingType'] = $namespace;
4478                             $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace;
4479                             $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location'];
4480                             break;
4481                     }
4482                     break;
4483             }
4484         // set status
4485         switch ($name) {
4486             case 'import':
4487                 if (isset($attrs['location'])) {
4488                     $this->import[$attrs['namespace']][] = array('location' => $attrs['location'], 'loaded' => false);
4489                     $this->debug('parsing import ' . $attrs['namespace']. ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]).')');
4490                 } else {
4491                     $this->import[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
4492                     if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
4493                         $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
4494                     }
4495                     $this->debug('parsing import ' . $attrs['namespace']. ' - [no location] (' . count($this->import[$attrs['namespace']]).')');
4496                 }
4497                 break;
4498             //wait for schema
4499             //case 'types':
4500             //    $this->status = 'schema';
4501             //    break;
4502             case 'message':
4503                 $this->status = 'message';
4504                 $this->messages[$attrs['name']] = array();
4505                 $this->currentMessage = $attrs['name'];
4506                 break;
4507             case 'portType':
4508                 $this->status = 'portType';
4509                 $this->portTypes[$attrs['name']] = array();
4510                 $this->currentPortType = $attrs['name'];
4511                 break;
4512             case "binding":
4513                 if (isset($attrs['name'])) {
4514                 // get binding name
4515                     if (strpos($attrs['name'], ':')) {
4516                         $this->currentBinding = $this->getLocalPart($attrs['name']);
4517                     } else {
4518                         $this->currentBinding = $attrs['name'];
4519                     }
4520                     $this->status = 'binding';
4521                     $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
4522                     $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
4523                 }
4524                 break;
4525             case 'service':
4526                 $this->serviceName = $attrs['name'];
4527                 $this->status = 'service';
4528                 $this->debug('current service: ' . $this->serviceName);
4529                 break;
4530             case 'definitions':
4531                 foreach ($attrs as $name => $value) {
4532                     $this->wsdl_info[$name] = $value;
4533                 }
4534                 break;
4535             }
4536         }
4537     }
4538
4539     /**
4540     * end-element handler
4541     *
4542     * @param string $parser XML parser object
4543     * @param string $name element name
4544     * @access private
4545     */
4546     function end_element($parser, $name){
4547         // unset schema status
4548         if (/*ereg('types$', $name) ||*/ ereg('schema$', $name)) {
4549             $this->status = "";
4550             $this->appendDebug($this->currentSchema->getDebug());
4551             $this->currentSchema->clearDebug();
4552             $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
4553             $this->debug('Parsing WSDL schema done');
4554         }
4555         if ($this->status == 'schema') {
4556             $this->currentSchema->schemaEndElement($parser, $name);
4557         } else {
4558             // bring depth down a notch
4559             $this->depth--;
4560         }
4561         // end documentation
4562         if ($this->documentation) {
4563             //TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
4564             //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
4565             $this->documentation = false;
4566         }
4567     }
4568
4569     /**
4570      * element content handler
4571      *
4572      * @param string $parser XML parser object
4573      * @param string $data element content
4574      * @access private
4575      */
4576     function character_data($parser, $data)
4577     {
4578         $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
4579         if (isset($this->message[$pos]['cdata'])) {
4580             $this->message[$pos]['cdata'] .= $data;
4581         }
4582         if ($this->documentation) {
4583             $this->documentation .= $data;
4584         }
4585     }
4586     
4587     function getBindingData($binding)
4588     {
4589         if (is_array($this->bindings[$binding])) {
4590             return $this->bindings[$binding];
4591         }
4592     }
4593     
4594     /**
4595      * returns an assoc array of operation names => operation data
4596      *
4597      * @param string $bindingType eg: soap, smtp, dime (only soap is currently supported)
4598      * @return array
4599      * @access public
4600      */
4601     function getOperations($bindingType = 'soap')
4602     {
4603         $ops = array();
4604         if ($bindingType == 'soap') {
4605             $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
4606         }
4607         // loop thru ports
4608         foreach($this->ports as $port => $portData) {
4609             // binding type of port matches parameter
4610             if ($portData['bindingType'] == $bindingType) {
4611                 //$this->debug("getOperations for port $port");
4612                 //$this->debug("port data: " . $this->varDump($portData));
4613                 //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ]));
4614                 // merge bindings
4615                 if (isset($this->bindings[ $portData['binding'] ]['operations'])) {
4616                     $ops = array_merge ($ops, $this->bindings[ $portData['binding'] ]['operations']);
4617                 }
4618             }
4619         }
4620         return $ops;
4621     }
4622     
4623     /**
4624      * returns an associative array of data necessary for calling an operation
4625      *
4626      * @param string $operation , name of operation
4627      * @param string $bindingType , type of binding eg: soap
4628      * @return array
4629      * @access public
4630      */
4631     function getOperationData($operation, $bindingType = 'soap')
4632     {
4633         if ($bindingType == 'soap') {
4634             $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
4635         }
4636         // loop thru ports
4637         foreach($this->ports as $port => $portData) {
4638             // binding type of port matches parameter
4639             if ($portData['bindingType'] == $bindingType) {
4640                 // get binding
4641                 //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
4642                 foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) {
4643                     // note that we could/should also check the namespace here
4644                     if ($operation == $bOperation) {
4645                         $opData = $this->bindings[ $portData['binding'] ]['operations'][$operation];
4646                         return $opData;
4647                     }
4648                 }
4649             }
4650         }
4651     }
4652     
4653     /**
4654      * returns an associative array of data necessary for calling an operation
4655      *
4656      * @param string $soapAction soapAction for operation
4657      * @param string $bindingType type of binding eg: soap
4658      * @return array
4659      * @access public
4660      */
4661     function getOperationDataForSoapAction($soapAction, $bindingType = 'soap') {
4662         if ($bindingType == 'soap') {
4663             $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
4664         }
4665         // loop thru ports
4666         foreach($this->ports as $port => $portData) {
4667             // binding type of port matches parameter
4668             if ($portData['bindingType'] == $bindingType) {
4669                 // loop through operations for the binding
4670                 foreach ($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
4671                     if ($opData['soapAction'] == $soapAction) {
4672                         return $opData;
4673                     }
4674                 }
4675             }
4676         }
4677     }
4678     
4679     /**
4680     * returns an array of information about a given type
4681     * returns false if no type exists by the given name
4682     *
4683     *     typeDef = array(
4684     *     'elements' => array(), // refs to elements array
4685     *    'restrictionBase' => '',
4686     *    'phpType' => '',
4687     *    'order' => '(sequence|all)',
4688     *    'attrs' => array() // refs to attributes array
4689     *    )
4690     *
4691     * @param $type string the type
4692     * @param $ns string namespace (not prefix) of the type
4693     * @return mixed
4694     * @access public
4695     * @see xmlschema
4696     */
4697     function getTypeDef($type, $ns) {
4698         $this->debug("in getTypeDef: type=$type, ns=$ns");
4699         if ((! $ns) && isset($this->namespaces['tns'])) {
4700             $ns = $this->namespaces['tns'];
4701             $this->debug("in getTypeDef: type namespace forced to $ns");
4702         }
4703         if (isset($this->schemas[$ns])) {
4704             $this->debug("in getTypeDef: have schema for namespace $ns");
4705             for ($i = 0; $i < count($this->schemas[$ns]); $i++) {
4706                 $xs = &$this->schemas[$ns][$i];
4707                 $t = $xs->getTypeDef($type);
4708                 $this->appendDebug($xs->getDebug());
4709                 $xs->clearDebug();
4710                 if ($t) {
4711                     if (!isset($t['phpType'])) {
4712                         // get info for type to tack onto the element
4713                         $uqType = substr($t['type'], strrpos($t['type'], ':') + 1);
4714                         $ns = substr($t['type'], 0, strrpos($t['type'], ':'));
4715                         $etype = $this->getTypeDef($uqType, $ns);
4716                         if ($etype) {
4717                             $this->debug("found type for [element] $type:");
4718                             $this->debug($this->varDump($etype));
4719                             if (isset($etype['phpType'])) {
4720                                 $t['phpType'] = $etype['phpType'];
4721                             }
4722                             if (isset($etype['elements'])) {
4723                                 $t['elements'] = $etype['elements'];
4724                             }
4725                             if (isset($etype['attrs'])) {
4726                                 $t['attrs'] = $etype['attrs'];
4727                             }
4728                         }
4729                     }
4730                     return $t;
4731                 }
4732             }
4733         } else {
4734             $this->debug("in getTypeDef: do not have schema for namespace $ns");
4735         }
4736         return false;
4737     }
4738
4739     /**
4740     * prints html description of services
4741     *
4742     * @access private
4743     */
4744     function webDescription(){
4745         global $HTTP_SERVER_VARS;
4746
4747         if (isset($_SERVER)) {
4748             $PHP_SELF = $_SERVER['PHP_SELF'];
4749         } elseif (isset($HTTP_SERVER_VARS)) {
4750             $PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF'];
4751         } else {
4752             $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
4753         }
4754
4755         $b = '
4756         <html><head><title>NuSOAP: '.$this->serviceName.'</title>
4757         <style type="text/css">
4758             body    { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
4759             p       { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
4760             pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
4761             ul      { margin-top: 10px; margin-left: 20px; }
4762             li      { list-style-type: none; margin-top: 10px; color: #000000; }
4763             .content{
4764             margin-left: 0px; padding-bottom: 2em; }
4765             .nav {
4766             padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
4767             margin-top: 10px; margin-left: 0px; color: #000000;
4768             background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
4769             .title {
4770             font-family: arial; font-size: 26px; color: #ffffff;
4771             background-color: #999999; width: 105%; margin-left: 0px;
4772             padding-top: 10px; padding-bottom: 10px; padding-left: 15px;}
4773             .hidden {
4774             position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
4775             font-family: arial; overflow: hidden; width: 600;
4776             padding: 20px; font-size: 10px; background-color: #999999;
4777             layer-background-color:#FFFFFF; }
4778             a,a:active  { color: charcoal; font-weight: bold; }
4779             a:visited   { color: #666666; font-weight: bold; }
4780             a:hover     { color: cc3300; font-weight: bold; }
4781         </style>
4782         <script language="JavaScript" type="text/javascript">
4783         <!--
4784         // POP-UP CAPTIONS...
4785         function lib_bwcheck(){ //Browsercheck (needed)
4786             this.ver=navigator.appVersion
4787             this.agent=navigator.userAgent
4788             this.dom=document.getElementById?1:0
4789             this.opera5=this.agent.indexOf("Opera 5")>-1
4790             this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
4791             this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
4792             this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
4793             this.ie=this.ie4||this.ie5||this.ie6
4794             this.mac=this.agent.indexOf("Mac")>-1
4795             this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
4796             this.ns4=(document.layers && !this.dom)?1:0;
4797             this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
4798             return this
4799         }
4800         var bw = new lib_bwcheck()
4801         //Makes crossbrowser object.
4802         function makeObj(obj){
4803             this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
4804             if(!this.evnt) return false
4805             this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
4806             this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
4807             this.writeIt=b_writeIt;
4808             return this
4809         }
4810         // A unit of measure that will be added when setting the position of a layer.
4811         //var px = bw.ns4||window.opera?"":"px";
4812         function b_writeIt(text){
4813             if (bw.ns4){this.wref.write(text);this.wref.close()}
4814             else this.wref.innerHTML = text
4815         }
4816         //Shows the messages
4817         var oDesc;
4818         function popup(divid){
4819             if(oDesc = new makeObj(divid)){
4820             oDesc.css.visibility = "visible"
4821             }
4822         }
4823         function popout(){ // Hides message
4824             if(oDesc) oDesc.css.visibility = "hidden"
4825         }
4826         //-->
4827         </script>
4828         </head>
4829         <body>
4830         <div class=content>
4831             <br><br>
4832             <div class=title>'.$this->serviceName.'</div>
4833             <div class=nav>
4834                 <p>View the <a href="'.$PHP_SELF.'?wsdl">WSDL</a> for the service.
4835                 Click on an operation name to view it&apos;s details.</p>
4836                 <ul>';
4837                 foreach($this->getOperations() as $op => $data){
4838                     $b .= "<li><a href='#' onclick=\"popout();popup('$op')\">$op</a></li>";
4839                     // create hidden div
4840                     $b .= "<div id='$op' class='hidden'>
4841                     <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
4842                     foreach($data as $donnie => $marie){ // loop through opdata
4843                         if($donnie == 'input' || $donnie == 'output'){ // show input/output data
4844                             $b .= "<font color='white'>".ucfirst($donnie).':</font><br>';
4845                             foreach($marie as $captain => $tenille){ // loop through data
4846                                 if($captain == 'parts'){ // loop thru parts
4847                                     $b .= "&nbsp;&nbsp;$captain:<br>";
4848                                     //if(is_array($tenille)){
4849                                         foreach($tenille as $joanie => $chachi){
4850                                             $b .= "&nbsp;&nbsp;&nbsp;&nbsp;$joanie: $chachi<br>";
4851                                         }
4852                                     //}
4853                                 } else {
4854                                     $b .= "&nbsp;&nbsp;$captain: $tenille<br>";
4855                                 }
4856                             }
4857                         } else {
4858                             $b .= "<font color='white'>".ucfirst($donnie).":</font> $marie<br>";
4859                         }
4860                     }
4861                     $b .= '</div>';
4862                 }
4863                 $b .= '
4864                 <ul>
4865             </div>
4866         </div></body></html>';
4867         return $b;
4868     }
4869
4870     /**
4871     * serialize the parsed wsdl
4872     *
4873     * @param mixed $debug whether to put debug=1 in endpoint URL
4874     * @return string serialization of WSDL
4875     * @access public
4876     */
4877     function serialize($debug = 0)
4878     {
4879         $xml = '<?xml version="1.0" encoding="ISO-8859-1"?>';
4880         $xml .= "\n<definitions";
4881         foreach($this->namespaces as $k => $v) {
4882             $xml .= " xmlns:$k=\"$v\"";
4883         }
4884         // 10.9.02 - add poulter fix for wsdl and tns declarations
4885         if (isset($this->namespaces['wsdl'])) {
4886             $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
4887         }
4888         if (isset($this->namespaces['tns'])) {
4889             $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
4890         }
4891         $xml .= '>';
4892         // imports
4893         if (sizeof($this->import) > 0) {
4894             foreach($this->import as $ns => $list) {
4895                 foreach ($list as $ii) {
4896                     if ($ii['location'] != '') {
4897                         $xml .= '<import location="' . $ii['location'] . '" namespace="' . $ns . '" />';
4898                     } else {
4899                         $xml .= '<import namespace="' . $ns . '" />';
4900                     }
4901                 }
4902             }
4903         }
4904         // types
4905         if (count($this->schemas)>=1) {
4906             $xml .= "\n<types>";
4907             foreach ($this->schemas as $ns => $list) {
4908                 foreach ($list as $xs) {
4909                     $xml .= $xs->serializeSchema();
4910                 }
4911             }
4912             $xml .= '</types>';
4913         }
4914         // messages
4915         if (count($this->messages) >= 1) {
4916             foreach($this->messages as $msgName => $msgParts) {
4917                 $xml .= "\n<message name=\"" . $msgName . '">';
4918                 if(is_array($msgParts)){
4919                     foreach($msgParts as $partName => $partType) {
4920                         // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
4921                         if (strpos($partType, ':')) {
4922                             $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
4923                         } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
4924                             // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
4925                             $typePrefix = 'xsd';
4926                         } else {
4927                             foreach($this->typemap as $ns => $types) {
4928                                 if (isset($types[$partType])) {
4929                                     $typePrefix = $this->getPrefixFromNamespace($ns);
4930                                 }
4931                             }
4932                             if (!isset($typePrefix)) {
4933                                 die("$partType has no namespace!");
4934                             }
4935                         }
4936                         $ns = $this->getNamespaceFromPrefix($typePrefix);
4937                         $typeDef = $this->getTypeDef($this->getLocalPart($partType), $ns);
4938                         if ($typeDef['typeClass'] == 'element') {
4939                             $elementortype = 'element';
4940                         } else {
4941                             $elementortype = 'type';
4942                         }
4943                         $xml .= '<part name="' . $partName . '" ' . $elementortype . '="' . $typePrefix . ':' . $this->getLocalPart($partType) . '" />';
4944                     }
4945                 }
4946                 $xml .= '</message>';
4947             }
4948         }
4949         // bindings & porttypes
4950         if (count($this->bindings) >= 1) {
4951             $binding_xml = '';
4952             $portType_xml = '';
4953             foreach($this->bindings as $bindingName => $attrs) {
4954                 $binding_xml .= "\n<binding name=\"" . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
4955                 $binding_xml .= '<soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
4956                 $portType_xml .= "\n<portType name=\"" . $attrs['portType'] . '">';
4957                 foreach($attrs['operations'] as $opName => $opParts) {
4958                     $binding_xml .= '<operation name="' . $opName . '">';
4959                     $binding_xml .= '<soap:operation soapAction="' . $opParts['soapAction'] . '" style="'. $opParts['style'] . '"/>';
4960                     if (isset($opParts['input']['encodingStyle']) && $opParts['input']['encodingStyle'] != '') {
4961                         $enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"';
4962                     } else {
4963                         $enc_style = '';
4964                     }
4965                     $binding_xml .= '<input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '"' . $enc_style . '/></input>';
4966                     if (isset($opParts['output']['encodingStyle']) && $opParts['output']['encodingStyle'] != '') {
4967                         $enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"';
4968                     } else {
4969                         $enc_style = '';
4970                     }
4971                     $binding_xml .= '<output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '"' . $enc_style . '/></output>';
4972                     $binding_xml .= '</operation>';
4973                     $portType_xml .= '<operation name="' . $opParts['name'] . '"';
4974                     if (isset($opParts['parameterOrder'])) {
4975                         $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
4976                     }
4977                     $portType_xml .= '>';
4978                     if(isset($opParts['documentation']) && $opParts['documentation'] != '') {
4979                         $portType_xml .= '<documentation>' . htmlspecialchars($opParts['documentation']) . '</documentation>';
4980                     }
4981                     $portType_xml .= '<input message="tns:' . $opParts['input']['message'] . '"/>';
4982                     $portType_xml .= '<output message="tns:' . $opParts['output']['message'] . '"/>';
4983                     $portType_xml .= '</operation>';
4984                 }
4985                 $portType_xml .= '</portType>';
4986                 $binding_xml .= '</binding>';
4987             }
4988             $xml .= $portType_xml . $binding_xml;
4989         }
4990         // services
4991         $xml .= "\n<service name=\"" . $this->serviceName . '">';
4992         if (count($this->ports) >= 1) {
4993             foreach($this->ports as $pName => $attrs) {
4994                 $xml .= '<port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
4995                 $xml .= '<soap:address location="' . $attrs['location'] . ($debug ? '?debug=1' : '') . '"/>';
4996                 $xml .= '</port>';
4997             }
4998         }
4999         $xml .= '</service>';
5000         return $xml . "\n</definitions>";
5001     }
5002     
5003     /**
5004      * serialize PHP values according to a WSDL message definition
5005      *
5006      * TODO
5007      * - multi-ref serialization
5008      * - validate PHP values against type definitions, return errors if invalid
5009      *
5010      * @param string $operation operation name
5011      * @param string $direction (input|output)
5012      * @param mixed $parameters parameter value(s)
5013      * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
5014      * @access public
5015      */
5016     function serializeRPCParameters($operation, $direction, $parameters)
5017     {
5018         $this->debug("in serializeRPCParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion");
5019         $this->appendDebug('parameters=' . $this->varDump($parameters));
5020         
5021         if ($direction != 'input' && $direction != 'output') {
5022             $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5023             $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5024             return false;
5025         }
5026         if (!$opData = $this->getOperationData($operation)) {
5027             $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
5028             $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
5029             return false;
5030         }
5031         $this->debug('opData:');
5032         $this->appendDebug($this->varDump($opData));
5033
5034         // Get encoding style for output and set to current
5035         $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5036         if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5037             $encodingStyle = $opData['output']['encodingStyle'];
5038             $enc_style = $encodingStyle;
5039         }
5040
5041         // set input params
5042         $xml = '';
5043         if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
5044             
5045             $use = $opData[$direction]['use'];
5046             $this->debug('have ' . count($opData[$direction]['parts']) . ' part(s) to serialize');
5047             if (is_array($parameters)) {
5048                 $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5049                 $this->debug('have ' . count($parameters) . ' parameter(s) provided as ' . $parametersArrayType . ' to serialize');
5050                 foreach($opData[$direction]['parts'] as $name => $type) {
5051                     $this->debug('serializing part "'.$name.'" of type "'.$type.'"');
5052                     // Track encoding style
5053                     if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
5054                         $encodingStyle = $opData[$direction]['encodingStyle'];           
5055                         $enc_style = $encodingStyle;
5056                     } else {
5057                         $enc_style = false;
5058                     }
5059                     // NOTE: add error handling here
5060                     // if serializeType returns false, then catch global error and fault
5061                     if ($parametersArrayType == 'arraySimple') {
5062                         $p = array_shift($parameters);
5063                         $this->debug('calling serializeType w/indexed param');
5064                         $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5065                     } elseif (isset($parameters[$name])) {
5066                         $this->debug('calling serializeType w/named param');
5067                         $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5068                     } else {
5069                         // TODO: only send nillable
5070                         $this->debug('calling serializeType w/null param');
5071                         $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
5072                     }
5073                 }
5074             } else {
5075                 $this->debug('no parameters passed.');
5076             }
5077         }
5078         $this->debug("serializeRPCParameters returning: $xml");
5079         return $xml;
5080     }
5081     
5082     /**
5083      * serialize a PHP value according to a WSDL message definition
5084      *
5085      * TODO
5086      * - multi-ref serialization
5087      * - validate PHP values against type definitions, return errors if invalid
5088      *
5089      * @param string $ type name
5090      * @param mixed $ param value
5091      * @return mixed new param or false if initial value didn't validate
5092      * @access public
5093      * @deprecated
5094      */
5095     function serializeParameters($operation, $direction, $parameters)
5096     {
5097         $this->debug("in serializeParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion");
5098         $this->appendDebug('parameters=' . $this->varDump($parameters));
5099         
5100         if ($direction != 'input' && $direction != 'output') {
5101             $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5102             $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5103             return false;
5104         }
5105         if (!$opData = $this->getOperationData($operation)) {
5106             $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
5107             $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
5108             return false;
5109         }
5110         $this->debug('opData:');
5111         $this->appendDebug($this->varDump($opData));
5112         
5113         // Get encoding style for output and set to current
5114         $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5115         if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5116             $encodingStyle = $opData['output']['encodingStyle'];
5117             $enc_style = $encodingStyle;
5118         }
5119         
5120         // set input params
5121         $xml = '';
5122         if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
5123             
5124             $use = $opData[$direction]['use'];
5125             $this->debug("use=$use");
5126             $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
5127             if (is_array($parameters)) {
5128                 $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5129                 $this->debug('have ' . $parametersArrayType . ' parameters');
5130                 foreach($opData[$direction]['parts'] as $name => $type) {
5131                     $this->debug('serializing part "'.$name.'" of type "'.$type.'"');
5132                     // Track encoding style
5133                     if(isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
5134                         $encodingStyle = $opData[$direction]['encodingStyle'];           
5135                         $enc_style = $encodingStyle;
5136                     } else {
5137                         $enc_style = false;
5138                     }
5139                     // NOTE: add error handling here
5140                     // if serializeType returns false, then catch global error and fault
5141                     if ($parametersArrayType == 'arraySimple') {
5142                         $p = array_shift($parameters);
5143                         $this->debug('calling serializeType w/indexed param');
5144                         $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5145                     } elseif (isset($parameters[$name])) {
5146                         $this->debug('calling serializeType w/named param');
5147                         $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5148                     } else {
5149                         // TODO: only send nillable
5150                         $this->debug('calling serializeType w/null param');
5151                         $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
5152                     }
5153                 }
5154             } else {
5155                 $this->debug('no parameters passed.');
5156             }
5157         }
5158         $this->debug("serializeParameters returning: $xml");
5159         return $xml;
5160     }
5161     
5162     /**
5163      * serializes a PHP value according a given type definition
5164      *
5165      * @param string $name name of value (part or element)
5166      * @param string $type XML schema type of value (type or element)
5167      * @param mixed $value a native PHP value (parameter value)
5168      * @param string $use use for part (encoded|literal)
5169      * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
5170      * @param boolean $unqualified a kludge for what should be XML namespace form handling
5171      * @return string value serialized as an XML string
5172      * @access private
5173      */
5174     function serializeType($name, $type, $value, $use='encoded', $encodingStyle=false, $unqualified=false)
5175     {
5176         $this->debug("in serializeType: name=$name, type=$type, use=$use, encodingStyle=$encodingStyle, unqualified=" . ($unqualified ? "unqualified" : "qualified"));
5177         $this->appendDebug("value=" . $this->varDump($value));
5178         if($use == 'encoded' && $encodingStyle) {
5179             $encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"';
5180         }
5181
5182         // if a soapval has been supplied, let its type override the WSDL
5183         if (is_object($value) && get_class($value) == 'soapval') {
5184             if ($value->type_ns) {
5185                 $type = $value->type_ns . ':' . $value->type;
5186                 $forceType = true;
5187                 $this->debug("in serializeType: soapval overrides type to $type");
5188             } elseif ($value->type) {
5189                 $type = $value->type;
5190                 $forceType = true;
5191                 $this->debug("in serializeType: soapval overrides type to $type");
5192             } else {
5193                 $forceType = false;
5194                 $this->debug("in serializeType: soapval does not override type");
5195             }
5196             $attrs = $value->attributes;
5197             $value = $value->value;
5198             $this->debug("in serializeType: soapval overrides value to $value");
5199             if ($attrs) {
5200                 if (!is_array($value)) {
5201                     $value['!'] = $value;
5202                 }
5203                 foreach ($attrs as $n => $v) {
5204                     $value['!' . $n] = $v;
5205                 }
5206                 $this->debug("in serializeType: soapval provides attributes");
5207             }
5208         } else {
5209             $forceType = false;
5210         }
5211
5212         $xml = '';
5213         if (strpos($type, ':')) {
5214             $uqType = substr($type, strrpos($type, ':') + 1);
5215             $ns = substr($type, 0, strrpos($type, ':'));
5216             $this->debug("in serializeType: got a prefixed type: $uqType, $ns");
5217             if ($this->getNamespaceFromPrefix($ns)) {
5218                 $ns = $this->getNamespaceFromPrefix($ns);
5219                 $this->debug("in serializeType: expanded prefixed type: $uqType, $ns");
5220             }
5221
5222             if($ns == $this->XMLSchemaVersion || $ns == 'http://schemas.xmlsoap.org/soap/encoding/'){
5223                 $this->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type');
5224                 if ($unqualified  && $use == 'literal') {
5225                     $elementNS = " xmlns=\"\"";
5226                 } else {
5227                     $elementNS = '';
5228                 }
5229                 if (is_null($value)) {
5230                     if ($use == 'literal') {
5231                         // TODO: depends on minOccurs
5232                         $xml = "<$name$elementNS/>";
5233                     } else {
5234                         // TODO: depends on nillable, which should be checked before calling this method
5235                         $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
5236                     }
5237                     $this->debug("in serializeType: returning: $xml");
5238                     return $xml;
5239                 }
5240                 if ($uqType == 'boolean') {
5241                     if ((is_string($value) && $value == 'false') || (! $value)) {
5242                         $value = 'false';
5243                     } else {
5244                         $value = 'true';
5245                     }
5246                 }
5247                 if ($uqType == 'string' && gettype($value) == 'string') {
5248                     $value = $this->expandEntities($value);
5249                 }
5250                 if (($uqType == 'long' || $uqType == 'unsignedLong') && gettype($value) == 'double') {
5251                     $value = sprintf("%.0lf", $value);
5252                 }
5253                 // it's a scalar
5254                 // TODO: what about null/nil values?
5255                 // check type isn't a custom type extending xmlschema namespace
5256                 if (!$this->getTypeDef($uqType, $ns)) {
5257                     if ($use == 'literal') {
5258                         if ($forceType) {
5259                             $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
5260                         } else {
5261                             $xml = "<$name$elementNS>$value</$name>";
5262                         }
5263                     } else {
5264                         $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
5265                     }
5266                     $this->debug("in serializeType: returning: $xml");
5267                     return $xml;
5268                 }
5269                 $this->debug('custom type extends XML Schema or SOAP Encoding namespace (yuck)');
5270             } else if ($ns == 'http://xml.apache.org/xml-soap') {
5271                 $this->debug('in serializeType: appears to be Apache SOAP type');
5272                 if ($uqType == 'Map') {
5273                     $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
5274                     if (! $tt_prefix) {
5275                         $this->debug('in serializeType: Add namespace for Apache SOAP type');
5276                         $tt_prefix = 'ns' . rand(1000, 9999);
5277                         $this->namespaces[$tt_prefix] = 'http://xml.apache.org/xml-soap';
5278                         // force this to be added to usedNamespaces
5279                         $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
5280                     }
5281                     $contents = '';
5282                     foreach($value as $k => $v) {
5283                         $this->debug("serializing map element: key $k, value $v");
5284                         $contents .= '<item>';
5285                         $contents .= $this->serialize_val($k,'key',false,false,false,false,$use);
5286                         $contents .= $this->serialize_val($v,'value',false,false,false,false,$use);
5287                         $contents .= '</item>';
5288                     }
5289                     if ($use == 'literal') {
5290                         if ($forceType) {
5291                             $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\">$contents</$name>";
5292                         } else {
5293                             $xml = "<$name>$contents</$name>";
5294                         }
5295                     } else {
5296                         $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\"$encodingStyle>$contents</$name>";
5297                     }
5298                     $this->debug("in serializeType: returning: $xml");
5299                     return $xml;
5300                 }
5301                 $this->debug('in serializeType: Apache SOAP type, but only support Map');
5302             }
5303         } else {
5304             // TODO: should the type be compared to types in XSD, and the namespace
5305             // set to XSD if the type matches?
5306             $this->debug("in serializeType: No namespace for type $type");
5307             $ns = '';
5308             $uqType = $type;
5309         }
5310         if(!$typeDef = $this->getTypeDef($uqType, $ns)){
5311             $this->setError("$type ($uqType) is not a supported type.");
5312             $this->debug("in serializeType: $type ($uqType) is not a supported type.");
5313             return false;
5314         } else {
5315             $this->debug("in serializeType: found typeDef");
5316             $this->appendDebug('typeDef=' . $this->varDump($typeDef));
5317         }
5318         $phpType = $typeDef['phpType'];
5319         $this->debug("in serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') );
5320         // if php type == struct, map value to the <all> element names
5321         if ($phpType == 'struct') {
5322             if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') {
5323                 $elementName = $uqType;
5324                 if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
5325                     $elementNS = " xmlns=\"$ns\"";
5326                 } else {
5327                     $elementNS = " xmlns=\"\"";
5328                 }
5329             } else {
5330                 $elementName = $name;
5331                 if ($unqualified) {
5332                     $elementNS = " xmlns=\"\"";
5333                 } else {
5334                     $elementNS = '';
5335                 }
5336             }
5337             if (is_null($value)) {
5338                 if ($use == 'literal') {
5339                     // TODO: depends on minOccurs
5340                     $xml = "<$elementName$elementNS/>";
5341                 } else {
5342                     $xml = "<$elementName$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
5343                 }
5344                 $this->debug("in serializeType: returning: $xml");
5345                 return $xml;
5346             }
5347             if (is_object($value)) {
5348                 $value = get_object_vars($value);
5349             }
5350             if (is_array($value)) {
5351                 $elementAttrs = $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
5352                 if ($use == 'literal') {
5353                     if ($forceType) {
5354                         $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
5355                     } else {
5356                         $xml = "<$elementName$elementNS$elementAttrs>";
5357                     }
5358                 } else {
5359                     $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>";
5360                 }
5361     
5362                 $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
5363                 $xml .= "</$elementName>";
5364             } else {
5365                 $this->debug("in serializeType: phpType is struct, but value is not an array");
5366                 $this->setError("phpType is struct, but value is not an array: see debug output for details");
5367                 $xml = '';
5368             }
5369         } elseif ($phpType == 'array') {
5370             if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
5371                 $elementNS = " xmlns=\"$ns\"";
5372             } else {
5373                 if ($unqualified) {
5374                     $elementNS = " xmlns=\"\"";
5375                 } else {
5376                     $elementNS = '';
5377                 }
5378             }
5379             if (is_null($value)) {
5380                 if ($use == 'literal') {
5381                     // TODO: depends on minOccurs
5382                     $xml = "<$name$elementNS/>";
5383                 } else {
5384                     $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" .
5385                         $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
5386                         ":Array\" " .
5387                         $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
5388                         ':arrayType="' .
5389                         $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType'])) .
5390                         ':' .
5391                         $this->getLocalPart($typeDef['arrayType'])."[0]\"/>";
5392                 }
5393                 $this->debug("in serializeType: returning: $xml");
5394                 return $xml;
5395             }
5396             if (isset($typeDef['multidimensional'])) {
5397                 $nv = array();
5398                 foreach($value as $v) {
5399                     $cols = ',' . sizeof($v);
5400                     $nv = array_merge($nv, $v);
5401                 }
5402                 $value = $nv;
5403             } else {
5404                 $cols = '';
5405             }
5406             if (is_array($value) && sizeof($value) >= 1) {
5407                 $rows = sizeof($value);
5408                 $contents = '';
5409                 foreach($value as $k => $v) {
5410                     $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]");
5411                     //if (strpos($typeDef['arrayType'], ':') ) {
5412                     if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) {
5413                         $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
5414                     } else {
5415                         $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
5416                     }
5417                 }
5418             } else {
5419                 $rows = 0;
5420                 $contents = null;
5421             }
5422             // TODO: for now, an empty value will be serialized as a zero element
5423             // array.  Revisit this when coding the handling of null/nil values.
5424             if ($use == 'literal') {
5425                 $xml = "<$name$elementNS>"
5426                     .$contents
5427                     ."</$name>";
5428             } else {
5429                 $xml = "<$name$elementNS xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '.
5430                     $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
5431                     .':arrayType="'
5432                     .$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
5433                     .":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">"
5434                     .$contents
5435                     ."</$name>";
5436             }
5437         } elseif ($phpType == 'scalar') {
5438             if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
5439                 $elementNS = " xmlns=\"$ns\"";
5440             } else {
5441                 if ($unqualified) {
5442                     $elementNS = " xmlns=\"\"";
5443                 } else {
5444                     $elementNS = '';
5445                 }
5446             }
5447             if ($use == 'literal') {
5448                 if ($forceType) {
5449                     $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
5450                 } else {
5451                     $xml = "<$name$elementNS>$value</$name>";
5452                 }
5453             } else {
5454                 $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
5455             }
5456         }
5457         $this->debug("in serializeType: returning: $xml");
5458         return $xml;
5459     }
5460     
5461     /**
5462      * serializes the attributes for a complexType
5463      *
5464      * @param array $typeDef our internal representation of an XML schema type (or element)
5465      * @param mixed $value a native PHP value (parameter value)
5466      * @param string $ns the namespace of the type
5467      * @param string $uqType the local part of the type
5468      * @return string value serialized as an XML string
5469      * @access private
5470      */
5471     function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType) {
5472         $xml = '';
5473         if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) {
5474             $this->debug("serialize attributes for XML Schema type $ns:$uqType");
5475             if (is_array($value)) {
5476                 $xvalue = $value;
5477             } elseif (is_object($value)) {
5478                 $xvalue = get_object_vars($value);
5479             } else {
5480                 $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
5481                 $xvalue = array();
5482             }
5483             foreach ($typeDef['attrs'] as $aName => $attrs) {
5484                 if (isset($xvalue['!' . $aName])) {
5485                     $xname = '!' . $aName;
5486                     $this->debug("value provided for attribute $aName with key $xname");
5487                 } elseif (isset($xvalue[$aName])) {
5488                     $xname = $aName;
5489                     $this->debug("value provided for attribute $aName with key $xname");
5490                 } elseif (isset($attrs['default'])) {
5491                     $xname = '!' . $aName;
5492                     $xvalue[$xname] = $attrs['default'];
5493                     $this->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName);
5494                 } else {
5495                     $xname = '';
5496                     $this->debug("no value provided for attribute $aName");
5497                 }
5498                 if ($xname) {
5499                     $xml .=  " $aName=\"" . $this->expandEntities($xvalue[$xname]) . "\"";
5500                 }
5501             }
5502         } else {
5503             $this->debug("no attributes to serialize for XML Schema type $ns:$uqType");
5504         }
5505         if (isset($typeDef['extensionBase'])) {
5506             $ns = $this->getPrefix($typeDef['extensionBase']);
5507             $uqType = $this->getLocalPart($typeDef['extensionBase']);
5508             if ($this->getNamespaceFromPrefix($ns)) {
5509                 $ns = $this->getNamespaceFromPrefix($ns);
5510             }
5511             if ($typeDef = $this->getTypeDef($uqType, $ns)) {
5512                 $this->debug("serialize attributes for extension base $ns:$uqType");
5513                 $xml .= $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
5514             } else {
5515                 $this->debug("extension base $ns:$uqType is not a supported type");
5516             }
5517         }
5518         return $xml;
5519     }
5520
5521     /**
5522      * serializes the elements for a complexType
5523      *
5524      * @param array $typeDef our internal representation of an XML schema type (or element)
5525      * @param mixed $value a native PHP value (parameter value)
5526      * @param string $ns the namespace of the type
5527      * @param string $uqType the local part of the type
5528      * @param string $use use for part (encoded|literal)
5529      * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
5530      * @return string value serialized as an XML string
5531      * @access private
5532      */
5533     function serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use='encoded', $encodingStyle=false) {
5534         $xml = '';
5535         if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
5536             $this->debug("in serializeComplexTypeElements, serialize elements for XML Schema type $ns:$uqType");
5537             if (is_array($value)) {
5538                 $xvalue = $value;
5539             } elseif (is_object($value)) {
5540                 $xvalue = get_object_vars($value);
5541             } else {
5542                 $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
5543                 $xvalue = array();
5544             }
5545             // toggle whether all elements are present - ideally should validate against schema
5546             if (count($typeDef['elements']) != count($xvalue)){
5547                 $optionals = true;
5548             }
5549             foreach ($typeDef['elements'] as $eName => $attrs) {
5550                 if (!isset($xvalue[$eName])) {
5551                     if (isset($attrs['default'])) {
5552                         $xvalue[$eName] = $attrs['default'];
5553                         $this->debug('use default value of ' . $xvalue[$eName] . ' for element ' . $eName);
5554                     }
5555                 }
5556                 // if user took advantage of a minOccurs=0, then only serialize named parameters
5557                 if (isset($optionals)
5558                     && (!isset($xvalue[$eName]))
5559                     && ( (!isset($attrs['nillable'])) || $attrs['nillable'] != 'true')
5560                     ){
5561                     if (isset($attrs['minOccurs']) && $attrs['minOccurs'] <> '0') {
5562                         $this->debug("apparent error: no value provided for element $eName with minOccurs=" . $attrs['minOccurs']);
5563                     }
5564                     // do nothing
5565                     $this->debug("no value provided for complexType element $eName and element is not nillable, so serialize nothing");
5566                 } else {
5567                     // get value
5568                     if (isset($xvalue[$eName])) {
5569                         $v = $xvalue[$eName];
5570                     } else {
5571                         $v = null;
5572                     }
5573                     if (isset($attrs['form'])) {
5574                         $unqualified = ($attrs['form'] == 'unqualified');
5575                     } else {
5576                         $unqualified = false;
5577                     }
5578                     if (isset($attrs['maxOccurs']) && ($attrs['maxOccurs'] == 'unbounded' || $attrs['maxOccurs'] > 1) && isset($v) && is_array($v) && $this->isArraySimpleOrStruct($v) == 'arraySimple') {
5579                         $vv = $v;
5580                         foreach ($vv as $k => $v) {
5581                             if (isset($attrs['type']) || isset($attrs['ref'])) {
5582                                 // serialize schema-defined type
5583                                 $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
5584                             } else {
5585                                 // serialize generic type (can this ever really happen?)
5586                                 $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
5587                                 $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
5588                             }
5589                         }
5590                     } else {
5591                         if (isset($attrs['type']) || isset($attrs['ref'])) {
5592                             // serialize schema-defined type
5593                             $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
5594                         } else {
5595                             // serialize generic type (can this ever really happen?)
5596                             $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
5597                             $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
5598                         }
5599                     }
5600                 }
5601             }
5602         } else {
5603             $this->debug("no elements to serialize for XML Schema type $ns:$uqType");
5604         }
5605         if (isset($typeDef['extensionBase'])) {
5606             $ns = $this->getPrefix($typeDef['extensionBase']);
5607             $uqType = $this->getLocalPart($typeDef['extensionBase']);
5608             if ($this->getNamespaceFromPrefix($ns)) {
5609                 $ns = $this->getNamespaceFromPrefix($ns);
5610             }
5611             if ($typeDef = $this->getTypeDef($uqType, $ns)) {
5612                 $this->debug("serialize elements for extension base $ns:$uqType");
5613                 $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
5614             } else {
5615                 $this->debug("extension base $ns:$uqType is not a supported type");
5616             }
5617         }
5618         return $xml;
5619     }
5620
5621     /**
5622     * adds an XML Schema complex type to the WSDL types
5623     *
5624     * @param string    name
5625     * @param string typeClass (complexType|simpleType|attribute)
5626     * @param string phpType: currently supported are array and struct (php assoc array)
5627     * @param string compositor (all|sequence|choice)
5628     * @param string restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
5629     * @param array elements = array ( name => array(name=>'',type=>'') )
5630     * @param array attrs =     array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'xsd:string[]'))
5631     * @param string arrayType: namespace:name (xsd:string)
5632     * @see xmlschema
5633     * @access public
5634     */
5635     function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType='') {
5636         if (count($elements) > 0) {
5637             foreach($elements as $n => $e){
5638                 // expand each element
5639                 foreach ($e as $k => $v) {
5640                     $k = strpos($k,':') ? $this->expandQname($k) : $k;
5641                     $v = strpos($v,':') ? $this->expandQname($v) : $v;
5642                     $ee[$k] = $v;
5643                 }
5644                 $eElements[$n] = $ee;
5645             }
5646             $elements = $eElements;
5647         }
5648         
5649         if (count($attrs) > 0) {
5650             foreach($attrs as $n => $a){
5651                 // expand each attribute
5652                 foreach ($a as $k => $v) {
5653                     $k = strpos($k,':') ? $this->expandQname($k) : $k;
5654                     $v = strpos($v,':') ? $this->expandQname($v) : $v;
5655                     $aa[$k] = $v;
5656                 }
5657                 $eAttrs[$n] = $aa;
5658             }
5659             $attrs = $eAttrs;
5660         }
5661
5662         $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
5663         $arrayType = strpos($arrayType,':') ? $this->expandQname($arrayType) : $arrayType;
5664
5665         $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
5666         $this->schemas[$typens][0]->addComplexType($name,$typeClass,$phpType,$compositor,$restrictionBase,$elements,$attrs,$arrayType);
5667     }
5668
5669     /**
5670     * adds an XML Schema simple type to the WSDL types
5671     *
5672     * @param string $name
5673     * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
5674     * @param string $typeClass (should always be simpleType)
5675     * @param string $phpType (should always be scalar)
5676     * @param array $enumeration array of values
5677     * @see xmlschema
5678     * @access public
5679     */
5680     function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
5681         $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
5682
5683         $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
5684         $this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType, $enumeration);
5685     }
5686
5687     /**
5688     * adds an element to the WSDL types
5689     *
5690     * @param array $attrs attributes that must include name and type
5691     * @see xmlschema
5692     * @access public
5693     */
5694     function addElement($attrs) {
5695         $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
5696         $this->schemas[$typens][0]->addElement($attrs);
5697     }
5698
5699     /**
5700     * register an operation with the server
5701     *
5702     * @param string $name operation (method) name
5703     * @param array $in assoc array of input values: key = param name, value = param type
5704     * @param array $out assoc array of output values: key = param name, value = param type
5705     * @param string $namespace optional The namespace for the operation
5706     * @param string $soapaction optional The soapaction for the operation
5707     * @param string $style (rpc|document) optional The style for the operation Note: when 'document' is specified, parameter and return wrappers are created for you automatically
5708     * @param string $use (encoded|literal) optional The use for the parameters (cannot mix right now)
5709     * @param string $documentation optional The description to include in the WSDL
5710     * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
5711     * @access public
5712     */
5713     function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '', $encodingStyle = ''){
5714         if ($use == 'encoded' && $encodingStyle == '') {
5715             $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5716         }
5717
5718         if ($style == 'document') {
5719             $elements = array();
5720             foreach ($in as $n => $t) {
5721                 $elements[$n] = array('name' => $n, 'type' => $t);
5722             }
5723             $this->addComplexType($name . 'RequestType', 'complexType', 'struct', 'all', '', $elements);
5724             $this->addElement(array('name' => $name, 'type' => $name . 'RequestType'));
5725             $in = array('parameters' => 'tns:' . $name);
5726
5727             $elements = array();
5728             foreach ($out as $n => $t) {
5729                 $elements[$n] = array('name' => $n, 'type' => $t);
5730             }
5731             $this->addComplexType($name . 'ResponseType', 'complexType', 'struct', 'all', '', $elements);
5732             $this->addElement(array('name' => $name . 'Response', 'type' => $name . 'ResponseType'));
5733             $out = array('parameters' => 'tns:' . $name . 'Response');
5734         }
5735
5736         // get binding
5737         $this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] =
5738         array(
5739         'name' => $name,
5740         'binding' => $this->serviceName . 'Binding',
5741         'endpoint' => $this->endpoint,
5742         'soapAction' => $soapaction,
5743         'style' => $style,
5744         'input' => array(
5745             'use' => $use,
5746             'namespace' => $namespace,
5747             'encodingStyle' => $encodingStyle,
5748             'message' => $name . 'Request',
5749             'parts' => $in),
5750         'output' => array(
5751             'use' => $use,
5752             'namespace' => $namespace,
5753             'encodingStyle' => $encodingStyle,
5754             'message' => $name . 'Response',
5755             'parts' => $out),
5756         'namespace' => $namespace,
5757         'transport' => 'http://schemas.xmlsoap.org/soap/http',
5758         'documentation' => $documentation);
5759         // add portTypes
5760         // add messages
5761         if($in)
5762         {
5763             foreach($in as $pName => $pType)
5764             {
5765                 if(strpos($pType,':')) {
5766                     $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
5767                 }
5768                 $this->messages[$name.'Request'][$pName] = $pType;
5769             }
5770         } else {
5771             $this->messages[$name.'Request']= '0';
5772         }
5773         if($out)
5774         {
5775             foreach($out as $pName => $pType)
5776             {
5777                 if(strpos($pType,':')) {
5778                     $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
5779                 }
5780                 $this->messages[$name.'Response'][$pName] = $pType;
5781             }
5782         } else {
5783             $this->messages[$name.'Response']= '0';
5784         }
5785         return true;
5786     }
5787 }
5788 ?><?php
5789
5790
5791
5792 /**
5793 *
5794 * soap_parser class parses SOAP XML messages into native PHP values
5795 *
5796 * @author   Dietrich Ayala <dietrich@ganx4.com>
5797 * @version  $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
5798 * @access   public
5799 */
5800 class soap_parser extends nusoap_base {
5801
5802     var $xml = '';
5803     var $xml_encoding = '';
5804     var $method = '';
5805     var $root_struct = '';
5806     var $root_struct_name = '';
5807     var $root_struct_namespace = '';
5808     var $root_header = '';
5809     var $document = '';            // incoming SOAP body (text)
5810     // determines where in the message we are (envelope,header,body,method)
5811     var $status = '';
5812     var $position = 0;
5813     var $depth = 0;
5814     var $default_namespace = '';
5815     var $namespaces = array();
5816     var $message = array();
5817     var $parent = '';
5818     var $fault = false;
5819     var $fault_code = '';
5820     var $fault_str = '';
5821     var $fault_detail = '';
5822     var $depth_array = array();
5823     var $debug_flag = true;
5824     var $soapresponse = NULL;
5825     var $responseHeaders = '';    // incoming SOAP headers (text)
5826     var $body_position = 0;
5827     // for multiref parsing:
5828     // array of id => pos
5829     var $ids = array();
5830     // array of id => hrefs => pos
5831     var $multirefs = array();
5832     // toggle for auto-decoding element content
5833     var $decode_utf8 = true;
5834
5835     /**
5836     * constructor that actually does the parsing
5837     *
5838     * @param    string $xml SOAP message
5839     * @param    string $encoding character encoding scheme of message
5840     * @param    string $method method for which XML is parsed (unused?)
5841     * @param    string $decode_utf8 whether to decode UTF-8 to ISO-8859-1
5842     * @access   public
5843     */
5844     function soap_parser($xml,$encoding='UTF-8',$method='',$decode_utf8=true){
5845         parent::nusoap_base();
5846         $this->xml = $xml;
5847         $this->xml_encoding = $encoding;
5848         $this->method = $method;
5849         $this->decode_utf8 = $decode_utf8;
5850
5851         // Check whether content has been read.
5852         if(!empty($xml)){
5853             // Check XML encoding
5854             $pos_xml = strpos($xml, '<?xml');
5855             if ($pos_xml !== FALSE) {
5856                 $xml_decl = substr($xml, $pos_xml, strpos($xml, '?>', $pos_xml + 2) - $pos_xml + 1);
5857                 if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) {
5858                     $xml_encoding = $res[1];
5859                     if (strtoupper($xml_encoding) != $encoding) {
5860                         $err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'";
5861                         $this->debug($err);
5862                         if ($encoding != 'ISO-8859-1' || strtoupper($xml_encoding) != 'UTF-8') {
5863                             $this->setError($err);
5864                             return;
5865                         }
5866                         // when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed
5867                     } else {
5868                         $this->debug('Charset from HTTP Content-Type matches encoding from XML declaration');
5869                     }
5870                 } else {
5871                     $this->debug('No encoding specified in XML declaration');
5872                 }
5873             } else {
5874                 $this->debug('No XML declaration');
5875             }
5876             $this->debug('Entering soap_parser(), length='.strlen($xml).', encoding='.$encoding);
5877             // Create an XML parser - why not xml_parser_create_ns?
5878             $this->parser = xml_parser_create($this->xml_encoding);
5879             // Set the options for parsing the XML data.
5880             //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
5881             xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
5882             xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding);
5883             // Set the object for the parser.
5884             xml_set_object($this->parser, $this);
5885             // Set the element handlers for the parser.
5886             xml_set_element_handler($this->parser, 'start_element','end_element');
5887             xml_set_character_data_handler($this->parser,'character_data');
5888
5889             // Parse the XML file.
5890             if(!xml_parse($this->parser,$xml,true)){
5891                 // Display an error message.
5892                 $err = sprintf('XML error parsing SOAP payload on line %d: %s',
5893                 xml_get_current_line_number($this->parser),
5894                 xml_error_string(xml_get_error_code($this->parser)));
5895                 $this->debug($err);
5896                 $this->debug("XML payload:\n" . $xml);
5897                 $this->setError($err);
5898             } else {
5899                 $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name);
5900                 // get final value
5901                 $this->soapresponse = $this->message[$this->root_struct]['result'];
5902                 // get header value: no, because this is documented as XML string
5903 //                if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){
5904 //                    $this->responseHeaders = $this->message[$this->root_header]['result'];
5905 //                }
5906                 // resolve hrefs/ids
5907                 if(sizeof($this->multirefs) > 0){
5908                     foreach($this->multirefs as $id => $hrefs){
5909                         $this->debug('resolving multirefs for id: '.$id);
5910                         $idVal = $this->buildVal($this->ids[$id]);
5911                         if (is_array($idVal) && isset($idVal['!id'])) {
5912                             unset($idVal['!id']);
5913                         }
5914                         foreach($hrefs as $refPos => $ref){
5915                             $this->debug('resolving href at pos '.$refPos);
5916                             $this->multirefs[$id][$refPos] = $idVal;
5917                         }
5918                     }
5919                 }
5920             }
5921             xml_parser_free($this->parser);
5922         } else {
5923             $this->debug('xml was empty, didn\'t parse!');
5924             $this->setError('xml was empty, didn\'t parse!');
5925         }
5926     }
5927
5928     /**
5929     * start-element handler
5930     *
5931     * @param    resource $parser XML parser object
5932     * @param    string $name element name
5933     * @param    array $attrs associative array of attributes
5934     * @access   private
5935     */
5936     function start_element($parser, $name, $attrs) {
5937         // position in a total number of elements, starting from 0
5938         // update class level pos
5939         $pos = $this->position++;
5940         // and set mine
5941         $this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>'');
5942         // depth = how many levels removed from root?
5943         // set mine as current global depth and increment global depth value
5944         $this->message[$pos]['depth'] = $this->depth++;
5945
5946         // else add self as child to whoever the current parent is
5947         if($pos != 0){
5948             $this->message[$this->parent]['children'] .= '|'.$pos;
5949         }
5950         // set my parent
5951         $this->message[$pos]['parent'] = $this->parent;
5952         // set self as current parent
5953         $this->parent = $pos;
5954         // set self as current value for this depth
5955         $this->depth_array[$this->depth] = $pos;
5956         // get element prefix
5957         if(strpos($name,':')){
5958             // get ns prefix
5959             $prefix = substr($name,0,strpos($name,':'));
5960             // get unqualified name
5961             $name = substr(strstr($name,':'),1);
5962         }
5963         // set status
5964         if($name == 'Envelope'){
5965             $this->status = 'envelope';
5966         } elseif($name == 'Header'){
5967             $this->root_header = $pos;
5968             $this->status = 'header';
5969         } elseif($name == 'Body'){
5970             $this->status = 'body';
5971             $this->body_position = $pos;
5972         // set method
5973         } elseif($this->status == 'body' && $pos == ($this->body_position+1)){
5974             $this->status = 'method';
5975             $this->root_struct_name = $name;
5976             $this->root_struct = $pos;
5977             $this->message[$pos]['type'] = 'struct';
5978             $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
5979         }
5980         // set my status
5981         $this->message[$pos]['status'] = $this->status;
5982         // set name
5983         $this->message[$pos]['name'] = htmlspecialchars($name);
5984         // set attrs
5985         $this->message[$pos]['attrs'] = $attrs;
5986
5987         // loop through atts, logging ns and type declarations
5988         $attstr = '';
5989         foreach($attrs as $key => $value){
5990             $key_prefix = $this->getPrefix($key);
5991             $key_localpart = $this->getLocalPart($key);
5992             // if ns declarations, add to class level array of valid namespaces
5993             if($key_prefix == 'xmlns'){
5994                 if(ereg('^http://www.w3.org/[0-9]{4}/XMLSchema$',$value)){
5995                     $this->XMLSchemaVersion = $value;
5996                     $this->namespaces['xsd'] = $this->XMLSchemaVersion;
5997                     $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance';
5998                 }
5999                 $this->namespaces[$key_localpart] = $value;
6000                 // set method namespace
6001                 if($name == $this->root_struct_name){
6002                     $this->methodNamespace = $value;
6003                 }
6004             // if it's a type declaration, set type
6005             } elseif($key_localpart == 'type'){
6006                 $value_prefix = $this->getPrefix($value);
6007                 $value_localpart = $this->getLocalPart($value);
6008                 $this->message[$pos]['type'] = $value_localpart;
6009                 $this->message[$pos]['typePrefix'] = $value_prefix;
6010                 if(isset($this->namespaces[$value_prefix])){
6011                     $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
6012                 } else if(isset($attrs['xmlns:'.$value_prefix])) {
6013                     $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix];
6014                 }
6015                 // should do something here with the namespace of specified type?
6016             } elseif($key_localpart == 'arrayType'){
6017                 $this->message[$pos]['type'] = 'array';
6018                 /* do arrayType ereg here
6019                 [1]    arrayTypeValue    ::=    atype asize
6020                 [2]    atype    ::=    QName rank*
6021                 [3]    rank    ::=    '[' (',')* ']'
6022                 [4]    asize    ::=    '[' length~ ']'
6023                 [5]    length    ::=    nextDimension* Digit+
6024                 [6]    nextDimension    ::=    Digit+ ','
6025                 */
6026                 $expr = '([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]';
6027                 if(ereg($expr,$value,$regs)){
6028                     $this->message[$pos]['typePrefix'] = $regs[1];
6029                     $this->message[$pos]['arrayTypePrefix'] = $regs[1];
6030                     if (isset($this->namespaces[$regs[1]])) {
6031                         $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]];
6032                     } else if (isset($attrs['xmlns:'.$regs[1]])) {
6033                         $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:'.$regs[1]];
6034                     }
6035                     $this->message[$pos]['arrayType'] = $regs[2];
6036                     $this->message[$pos]['arraySize'] = $regs[3];
6037                     $this->message[$pos]['arrayCols'] = $regs[4];
6038                 }
6039             // specifies nil value (or not)
6040             } elseif ($key_localpart == 'nil'){
6041                 $this->message[$pos]['nil'] = ($value == 'true' || $value == '1');
6042             // some other attribute
6043             } elseif ($key != 'href' && $key != 'xmlns' && $key_localpart != 'encodingStyle' && $key_localpart != 'root') {
6044                 $this->message[$pos]['xattrs']['!' . $key] = $value;
6045             }
6046
6047             if ($key == 'xmlns') {
6048                 $this->default_namespace = $value;
6049             }
6050             // log id
6051             if($key == 'id'){
6052                 $this->ids[$value] = $pos;
6053             }
6054             // root
6055             if($key_localpart == 'root' && $value == 1){
6056                 $this->status = 'method';
6057                 $this->root_struct_name = $name;
6058                 $this->root_struct = $pos;
6059                 $this->debug("found root struct $this->root_struct_name, pos $pos");
6060             }
6061             // for doclit
6062             $attstr .= " $key=\"$value\"";
6063         }
6064         // get namespace - must be done after namespace atts are processed
6065         if(isset($prefix)){
6066             $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
6067             $this->default_namespace = $this->namespaces[$prefix];
6068         } else {
6069             $this->message[$pos]['namespace'] = $this->default_namespace;
6070         }
6071         if($this->status == 'header'){
6072             if ($this->root_header != $pos) {
6073                 $this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
6074             }
6075         } elseif($this->root_struct_name != ''){
6076             $this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
6077         }
6078     }
6079
6080     /**
6081     * end-element handler
6082     *
6083     * @param    resource $parser XML parser object
6084     * @param    string $name element name
6085     * @access   private
6086     */
6087     function end_element($parser, $name) {
6088         // position of current element is equal to the last value left in depth_array for my depth
6089         $pos = $this->depth_array[$this->depth--];
6090
6091         // get element prefix
6092         if(strpos($name,':')){
6093             // get ns prefix
6094             $prefix = substr($name,0,strpos($name,':'));
6095             // get unqualified name
6096             $name = substr(strstr($name,':'),1);
6097         }
6098         
6099         // build to native type
6100         if(isset($this->body_position) && $pos > $this->body_position){
6101             // deal w/ multirefs
6102             if(isset($this->message[$pos]['attrs']['href'])){
6103                 // get id
6104                 $id = substr($this->message[$pos]['attrs']['href'],1);
6105                 // add placeholder to href array
6106                 $this->multirefs[$id][$pos] = 'placeholder';
6107                 // add set a reference to it as the result value
6108                 $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
6109             // build complexType values
6110             } elseif($this->message[$pos]['children'] != ''){
6111                 // if result has already been generated (struct/array)
6112                 if(!isset($this->message[$pos]['result'])){
6113                     $this->message[$pos]['result'] = $this->buildVal($pos);
6114                 }
6115             // build complexType values of attributes and possibly simpleContent
6116             } elseif (isset($this->message[$pos]['xattrs'])) {
6117                 if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
6118                     $this->message[$pos]['xattrs']['!'] = null;
6119                 } elseif (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
6120                     if (isset($this->message[$pos]['type'])) {
6121                         $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
6122                     } else {
6123                         $parent = $this->message[$pos]['parent'];
6124                         if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
6125                             $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6126                         } else {
6127                             $this->message[$pos]['xattrs']['!'] = $this->message[$pos]['cdata'];
6128                         }
6129                     }
6130                 }
6131                 $this->message[$pos]['result'] = $this->message[$pos]['xattrs'];
6132             // set value of simpleType (or nil complexType)
6133             } else {
6134                 //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
6135                 if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
6136                     $this->message[$pos]['xattrs']['!'] = null;
6137                 } elseif (isset($this->message[$pos]['type'])) {
6138                     $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
6139                 } else {
6140                     $parent = $this->message[$pos]['parent'];
6141                     if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
6142                         $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6143                     } else {
6144                         $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
6145                     }
6146                 }
6147
6148                 /* add value to parent's result, if parent is struct/array
6149                 $parent = $this->message[$pos]['parent'];
6150                 if($this->message[$parent]['type'] != 'map'){
6151                     if(strtolower($this->message[$parent]['type']) == 'array'){
6152                         $this->message[$parent]['result'][] = $this->message[$pos]['result'];
6153                     } else {
6154                         $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result'];
6155                     }
6156                 }
6157                 */
6158             }
6159         }
6160         
6161         // for doclit
6162         if($this->status == 'header'){
6163             if ($this->root_header != $pos) {
6164                 $this->responseHeaders .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
6165             }
6166         } elseif($pos >= $this->root_struct){
6167             $this->document .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
6168         }
6169         // switch status
6170         if($pos == $this->root_struct){
6171             $this->status = 'body';
6172             $this->root_struct_namespace = $this->message[$pos]['namespace'];
6173         } elseif($name == 'Body'){
6174             $this->status = 'envelope';
6175          } elseif($name == 'Header'){
6176             $this->status = 'envelope';
6177         } elseif($name == 'Envelope'){
6178             //
6179         }
6180         // set parent back to my parent
6181         $this->parent = $this->message[$pos]['parent'];
6182     }
6183
6184     /**
6185     * element content handler
6186     *
6187     * @param    resource $parser XML parser object
6188     * @param    string $data element content
6189     * @access   private
6190     */
6191     function character_data($parser, $data){
6192         $pos = $this->depth_array[$this->depth];
6193         if ($this->xml_encoding=='UTF-8'){
6194             // TODO: add an option to disable this for folks who want
6195             // raw UTF-8 that, e.g., might not map to iso-8859-1
6196             // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
6197             if($this->decode_utf8){
6198                 $data = utf8_decode($data);
6199             }
6200         }
6201         $this->message[$pos]['cdata'] .= $data;
6202         // for doclit
6203         if($this->status == 'header'){
6204             $this->responseHeaders .= $data;
6205         } else {
6206             $this->document .= $data;
6207         }
6208     }
6209
6210     /**
6211     * get the parsed message
6212     *
6213     * @return    mixed
6214     * @access   public
6215     */
6216     function get_response(){
6217         return $this->soapresponse;
6218     }
6219
6220     /**
6221     * get the parsed headers
6222     *
6223     * @return    string XML or empty if no headers
6224     * @access   public
6225     */
6226     function getHeaders(){
6227         return $this->responseHeaders;
6228     }
6229
6230     /**
6231     * decodes simple types into PHP variables
6232     *
6233     * @param    string $value value to decode
6234     * @param    string $type XML type to decode
6235     * @param    string $typens XML type namespace to decode
6236     * @return    mixed PHP value
6237     * @access   private
6238     */
6239     function decodeSimple($value, $type, $typens) {
6240         // TODO: use the namespace!
6241         if ((!isset($type)) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') {
6242             return (string) $value;
6243         }
6244         if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') {
6245             return (int) $value;
6246         }
6247         if ($type == 'float' || $type == 'double' || $type == 'decimal') {
6248             return (double) $value;
6249         }
6250         if ($type == 'boolean') {
6251             if (strtolower($value) == 'false' || strtolower($value) == 'f') {
6252                 return false;
6253             }
6254             return (boolean) $value;
6255         }
6256         if ($type == 'base64' || $type == 'base64Binary') {
6257             $this->debug('Decode base64 value');
6258             return base64_decode($value);
6259         }
6260         // obscure numeric types
6261         if ($type == 'nonPositiveInteger' || $type == 'negativeInteger'
6262             || $type == 'nonNegativeInteger' || $type == 'positiveInteger'
6263             || $type == 'unsignedInt'
6264             || $type == 'unsignedShort' || $type == 'unsignedByte') {
6265             return (int) $value;
6266         }
6267         // bogus: parser treats array with no elements as a simple type
6268         if ($type == 'array') {
6269             return array();
6270         }
6271         // everything else
6272         return (string) $value;
6273     }
6274
6275     /**
6276     * builds response structures for compound values (arrays/structs)
6277     * and scalars
6278     *
6279     * @param    integer $pos position in node tree
6280     * @return    mixed    PHP value
6281     * @access   private
6282     */
6283     function buildVal($pos){
6284         if(!isset($this->message[$pos]['type'])){
6285             $this->message[$pos]['type'] = '';
6286         }
6287         $this->debug('in buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']);
6288         // if there are children...
6289         if($this->message[$pos]['children'] != ''){
6290             $this->debug('in buildVal, there are children');
6291             $children = explode('|',$this->message[$pos]['children']);
6292             array_shift($children); // knock off empty
6293             // md array
6294             if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != ''){
6295                 $r=0; // rowcount
6296                 $c=0; // colcount
6297                 foreach($children as $child_pos){
6298                     $this->debug("in buildVal, got an MD array element: $r, $c");
6299                     $params[$r][] = $this->message[$child_pos]['result'];
6300                     $c++;
6301                     if($c == $this->message[$pos]['arrayCols']){
6302                         $c = 0;
6303                         $r++;
6304                     }
6305                 }
6306             // array
6307             } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array'){
6308                 $this->debug('in buildVal, adding array '.$this->message[$pos]['name']);
6309                 foreach($children as $child_pos){
6310                     $params[] = &$this->message[$child_pos]['result'];
6311                 }
6312             // apache Map type: java hashtable
6313             } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){
6314                 $this->debug('in buildVal, Java Map '.$this->message[$pos]['name']);
6315                 foreach($children as $child_pos){
6316                     $kv = explode("|",$this->message[$child_pos]['children']);
6317                        $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
6318                 }
6319             // generic compound type
6320             //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
6321             } else {
6322                 // Apache Vector type: treat as an array
6323                 $this->debug('in buildVal, adding Java Vector '.$this->message[$pos]['name']);
6324                 if ($this->message[$pos]['type'] == 'Vector' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
6325                     $notstruct = 1;
6326                 } else {
6327                     $notstruct = 0;
6328                 }
6329                 //
6330                 foreach($children as $child_pos){
6331                     if($notstruct){
6332                         $params[] = &$this->message[$child_pos]['result'];
6333                     } else {
6334                         if (isset($params[$this->message[$child_pos]['name']])) {
6335                             // de-serialize repeated element name into an array
6336                             if ((!is_array($params[$this->message[$child_pos]['name']])) || (!isset($params[$this->message[$child_pos]['name']][0]))) {
6337                                 $params[$this->message[$child_pos]['name']] = array($params[$this->message[$child_pos]['name']]);
6338                             }
6339                             $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result'];
6340                         } else {
6341                             $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
6342                         }
6343                     }
6344                 }
6345             }
6346             if (isset($this->message[$pos]['xattrs'])) {
6347                 $this->debug('in buildVal, handling attributes');
6348                 foreach ($this->message[$pos]['xattrs'] as $n => $v) {
6349                     $params[$n] = $v;
6350                 }
6351             }
6352             // handle simpleContent
6353             if (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
6354                 $this->debug('in buildVal, handling simpleContent');
6355                 if (isset($this->message[$pos]['type'])) {
6356                     $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
6357                 } else {
6358                     $parent = $this->message[$pos]['parent'];
6359                     if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
6360                         $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6361                     } else {
6362                         $params['!'] = $this->message[$pos]['cdata'];
6363                     }
6364                 }
6365             }
6366             return is_array($params) ? $params : array();
6367         } else {
6368             $this->debug('in buildVal, no children, building scalar');
6369             $cdata = isset($this->message[$pos]['cdata']) ? $this->message[$pos]['cdata'] : '';
6370             if (isset($this->message[$pos]['type'])) {
6371                 return $this->decodeSimple($cdata, $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
6372             }
6373             $parent = $this->message[$pos]['parent'];
6374             if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
6375                 return $this->decodeSimple($cdata, $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6376             }
6377                return $this->message[$pos]['cdata'];
6378         }
6379     }
6380 }
6381
6382
6383
6384 ?><?php
6385
6386
6387
6388 /**
6389 *
6390 * soapclient higher level class for easy usage.
6391 *
6392 * usage:
6393 *
6394 * // instantiate client with server info
6395 * $soapclient = new soapclient( string path [ ,boolean wsdl] );
6396 *
6397 * // call method, get results
6398 * echo $soapclient->call( string methodname [ ,array parameters] );
6399 *
6400 * // bye bye client
6401 * unset($soapclient);
6402 *
6403 * @author   Dietrich Ayala <dietrich@ganx4.com>
6404 * @version  $Id: nusoap.php,v 1.94 2005/08/04 01:27:42 snichol Exp $
6405 * @access   public
6406 */
6407 class soapclient extends nusoap_base  {
6408
6409     var $username = '';
6410     var $password = '';
6411     var $authtype = '';
6412     var $certRequest = array();
6413     var $requestHeaders = false;    // SOAP headers in request (text)
6414     var $responseHeaders = '';        // SOAP headers from response (incomplete namespace resolution) (text)
6415     var $document = '';                // SOAP body response portion (incomplete namespace resolution) (text)
6416     var $endpoint;
6417     var $forceEndpoint = '';        // overrides WSDL endpoint
6418     var $proxyhost = '';
6419     var $proxyport = '';
6420     var $proxyusername = '';
6421     var $proxypassword = '';
6422     var $xml_encoding = '';            // character set encoding of incoming (response) messages
6423     var $http_encoding = false;
6424     var $timeout = 0;                // HTTP connection timeout
6425     var $response_timeout = 30;        // HTTP response timeout
6426     var $endpointType = '';            // soap|wsdl, empty for WSDL initialization error
6427     var $persistentConnection = false;
6428     var $defaultRpcParams = false;    // This is no longer used
6429     var $request = '';                // HTTP request
6430     var $response = '';                // HTTP response
6431     var $responseData = '';            // SOAP payload of response
6432     var $cookies = array();            // Cookies from response or for request
6433     var $decode_utf8 = true;        // toggles whether the parser decodes element content w/ utf8_decode()
6434     var $operations = array();        // WSDL operations, empty for WSDL initialization error
6435     
6436     /*
6437      * fault related variables
6438      */
6439     /**
6440      * @var      fault
6441      * @access   public
6442      */
6443     var $fault;
6444     /**
6445      * @var      faultcode
6446      * @access   public
6447      */
6448     var $faultcode;
6449     /**
6450      * @var      faultstring
6451      * @access   public
6452      */
6453     var $faultstring;
6454     /**
6455      * @var      faultdetail
6456      * @access   public
6457      */
6458     var $faultdetail;
6459
6460     /**
6461     * constructor
6462     *
6463     * @param    mixed $endpoint SOAP server or WSDL URL (string), or wsdl instance (object)
6464     * @param    bool $wsdl optional, set to true if using WSDL
6465     * @param    int $portName optional portName in WSDL document
6466     * @param    string $proxyhost
6467     * @param    string $proxyport
6468     * @param    string $proxyusername
6469     * @param    string $proxypassword
6470     * @param    integer $timeout set the connection timeout
6471     * @param    integer $response_timeout set the response timeout
6472     * @access   public
6473     */
6474     function soapclient($endpoint,$wsdl = false,$proxyhost = false,$proxyport = false,$proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30){
6475         parent::nusoap_base();
6476         $this->endpoint = $endpoint;
6477         $this->proxyhost = $proxyhost;
6478         $this->proxyport = $proxyport;
6479         $this->proxyusername = $proxyusername;
6480         $this->proxypassword = $proxypassword;
6481         $this->timeout = $timeout;
6482         $this->response_timeout = $response_timeout;
6483
6484         // make values
6485         if($wsdl){
6486             if (is_object($endpoint) && (get_class($endpoint) == 'wsdl')) {
6487                 $this->wsdl = $endpoint;
6488                 $this->endpoint = $this->wsdl->wsdl;
6489                 $this->wsdlFile = $this->endpoint;
6490                 $this->debug('existing wsdl instance created from ' . $this->endpoint);
6491             } else {
6492                 $this->wsdlFile = $this->endpoint;
6493                 
6494                 // instantiate wsdl object and parse wsdl file
6495                 $this->debug('instantiating wsdl class with doc: '.$endpoint);
6496                 $this->wsdl =& new wsdl($this->wsdlFile,$this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword,$this->timeout,$this->response_timeout);
6497             }
6498             $this->appendDebug($this->wsdl->getDebug());
6499             $this->wsdl->clearDebug();
6500             // catch errors
6501             if($errstr = $this->wsdl->getError()){
6502                 $this->debug('got wsdl error: '.$errstr);
6503                 $this->setError('wsdl error: '.$errstr);
6504             } elseif($this->operations = $this->wsdl->getOperations()){
6505                 $this->debug( 'got '.count($this->operations).' operations from wsdl '.$this->wsdlFile);
6506                 $this->endpointType = 'wsdl';
6507             } else {
6508                 $this->debug( 'getOperations returned false');
6509                 $this->setError('no operations defined in the WSDL document!');
6510             }
6511         } else {
6512             $this->debug("instantiate SOAP with endpoint at $endpoint");
6513             $this->endpointType = 'soap';
6514         }
6515     }
6516
6517     /**
6518     * calls method, returns PHP native type
6519     *
6520     * @param    string $method SOAP server URL or path
6521     * @param    mixed $params An array, associative or simple, of the parameters
6522     *                          for the method call, or a string that is the XML
6523     *                          for the call.  For rpc style, this call will
6524     *                          wrap the XML in a tag named after the method, as
6525     *                          well as the SOAP Envelope and Body.  For document
6526     *                          style, this will only wrap with the Envelope and Body.
6527     *                          IMPORTANT: when using an array with document style,
6528     *                          in which case there
6529     *                         is really one parameter, the root of the fragment
6530     *                         used in the call, which encloses what programmers
6531     *                         normally think of parameters.  A parameter array
6532     *                         *must* include the wrapper.
6533     * @param    string $namespace optional method namespace (WSDL can override)
6534     * @param    string $soapAction optional SOAPAction value (WSDL can override)
6535     * @param    mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers
6536     * @param    boolean $rpcParams optional (no longer used)
6537     * @param    string    $style optional (rpc|document) the style to use when serializing parameters (WSDL can override)
6538     * @param    string    $use optional (encoded|literal) the use when serializing parameters (WSDL can override)
6539     * @return    mixed    response from SOAP call
6540     * @access   public
6541     */
6542     function call($operation,$params=array(),$namespace='http://tempuri.org',$soapAction='',$headers=false,$rpcParams=null,$style='rpc',$use='encoded'){
6543         $this->operation = $operation;
6544         $this->fault = false;
6545         $this->setError('');
6546         $this->request = '';
6547         $this->response = '';
6548         $this->responseData = '';
6549         $this->faultstring = '';
6550         $this->faultcode = '';
6551         $this->opData = array();
6552         
6553         $this->debug("call: operation=$operation, namespace=$namespace, soapAction=$soapAction, rpcParams=$rpcParams, style=$style, use=$use, endpointType=$this->endpointType");
6554         $this->appendDebug('params=' . $this->varDump($params));
6555         $this->appendDebug('headers=' . $this->varDump($headers));
6556         if ($headers) {
6557             $this->requestHeaders = $headers;
6558         }
6559         // serialize parameters
6560         if($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)){
6561             // use WSDL for operation
6562             $this->opData = $opData;
6563             $this->debug("found operation");
6564             $this->appendDebug('opData=' . $this->varDump($opData));
6565             if (isset($opData['soapAction'])) {
6566                 $soapAction = $opData['soapAction'];
6567             }
6568             if (! $this->forceEndpoint) {
6569                 $this->endpoint = $opData['endpoint'];
6570             } else {
6571                 $this->endpoint = $this->forceEndpoint;
6572             }
6573             $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] :    $namespace;
6574             $style = $opData['style'];
6575             $use = $opData['input']['use'];
6576             // add ns to ns array
6577             if($namespace != '' && !isset($this->wsdl->namespaces[$namespace])){
6578                 $nsPrefix = 'ns' . rand(1000, 9999);
6579                 $this->wsdl->namespaces[$nsPrefix] = $namespace;
6580             }
6581             $nsPrefix = $this->wsdl->getPrefixFromNamespace($namespace);
6582             // serialize payload
6583             if (is_string($params)) {
6584                 $this->debug("serializing param string for WSDL operation $operation");
6585                 $payload = $params;
6586             } elseif (is_array($params)) {
6587                 $this->debug("serializing param array for WSDL operation $operation");
6588                 $payload = $this->wsdl->serializeRPCParameters($operation,'input',$params);
6589             } else {
6590                 $this->debug('params must be array or string');
6591                 $this->setError('params must be array or string');
6592                 return false;
6593             }
6594             $usedNamespaces = $this->wsdl->usedNamespaces;
6595             if (isset($opData['input']['encodingStyle'])) {
6596                 $encodingStyle = $opData['input']['encodingStyle'];
6597             } else {
6598                 $encodingStyle = '';
6599             }
6600             $this->appendDebug($this->wsdl->getDebug());
6601             $this->wsdl->clearDebug();
6602             if ($errstr = $this->wsdl->getError()) {
6603                 $this->debug('got wsdl error: '.$errstr);
6604                 $this->setError('wsdl error: '.$errstr);
6605                 return false;
6606             }
6607         } elseif($this->endpointType == 'wsdl') {
6608             // operation not in WSDL
6609             $this->appendDebug($this->wsdl->getDebug());
6610             $this->wsdl->clearDebug();
6611             $this->setError( 'operation '.$operation.' not present.');
6612             $this->debug("operation '$operation' not present.");
6613             return false;
6614         } else {
6615             // no WSDL
6616             //$this->namespaces['ns1'] = $namespace;
6617             $nsPrefix = 'ns' . rand(1000, 9999);
6618             // serialize
6619             $payload = '';
6620             if (is_string($params)) {
6621                 $this->debug("serializing param string for operation $operation");
6622                 $payload = $params;
6623             } elseif (is_array($params)) {
6624                 $this->debug("serializing param array for operation $operation");
6625                 foreach($params as $k => $v){
6626                     $payload .= $this->serialize_val($v,$k,false,false,false,false,$use);
6627                 }
6628             } else {
6629                 $this->debug('params must be array or string');
6630                 $this->setError('params must be array or string');
6631                 return false;
6632             }
6633             $usedNamespaces = array();
6634             if ($use == 'encoded') {
6635                 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
6636             } else {
6637                 $encodingStyle = '';
6638             }
6639         }
6640         // wrap RPC calls with method element
6641         if ($style == 'rpc') {
6642             if ($use == 'literal') {
6643                 $this->debug("wrapping RPC request with literal method element");
6644                 if ($namespace) {
6645                     $payload = "<$operation xmlns=\"$namespace\">" . $payload . "</$operation>";
6646                 } else {
6647                     $payload = "<$operation>" . $payload . "</$operation>";
6648                 }
6649             } else {
6650                 $this->debug("wrapping RPC request with encoded method element");
6651                 if ($namespace) {
6652                     $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
6653                                 $payload .
6654                                 "</$nsPrefix:$operation>";
6655                 } else {
6656                     $payload = "<$operation>" .
6657                                 $payload .
6658                                 "</$operation>";
6659                 }
6660             }
6661         }
6662         // serialize envelope
6663         $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,$usedNamespaces,$style,$use,$encodingStyle);
6664         $this->debug("endpoint=$this->endpoint, soapAction=$soapAction, namespace=$namespace, style=$style, use=$use, encodingStyle=$encodingStyle");
6665         $this->debug('SOAP message length=' . strlen($soapmsg) . ' contents (max 1000 bytes)=' . substr($soapmsg, 0, 1000));
6666         // send
6667         $return = $this->send($this->getHTTPBody($soapmsg),$soapAction,$this->timeout,$this->response_timeout);
6668         if($errstr = $this->getError()){
6669             $this->debug('Error: '.$errstr);
6670             return false;
6671         } else {
6672             $this->return = $return;
6673             $this->debug('sent message successfully and got a(n) '.gettype($return));
6674                $this->appendDebug('return=' . $this->varDump($return));
6675             
6676             // fault?
6677             if(is_array($return) && isset($return['faultcode'])){
6678                 $this->debug('got fault');
6679                 $this->setError($return['faultcode'].': '.$return['faultstring']);
6680                 $this->fault = true;
6681                 foreach($return as $k => $v){
6682                     $this->$k = $v;
6683                     $this->debug("$k = $v<br>");
6684                 }
6685                 return $return;
6686             } elseif ($style == 'document') {
6687                 // NOTE: if the response is defined to have multiple parts (i.e. unwrapped),
6688                 // we are only going to return the first part here...sorry about that
6689                 return $return;
6690             } else {
6691                 // array of return values
6692                 if(is_array($return)){
6693                     // multiple 'out' parameters, which we return wrapped up
6694                     // in the array
6695                     if(sizeof($return) > 1){
6696                         return $return;
6697                     }
6698                     // single 'out' parameter (normally the return value)
6699                     $return = array_shift($return);
6700                     $this->debug('return shifted value: ');
6701                     $this->appendDebug($this->varDump($return));
6702                        return $return;
6703                 // nothing returned (ie, echoVoid)
6704                 } else {
6705                     return "";
6706                 }
6707             }
6708         }
6709     }
6710
6711     /**
6712     * get available data pertaining to an operation
6713     *
6714     * @param    string $operation operation name
6715     * @return    array array of data pertaining to the operation
6716     * @access   public
6717     */
6718     function getOperationData($operation){
6719         if(isset($this->operations[$operation])){
6720             return $this->operations[$operation];
6721         }
6722         $this->debug("No data for operation: $operation");
6723     }
6724
6725     /**
6726     * send the SOAP message
6727     *
6728     * Note: if the operation has multiple return values
6729     * the return value of this method will be an array
6730     * of those values.
6731     *
6732     * @param    string $msg a SOAPx4 soapmsg object
6733     * @param    string $soapaction SOAPAction value
6734     * @param    integer $timeout set connection timeout in seconds
6735     * @param    integer $response_timeout set response timeout in seconds
6736     * @return    mixed native PHP types.
6737     * @access   private
6738     */
6739     function send($msg, $soapaction = '', $timeout=0, $response_timeout=30) {
6740         $this->checkCookies();
6741         // detect transport
6742         switch(true){
6743             // http(s)
6744             case ereg('^http',$this->endpoint):
6745                 $this->debug('transporting via HTTP');
6746                 if($this->persistentConnection == true && is_object($this->persistentConnection)){
6747                     $http =& $this->persistentConnection;
6748                 } else {
6749                     $http = new soap_transport_http($this->endpoint);
6750                     if ($this->persistentConnection) {
6751                         $http->usePersistentConnection();
6752                     }
6753                 }
6754                 $http->setContentType($this->getHTTPContentType(), $this->getHTTPContentTypeCharset());
6755                 $http->setSOAPAction($soapaction);
6756                 if($this->proxyhost && $this->proxyport){
6757                     $http->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
6758                 }
6759                 if($this->authtype != '') {
6760                     $http->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
6761                 }
6762                 if($this->http_encoding != ''){
6763                     $http->setEncoding($this->http_encoding);
6764                 }
6765                 $this->debug('sending message, length='.strlen($msg));
6766                 if(ereg('^http:',$this->endpoint)){
6767                 //if(strpos($this->endpoint,'http:')){
6768                     $this->responseData = $http->send($msg,$timeout,$response_timeout,$this->cookies);
6769                 } elseif(ereg('^https',$this->endpoint)){
6770                 //} elseif(strpos($this->endpoint,'https:')){
6771                     //if(phpversion() == '4.3.0-dev'){
6772                         //$response = $http->send($msg,$timeout,$response_timeout);
6773                            //$this->request = $http->outgoing_payload;
6774                         //$this->response = $http->incoming_payload;
6775                     //} else
6776                     $this->responseData = $http->sendHTTPS($msg,$timeout,$response_timeout,$this->cookies);
6777                 } else {
6778                     $this->setError('no http/s in endpoint url');
6779                 }
6780                 $this->request = $http->outgoing_payload;
6781                 $this->response = $http->incoming_payload;
6782                 $this->appendDebug($http->getDebug());
6783                 $this->UpdateCookies($http->incoming_cookies);
6784
6785                 // save transport object if using persistent connections
6786                 if ($this->persistentConnection) {
6787                     $http->clearDebug();
6788                     if (!is_object($this->persistentConnection)) {
6789                         $this->persistentConnection = $http;
6790                     }
6791                 }
6792                 
6793                 if($err = $http->getError()){
6794                     $this->setError('HTTP Error: '.$err);
6795                     return false;
6796                 } elseif($this->getError()){
6797                     return false;
6798                 } else {
6799                     $this->debug('got response, length='. strlen($this->responseData).' type='.$http->incoming_headers['content-type']);
6800                     return $this->parseResponse($http->incoming_headers, $this->responseData);
6801                 }
6802             break;
6803             default:
6804                 $this->setError('no transport found, or selected transport is not yet supported!');
6805             return false;
6806             break;
6807         }
6808     }
6809
6810     /**
6811     * processes SOAP message returned from server
6812     *
6813     * @param    array    $headers    The HTTP headers
6814     * @param    string    $data        unprocessed response data from server
6815     * @return    mixed    value of the message, decoded into a PHP type
6816     * @access   private
6817     */
6818     function parseResponse($headers, $data) {
6819         $this->debug('Entering parseResponse() for data of length ' . strlen($data) . ' and type ' . $headers['content-type']);
6820         if (!strstr($headers['content-type'], 'text/xml')) {
6821             $this->setError('Response not of type text/xml');
6822             return false;
6823         }
6824         if (strpos($headers['content-type'], '=')) {
6825             $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
6826             $this->debug('Got response encoding: ' . $enc);
6827             if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
6828                 $this->xml_encoding = strtoupper($enc);
6829             } else {
6830                 $this->xml_encoding = 'US-ASCII';
6831             }
6832         } else {
6833             // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
6834             $this->xml_encoding = 'ISO-8859-1';
6835         }
6836         $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating soap_parser');
6837         $parser = new soap_parser($data,$this->xml_encoding,$this->operation,$this->decode_utf8);
6838         // add parser debug data to our debug
6839         $this->appendDebug($parser->getDebug());
6840         // if parse errors
6841         if($errstr = $parser->getError()){
6842             $this->setError( $errstr);
6843             // destroy the parser object
6844             unset($parser);
6845             return false;
6846         } else {
6847             // get SOAP headers
6848             $this->responseHeaders = $parser->getHeaders();
6849             // get decoded message
6850             $return = $parser->get_response();
6851             // add document for doclit support
6852             $this->document = $parser->document;
6853             // destroy the parser object
6854             unset($parser);
6855             // return decode message
6856             return $return;
6857         }
6858      }
6859
6860     /**
6861     * sets the SOAP endpoint, which can override WSDL
6862     *
6863     * @param    $endpoint string The endpoint URL to use, or empty string or false to prevent override
6864     * @access   public
6865     */
6866     function setEndpoint($endpoint) {
6867         $this->forceEndpoint = $endpoint;
6868     }
6869
6870     /**
6871     * set the SOAP headers
6872     *
6873     * @param    $headers mixed String of XML with SOAP header content, or array of soapval objects for SOAP headers
6874     * @access   public
6875     */
6876     function setHeaders($headers){
6877         $this->requestHeaders = $headers;
6878     }
6879
6880     /**
6881     * get the SOAP response headers (namespace resolution incomplete)
6882     *
6883     * @return    string
6884     * @access   public
6885     */
6886     function getHeaders(){
6887         return $this->responseHeaders;
6888     }
6889
6890     /**
6891     * set proxy info here
6892     *
6893     * @param    string $proxyhost
6894     * @param    string $proxyport
6895     * @param    string $proxyusername
6896     * @param    string $proxypassword
6897     * @access   public
6898     */
6899     function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
6900         $this->proxyhost = $proxyhost;
6901         $this->proxyport = $proxyport;
6902         $this->proxyusername = $proxyusername;
6903         $this->proxypassword = $proxypassword;
6904     }
6905
6906     /**
6907     * if authenticating, set user credentials here
6908     *
6909     * @param    string $username
6910     * @param    string $password
6911     * @param    string $authtype (basic|digest|certificate)
6912     * @param    array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
6913     * @access   public
6914     */
6915     function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) {
6916         $this->username = $username;
6917         $this->password = $password;
6918         $this->authtype = $authtype;
6919         $this->certRequest = $certRequest;
6920     }
6921     
6922     /**
6923     * use HTTP encoding
6924     *
6925     * @param    string $enc
6926     * @access   public
6927     */
6928     function setHTTPEncoding($enc='gzip, deflate'){
6929         $this->http_encoding = $enc;
6930     }
6931     
6932     /**
6933     * use HTTP persistent connections if possible
6934     *
6935     * @access   public
6936     */
6937     function useHTTPPersistentConnection(){
6938         $this->persistentConnection = true;
6939     }
6940     
6941     /**
6942     * gets the default RPC parameter setting.
6943     * If true, default is that call params are like RPC even for document style.
6944     * Each call() can override this value.
6945     *
6946     * This is no longer used.
6947     *
6948     * @return boolean
6949     * @access public
6950     * @deprecated
6951     */
6952     function getDefaultRpcParams() {
6953         return $this->defaultRpcParams;
6954     }
6955
6956     /**
6957     * sets the default RPC parameter setting.
6958     * If true, default is that call params are like RPC even for document style
6959     * Each call() can override this value.
6960     *
6961     * This is no longer used.
6962     *
6963     * @param    boolean $rpcParams
6964     * @access public
6965     * @deprecated
6966     */
6967     function setDefaultRpcParams($rpcParams) {
6968         $this->defaultRpcParams = $rpcParams;
6969     }
6970     
6971     /**
6972     * dynamically creates an instance of a proxy class,
6973     * allowing user to directly call methods from wsdl
6974     *
6975     * @return   object soap_proxy object
6976     * @access   public
6977     */
6978     function getProxy(){
6979         $r = rand();
6980         $evalStr = $this->_getProxyClassCode($r);
6981         //$this->debug("proxy class: $evalStr";
6982         // eval the class
6983         eval($evalStr);
6984         // instantiate proxy object
6985         eval("\$proxy = new soap_proxy_$r('');");
6986         // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
6987         $proxy->endpointType = 'wsdl';
6988         $proxy->wsdlFile = $this->wsdlFile;
6989         $proxy->wsdl = $this->wsdl;
6990         $proxy->operations = $this->operations;
6991         $proxy->defaultRpcParams = $this->defaultRpcParams;
6992         // transfer other state
6993         $proxy->username = $this->username;
6994         $proxy->password = $this->password;
6995         $proxy->authtype = $this->authtype;
6996         $proxy->proxyhost = $this->proxyhost;
6997         $proxy->proxyport = $this->proxyport;
6998         $proxy->proxyusername = $this->proxyusername;
6999         $proxy->proxypassword = $this->proxypassword;
7000         $proxy->timeout = $this->timeout;
7001         $proxy->response_timeout = $this->response_timeout;
7002         $proxy->http_encoding = $this->http_encoding;
7003         $proxy->persistentConnection = $this->persistentConnection;
7004         $proxy->requestHeaders = $this->requestHeaders;
7005         $proxy->soap_defencoding = $this->soap_defencoding;
7006         $proxy->endpoint = $this->endpoint;
7007         $proxy->forceEndpoint = $this->forceEndpoint;
7008         return $proxy;
7009     }
7010
7011     /**
7012     * dynamically creates proxy class code
7013     *
7014     * @return   string PHP/NuSOAP code for the proxy class
7015     * @access   private
7016     */
7017     function _getProxyClassCode($r) {
7018         if ($this->endpointType != 'wsdl') {
7019             $evalStr = 'A proxy can only be created for a WSDL client';
7020             $this->setError($evalStr);
7021             return $evalStr;
7022         }
7023         $evalStr = '';
7024         foreach ($this->operations as $operation => $opData) {
7025             if ($operation != '') {
7026                 // create param string and param comment string
7027                 if (sizeof($opData['input']['parts']) > 0) {
7028                     $paramStr = '';
7029                     $paramArrayStr = '';
7030                     $paramCommentStr = '';
7031                     foreach ($opData['input']['parts'] as $name => $type) {
7032                         $paramStr .= "\$$name, ";
7033                         $paramArrayStr .= "'$name' => \$$name, ";
7034                         $paramCommentStr .= "$type \$$name, ";
7035                     }
7036                     $paramStr <