]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/nusoap/nusoap.php
more php-4.0.6 compatibility: superglobals
[SourceForge/phpwiki.git] / lib / nusoap / nusoap.php
1 <?php
2 /**
3  * NuSOAP - Web Services Toolkit for PHP
4  *
5  * @author: Dietrich Ayala
6  */
7
8 /*
9 Copyright (c) 2002 NuSphere Corporation
10
11 This library is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Lesser General Public
13 License as published by the Free Software Foundation; either
14 version 2.1 of the License, or (at your option) any later version.
15
16 This library is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 Lesser General Public License for more details.
20
21 You should have received a copy of the GNU Lesser General Public
22 License along with this library; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24
25 If you have any questions or comments, please email:
26
27 Dietrich Ayala
28 dietrich@ganx4.com
29 http://dietrich.ganx4.com/nusoap
30
31 NuSphere Corporation
32 http://www.nusphere.com
33
34 */
35
36 /* load classes
37
38 // necessary classes
39 require_once('class.soapclient.php');
40 require_once('class.soap_val.php');
41 require_once('class.soap_parser.php');
42 require_once('class.soap_fault.php');
43
44 // transport classes
45 require_once('class.soap_transport_http.php');
46
47 // optional add-on classes
48 require_once('class.xmlschema.php');
49 require_once('class.wsdl.php');
50
51 // server class
52 require_once('class.soap_server.php');*/
53
54 /**
55 *
56 * nusoap_base
57 *
58 * @author   Dietrich Ayala <dietrich@ganx4.com>
59 * @version  v 0.6.3
60 * @access   public
61 */
62 class nusoap_base {
63
64     var $title = 'NuSOAP';
65     var $version = '0.6.3';
66     var $error_str = false;
67     var $debug_str = '';
68     // toggles automatic encoding of special characters
69     var $charencoding = true;
70
71     /**
72      *  set schema version
73      *
74      * @var      XMLSchemaVersion
75      * @access   public
76      */
77     var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
78         
79     /**
80      *  set default encoding
81      *
82      * @var      soap_defencoding
83      * @access   public
84      */
85     //var $soap_defencoding = 'UTF-8';
86     var $soap_defencoding = 'ISO-8859-1';
87
88     /**
89      *  load namespace uris into an array of uri => prefix
90      *
91      * @var      namespaces
92      * @access   public
93      */
94     var $namespaces = array(
95                             'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
96                             'xsd' => 'http://www.w3.org/2001/XMLSchema',
97                             'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
98                             'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/',
99                             'si' => 'http://soapinterop.org/xsd');
100     /**
101      * load types into typemap array
102      * is this legacy yet?
103      * no, this is used by the xmlschema class to verify type => namespace mappings.
104      * @var      typemap
105      * @access   public
106      */
107     var $typemap = 
108         array(
109               'http://www.w3.org/2001/XMLSchema' => 
110               array(
111                     'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
112                     'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
113                     'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
114                     // derived datatypes
115                     'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
116                     'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
117                     'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
118                     'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
119               'http://www.w3.org/1999/XMLSchema' => 
120               array(
121                     'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
122                     'float'=>'double','dateTime'=>'string',
123                     'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
124               'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
125               'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
126               'http://xml.apache.org/xml-soap' => array('Map')
127               );
128     
129     /**
130      *  entities to convert
131      *
132      * @var      xmlEntities
133      * @access   public
134      */
135     var $xmlEntities = array('quot' => '"','amp' => '&',
136                              'lt' => '<','gt' => '>','apos' => "'");
137
138     /**
139      * adds debug data to the class level debug string
140      *
141      * @param    string $string debug data
142      * @access   private
143      */
144     function debug($string){
145         $this->debug_str .= get_class($this).": $string\n";
146     }
147
148     /**
149      * returns error string if present
150      *
151      * @return   boolean $string error string
152      * @access   public
153      */
154     function getError(){
155         if($this->error_str != ''){
156             return $this->error_str;
157         }
158         return false;
159     }
160
161     /**
162      * sets error string
163      *
164      * @return   boolean $string error string
165      * @access   private
166      */
167     function setError($str){
168         $this->error_str = $str;
169     }
170
171     /**
172      * serializes PHP values in accordance w/ section 5. Type information is
173      * not serialized if $use == 'literal'.
174      *
175      * @return  string
176      * @access  public
177      */
178     function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded'){
179         if(is_object($val) && get_class($val) == 'soapval'){
180             return $val->serialize($use);
181         }
182         $this->debug( "in serialize_val: $val, $name, $type, $name_ns, $type_ns, $attributes, $use");
183         // if no name, use item
184         $name = (!$name|| is_numeric($name)) ? 'soapVal' : $name;
185         // if name has ns, add ns prefix to name
186         $xmlns = '';
187         if($name_ns){
188             $prefix = 'nu'.rand(1000,9999);
189             $name = $prefix.':'.$name;
190             $xmlns .= " xmlns:$prefix=\"$name_ns\"";
191         }
192         // if type is prefixed, create type prefix
193         if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
194             // need to fix this. shouldn't default to xsd if no ns specified
195             // w/o checking against typemap
196             $type_prefix = 'xsd';
197         } elseif($type_ns){
198             $type_prefix = 'ns'.rand(1000,9999);
199             $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
200         }
201         // serialize attributes if present
202         if($attributes){
203             foreach($attributes as $k => $v){
204                 $atts .= " $k=\"$v\"";
205             }
206         }
207         // serialize if an xsd built-in primitive type
208         if ($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
209             if ($use == 'literal') {
210                 return "<$name$xmlns>$val</$name>";
211             } else {
212                 return "<$name$xmlns xsi:type=\"xsd:$type\">$val</$name>";
213             }
214         }
215         // detect type and serialize
216         $xml = '';
217         $atts = '';
218         switch(true) {
219         case ($type == '' && is_null($val)):
220             if ($use == 'literal') {
221                 // TODO: depends on nillable
222                 $xml .= "<$name$xmlns/>";
223             } else {
224                 $xml .= "<$name$xmlns xsi:type=\"xsd:nil\"/>";
225             }
226             break;
227         case (is_bool($val) || $type == 'boolean'):
228             if(!$val){
229                 $val = 0;
230             }
231             if ($use == 'literal') {
232                 $xml .= "<$name$xmlns $atts>$val</$name>";
233             } else {
234                 $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
235             }
236             break;
237         case (is_int($val) || is_long($val) || $type == 'int'):
238             if ($use == 'literal') {
239                 $xml .= "<$name$xmlns $atts>$val</$name>";
240             } else {
241                 $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
242             }
243             break;
244         case (is_float($val)|| is_double($val) || $type == 'float'):
245             if ($use == 'literal') {
246                 $xml .= "<$name$xmlns $atts>$val</$name>";
247             } else {
248                 $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
249             }
250             break;
251         case (is_string($val) || $type == 'string'):
252             if($this->charencoding){
253                 $val = htmlspecialchars($val, ENT_QUOTES);
254             }
255             if ($use == 'literal') {
256                 $xml .= "<$name$xmlns $atts>$val</$name>";
257             } else {
258                 $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
259             }
260             break;
261         case is_object($val):
262             $name = get_class($val);
263             foreach(get_object_vars($val) as $k => $v){
264                 $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
265             }
266             $xml .= '<'.$name.'>'.$pXml.'</'.$name.'>';
267             break;
268             break;
269         case (is_array($val) || $type):
270             // detect if struct or array
271             $keyList = array_keys($val);
272             $valueType = 'arraySimple';
273             foreach($keyList as $keyListValue){
274                 if(!is_int($keyListValue)){
275                     $valueType = 'arrayStruct';
276                     break;
277                 }
278             }
279             if($valueType=='arraySimple' || ereg('^ArrayOf',$type)){
280                 $i = 0;
281                 if(is_array($val) && count($val)> 0){
282                     foreach($val as $v){
283                         if(is_object($v) && get_class($v) == 'soapval'){
284                             $tt = $v->type;
285                         } else {
286                             $tt = gettype($v);
287                         }
288                         $array_types[$tt] = 1;
289                         $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
290                         if(is_array($v) && is_numeric(key($v))){
291                             $i += sizeof($v);
292                         } else {
293                             ++$i;
294                         }
295                     }
296                     if(count($array_types) > 1){
297                         $array_typename = 'xsd:ur-type';
298                     } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
299                         $array_typename = 'xsd:'.$tt;
300                     } elseif($tt == 'array' || $tt == 'Array'){
301                         $array_typename = 'SOAP-ENC:Array';
302                     } else {
303                         $array_typename = $tt;
304                     }
305                     if(isset($array_types['array'])){
306                         $array_type = $i.",".$i;
307                     } else {
308                         $array_type = $i;
309                     }
310                     if ($use == 'literal') {
311                         $xml = "<$name $atts>".$xml."</$name>";
312                     } else {
313                         $xml = "<$name xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"$atts>".$xml."</$name>";
314                     }
315                     // empty array
316                 } else {
317                     if ($use == 'literal') {
318                         $xml = "<$name $atts>".$xml."</$name>";;
319                     } else {
320                         $xml = "<$name xsi:type=\"SOAP-ENC:Array\" $atts>".$xml."</$name>";;
321                     }
322                 }
323             } else {
324                 // got a struct
325                 if(isset($type) && isset($type_prefix)){
326                     $type_str = " xsi:type=\"$type_prefix:$type\"";
327                 } else {
328                     $type_str = '';
329                 }
330                 if ($use == 'literal') {
331                     $xml .= "<$name$xmlns $atts>";
332                 } else {
333                     $xml .= "<$name$xmlns$type_str$atts>";
334                 }
335                 foreach($val as $k => $v){
336                     $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
337                 }
338                 $xml .= "</$name>";
339             }
340             break;
341         default:
342             $xml .= 'not detected, got '.gettype($val).' for '.$val;
343             break;
344         }
345         return $xml;
346     }
347     
348     /**
349     * serialize message
350     *
351     * @param string body
352     * @param string headers
353     * @param array namespaces
354     * @param string style
355     * @return string message
356     * @access public
357     */
358     function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc'){
359         // serialize namespaces
360         $ns_string = '';
361         foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
362                 $ns_string .= "  xmlns:$k=\"$v\"";
363         }
364         if($style == 'rpc') {
365             $ns_string = ' SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' . $ns_string;
366         }
367
368         // serialize headers
369         if($headers){
370             $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
371         }
372         // serialize envelope
373         return
374             '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
375             '<SOAP-ENV:Envelope'.$ns_string.">".
376             $headers.
377             "<SOAP-ENV:Body>".
378             $body.
379             "</SOAP-ENV:Body>".
380             "</SOAP-ENV:Envelope>";
381     }
382
383     function formatDump($str){
384         $str = htmlspecialchars($str);
385         return nl2br($str);
386     }
387
388     /**
389      * returns the local part of a prefixed string
390      * returns the original string, if not prefixed
391      *
392      * @param string
393      * @return string
394      * @access public
395      */
396     function getLocalPart($str){
397         if($sstr = strrchr($str,':')){
398             // get unqualified name
399             return substr( $sstr, 1 );
400         } else {
401             return $str;
402         }
403     }
404     
405     /**
406      * returns the prefix part of a prefixed string
407      * returns false, if not prefixed
408      *
409      * @param string
410      * @return mixed
411      * @access public
412      */
413     function getPrefix($str){
414         if($pos = strrpos($str,':')){
415             // get prefix
416             return substr($str,0,$pos);
417         }
418         return false;
419     }
420
421     function varDump($data) {
422         ob_start();
423         var_dump($data);
424         $ret_val = ob_get_contents();
425         ob_end_clean();
426         return $ret_val;
427     }
428 }
429
430 // XML Schema Datatype Helper Functions
431
432 //xsd:dateTime helpers
433
434 /**
435 * convert unix timestamp to ISO 8601 compliant date string
436 *
437 * @param    string $timestamp Unix time stamp
438 * @access   public
439 */
440 function timestamp_to_iso8601($timestamp,$utc=true){
441     $datestr = date('Y-m-d\TH:i:sO',$timestamp);
442     if($utc){
443         $eregStr =
444             '([0-9]{4})-'.      // centuries & years CCYY-
445             '([0-9]{2})-'.      // months MM-
446             '([0-9]{2})'.       // days DD
447             'T'.                        // separator T
448             '([0-9]{2}):'.      // hours hh:
449             '([0-9]{2}):'.      // minutes mm:
450             '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
451             '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
452         
453         if(ereg($eregStr,$datestr,$regs)){
454             return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
455         }
456         return false;
457     } else {
458         return $datestr;
459     }
460 }
461
462 /**
463 * convert ISO 8601 compliant date string to unix timestamp
464 *
465 * @param    string $datestr ISO 8601 compliant date string
466 * @access   public
467 */
468 function iso8601_to_timestamp($datestr){
469     $eregStr =
470         '([0-9]{4})-'.  // centuries & years CCYY-
471         '([0-9]{2})-'.  // months MM-
472         '([0-9]{2})'.   // days DD
473         'T'.                    // separator T
474         '([0-9]{2}):'.  // hours hh:
475         '([0-9]{2}):'.  // minutes mm:
476         '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
477         '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
478     if(ereg($eregStr,$datestr,$regs)){
479         // not utc
480         if($regs[8] != 'Z'){
481             $op = substr($regs[8],0,1);
482             $h = substr($regs[8],1,2);
483             $m = substr($regs[8],strlen($regs[8])-2,2);
484             if($op == '-'){
485                 $regs[4] = $regs[4] + $h;
486                 $regs[5] = $regs[5] + $m;
487             } elseif($op == '+'){
488                 $regs[4] = $regs[4] - $h;
489                 $regs[5] = $regs[5] - $m;
490             }
491         }
492         return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
493     } else {
494         return false;
495     }
496 }
497
498 ?><?php
499
500 /**
501  * soap_fault class, allows for creation of faults
502  * mainly used for returning faults from deployed functions
503  * in a server instance.
504  * @author   Dietrich Ayala <dietrich@ganx4.com>
505  * @version  v 0.6.3
506  * @access public
507  */
508 class soap_fault extends nusoap_base {
509
510     var $faultcode;
511     var $faultactor;
512     var $faultstring;
513     var $faultdetail;
514     
515     /**
516      * constructor
517      *
518      * @param string $faultcode (client | server)
519      * @param string $faultactor only used when msg routed between multiple actors
520      * @param string $faultstring human readable error message
521      * @param string $faultdetail
522      */
523     function soap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
524         $this->faultcode = $faultcode;
525         $this->faultactor = $faultactor;
526         $this->faultstring = $faultstring;
527         $this->faultdetail = $faultdetail;
528     }
529     
530     /**
531      * serialize a fault
532      *
533      * @access   public
534      */
535     function serialize(){
536         $ns_string = '';
537         foreach($this->namespaces as $k => $v){
538             $ns_string .= "\n  xmlns:$k=\"$v\"";
539         }
540         $return_msg =
541             '<?xml version="1.0"?'.">\n".
542             '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
543             '<SOAP-ENV:Body>'.
544             '<SOAP-ENV:Fault>'.
545                 '<faultcode>'.$this->faultcode.'</faultcode>'.
546                 '<faultactor>'.$this->faultactor.'</faultactor>'.
547                 '<faultstring>'.$this->faultstring.'</faultstring>'.
548                 '<detail>'.$this->serialize_val($this->faultdetail).'</detail>'.
549             '</SOAP-ENV:Fault>'.
550             '</SOAP-ENV:Body>'.
551             '</SOAP-ENV:Envelope>';
552         return $return_msg;
553     }
554 }
555
556 ?><?php
557
558 /**
559 * parses an XML Schema, allows access to it's data, other utility methods
560 * no validation... yet.
561 * very experimental and limited. As is discussed on XML-DEV, I'm one of the people
562 * that just doesn't have time to read the spec(s) thoroughly, and just have a couple of trusty
563 * tutorials I refer to :)
564 *
565 * @author   Dietrich Ayala <dietrich@ganx4.com>
566 * @version  v 0.6.3
567 * @access   public
568 */
569 class XMLSchema extends nusoap_base  {
570         
571         // files
572         var $schema = '';
573         var $xml = '';
574         // define internal arrays of bindings, ports, operations, messages, etc.
575         var $complexTypes = array();
576         // target namespace
577         var $schemaTargetNamespace = '';
578         // parser vars
579         var $parser;
580         var $position;
581         var $depth = 0;
582         var $depth_array = array();
583     
584         /**
585         * constructor
586         *
587         * @param    string $schema schema document URI
588         * @param    string $xml xml document URI
589         * @access   public
590         */
591         function XMLSchema($schema='',$xml=''){
592
593                 $this->debug('xmlschema class instantiated, inside constructor');
594                 // files
595                 $this->schema = $schema;
596                 $this->xml = $xml;
597
598                 // parse schema file
599                 if($schema != ''){
600                         $this->debug('initial schema file: '.$schema);
601                         $this->parseFile($schema);
602                 }
603
604                 // parse xml file
605                 if($xml != ''){
606                         $this->debug('initial xml file: '.$xml);
607                         $this->parseFile($xml);
608                 }
609
610         }
611
612         /**
613          * parse an XML file
614          *
615          * @param string $xml, path/URL to XML file
616          * @param string $type, (schema | xml)
617          * @return boolean
618          * @access public
619          */
620         function parseFile($xml,$type){
621                 // parse xml file
622                 if($xml != ""){
623                         $this->debug('parsing $xml');
624                         $xmlStr = @join("",@file($xml));
625                         if($xmlStr == ""){
626                                 $this->setError('No file at the specified URL: '.$xml);
627                         return false;
628                         } else {
629                                 $this->parseString($xmlStr,$type);
630                         return true;
631                         }
632                 }
633                 return false;
634         }
635
636         /**
637          * parse an XML string
638          *
639          * @param    string $xml path or URL
640          * @param string $type, (schema|xml)
641          * @access   private
642          */
643         function parseString($xml,$type){
644                 // parse xml string
645                 if($xml != ""){
646
647                 // Create an XML parser.
648                 $this->parser = xml_parser_create();
649                 // Set the options for parsing the XML data.
650                 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
651
652                 // Set the object for the parser.
653                 xml_set_object($this->parser, $this);
654
655                 // Set the element handlers for the parser.
656                         if($type == "schema"){
657                         xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
658                         xml_set_character_data_handler($this->parser,'schemaCharacterData');
659                         } elseif($type == "xml"){
660                                 xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
661                         xml_set_character_data_handler($this->parser,'xmlCharacterData');
662                         }
663
664                     // Parse the XML file.
665                     if(!xml_parse($this->parser,$xml,true)){
666                         // Display an error message.
667                                 $errstr = sprintf('XML error on line %d: %s',
668                                 xml_get_current_line_number($this->parser),
669                                 xml_error_string(xml_get_error_code($this->parser))
670                                 );
671                                 $this->debug('XML parse error: '.$errstr);
672                                 $this->setError('Parser error: '.$errstr);
673                 }
674             
675                         xml_parser_free($this->parser);
676                 } else{
677                         $this->debug('no xml passed to parseString()!!');
678                         $this->setError('no xml passed to parseString()!!');
679                 }
680         }
681
682         /**
683         * start-element handler
684         *
685         * @param    string $parser XML parser object
686         * @param    string $name element name
687         * @param    string $attrs associative array of attributes
688         * @access   private
689         */
690         function schemaStartElement($parser, $name, $attrs) {
691                 
692             // position in the total number of elements, starting from 0
693             $pos = $this->position++;
694             $depth = $this->depth++;
695             // set self as current value for this depth
696             $this->depth_array[$depth] = $pos;
697
698             // get element prefix
699             if($prefix = $this->getPrefix($name)){
700                 // get unqualified name
701                 $name = $this->getLocalPart($name);
702             } else {
703                 $prefix = '';
704             }
705                 
706             // loop thru attributes, expanding, and registering namespace declarations
707             if(count($attrs) > 0){
708                 foreach($attrs as $k => $v){
709                     // if ns declarations, add to class level array of valid namespaces
710                     if(ereg("^xmlns",$k)){
711                         //$this->xdebug("$k: $v");
712                         //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
713                         if($ns_prefix = substr(strrchr($k,':'),1)){
714                             $this->namespaces[$ns_prefix] = $v;
715                         } else {
716                             $this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
717                         }
718                         if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema'){
719                             $this->XMLSchemaVersion = $v;
720                             $this->namespaces['xsi'] = $v.'-instance';
721                         }
722                     }
723                 }
724                 foreach($attrs as $k => $v) {
725                     // expand each attribute
726                     $k = strpos($k,':') ? $this->expandQname($k) : $k;
727                     $v = strpos($v,':') ? $this->expandQname($v) : $v;
728                     $eAttrs[$k] = $v;
729                 }
730                 $attrs = $eAttrs;
731             } else {
732                 $attrs = array();
733             }
734             // find status, register data
735             switch($name) {
736             case ('all'|'choice'|'sequence'):
737                 //$this->complexTypes[$this->currentComplexType]['compositor'] = 'all';
738                 $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
739                 if($name == 'all') {
740                     $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
741                 }
742                 break;
743             case 'attribute':
744                 //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
745                 if(isset($attrs['name'])){
746                                         $this->attributes[$attrs['name']] = $attrs;
747                                         $aname = $attrs['name'];
748                 } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
749                     $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
750                 } elseif(isset($attrs['ref'])){
751                     $aname = $attrs['ref'];
752                     $this->attributes[$attrs['ref']] = $attrs;
753                 }
754                 
755                 if(isset($this->currentComplexType)){
756                     $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
757                 } elseif(isset($this->currentElement)){
758                     $this->elements[$this->currentElement]['attrs'][$aname] = $attrs;
759                 }
760                 // arrayType attribute
761                 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
762                     $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
763                     $prefix = $this->getPrefix($aname);
764                     if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
765                         $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
766                     } else {
767                         $v = '';
768                     }
769                     if(strpos($v,'[,]')){
770                         $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
771                     }
772                     $v = substr($v,0,strpos($v,'[')); // clip the []
773                     if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
774                         $v = $this->XMLSchemaVersion.':'.$v;
775                     }
776                     $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
777                 }
778                 break;
779             case 'complexType':
780                 if(isset($attrs['name'])){
781                     $this->currentElement = false;
782                     $this->currentComplexType = $attrs['name'];
783                     $this->complexTypes[$this->currentComplexType] = $attrs;
784                     $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
785                     if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
786                         $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
787                     } else {
788                         $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
789                     }
790                     $this->xdebug('processing complexType '.$attrs['name']);
791                 }
792                 break;
793             case 'element':
794                 if(isset($attrs['type'])){
795                     $this->xdebug("processing element ".$attrs['name']);
796                     $this->currentElement = $attrs['name'];
797                     $this->elements[ $attrs['name'] ] = $attrs;
798                     $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
799                     $ename = $attrs['name'];
800                 } elseif(isset($attrs['ref'])){
801                     $ename = $attrs['ref'];
802                 } else {
803                     $this->xdebug('adding complexType '.$attrs['name']);
804                     $this->currentComplexType = $attrs['name'];
805                     $this->complexTypes[ $attrs['name'] ] = $attrs;
806                     $this->complexTypes[ $attrs['name'] ]['element'] = 1;
807                     $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
808                 }
809                 if(isset($ename) && $this->currentComplexType){
810                     $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
811                 }
812                 break;
813             case 'restriction':
814                 $this->xdebug("in restriction for ct: $this->currentComplexType and ce: $this->currentElement");
815                 if($this->currentElement){
816                     $this->elements[$this->currentElement]['type'] = $attrs['base'];
817                 } elseif($this->currentComplexType){
818                     $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
819                     if(strstr($attrs['base'],':') == ':Array'){
820                         $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
821                     }
822                 }
823                 break;
824             case 'schema':
825                 $this->schema = $attrs;
826                 $this->schema['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
827                 break;
828             case 'simpleType':
829                 $this->currentElement = $attrs['name'];
830                 $this->elements[ $attrs['name'] ] = $attrs;
831                 $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
832                 break;
833             }
834         }
835
836         /**
837          * end-element handler
838          *
839          * @param    string $parser XML parser object
840          * @param    string $name element name
841          * @access   private
842          */
843         function schemaEndElement($parser, $name) {
844             // position of current element is equal to the last value left in depth_array for my depth
845             if(isset($this->depth_array[$this->depth])){
846                 $pos = $this->depth_array[$this->depth];
847             }
848             // bring depth down a notch
849             $this->depth--;
850             // move on...
851             if($name == 'complexType'){
852                 $this->currentComplexType = false;
853                 $this->currentElement = false;
854             }
855             if($name == 'element'){
856                 $this->currentElement = false;
857             }
858         }
859
860         /**
861          * element content handler
862          *
863          * @param    string $parser XML parser object
864          * @param    string $data element content
865          * @access   private
866          */
867         function schemaCharacterData($parser, $data){
868             $pos = $this->depth_array[$this->depth];
869             $this->message[$pos]['cdata'] .= $data;
870         }
871
872         /**
873          * serialize the schema
874          *
875          * @access   public
876          */
877         function serializeSchema(){
878             
879             $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
880             $xml = '';
881             // complex types
882             foreach($this->complexTypes as $typeName => $attrs){
883                 $contentStr = '';
884                 // serialize child elements
885                 if(count($attrs['elements']) > 0){
886                     foreach($attrs['elements'] as $element => $eParts){
887                         if(isset($eParts['ref'])){
888                             $contentStr .= "<element ref=\"$element\"/>";
889                         } else {
890                             $contentStr .= "<element name=\"$element\" type=\"$eParts[type]\"/>";
891                         }
892                     }
893                 }
894                 // attributes
895                 if(count($attrs['attrs']) >= 1){
896                     foreach($attrs['attrs'] as $attr => $aParts){
897                         $contentStr .= '<attribute ref="'.$aParts['ref'].'"';
898                         if(isset($aParts['wsdl:arrayType'])){
899                             $contentStr .= ' wsdl:arrayType="'.$aParts['wsdl:arrayType'].'"';
900                         }
901                         $contentStr .= '/>';
902                     }
903                 }
904                 // if restriction
905                 if( isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
906                     $contentStr = "<$schemaPrefix:restriction base=\"".$attrs['restrictionBase']."\">".$contentStr."</$schemaPrefix:restriction>";
907                 }
908                 // "all" compositor obviates complex/simple content
909                 if(isset($attrs['compositor']) && $attrs['compositor'] == 'all'){
910                     $contentStr = "<$schemaPrefix:$attrs[compositor]>".$contentStr."</$schemaPrefix:$attrs[compositor]>";
911                 }
912                 // complex or simple content
913                 elseif( count($attrs['elements']) > 0 || count($attrs['attrs']) > 0){
914                     $contentStr = "<$schemaPrefix:complexContent>".$contentStr."</$schemaPrefix:complexContent>";
915                 }
916                 // compositors
917                 if(isset($attrs['compositor']) && $attrs['compositor'] != '' && $attrs['compositor'] != 'all'){
918                     $contentStr = "<$schemaPrefix:$attrs[compositor]>".$contentStr."</$schemaPrefix:$attrs[compositor]>";
919                 }
920                 // finalize complex type
921                 if($contentStr != ''){
922                     $contentStr = "<$schemaPrefix:complexType name=\"$typeName\">".$contentStr."</$schemaPrefix:complexType>";
923                 } else {
924                     $contentStr = "<$schemaPrefix:complexType name=\"$typeName\"/>";
925                 }
926                 $xml .= $contentStr;
927             }
928             // elements
929             if(isset($this->elements) && count($this->elements) > 0){
930                 foreach($this->elements as $element => $eParts){
931                     $xml .= "<$schemaPrefix:element name=\"$element\" type=\"".$eParts['type']."\"/>";
932                 }
933             }
934             // attributes
935             if(isset($this->attributes) && count($this->attributes) > 0){
936                 foreach($this->attributes as $attr => $aParts){
937                     $xml .= "<$schemaPrefix:attribute name=\"$attr\" type=\"".$aParts['type']."\"/>";
938                 }
939             }
940             // finish 'er up
941             $xml = "<$schemaPrefix:schema xmlns=\"$this->XMLSchemaVersion\" targetNamespace=\"$this->schemaTargetNamespace\">".$xml."</$schemaPrefix:schema>";
942             return $xml;
943         }
944
945         /**
946         * expands a qualified name
947         *
948         * @param    string $string qname
949         * @return       string expanded qname
950         * @access   private
951         */
952         function expandQname($qname){
953             // get element prefix
954             if(strpos($qname,':') && !ereg('^http://',$qname)){
955                 // get unqualified name
956                 $name = substr(strstr($qname,':'),1);
957                 // get ns prefix
958                 $prefix = substr($qname,0,strpos($qname,':'));
959                 if(isset($this->namespaces[$prefix])){
960                     return $this->namespaces[$prefix].':'.$name;
961                 } else {
962                     return $qname;
963                 }
964             } else {
965                 return $qname;
966             }
967         }
968
969         /**
970          * adds debug data to the clas level debug string
971          *
972          * @param    string $string debug data
973          * @access   private
974          */
975         function xdebug($string){
976             $this->debug(' xmlschema: '.$string);
977         }
978
979         /**
980          * get the PHP type of a user defined type in the schema
981          * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
982          * returns false if no type exists, or not w/ the given namespace
983          * else returns a string that is either a native php type, or 'struct'
984          *
985          * @param string $type, name of defined type
986          * @param string $ns, namespace of type
987          * @return mixed
988          * @access public
989          */
990         function getPHPType($type,$ns){
991             global $typemap;
992             if(isset($typemap[$ns][$type])){
993                 //print "found type '$type' and ns $ns in typemap<br>";
994                 return $typemap[$ns][$type];
995             } elseif(isset($this->complexTypes[$type])){
996                 //print "getting type '$type' and ns $ns from complexTypes array<br>";
997                 return $this->complexTypes[$type]['phpType'];
998             }
999             return false;
1000         }
1001
1002         /**
1003          * returns the local part of a prefixed string
1004          * returns the original string, if not prefixed
1005          *
1006          * @param string
1007          * @return string
1008          * @access public
1009          */
1010         function getLocalPart($str){
1011             if($sstr = strrchr($str,':')){
1012                 // get unqualified name
1013                 return substr( $sstr, 1 );
1014             } else {
1015                 return $str;
1016             }
1017         }
1018
1019         /**
1020          * returns the prefix part of a prefixed string
1021          * returns false, if not prefixed
1022          *
1023          * @param string
1024          * @return mixed
1025          * @access public
1026          */
1027         function getPrefix($str){
1028             if($pos = strrpos($str,':')){
1029                 // get prefix
1030                 return substr($str,0,$pos);
1031             }
1032             return false;
1033         }
1034
1035         /**
1036          * pass it a prefix, it returns a namespace
1037          * returns false if no namespace registered with the given prefix
1038          *
1039          * @param string
1040          * @return mixed
1041          * @access public
1042          */
1043         function getNamespaceFromPrefix($prefix){
1044             if(isset($this->namespaces[$prefix])){
1045                 return $this->namespaces[$prefix];
1046             }
1047             //$this->setError("No namespace registered for prefix '$prefix'");
1048             return false;
1049         }
1050         
1051         /**
1052          * returns the prefix for a given namespace (or prefix)
1053          * or false if no prefixes registered for the given namespace
1054          *
1055          * @param string
1056          * @return mixed
1057          * @access public
1058          */
1059         function getPrefixFromNamespace($ns){
1060             foreach($this->namespaces as $p => $n){
1061                 if($ns == $n || $ns == $p){
1062                     $this->usedNamespaces[$p] = $n;
1063                     return $p;
1064                 }
1065             }
1066             return false;
1067         }
1068
1069         /**
1070          * returns an array of information about a given type
1071          * returns false if no type exists by the given name
1072          *
1073          *       typeDef = array(
1074          *       'elements' => array(), // refs to elements array
1075          *      'restrictionBase' => '',
1076          *      'phpType' => '',
1077          *      'order' => '(sequence|all)',
1078          *      'attrs' => array() // refs to attributes array
1079          *      )
1080          *
1081          * @param string
1082          * @return mixed
1083          * @access public
1084          */
1085         function getTypeDef($type){
1086             if(isset($this->complexTypes[$type])){
1087                 return $this->complexTypes[$type];
1088             } elseif(isset($this->elements[$type])){
1089                 return $this->elements[$type];
1090             } elseif(isset($this->attributes[$type])){
1091                 return $this->attributes[$type];
1092             }
1093             return false;
1094         }
1095
1096         /**
1097          * returns a sample serialization of a given type, or false if no type by the given name
1098          *
1099          * @param string $type, name of type
1100          * @return mixed
1101          * @access public
1102          */
1103         function serializeTypeDef($type){
1104             //print "in sTD() for type $type<br>";
1105             if($typeDef = $this->getTypeDef($type)){
1106                 $str .= '<'.$type;
1107                 if(is_array($typeDef['attrs'])){
1108                     foreach($attrs as $attName => $data){
1109                         $str .= " $attName=\"{type = ".$data['type']."}\"";
1110                     }
1111                 }
1112                 $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
1113                 if(count($typeDef['elements']) > 0){
1114                     $str .= ">";
1115                     foreach($typeDef['elements'] as $element => $eData){
1116                         $str .= $this->serializeTypeDef($element);
1117                     }
1118                     $str .= "</$type>";
1119                 } elseif($typeDef['typeClass'] == 'element') {
1120                     $str .= "></$type>";
1121                 } else {
1122                     $str .= "/>";
1123                 }
1124                 return $str;
1125             }
1126             return false;
1127         }
1128
1129         /**
1130          * returns HTML form elements that allow a user
1131          * to enter values for creating an instance of the given type.
1132          *
1133          * @param string $name, name for type instance
1134          * @param string $type, name of type
1135          * @return string
1136          * @access public
1137          */
1138         function typeToForm($name,$type){
1139             // get typedef
1140             if($typeDef = $this->getTypeDef($type)){
1141                 // if struct
1142                 if($typeDef['phpType'] == 'struct'){
1143                     $buffer .= '<table>';
1144                     foreach($typeDef['elements'] as $child => $childDef){
1145                         $buffer .= "<tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>".
1146                                    "<td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
1147                     }
1148                     $buffer .= '</table>';
1149                     // if array
1150                 } elseif($typeDef['phpType'] == 'array'){
1151                     $buffer .= '<table>';
1152                     for($i=0;$i < 3; $i++){
1153                         $buffer .= "<tr><td align='right'>array item (type: $typeDef[arrayType]):</td>".
1154                                    "<td><input type='text' name='parameters[".$name."][]'></td></tr>";
1155                     }
1156                     $buffer .= '</table>';
1157                     // if scalar
1158                 } else {
1159                     $buffer .= "<input type='text' name='parameters[$name]'>";
1160                 }
1161             } else {
1162                 $buffer .= "<input type='text' name='parameters[$name]'>";
1163             }
1164             return $buffer;
1165         }
1166         
1167         /**
1168         * adds an XML Schema complex type to the WSDL types
1169         * 
1170         * example: array
1171         * 
1172         * addType(
1173         *       'ArrayOfstring',
1174         *       'complexType',
1175         *       'array',
1176         *       '',
1177         *       'SOAP-ENC:Array',
1178         *       array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1179         *       'xsd:string'
1180         * );
1181         * 
1182         * example: PHP associative array ( SOAP Struct )
1183         * 
1184         * addType(
1185         *       'SOAPStruct',
1186         *       'complexType',
1187         *       'struct',
1188         *       'all',
1189         *       array('myVar'=> array('name'=>'myVar','type'=>'string')
1190         * );
1191         * 
1192         * @param name
1193         * @param typeClass (complexType|simpleType|attribute)
1194         * @param phpType: currently supported are array and struct (php assoc array)
1195         * @param compositor (all|sequence|choice)
1196         * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1197         * @param elements = array ( name = array(name=>'',type=>'') )
1198         * @param attrs = array(
1199         *       array(
1200         *               'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1201         *               "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1202         *       )
1203         * )
1204         * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1205         *
1206         */
1207         function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
1208             $this->complexTypes[$name] = array(
1209                                                'name'           => $name,
1210                                                'typeClass'      => $typeClass,
1211                                                'phpType'        => $phpType,
1212                                                'compositor'=> $compositor,
1213                                                'restrictionBase' => $restrictionBase,
1214                                                'elements'       => $elements,
1215                                                'attrs'          => $attrs,
1216                                                'arrayType'      => $arrayType
1217                                                );
1218         }
1219 }
1220
1221 ?><?php
1222
1223 /**
1224 * for creating serializable abstractions of native PHP types
1225 * NOTE: this is only really used when WSDL is not available.
1226 *
1227 * @author   Dietrich Ayala <dietrich@ganx4.com>
1228 * @version  v 0.6.3
1229 * @access   public
1230 */
1231 class soapval extends nusoap_base {
1232     /**
1233      * constructor
1234      *
1235      * @param    string $name optional name
1236      * @param    string $type optional type name
1237      * @param   mixed $value optional value
1238      * @param   string $namespace optional namespace of value
1239      * @param   string $type_namespace optional namespace of type
1240      * @param   array $attributes associative array of attributes to add to element serialization
1241      * @access   public
1242      */
1243     function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
1244         $this->name = $name;
1245         $this->value = $value;
1246         $this->type = $type;
1247         $this->element_ns = $element_ns;
1248         $this->type_ns = $type_ns;
1249         $this->attributes = $attributes;
1250     }
1251
1252     /**
1253      * return serialized value
1254      *
1255      * @return  string XML data
1256      * @access   private
1257      */
1258     function serialize($use='encoded') {
1259         return $this->serialize_val($this->value,$this->name,$this->type,$this->element_ns,$this->type_ns,$this->attributes,$use);
1260     }
1261
1262     /**
1263      * decodes a soapval object into a PHP native type
1264      *
1265      * @param   object $soapval optional SOAPx4 soapval object, else uses self
1266      * @return  mixed
1267      * @access   public
1268      */
1269     function decode(){
1270         return $this->value;
1271     }
1272 }
1273
1274 ?><?php
1275
1276 /**
1277 * transport class for sending/receiving data via HTTP and HTTPS
1278 * NOTE: PHP must be compiled with the CURL extension for HTTPS support
1279 *
1280 * @author   Dietrich Ayala <dietrich@ganx4.com>
1281 * @version  v 0.6.3
1282 * @access public
1283 */
1284 class soap_transport_http extends nusoap_base {
1285
1286     var $username = '';
1287     var $password = '';
1288     var $url = '';
1289     var $proxyhost = '';
1290     var $proxyport = '';
1291     var $scheme = '';
1292     var $request_method = 'POST';
1293     var $protocol_version = '1.0';
1294     var $encoding = '';
1295     var $outgoing_headers = array();
1296     var $incoming_headers = array();
1297     var $outgoing_payload = '';
1298     var $incoming_payload = '';
1299     var $useSOAPAction = true;
1300         
1301     /**
1302      * constructor
1303      */
1304     function soap_transport_http($url){
1305         $this->url = $url;
1306         $u = parse_url($url);
1307         foreach($u as $k => $v){
1308             $this->debug("$k = $v");
1309             $this->$k = $v;
1310         }
1311         if(isset($u['query']) && $u['query'] != ''){
1312             $this->path .= '?' . $u['query'];
1313         }
1314         if(!isset($u['port']) && $u['scheme'] == 'http'){
1315             $this->port = 80;
1316         }
1317     }
1318     
1319     function connect($timeout){
1320         
1321         // proxy
1322         if($this->proxyhost != '' && $this->proxyport != ''){
1323             $host = $this->proxyhost;
1324             $port = $this->proxyport;
1325             $this->debug("using http proxy: $host, $port");
1326         } else {
1327             $host = $this->host;
1328             $port = $this->port;
1329         }
1330         // ssl
1331         if($this->scheme == 'https'){
1332             $host = 'ssl://'.$host;
1333             $port = 443;
1334         }
1335         
1336         $this->debug("connection params: $host, $port");
1337         // timeout
1338         if($timeout > 0){
1339             $fp = fsockopen($host, $port, $this->errno, $this->error_str, $timeout);
1340         } else {
1341             $fp = fsockopen($host, $port, $this->errno, $this->error_str);
1342         }
1343         
1344         // test pointer
1345         if(!$fp) {
1346             $this->debug('Couldn\'t open socket connection to server '.$this->url.', Error: '.$this->error_str);
1347             $this->setError('Couldn\'t open socket connection to server: '.$this->url.', Error: '.$this->error_str);
1348             return false;
1349         }
1350         return $fp;
1351     }
1352         
1353     /**
1354      * send the SOAP message via HTTP
1355      *
1356      * @param    string $data message data
1357      * @param    integer $timeout set timeout in seconds
1358      * @return  string data
1359      * @access   public
1360      */
1361     function send($data, $timeout=0) {
1362         $this->debug('entered send() with data of length: '.strlen($data));
1363         // get connnection
1364         if(!$fp = $this->connect($timeout)){
1365             return false;
1366         }
1367         $this->debug('socket connected');
1368         
1369         // start building outgoing payload:
1370         // swap url for path if going through a proxy
1371         if($this->proxyhost != '' && $this->proxyport != ''){
1372             $this->outgoing_payload = "$this->request_method $this->url ".strtoupper($this->scheme)."/$this->protocol_version\r\n";
1373         } else {
1374             $this->outgoing_payload = "$this->request_method $this->path ".strtoupper($this->scheme)."/$this->protocol_version\r\n";
1375         }
1376         // make payload
1377         $this->outgoing_payload .=
1378             "User-Agent: $this->title/$this->version\r\n".
1379             "Host: ".$this->host."\r\n";
1380         // http auth
1381         $credentials = '';
1382         if($this->username != '') {
1383             $this->debug('setting http auth credentials');
1384             $this->outgoing_payload .= 'Authorization: Basic '.base64_encode("$this->username:$this->password")."\r\n";
1385         }
1386         // set content type
1387         $this->outgoing_payload .= 'Content-Type: text/xml; charset='.$this->soap_defencoding."\r\nContent-Length: ".strlen($data)."\r\n";
1388         // http encoding
1389         if($this->encoding != '' && function_exists('gzdeflate')){
1390             $this->outgoing_payload .= "Accept-Encoding: $this->encoding\r\n".
1391                 "Connection: close\r\n";
1392             set_magic_quotes_runtime(0);
1393         }
1394         // set soapaction
1395         if($this->useSOAPAction){
1396             $this->outgoing_payload .= "SOAPAction: \"$this->soapaction\""."\r\n";
1397         }
1398         $this->outgoing_payload .= "\r\n";
1399         // add data
1400         $this->outgoing_payload .= $data;
1401                 
1402         // send payload
1403         if(!fputs($fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
1404             $this->setError('couldn\'t write message data to socket');
1405             $this->debug('Write error');
1406         }
1407         $this->debug('wrote data to socket');
1408                 
1409         // get response
1410         $this->incoming_payload = '';
1411         //$strlen = 0;
1412         while( $data = fread($fp, 32768) ){
1413             $this->incoming_payload .= $data;
1414             //$strlen += strlen($data);
1415         }
1416         $this->debug('received '.strlen($this->incoming_payload).' bytes of data from server');
1417         
1418         // close filepointer
1419         fclose($fp);
1420         $this->debug('closed socket');
1421                 
1422         // connection was closed unexpectedly
1423         if($this->incoming_payload == ''){
1424             $this->setError('no response from server');
1425             return false;
1426         }
1427                 
1428         $this->debug('received incoming payload: '.strlen($this->incoming_payload));
1429         $data = $this->incoming_payload."\r\n\r\n\r\n\r\n";
1430                 
1431         // remove 100 header
1432         if(ereg('^HTTP/1.1 100',$data)){
1433             if($pos = strpos($data,"\r\n\r\n") ){
1434                 $data = ltrim(substr($data,$pos));
1435             } elseif($pos = strpos($data,"\n\n") ){
1436                 $data = ltrim(substr($data,$pos));
1437             }
1438         }//
1439                 
1440         // separate content from HTTP headers
1441         if( $pos = strpos($data,"\r\n\r\n") ){
1442             $lb = "\r\n";
1443         } elseif( $pos = strpos($data,"\n\n") ){
1444             $lb = "\n";
1445         } else {
1446             $this->setError('no proper separation of headers and document');
1447             return false;
1448         }
1449         $header_data = trim(substr($data,0,$pos));
1450         $header_array = explode($lb,$header_data);
1451         $data = ltrim(substr($data,$pos));
1452         $this->debug('found proper separation of headers and document');
1453         $this->debug('cleaned data, stringlen: '.strlen($data));
1454         // clean headers
1455         foreach($header_array as $header_line){
1456             $arr = explode(':',$header_line);
1457             if(count($arr) >= 2){
1458                 $headers[trim($arr[0])] = trim($arr[1]);
1459             }
1460         }
1461         //print "headers: <pre>$header_data</pre><br>";
1462         //print "data: <pre>$data</pre><br>";
1463                 
1464         // decode transfer-encoding
1465         if(isset($headers['Transfer-Encoding']) && $headers['Transfer-Encoding'] == 'chunked'){
1466             //$timer->setMarker('starting to decode chunked content');
1467             if(!$data = $this->decodeChunked($data)){
1468                 $this->setError('Decoding of chunked data failed');
1469                 return false;
1470             }
1471             //$timer->setMarker('finished decoding of chunked content');
1472             //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
1473         }
1474                 
1475         // decode content-encoding
1476         if(isset($headers['Content-Encoding']) && $headers['Content-Encoding'] != ''){
1477             if($headers['Content-Encoding'] == 'deflate' || $headers['Content-Encoding'] == 'gzip'){
1478                 // if decoding works, use it. else assume data wasn't gzencoded
1479                 if(function_exists('gzinflate')){
1480                     //$timer->setMarker('starting decoding of gzip/deflated content');
1481                     if($headers['Content-Encoding'] == 'deflate' && $degzdata = @gzinflate($data)){
1482                         $data = $degzdata;
1483                     } elseif($headers['Content-Encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))){
1484                         $data = $degzdata;
1485                     } else {
1486                         $this->setError('Errors occurred when trying to decode the data');
1487                     }
1488                     //$timer->setMarker('finished decoding of gzip/deflated content');
1489                     //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
1490                 } else {
1491                     $this->setError('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
1492                 }
1493             }
1494         }
1495                 
1496         if(strlen($data) == 0){
1497             $this->debug('no data after headers!');
1498             $this->setError('no data present after HTTP headers');
1499             return false;
1500         }
1501         $this->debug('end of send()');
1502         return $data;
1503     }
1504
1505     
1506     /**
1507      * send the SOAP message via HTTPS 1.0 using CURL
1508      *
1509      * @param    string $msg message data
1510      * @param    integer $timeout set timeout in seconds
1511      * @return  string data
1512      * @access   public
1513      */
1514     function sendHTTPS($data, $timeout=0) {
1515         //global $t;
1516         //$t->setMarker('inside sendHTTPS()');
1517         $this->debug('entered sendHTTPS() with data of length: '.strlen($data));
1518         // init CURL
1519         $ch = curl_init();
1520         //$t->setMarker('got curl handle');
1521         // set proxy
1522         if($this->proxyhost && $this->proxyport){
1523             $host = $this->proxyhost;
1524             $port = $this->proxyport;
1525         } else {
1526             $host = $this->host;
1527             $port = $this->port;
1528         }
1529         // set url
1530         $hostURL = ($port != '') ? "https://$host:$port" : "https://$host";
1531         // add path
1532         $hostURL .= $this->path;
1533         curl_setopt($ch, CURLOPT_URL, $hostURL);
1534         // set other options
1535         curl_setopt($ch, CURLOPT_HEADER, 1);
1536         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1537         // encode
1538         if(function_exists('gzinflate')){
1539             curl_setopt($ch, CURLOPT_ENCODING, 'deflate');
1540         }
1541         // persistent connection
1542         //curl_setopt($ch, CURL_HTTP_VERSION_1_1, true);
1543                 
1544         // set timeout
1545         if($timeout != 0){
1546             curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
1547         }
1548                 
1549         $credentials = '';
1550         if($this->username != '') {
1551             $credentials = 'Authorization: Basic '.base64_encode("$this->username:$this->password").'\r\n';
1552         }
1553                 
1554         if($this->encoding != ''){
1555             if(function_exists('gzdeflate')){
1556                 $encoding_headers = "Accept-Encoding: $this->encoding\r\n".
1557                     "Connection: close\r\n";
1558                 set_magic_quotes_runtime(0);
1559             }
1560         }
1561                 
1562         if($this->proxyhost && $this->proxyport){
1563             $this->outgoing_payload = "POST $this->url HTTP/$this->protocol_version\r\n";
1564         } else {
1565             $this->outgoing_payload = "POST $this->path HTTP/$this->protocol_version\r\n";
1566         }
1567                 
1568         $this->outgoing_payload .=
1569             "User-Agent: $this->title v$this->version\r\n".
1570             "Host: ".$this->host."\r\n".
1571             $encoding_headers.
1572             $credentials.
1573             "Content-Type: text/xml; charset=\"$this->soap_defencoding\"\r\n".
1574             "Content-Length: ".strlen($data)."\r\n".
1575             "SOAPAction: \"$this->soapaction\""."\r\n\r\n".
1576             $data;
1577         
1578         // set payload
1579         curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
1580         //$t->setMarker('set curl options, executing...');
1581         // send and receive
1582         $this->incoming_payload = curl_exec($ch);
1583         //$t->setMarker('executed transfer');
1584         $data = $this->incoming_payload;
1585
1586         $cErr = curl_error($ch);
1587
1588         if($cErr != ''){
1589             $err = 'cURL ERROR: '.curl_errno($ch).': '.$cErr.'<br>';
1590             foreach(curl_getinfo($ch) as $k => $v){
1591                 $err .= "$k: $v<br>";
1592             }
1593             $this->setError($err);
1594             curl_close($ch);
1595             return false;
1596         } else {
1597             //echo '<pre>';
1598             //var_dump(curl_getinfo($ch));
1599             //echo '</pre>';
1600         }
1601         // close curl
1602         curl_close($ch);
1603         //$t->setMarker('closed curl');
1604                 
1605         // remove 100 header
1606         if(ereg('^HTTP/1.1 100',$data)){
1607             if($pos = strpos($data,"\r\n\r\n") ){
1608                 $data = ltrim(substr($data,$pos));
1609             } elseif($pos = strpos($data,"\n\n") ){
1610                 $data = ltrim(substr($data,$pos));
1611             }
1612         }//
1613                 
1614         // separate content from HTTP headers
1615         if( $pos = strpos($data,"\r\n\r\n") ){
1616             $lb = "\r\n";
1617         } elseif( $pos = strpos($data,"\n\n") ){
1618             $lb = "\n";
1619         } else {
1620             $this->setError('no proper separation of headers and document');
1621             return false;
1622         }
1623         $header_data = trim(substr($data,0,$pos));
1624         $header_array = explode($lb,$header_data);
1625         $data = ltrim(substr($data,$pos));
1626         $this->debug('found proper separation of headers and document');
1627         $this->debug('cleaned data, stringlen: '.strlen($data));
1628         // clean headers
1629         foreach($header_array as $header_line){
1630             $arr = explode(':',$header_line);
1631             $headers[trim($arr[0])] = trim($arr[1]);
1632         }
1633         if(strlen($data) == 0){
1634             $this->debug('no data after headers!');
1635             $this->setError('no data present after HTTP headers.');
1636             return false;
1637         }
1638                 
1639         // decode transfer-encoding
1640         if($headers['Transfer-Encoding'] == 'chunked'){
1641             if(!$data = $this->decodeChunked($data)){
1642                 $this->setError('Decoding of chunked data failed');
1643                 return false;
1644             }
1645         }
1646         // decode content-encoding
1647         if($headers['Content-Encoding'] != ''){
1648             if($headers['Content-Encoding'] == 'deflate' || $headers['Content-Encoding'] == 'gzip'){
1649                 // if decoding works, use it. else assume data wasn't gzencoded
1650                 if(function_exists('gzinflate')){
1651                     if($headers['Content-Encoding'] == 'deflate' && $degzdata = @gzinflate($data)){
1652                         $data = $degzdata;
1653                     } elseif($headers['Content-Encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))){
1654                         $data = $degzdata;
1655                     } else {
1656                         $this->setError('Errors occurred when trying to decode the data');
1657                     }
1658                 } else {
1659                     $this->setError('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
1660                 }
1661             }
1662         }
1663         // set decoded payload
1664         $this->incoming_payload = $header_data."\r\n\r\n".$data;
1665         return $data;
1666     }
1667         
1668     /**
1669      * if authenticating, set user credentials here
1670      *
1671      * @param    string $user
1672      * @param    string $pass
1673      * @access   public
1674      */
1675     function setCredentials($username, $password) {
1676         $this->username = $username;
1677         $this->password = $password;
1678     }
1679         
1680     /**
1681      * set the soapaction value
1682      *
1683      * @param    string $soapaction
1684      * @access   public
1685      */
1686     function setSOAPAction($soapaction) {
1687         $this->soapaction = $soapaction;
1688     }
1689         
1690     /**
1691      * use http encoding
1692      *
1693      * @param    string $enc encoding style. supported values: gzip, deflate, or both
1694      * @access   public
1695      */
1696     function setEncoding($enc='gzip, deflate'){
1697         $this->encoding = $enc;
1698         $this->protocol_version = '1.1';
1699     }
1700         
1701     /**
1702      * set proxy info here
1703      *
1704      * @param    string $proxyhost
1705      * @param    string $proxyport
1706      * @access   public
1707      */
1708     function setProxy($proxyhost, $proxyport) {
1709         $this->proxyhost = $proxyhost;
1710         $this->proxyport = $proxyport;
1711     }
1712         
1713     /**
1714      * decode a string that is encoded w/ "chunked' transfer encoding
1715      * as defined in RFC2068 19.4.6
1716      *
1717      * @param    string $buffer
1718      * @returns string
1719      * @access   public
1720      */
1721     function decodeChunked($buffer){
1722         // length := 0
1723         $length = 0;
1724         $new = '';
1725         
1726         // read chunk-size, chunk-extension (if any) and CRLF
1727         // get the position of the linebreak
1728         $chunkend = strpos($buffer,"\r\n") + 2;
1729         $temp = substr($buffer,0,$chunkend);
1730         $chunk_size = hexdec( trim($temp) );
1731         $chunkstart = $chunkend;
1732         // while (chunk-size > 0) {
1733         while ($chunk_size > 0) {
1734                         
1735             $chunkend = strpos( $buffer, "\r\n", $chunkstart + $chunk_size);
1736             
1737             // Just in case we got a broken connection
1738             if ($chunkend == FALSE) {
1739                 $chunk = substr($buffer,$chunkstart);
1740                 // append chunk-data to entity-body
1741                 $new .= $chunk;
1742                 $length += strlen($chunk);
1743                 break;
1744             }
1745                         
1746             // read chunk-data and CRLF
1747             $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
1748             // append chunk-data to entity-body
1749             $new .= $chunk;
1750             // length := length + chunk-size
1751             $length += strlen($chunk);
1752             // read chunk-size and CRLF
1753             $chunkstart = $chunkend + 2;
1754                         
1755             $chunkend = strpos($buffer,"\r\n",$chunkstart)+2;
1756             if ($chunkend == FALSE) {
1757                 break; //Just in case we got a broken connection
1758             }
1759             $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
1760             $chunk_size = hexdec( trim($temp) );
1761             $chunkstart = $chunkend;
1762         }
1763         // Update headers
1764         //$this->Header['content-length'] = $length;
1765         //unset($this->Header['transfer-encoding']);
1766         return $new;
1767     }
1768
1769 }
1770
1771 ?><?php
1772
1773 /**
1774 *
1775 * soap_server allows the user to create a SOAP server
1776 * that is capable of receiving messages and returning responses
1777 *
1778 * NOTE: WSDL functionality is experimental
1779 *
1780 * @author   Dietrich Ayala <dietrich@ganx4.com>
1781 * @version  v 0.6.3
1782 * @access   public
1783 */
1784 class soap_server extends nusoap_base {
1785
1786     var $service = ''; // service name
1787     var $operations = array(); // assoc array of operations => opData
1788     var $responseHeaders = false;
1789     var $headers = '';
1790     var $request = '';
1791     var $charset_encoding = 'UTF-8';
1792     var $fault = false;
1793     var $result = 'successful';
1794     var $wsdl = false;
1795     var $externalWSDLURL = false;
1796     var $debug_flag = true;
1797         
1798     /**
1799      * constructor
1800      * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
1801      *
1802      * @param string $wsdl path or URL to a WSDL file
1803      * @access   public
1804      */
1805     function soap_server($wsdl=false){
1806         
1807         // turn on debugging?
1808         global $debug;
1809         if(isset($debug)){
1810             $this->debug_flag = 1;
1811         }
1812
1813         // wsdl
1814         if($wsdl){
1815             $this->wsdl = new wsdl($wsdl);
1816             $this->externalWSDLURL = $wsdl;
1817             if($err = $this->wsdl->getError()){
1818                 die('WSDL ERROR: '.$err);
1819             }
1820         }
1821     }
1822     
1823     /**
1824      * processes request and returns response
1825      *
1826      * @param    string $data usually is the value of $HTTP_RAW_POST_DATA
1827      * @access   public
1828      */
1829     function service($data){
1830         // print wsdl
1831         global $QUERY_STRING, $HTTP_SERVER_VARS;
1832         if(isset($HTTP_SERVER_VARS['QUERY_STRING'])){
1833             $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
1834         } elseif(isset($GLOBALS['QUERY_STRING'])){
1835             $qs = $GLOBALS['QUERY_STRING'];
1836         } elseif(isset($QUERY_STRING) && $QUERY_STRING != ''){
1837             $qs = $QUERY_STRING;
1838         }
1839         // gen wsdl
1840         if(isset($qs) && ereg('wsdl', $qs) ){
1841             if($this->externalWSDLURL){
1842                 header('Location: '.$this->externalWSDLURL);
1843                 exit();
1844             } else {
1845                 header("Content-Type: text/xml\r\n");
1846                 print $this->wsdl->serialize();
1847                 exit();
1848             }
1849         }
1850                 
1851         // print web interface
1852         if($data == '' && $this->wsdl){
1853             print $this->webDescription();
1854         } else {
1855                         
1856             // $response is the serialized response message
1857             $response = $this->parse_request($data);
1858             $this->debug('server sending...');
1859             $payload = $response;
1860             // add debug data if in debug mode
1861             if(isset($this->debug_flag) && $this->debug_flag == 1){
1862                 $payload .= "<!--\n".str_replace('--','- -',$this->debug_str)."\n-->";
1863             }
1864             // print headers
1865             if($this->fault){
1866                 $header[] = "HTTP/1.0 500 Internal Server Error\r\n";
1867                 $header[] = "Status: 500 Internal Server Error\r\n";
1868             } else {
1869                 $header[] = "Status: 200 OK\r\n";
1870             }
1871             $header[] = "Server: $this->title Server v$this->version\r\n";
1872             $header[] = "Connection: Close\r\n";
1873             $header[] = "Content-Type: text/xml; charset=$this->charset_encoding\r\n";
1874             $header[] = "Content-Length: ".strlen($payload)."\r\n\r\n";
1875             reset($header);
1876             foreach($header as $hdr){
1877                 header($hdr);
1878             }
1879             $this->response = join("\r\n",$header).$payload;
1880             print $payload;
1881         }
1882     }
1883
1884     /**
1885      * parses request and posts response
1886      *
1887      * @param    string $data XML string
1888      * @return  string XML response msg
1889      * @access   private
1890      */
1891     function parse_request($data='') {
1892         if (!isset($_SERVER))
1893             $_SERVER =& $GLOBALS['HTTP_SERVER_VARS'];
1894         $this->debug('entering parseRequest() on '.date('H:i Y-m-d'));
1895         $dump = '';
1896         // get headers
1897         if(function_exists('getallheaders')){
1898             $this->headers = getallheaders();
1899             foreach($this->headers as $k=>$v){
1900                 $dump .= "$k: $v\r\n";
1901                 $this->debug("$k: $v");
1902             }
1903             // get SOAPAction header
1904             if(isset($this->headers['SOAPAction'])){
1905                 $this->SOAPAction = str_replace('"','',$this->headers['SOAPAction']);
1906             }
1907             // get the character encoding of the incoming request
1908             if(strpos($this->headers['Content-Type'],'=')){
1909                 $enc = str_replace('"','',substr(strstr($this->headers["Content-Type"],'='),1));
1910                 if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
1911                     $this->xml_encoding = $enc;
1912                 } else {
1913                     $this->xml_encoding = 'us-ascii';
1914                 }
1915             }
1916             $this->debug('got encoding: '.$this->charset_encoding);
1917         } elseif(is_array($_SERVER)){
1918             $this->headers['User-Agent'] = $_SERVER['HTTP_USER_AGENT'];
1919             $this->SOAPAction = isset($_SERVER['SOAPAction']) ? $_SERVER['SOAPAction'] : '';
1920         }
1921         $this->request = $dump."\r\n\r\n".$data;
1922         // parse response, get soap parser obj
1923         $parser = new soap_parser($data,$this->charset_encoding);
1924         // if fault occurred during message parsing
1925         if($err = $parser->getError()){
1926             // parser debug
1927             $this->debug("parser debug: \n".$parser->debug_str);
1928             $this->result = 'fault: error in msg parsing: '.$err;
1929             $this->fault('Server',"error in msg parsing:\n".$err);
1930             // return soapresp
1931             return $this->fault->serialize();
1932             // else successfully parsed request into soapval object
1933         } else {
1934             // get/set methodname
1935             $this->methodname = $parser->root_struct_name;
1936             $this->debug('method name: '.$this->methodname);
1937             // does method exist?
1938             if(!function_exists($this->methodname)){
1939                 // "method not found" fault here
1940                 $this->debug("method '$this->methodname' not found!");
1941                 $this->debug("parser debug: \n".$parser->debug_str);
1942                 $this->result = 'fault: method not found';
1943                 $this->fault('Server',"method '$this->methodname' not defined in service '$this->service'");
1944                 return $this->fault->serialize();
1945             }
1946             if($this->wsdl){
1947                 if(!$this->opData = $this->wsdl->getOperationData($this->methodname)){
1948                     //if(
1949                     $this->fault('Server',"Operation '$this->methodname' is not defined in the WSDL for this service");
1950                     return $this->fault->serialize();
1951                 }
1952             }
1953             $this->debug("method '$this->methodname' exists");
1954             // evaluate message, getting back parameters
1955             $this->debug('calling parser->get_response()');
1956             $request_data = $parser->get_response();
1957             // parser debug
1958             $this->debug("parser debug: \n".$parser->debug_str);
1959             // verify that request parameters match the method's signature
1960             if($this->verify_method($this->methodname,$request_data)){
1961                 // if there are parameters to pass
1962                 $this->debug('params var dump '.$this->varDump($request_data));
1963                 if($request_data){
1964                     $this->debug("calling '$this->methodname' with params");
1965                     if (! function_exists('call_user_func_array')) {
1966                         $this->debug('calling method using eval()');
1967                         $funcCall = $this->methodname.'(';
1968                         foreach($request_data as $param) {
1969                             $funcCall .= "\"$param\",";
1970                         }
1971                         $funcCall = substr($funcCall, 0, -1).')';
1972                         $this->debug('function call:<br>'.$funcCall);
1973                         @eval("\$method_response = $funcCall;");
1974                     } else {
1975                         $this->debug('calling method using call_user_func_array()');
1976                         $method_response = call_user_func_array("$this->methodname",$request_data);
1977                     }
1978                     $this->debug('response var dump'.$this->varDump($method_response));
1979                 } else {
1980                     // call method w/ no parameters
1981                     $this->debug("calling $this->methodname w/ no params");
1982                     $m = $this->methodname;
1983                     $method_response = @$m();
1984                 }
1985                 $this->debug("done calling method: $this->methodname, received $method_response of type".gettype($method_response));
1986                 // if we got nothing back. this might be ok (echoVoid)
1987                 if(isset($method_response) && $method_response != '' || is_bool($method_response)) {
1988                     // if fault
1989                     if(get_class($method_response) == 'soap_fault'){
1990                         $this->debug('got a fault object from method');
1991                         $this->fault = $method_response;
1992                         return $method_response->serialize();
1993                         // if return val is soapval object
1994                     } elseif(get_class($method_response) == 'soapval'){
1995                         $this->debug('got a soapval object from method');
1996                         $return_val = $method_response->serialize();
1997                         // returned other
1998                     } else {
1999                         $this->debug('got a(n) '.gettype($method_response).' from method');
2000                         $this->debug('serializing return value');
2001                         if($this->wsdl){
2002                             // weak attempt at supporting multiple output params
2003                             if(sizeof($this->opData['output']['parts']) > 1){
2004                                 $opParams = $method_response;
2005                             } else {
2006                                 $opParams = array($method_response);
2007                             }
2008                             $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
2009                         } else {
2010                             $return_val = $this->serialize_val($method_response);
2011                         }
2012                     }
2013                     $this->debug('return val:'.$this->varDump($return_val));
2014                 } else {
2015                     $return_val = '';
2016                     $this->debug('got no response from method');
2017                 }
2018                 $this->debug('serializing response');
2019                 $payload = '<'.$this->methodname."Response>".$return_val.'</'.$this->methodname."Response>";
2020                 $this->result = 'successful';
2021                 if($this->wsdl){
2022                     //if($this->debug_flag){
2023                     $this->debug("WSDL debug data:\n".$this->wsdl->debug_str);
2024                     //  }
2025                     // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
2026                     return $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style']);
2027                 } else {
2028                     return $this->serializeEnvelope($payload,$this->responseHeaders);
2029                 }
2030             } else {
2031                 // debug
2032                 $this->debug('ERROR: request not verified against method signature');
2033                 $this->result = 'fault: request failed validation against method signature';
2034                 // return fault
2035                 $this->fault('Server',"Operation '$this->methodname' not defined in service.");
2036                 return $this->fault->serialize();
2037             }
2038         }
2039     }
2040
2041     /**
2042      * takes the value that was created by parsing the request
2043      * and compares to the method's signature, if available.
2044      *
2045      * @param   mixed
2046      * @return  boolean
2047      * @access   private
2048      */
2049     function verify_method($operation,$request){
2050         if(isset($this->wsdl) && is_object($this->wsdl)){
2051             if($this->wsdl->getOperationData($operation)){
2052                 return true;
2053             }
2054         } elseif(isset($this->operations[$operation])){
2055             return true;
2056         }
2057         return false;
2058     }
2059
2060     /**
2061      * add a method to the dispatch map
2062      *
2063      * @param    string $methodname
2064      * @param    string $in array of input values
2065      * @param    string $out array of output values
2066      * @access   public
2067      */
2068     function add_to_map($methodname,$in,$out){
2069         $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
2070     }
2071
2072     /**
2073      * register a service with the server
2074      *
2075      * @param    string $methodname
2076      * @param    string $in assoc array of input values: key = param name, value = param type
2077      * @param    string $out assoc array of output values: key = param name, value = param type
2078      * @param   string $namespace
2079      * @param   string $soapaction
2080      * @param   string $style (rpc|literal)
2081      * @access   public
2082      */
2083     function register($name,$in=false,$out=false,$namespace=false,$soapaction=false,$style=false,$use=false){
2084         if($this->externalWSDLURL){
2085             die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
2086         }
2087         if(false == $in) {
2088         }
2089         if(false == $out) {
2090         }
2091         if(false == $namespace) {
2092         }
2093         if(false == $soapaction) {
2094             global $SERVER_NAME, $SCRIPT_NAME;
2095             $soapaction = "http://$SERVER_NAME$SCRIPT_NAME";
2096         }
2097         if(false == $style) {
2098             $style = "rpc";
2099         }
2100         if(false == $use) {
2101             $use = "encoded";
2102         }
2103                 
2104         $this->operations[] = array($name => array());
2105         $this->operations[$name] = array(
2106                                          'name' => $name,
2107                                          'in' => $in,
2108                                          'out' => $out,
2109                                          'namespace' => $namespace,
2110                                          'soapaction' => $soapaction,
2111                                          'style' => $style);
2112         if($this->wsdl){
2113             $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use);
2114         }
2115         return true;
2116     }
2117
2118     /**
2119      * create a fault. this also acts as a flag to the server that a fault has occured.
2120      *
2121      * @param   string faultcode
2122      * @param   string faultactor
2123      * @param   string faultstring
2124      * @param   string faultdetail
2125      * @access   public
2126      */
2127     function fault($faultcode,$faultactor,$faultstring='',$faultdetail=''){
2128         $this->fault = new soap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
2129     }
2130
2131     /**
2132     * prints html description of services
2133     *
2134     * @access private
2135     */
2136     function webDescription(){
2137                 $b = '
2138                 <html><head><title>NuSOAP: '.$this->wsdl->serviceName.'</title>
2139                 <style type="text/css">
2140                     body    { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
2141                     p       { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
2142                     pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
2143                     ul      { margin-top: 10px; margin-left: 20px; }
2144                     li      { list-style-type: none; margin-top: 10px; color: #000000; }
2145                     .content{
2146                         margin-left: 0px; padding-bottom: 2em; }
2147                     .nav {
2148                         padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
2149                         margin-top: 10px; margin-left: 0px; color: #000000;
2150                         background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
2151                     .title {
2152                         font-family: arial; font-size: 26px; color: #ffffff;
2153                         background-color: #999999; width: 105%; margin-left: 0px;
2154                         padding-top: 10px; padding-bottom: 10px; padding-left: 15px;}
2155                     .hidden {
2156                         position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
2157                         font-family: arial; overflow: hidden; width: 600;
2158                         padding: 20px; font-size: 10px; background-color: #999999;
2159                         layer-background-color:#FFFFFF; }
2160                     a,a:active  { color: charcoal; font-weight: bold; }
2161                     a:visited   { color: #666666; font-weight: bold; }
2162                     a:hover     { color: cc3300; font-weight: bold; }
2163                 </style>
2164                 <script language="JavaScript" type="text/javascript">
2165                 <!--
2166                 // POP-UP CAPTIONS...
2167                 function lib_bwcheck(){ //Browsercheck (needed)
2168                     this.ver=navigator.appVersion
2169                     this.agent=navigator.userAgent
2170                     this.dom=document.getElementById?1:0
2171                     this.opera5=this.agent.indexOf("Opera 5")>-1
2172                     this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
2173                     this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
2174                     this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
2175                     this.ie=this.ie4||this.ie5||this.ie6
2176                     this.mac=this.agent.indexOf("Mac")>-1
2177                     this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
2178                     this.ns4=(document.layers && !this.dom)?1:0;
2179                     this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
2180                     return this
2181                 }
2182                 var bw = new lib_bwcheck()
2183                 //Makes crossbrowser object.
2184                 function makeObj(obj){
2185                     this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
2186                     if(!this.evnt) return false
2187                     this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
2188                     this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
2189                     this.writeIt=b_writeIt;
2190                     return this
2191                 }
2192                 // A unit of measure that will be added when setting the position of a layer.
2193                 //var px = bw.ns4||window.opera?"":"px";
2194                 function b_writeIt(text){
2195                     if (bw.ns4){this.wref.write(text);this.wref.close()}
2196                     else this.wref.innerHTML = text
2197                 }
2198                 //Shows the messages
2199                 var oDesc;
2200                 function popup(divid){
2201                     if(oDesc = new makeObj(divid)){
2202                         oDesc.css.visibility = "visible"
2203                     }
2204                 }
2205                 function popout(){ // Hides message
2206                     if(oDesc) oDesc.css.visibility = "hidden"
2207                 }
2208                 //-->
2209                 </script>
2210                 </head>
2211                 <body>
2212                 <div class=content>
2213                         <br><br>
2214                         <div class=title>'.$this->wsdl->serviceName.'</div>
2215                         <div class=nav>
2216                                 <p>View the <a href="'.$GLOBALS['PHP_SELF'].'?wsdl">WSDL</a> for the service.
2217                                 Click on an operation name to view it&apos;s details.</p>
2218                                 <ul>';
2219                                 foreach($this->wsdl->getOperations() as $op => $data){
2220                                     $b .= "<li><a href='#' onclick=\"popup('$op')\">$op</a></li>";
2221                                     // create hidden div
2222                                     $b .= "<div id='$op' class='hidden'>
2223                                     <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
2224                                     foreach($data as $donnie => $marie){ // loop through opdata
2225                                         if($donnie == 'input' || $donnie == 'output'){ // show input/output data
2226                                             $b .= "<font color='white'>".ucfirst($donnie).':</font><br>';
2227                                             foreach($marie as $captain => $tenille){ // loop through data
2228                                                 if($captain == 'parts'){ // loop thru parts
2229                                                     $b .= "&nbsp;&nbsp;$captain:<br>";
2230                                                     //if(is_array($tenille)){
2231                                                     foreach($tenille as $joanie => $chachi){
2232                                                         $b .= "&nbsp;&nbsp;&nbsp;&nbsp;$joanie: $chachi<br>";
2233                                                     }
2234                                                     //}
2235                                                 } else {
2236                                                     $b .= "&nbsp;&nbsp;$captain: $tenille<br>";
2237                                                 }
2238                                             }
2239                                         } else {
2240                                             $b .= "<font color='white'>".ucfirst($donnie).":</font> $marie<br>";
2241                                         }
2242                                     }
2243                                     $b .= '</div>';
2244                                 }
2245                                 $b .= '
2246                                 <ul>
2247                         </div>
2248                 </div></body></html>';
2249                 return $b;
2250     }
2251
2252     /**
2253     * sets up wsdl object
2254     * this acts as a flag to enable internal WSDL generation
2255     * NOTE: NOT FUNCTIONAL
2256     *
2257     * @param string $serviceName, name of the service
2258     * @param string $namespace, tns namespace
2259     */
2260     function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http') {
2261         if (!isset($_SERVER))
2262             $_SERVER =& $GLOBALS['HTTP_SERVER_VARS'];
2263         $SERVER_NAME = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : $GLOBALS['SERVER_NAME'];
2264         $SCRIPT_NAME = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : $GLOBALS['SCRIPT_NAME'];
2265         if(false == $namespace) {
2266             $namespace = "http://$SERVER_NAME/soap/$serviceName";
2267         }
2268         
2269         if(false == $endpoint) {
2270             $endpoint = "http://$SERVER_NAME$SCRIPT_NAME";
2271         }
2272         
2273         $this->wsdl = new wsdl;
2274         $this->wsdl->serviceName = $serviceName;
2275         $this->wsdl->endpoint = $endpoint;
2276         $this->wsdl->namespaces['tns'] = $namespace;
2277         $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
2278         $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
2279         $this->wsdl->bindings[$serviceName.'Binding'] = array(
2280                                                               'name'=>$serviceName.'Binding',
2281                                                               'style'=>$style,
2282                                                               'transport'=>$transport,
2283                                                               'portType'=>$serviceName.'PortType');
2284         $this->wsdl->ports[$serviceName.'Port'] = array(
2285                                                         'binding'=>$serviceName.'Binding',
2286                                                         'location'=>$endpoint,
2287                                                         'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');
2288     }
2289 }
2290
2291 ?><?php
2292
2293 /**
2294  * parses a WSDL file, allows access to it's data, other utility methods
2295  * 
2296  * @author   Dietrich Ayala <dietrich@ganx4.com>
2297  * @version  v 0.6.3
2298  * @access public 
2299  */
2300 class wsdl extends XMLSchema {
2301     var $wsdl; 
2302     // define internal arrays of bindings, ports, operations, messages, etc.
2303     var $message = array();
2304     var $complexTypes = array();
2305     var $messages = array();
2306     var $currentMessage;
2307     var $currentOperation;
2308     var $portTypes = array();
2309     var $currentPortType;
2310     var $bindings = array();
2311     var $currentBinding;
2312     var $ports = array();
2313     var $currentPort;
2314     var $opData = array();
2315     var $status = '';
2316     var $documentation = false;
2317     var $endpoint = ''; 
2318     // array of wsdl docs to import
2319     var $import = array(); 
2320     // parser vars
2321     var $parser;
2322     var $position = 0;
2323     var $depth = 0;
2324     var $depth_array = array();
2325     var $usedNamespaces = array();
2326     // for getting wsdl
2327     var $proxyhost = '';
2328     var $proxyport = '';
2329     
2330     /**
2331      * constructor
2332      * 
2333      * @param string $wsdl WSDL document URL
2334      * @access public 
2335      */
2336     function wsdl($wsdl = '',$proxyhost=false,$proxyport=false){
2337         $this->wsdl = $wsdl;
2338         $this->proxyhost = $proxyhost;
2339         $this->proxyport = $proxyport;
2340         
2341         // parse wsdl file
2342         if ($wsdl != "") {
2343             $this->debug('initial wsdl file: ' . $wsdl);
2344             $this->parseWSDL($wsdl);
2345         } 
2346         // imports
2347         if (sizeof($this->import) > 0) {
2348             foreach($this->import as $ns => $url) {
2349                 $this->debug('importing wsdl from ' . $url);
2350                 $this->parseWSDL($url);
2351             } 
2352         } 
2353     } 
2354
2355     /**
2356      * parses the wsdl document
2357      * 
2358      * @param string $wsdl path or URL
2359      * @access private 
2360      */
2361     function parseWSDL($wsdl = '')
2362     {
2363         if ($wsdl == '') {
2364             $this->debug('no wsdl passed to parseWSDL()!!');
2365             $this->setError('no wsdl passed to parseWSDL()!!');
2366             return false;
2367         } 
2368
2369         $this->debug('getting ' . $wsdl);
2370         
2371         // parse $wsdl for url format
2372         $wsdl_props = parse_url($wsdl);
2373
2374         if (isset($wsdl_props['host'])) {
2375                 
2376             // get wsdl
2377             $tr = new soap_transport_http($wsdl);
2378             $tr->request_method = 'GET';
2379             $tr->useSOAPAction = false;
2380             if($this->proxyhost && $this->proxyport){
2381                 $tr->setProxy($this->proxyhost,$this->proxyport);
2382             }
2383             if (isset($wsdl_props['user'])) {
2384                 $tr->setCredentials($wsdl_props['user'],$wsdl_props['pass']);
2385             }
2386             $wsdl_string = $tr->send('');
2387             // catch errors
2388             if($err = $tr->getError() ){
2389                 $this->debug('HTTP ERROR: '.$err);
2390                 $this->setError('HTTP ERROR: '.$err);
2391                 return false;
2392             }
2393             unset($tr);
2394             /* $wsdl seems to be a valid url, not a file path, do an fsockopen/HTTP GET
2395             $fsockopen_timeout = 30; 
2396             // check if a port value is supplied in url
2397             if (isset($wsdl_props['port'])) {
2398                 // yes
2399                 $wsdl_url_port = $wsdl_props['port'];
2400             } else {
2401                 // no, assign port number, based on url protocol (scheme)
2402                 switch ($wsdl_props['scheme']) {
2403                     case ('https') :
2404                     case ('ssl') :
2405                     case ('tls') :
2406                         $wsdl_url_port = 443;
2407                         break;
2408                     case ('http') :
2409                     default :
2410                         $wsdl_url_port = 80;
2411                 } 
2412             } 
2413             // FIXME: should implement SSL/TLS support here if CURL is available
2414             if ($fp = fsockopen($wsdl_props['host'], $wsdl_url_port, $fsockopen_errnum, $fsockopen_errstr, $fsockopen_timeout)) {
2415                 // perform HTTP GET for WSDL file
2416                 // 10.9.02 - added poulter fix for doing this properly
2417                 $sHeader = "GET " . $wsdl_props['path'];
2418                 if (isset($wsdl_props['query'])) {
2419                     $sHeader .= "?" . $wsdl_props['query'];
2420                 } 
2421                 $sHeader .= " HTTP/1.0\r\n";
2422
2423                 if (isset($wsdl_props['user'])) {
2424                     $base64auth = base64_encode($wsdl_props['user'] . ":" . $wsdl_props['pass']);
2425                     $sHeader .= "Authorization: Basic $base64auth\r\n";
2426                 }
2427                                 $sHeader .= "Host: " . $wsdl_props['host'] . ( isset($wsdl_props['port']) ? ":".$wsdl_props['port'] : "" ) . "\r\n\r\n";
2428                 fputs($fp, $sHeader);
2429
2430                 while (fgets($fp, 1024) != "\r\n") {
2431                     // do nothing, just read/skip past HTTP headers
2432                     // FIXME: should actually detect HTTP response code, and act accordingly if error
2433                     // HTTP headers end with extra CRLF before content body
2434                 } 
2435                 // read in WSDL just like regular fopen()
2436                 $wsdl_string = '';
2437                 while ($data = fread($fp, 32768)) {
2438                     $wsdl_string .= $data;
2439                 } 
2440                 fclose($fp);
2441             } else {
2442                 $this->setError('bad path to WSDL file.');
2443                 return false;
2444             }
2445             */
2446         } else {
2447             // $wsdl seems to be a non-url file path, do the regular fopen
2448             if ($fp = @fopen($wsdl, 'r')) {
2449                 $wsdl_string = '';
2450                 while ($data = fread($fp, 32768)) {
2451                     $wsdl_string .= $data;
2452                 } 
2453                 fclose($fp);
2454             } else {
2455                 $this->setError('bad path to WSDL file.');
2456                 return false;
2457             } 
2458         }
2459         // end new code added
2460         // Create an XML parser.
2461         $this->parser = xml_parser_create(); 
2462         // Set the options for parsing the XML data.
2463         // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
2464         xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); 
2465         // Set the object for the parser.
2466         xml_set_object($this->parser, $this); 
2467         // Set the element handlers for the parser.
2468         xml_set_element_handler($this->parser, 'start_element', 'end_element');
2469         xml_set_character_data_handler($this->parser, 'character_data');
2470         // Parse the XML file.
2471         if (!xml_parse($this->parser, $wsdl_string, true)) {
2472             // Display an error message.
2473             $errstr = sprintf(
2474                               'XML error on line %d: %s',
2475                               xml_get_current_line_number($this->parser),
2476                               xml_error_string(xml_get_error_code($this->parser))
2477                               );
2478             $this->debug('XML parse error: ' . $errstr);
2479             $this->setError('Parser error: ' . $errstr);
2480             return false;
2481         } 
2482         // free the parser
2483         xml_parser_free($this->parser);
2484         // catch wsdl parse errors
2485         if($this->getError()){
2486             return false;
2487         }
2488         // add new data to operation data
2489         foreach($this->bindings as $binding => $bindingData) {
2490             if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
2491                 foreach($bindingData['operations'] as $operation => $data) {
2492                     $this->debug('post-parse data gathering for ' . $operation);
2493                     $this->bindings[$binding]['operations'][$operation]['input'] = 
2494                                                 isset($this->bindings[$binding]['operations'][$operation]['input']) ? 
2495                                                 array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) :
2496                                                 $this->portTypes[ $bindingData['portType'] ][$operation]['input'];
2497                     $this->bindings[$binding]['operations'][$operation]['output'] = 
2498                                                 isset($this->bindings[$binding]['operations'][$operation]['output']) ?
2499                                                 array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) :
2500                                                 $this->portTypes[ $bindingData['portType'] ][$operation]['output'];
2501                     if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){
2502                                                 $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ];
2503                                         }
2504                                         if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){
2505                                 $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ];
2506                     }
2507                                         if (isset($bindingData['style'])) {
2508                         $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
2509                     }
2510                     $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
2511                     $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : '';
2512                     $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
2513                 } 
2514             } 
2515         }
2516         return true;
2517     } 
2518
2519     /**
2520      * start-element handler
2521      * 
2522      * @param string $parser XML parser object
2523      * @param string $name element name
2524      * @param string $attrs associative array of attributes
2525      * @access private 
2526      */
2527     function start_element($parser, $name, $attrs)
2528     {
2529         if ($this->status == 'schema' || ereg('schema$', $name)) {
2530             // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
2531             $this->status = 'schema';
2532             $this->schemaStartElement($parser, $name, $attrs);
2533         } else {
2534             // position in the total number of elements, starting from 0
2535             $pos = $this->position++;
2536             $depth = $this->depth++; 
2537             // set self as current value for this depth
2538             $this->depth_array[$depth] = $pos;
2539             $this->message[$pos] = array('cdata' => ''); 
2540             // get element prefix
2541             if (ereg(':', $name)) {
2542                 // get ns prefix
2543                 $prefix = substr($name, 0, strpos($name, ':')); 
2544                 // get ns
2545                 $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : ''; 
2546                 // get unqualified name
2547                 $name = substr(strstr($name, ':'), 1);
2548             } 
2549
2550             if (count($attrs) > 0) {
2551                 foreach($attrs as $k => $v) {
2552                     // if ns declarations, add to class level array of valid namespaces
2553                     if (ereg("^xmlns", $k)) {
2554                         if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
2555                             $this->namespaces[$ns_prefix] = $v;
2556                         } else {
2557                             $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
2558                         } 
2559                         if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema') {
2560                             $this->XMLSchemaVersion = $v;
2561                             $this->namespaces['xsi'] = $v . '-instance';
2562                         } 
2563                     } //  
2564                     // expand each attribute
2565                     $k = strpos($k, ':') ? $this->expandQname($k) : $k;
2566                     if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
2567                         $v = strpos($v, ':') ? $this->expandQname($v) : $v;
2568                     } 
2569                     $eAttrs[$k] = $v;
2570                 } 
2571                 $attrs = $eAttrs;
2572             } else {
2573                 $attrs = array();
2574             } 
2575             // find status, register data
2576             switch ($this->status) {
2577                 case 'message':
2578                     if ($name == 'part') {
2579                         if (isset($attrs['type'])) {
2580                                     $this->debug("msg " . $this->currentMessage . ": found part $attrs[name]: " . implode(',', $attrs));
2581                                     $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
2582                                 } 
2583                                     if (isset($attrs['element'])) {
2584                                         $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'];
2585                                     } 
2586                                 } 
2587                                 break;
2588                             case 'portType':
2589                                 switch ($name) {
2590                                     case 'operation':
2591                                         $this->currentPortOperation = $attrs['name'];
2592                                         $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
2593                                         if (isset($attrs['parameterOrder'])) {
2594                                                 $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
2595                                                 } 
2596                                                 break;
2597                                             case 'documentation':
2598                                                 $this->documentation = true;
2599                                                 break; 
2600                                             // merge input/output data
2601                                             default:
2602                                                 $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
2603                                                 $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
2604                                                 break;
2605                                         } 
2606                                 break;
2607                                 case 'binding':
2608                                     switch ($name) {
2609                                         case 'binding': 
2610                                             // get ns prefix
2611                                             if (isset($attrs['style'])) {
2612                                             $this->bindings[$this->currentBinding]['prefix'] = $prefix;
2613                                                 } 
2614                                                 $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
2615                                                 break;
2616                                                 case 'header':
2617                                                     $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
2618                                                     break;
2619                                                 case 'operation':
2620                                                     if (isset($attrs['soapAction'])) {
2621                                                         $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
2622                                                     } 
2623                                                     if (isset($attrs['style'])) {
2624                                                         $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
2625                                                     } 
2626                                                     if (isset($attrs['name'])) {
2627                                                         $this->currentOperation = $attrs['name'];
2628                                                         $this->debug("current binding operation: $this->currentOperation");
2629                                                         $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
2630                                                         $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
2631                                                         $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
2632                                                     } 
2633                                                     break;
2634                                                 case 'input':
2635                                                     $this->opStatus = 'input';
2636                                                     break;
2637                                                 case 'output':
2638                                                     $this->opStatus = 'output';
2639                                                     break;
2640                                                 case 'body':
2641                                                     if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
2642                                                         $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
2643                                                     } else {
2644                                                         $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
2645                                                     } 
2646                                                     break;
2647                                         } 
2648                                         break;
2649                                 case 'service':
2650                                         switch ($name) {
2651                                             case 'port':
2652                                                 $this->currentPort = $attrs['name'];
2653                                                 $this->debug('current port: ' . $this->currentPort);
2654                                                 $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
2655                                         
2656                                                 break;
2657                                             case 'address':
2658                                                 $this->ports[$this->currentPort]['location'] = $attrs['location'];
2659                                                 $this->ports[$this->currentPort]['bindingType'] = $namespace;
2660                                                 $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace;
2661                                                 $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location'];
2662                                                 break;
2663                                         } 
2664                                         break;
2665                         } 
2666                 // set status
2667                 switch ($name) {
2668                         case "import":
2669                             if (isset($attrs['location'])) {
2670                                 $this->import[$attrs['namespace']] = $attrs['location'];
2671                                 } 
2672                                 break;
2673                         case 'types':
2674                                 $this->status = 'schema';
2675                                 break;
2676                         case 'message':
2677                                 $this->status = 'message';
2678                                 $this->messages[$attrs['name']] = array();
2679                                 $this->currentMessage = $attrs['name'];
2680                                 break;
2681                         case 'portType':
2682                                 $this->status = 'portType';
2683                                 $this->portTypes[$attrs['name']] = array();
2684                                 $this->currentPortType = $attrs['name'];
2685                                 break;
2686                         case "binding":
2687                                 if (isset($attrs['name'])) {
2688                                 // get binding name
2689                                         if (strpos($attrs['name'], ':')) {
2690                                         $this->currentBinding = $this->getLocalPart($attrs['name']);
2691                                         } else {
2692                                         $this->currentBinding = $attrs['name'];
2693                                         } 
2694                                         $this->status = 'binding';
2695                                         $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
2696                                         $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
2697                                 } 
2698                                 break;
2699                         case 'service':
2700                                 $this->serviceName = $attrs['name'];
2701                                 $this->status = 'service';
2702                                 $this->debug('current service: ' . $this->serviceName);
2703                                 break;
2704                         case 'definitions':
2705                                 foreach ($attrs as $name => $value) {
2706                                         $this->wsdl_info[$name] = $value;
2707                                 } 
2708                                 break;
2709                         } 
2710                 } 
2711         } 
2712
2713     /**
2714      * end-element handler
2715      * 
2716      * @param string $parser XML parser object
2717      * @param string $name element name
2718      * @access private 
2719      */
2720     function end_element($parser, $name){ 
2721         // unset schema status
2722         if (ereg('types$', $name) || ereg('schema$', $name)) {
2723             $this->status = "";
2724         } 
2725         if ($this->status == 'schema') {
2726             $this->schemaEndElement($parser, $name);
2727         } else {
2728             // bring depth down a notch
2729             $this->depth--;
2730         } 
2731         // end documentation
2732         if ($this->documentation) {
2733             $this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
2734             $this->documentation = false;
2735         } 
2736     } 
2737
2738     /**
2739      * element content handler
2740      * 
2741      * @param string $parser XML parser object
2742      * @param string $data element content
2743      * @access private 
2744      */
2745     function character_data($parser, $data)
2746     {
2747         $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
2748         if (isset($this->message[$pos]['cdata'])) {
2749             $this->message[$pos]['cdata'] .= $data;
2750         } 
2751         if ($this->documentation) {
2752             $this->documentation .= $data;
2753         } 
2754     } 
2755         
2756     function getBindingData($binding)
2757     {
2758         if (is_array($this->bindings[$binding])) {
2759             return $this->bindings[$binding];
2760         } 
2761     }
2762         
2763     /**
2764      * returns an assoc array of operation names => operation data
2765      * NOTE: currently only supports multiple services of differing binding types
2766      * This method needs some work
2767      * 
2768      * @param string $bindingType eg: soap, smtp, dime (only soap is currently supported)
2769      * @return array 
2770      * @access public 
2771      */
2772     function getOperations($bindingType = 'soap')
2773     {
2774         if ($bindingType == 'soap') {
2775             $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
2776         }
2777         // loop thru ports
2778         foreach($this->ports as $port => $portData) {
2779             // binding type of port matches parameter
2780             if ($portData['bindingType'] == $bindingType) {
2781                 // get binding
2782                 return $this->bindings[ $portData['binding'] ]['operations'];
2783             }
2784         } 
2785         return array();
2786     } 
2787         
2788     /**
2789      * returns an associative array of data necessary for calling an operation
2790      * 
2791      * @param string $operation , name of operation
2792      * @param string $bindingType , type of binding eg: soap
2793      * @return array 
2794      * @access public 
2795      */
2796     function getOperationData($operation, $bindingType = 'soap')
2797     {
2798         if ($bindingType == 'soap') {
2799             $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
2800         }
2801         // loop thru ports
2802         foreach($this->ports as $port => $portData) {
2803             // binding type of port matches parameter
2804             if ($portData['bindingType'] == $bindingType) {
2805                 // get binding
2806                 //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
2807                 foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) {
2808                     if ($operation == $bOperation) {
2809                         $opData = $this->bindings[ $portData['binding'] ]['operations'][$operation];
2810                         return $opData;
2811                     } 
2812                 } 
2813             }
2814         } 
2815     }
2816         
2817     /**
2818      * serialize the parsed wsdl
2819      * 
2820      * @return string , serialization of WSDL
2821      * @access public 
2822      */
2823     function serialize()
2824     {
2825         $xml = '<?xml version="1.0"?><definitions';
2826         foreach($this->namespaces as $k => $v) {
2827             $xml .= " xmlns:$k=\"$v\"";
2828         } 
2829         // 10.9.02 - add poulter fix for wsdl and tns declarations
2830         if (isset($this->namespaces['wsdl'])) {
2831             $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
2832         } 
2833         if (isset($this->namespaces['tns'])) {
2834             $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
2835         } 
2836         $xml .= '>'; 
2837         // imports
2838         if (sizeof($this->import) > 0) {
2839             foreach($this->import as $ns => $url) {
2840                 $xml .= '<import location="' . $url . '" namespace="' . $ns . '" />';
2841             } 
2842         } 
2843         // types
2844         if (count($this->complexTypes)>=1) {
2845             $xml .= '<types>';
2846             $xml .= $this->serializeSchema();
2847             $xml .= '</types>';
2848         } 
2849         // messages
2850         if (count($this->messages) >= 1) {
2851             foreach($this->messages as $msgName => $msgParts) {
2852                 $xml .= '<message name="' . $msgName . '">';
2853                 foreach($msgParts as $partName => $partType) {
2854                     // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
2855                     if (strpos($partType, ':')) {
2856                         $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
2857                     } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
2858                         // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
2859                         $typePrefix = 'xsd';
2860                     } else {
2861                         foreach($this->typemap as $ns => $types) {
2862                             if (isset($types[$partType])) {
2863                                 $typePrefix = $this->getPrefixFromNamespace($ns);
2864                             } 
2865                         } 
2866                         if (!isset($typePrefix)) {
2867                             die("$partType has no namespace!");
2868                         } 
2869                     } 
2870                     $xml .= '<part name="' . $partName . '" type="' . $typePrefix . ':' . $this->getLocalPart($partType) . '" />';
2871                 } 
2872                 $xml .= '</message>';
2873             } 
2874         } 
2875         // bindings & porttypes
2876         if (count($this->bindings) >= 1) {
2877             $binding_xml = '';
2878             $portType_xml = '';
2879             foreach($this->bindings as $bindingName => $attrs) {
2880                 $binding_xml .= '<binding name="' . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
2881                 $binding_xml .= '<soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
2882                 $portType_xml .= '<portType name="' . $attrs['portType'] . '">';
2883                 foreach($attrs['operations'] as $opName => $opParts) {
2884                     $binding_xml .= '<operation name="' . $opName . '">';
2885                     $binding_xml .= '<soap:operation soapAction="' . $opParts['soapAction'] . '" style="'. $attrs['style'] . '"/>';
2886                     $binding_xml .= '<input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '" encodingStyle="' . $opParts['input']['encodingStyle'] . '"/></input>';
2887                     $binding_xml .= '<output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '" encodingStyle="' . $opParts['output']['encodingStyle'] . '"/></output>';
2888                     $binding_xml .= '</operation>';
2889                     $portType_xml .= '<operation name="' . $opParts['name'] . '"';
2890                     if (isset($opParts['parameterOrder'])) {
2891                         $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
2892                     } 
2893                     $portType_xml .= '>';
2894                     $portType_xml .= '<input message="tns:' . $opParts['input']['message'] . '"/>';
2895                     $portType_xml .= '<output message="tns:' . $opParts['output']['message'] . '"/>';
2896                     $portType_xml .= '</operation>';
2897                 } 
2898                 $portType_xml .= '</portType>';
2899                 $binding_xml .= '</binding>';
2900             } 
2901             $xml .= $portType_xml . $binding_xml;
2902         } 
2903         // services
2904         $xml .= '<service name="' . $this->serviceName . '">';
2905         if (count($this->ports) >= 1) {
2906             foreach($this->ports as $pName => $attrs) {
2907                 $xml .= '<port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
2908                 $xml .= '<soap:address location="' . $attrs['location'] . '"/>';
2909                 $xml .= '</port>';
2910             } 
2911         } 
2912         $xml .= '</service>';
2913         return $xml . '</definitions>';
2914     } 
2915         
2916     /**
2917      * serialize a PHP value according to a WSDL message definition
2918      * 
2919      * TODO
2920      * - multi-ref serialization
2921      * - validate PHP values against type definitions, return errors if invalid
2922      * 
2923      * @param string $ type name
2924      * @param mixed $ param value
2925      * @return mixed new param or false if initial value didn't validate
2926      */
2927     function serializeRPCParameters($operation, $direction, $parameters)
2928     {
2929         $this->debug('in serializeRPCParameters with operation '.$operation.', direction '.$direction.' and '.count($parameters).' param(s), and xml schema version ' . $this->XMLSchemaVersion); 
2930                 
2931         if ($direction != 'input' && $direction != 'output') {
2932             $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
2933             $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
2934             return false;
2935         } 
2936         if (!$opData = $this->getOperationData($operation)) {
2937             $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
2938             $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
2939             return false;
2940         }
2941         $this->debug($this->varDump($opData));
2942         // set input params
2943         $xml = '';
2944         if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
2945                         
2946             $use = $opData[$direction]['use'];
2947             $this->debug("use=$use");
2948             $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
2949             foreach($opData[$direction]['parts'] as $name => $type) {
2950                 $this->debug('serializing part "'.$name.'" of type "'.$type.'"');
2951                 // NOTE: add error handling here
2952                 // if serializeType returns false, then catch global error and fault
2953                 if (isset($parameters[$name])) {
2954                     $this->debug('calling serializeType w/ named param');
2955                     $xml .= $this->serializeType($name, $type, $parameters[$name], $use);
2956                 } elseif(is_array($parameters)) {
2957                     $this->debug('calling serializeType w/ unnamed param');
2958                     $xml .= $this->serializeType($name, $type, array_shift($parameters), $use);
2959                 } else {
2960                     $this->debug('no parameters passed.');
2961                 }
2962             }
2963         }
2964         return $xml;
2965     } 
2966         
2967     /**
2968      * serializes a PHP value according a given type definition
2969      * 
2970      * @param string $name , name of type (part)
2971      * @param string $type , type of type, heh (type or element)
2972      * @param mixed $value , a native PHP value (parameter value)
2973      * @param string $use , use for part (encoded|literal)
2974      * @return string serialization
2975      * @access public 
2976      */
2977     function serializeType($name, $type, $value, $use='encoded')
2978     {
2979         $this->debug("in serializeType: $name, $type, $value, $use");
2980         $xml = '';
2981         if (strpos($type, ':')) {
2982             $uqType = substr($type, strrpos($type, ':') + 1);
2983             $ns = substr($type, 0, strrpos($type, ':'));
2984             $this->debug("got a prefixed type: $uqType, $ns");
2985                         
2986             if($ns == $this->XMLSchemaVersion ||
2987                ($this->getNamespaceFromPrefix($ns)) == $this->XMLSchemaVersion){
2988                                 
2989                 if ($uqType == 'boolean' && !$value) {
2990                     $value = 0;
2991                 } elseif ($uqType == 'boolean') {
2992                     $value = 1;
2993                 } 
2994                 if ($this->charencoding && $uqType == 'string' && gettype($value) == 'string') {
2995                     $value = htmlspecialchars($value);
2996                 } 
2997                 // it's a scalar
2998                 if ($use == 'literal') {
2999                     return "<$name>$value</$name>";
3000                 } else {
3001                     return "<$name xsi:type=\"" . $this->getPrefixFromNamespace($this->XMLSchemaVersion) . ":$uqType\">$value</$name>";
3002                 }
3003             } 
3004         } else {
3005             $uqType = $type;
3006         }
3007         if(!$typeDef = $this->getTypeDef($uqType)){
3008             $this->setError("$uqType is not a supported type.");
3009             return false;
3010         } else {
3011             //foreach($typeDef as $k => $v) {
3012             //$this->debug("typedef, $k: $v");
3013             //}
3014         }
3015         $phpType = $typeDef['phpType'];
3016         $this->debug("serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') ); 
3017         // if php type == struct, map value to the <all> element names
3018         if ($phpType == 'struct') {
3019             if (isset($typeDef['element']) && $typeDef['element']) {
3020                 $elementName = $uqType;
3021                 // TODO: use elementFormDefault="qualified|unqualified" to determine
3022                 // how to scope the namespace
3023                 $elementNS = " xmlns=\"$ns\"";
3024             } else {
3025                 $elementName = $name;
3026                 $elementNS = '';
3027             }
3028             if ($use == 'literal') {
3029                 $xml = "<$elementName$elementNS>";
3030             } else {
3031                 $xml = "<$elementName$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
3032             }
3033                         
3034             if (isset($this->complexTypes[$uqType]['elements']) && is_array($this->complexTypes[$uqType]['elements'])) {
3035                         
3036                 //if (is_array($this->complexTypes[$uqType]['elements'])) {
3037                 // toggle whether all elements are present - ideally should validate against schema
3038                 if(count($this->complexTypes[$uqType]['elements']) != count($value)){
3039                     $optionals = true;
3040                 }
3041                 foreach($this->complexTypes[$uqType]['elements'] as $eName => $attrs) {
3042                     // if user took advantage of a minOccurs=0, then only serialize named parameters
3043                     if(isset($optionals) && !isset($value[$eName])){
3044                         // do nothing
3045                     } else {
3046                         // get value
3047                         if (isset($value[$eName])) {
3048                             $v = $value[$eName];
3049                         } elseif (is_array($value)) {
3050                             $v = array_shift($value);
3051                         }
3052                         // serialize schema-defined type
3053                         if (!isset($attrs['type'])) {
3054                             $xml .= $this->serializeType($eName, $attrs['name'], $v, $use);
3055                             // serialize generic type
3056                         } else {
3057                             $this->debug("calling serialize_val() for $eName, $v, " . $this->getLocalPart($attrs['type']), false, $use);
3058                             $xml .= $this->serialize_val($v, $eName, $this->getLocalPart($attrs['type']), null, $this->getNamespaceFromPrefix($this->getPrefix($attrs['type'])), false, $use);
3059                         }
3060                     }
3061                 } 
3062             }
3063             $xml .= "</$elementName>";
3064         } elseif ($phpType == 'array') {
3065             $rows = sizeof($value);
3066             if (isset($typeDef['multidimensional'])) {
3067                 $nv = array();
3068                 foreach($value as $v) {
3069                     $cols = ',' . sizeof($v);
3070                     $nv = array_merge($nv, $v);
3071                 } 
3072                 $value = $nv;
3073             } else {
3074                 $cols = '';
3075             } 
3076             if (is_array($value) && sizeof($value) >= 1) {
3077                 $contents = '';
3078                 foreach($value as $k => $v) {
3079                     $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]");
3080                     //if (strpos($typeDef['arrayType'], ':') ) {
3081                     if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) {
3082                         $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
3083                     } else {
3084                         $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
3085                     } 
3086                 }
3087                 $this->debug('contents: '.$this->varDump($contents));
3088             } else {
3089                 $contents = null;
3090             }
3091             if ($use == 'literal') {
3092                 $xml = "<$name>"
3093                     .$contents
3094                     ."</$name>";
3095             } else {
3096                 $xml = "<$name xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '.
3097                     $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
3098                     .':arrayType="'
3099                     .$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
3100                     .":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">"
3101                     .$contents
3102                     ."</$name>";
3103             }
3104         }
3105         $this->debug('returning: '.$this->varDump($xml));
3106         return $xml;
3107     }
3108         
3109     /**
3110      * register a service with the server
3111      * 
3112      * @param string $methodname 
3113      * @param string $in assoc array of input values: key = param name, value = param type
3114      * @param string $out assoc array of output values: key = param name, value = param type
3115      * @param string $namespace 
3116      * @param string $soapaction 
3117      * @param string $style (rpc|literal)
3118      * @access public 
3119      */
3120     function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '')
3121     {
3122         if ($style == 'rpc' && $use == 'encoded') {
3123             $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
3124         } else {
3125             $encodingStyle = '';
3126         } 
3127         // get binding
3128         $this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] =
3129         array(
3130               'name' => $name,
3131               'binding' => $this->serviceName . 'Binding',
3132               'endpoint' => $this->endpoint,
3133               'soapAction' => $soapaction,
3134               'style' => $style,
3135               'input' => array(
3136                                'use' => $use,
3137                                'namespace' => $namespace,
3138                                'encodingStyle' => $encodingStyle,
3139                                'message' => $name . 'Request',
3140                                'parts' => $in),
3141               'output' => array(
3142                                 'use' => $use,
3143                                 'namespace' => $namespace,
3144                                 'encodingStyle' => $encodingStyle,
3145                                 'message' => $name . 'Response',
3146                                 'parts' => $out),
3147               'namespace' => $namespace,
3148               'transport' => 'http://schemas.xmlsoap.org/soap/http',
3149               'documentation' => $documentation); 
3150         // add portTypes
3151         // add messages
3152         if($in)
3153             {
3154                 foreach($in as $pName => $pType)
3155                 {
3156                     if(strpos($pType,':')) {
3157                         $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
3158                     }
3159                     $this->messages[$name.'Request'][$pName] = $pType;
3160                 }
3161             }
3162         
3163         if($out)
3164             {
3165                 foreach($out as $pName => $pType)
3166                 {
3167                     if(strpos($pType,':')) {
3168                         $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
3169                     }
3170                     $this->messages[$name.'Response'][$pName] = $pType;
3171                 }
3172             }
3173         return true;
3174     } 
3175
3176
3177 ?><?php
3178
3179 /**
3180  *
3181  * soap_parser class parses SOAP XML messages into native PHP values
3182  *
3183  * @author   Dietrich Ayala <dietrich@ganx4.com>
3184  * @version  v 0.6.3
3185  * @access   public
3186  */
3187 class soap_parser extends nusoap_base {
3188
3189     var $xml = '';
3190     var $xml_encoding = '';
3191     var $method = '';
3192     var $root_struct = '';
3193     var $root_struct_name = '';
3194     var $root_header = '';
3195     var $document = '';
3196     // determines where in the message we are (envelope,header,body,method)
3197     var $status = '';
3198     var $position = 0;
3199     var $depth = 0;
3200     var $default_namespace = '';
3201     var $namespaces = array();
3202     var $message = array();
3203     var $parent = '';
3204     var $fault = false;
3205     var $fault_code = '';
3206     var $fault_str = '';
3207     var $fault_detail = '';
3208     var $depth_array = array();
3209     var $debug_flag = true;
3210     var $soapresponse = NULL;
3211     var $responseHeaders = '';
3212     var $body_position = 0;
3213     // for multiref parsing:
3214     // array of id => pos
3215     var $ids = array();
3216     // array of id => hrefs => pos
3217     var $multirefs = array();
3218
3219     /**
3220      * constructor
3221      *
3222      * @param    string $xml SOAP message
3223      * @param    string $encoding character encoding scheme of message
3224      * @access   public
3225      */
3226     function soap_parser($xml,$encoding='UTF-8',$method=''){
3227                 $this->xml = $xml;
3228                 $this->xml_encoding = $encoding;
3229                 $this->method = $method;
3230
3231                 // Check whether content has been read.
3232                 if(!empty($xml)){
3233                         $this->debug('Entering soap_parser()');
3234                         // Create an XML parser.
3235                         $this->parser = xml_parser_create($this->xml_encoding);
3236                         // Set the options for parsing the XML data.
3237                         //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
3238                         xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
3239                         // Set the object for the parser.
3240                         xml_set_object($this->parser, $this);
3241                         // Set the element handlers for the parser.
3242                         xml_set_element_handler($this->parser, 'start_element','end_element');
3243                         xml_set_character_data_handler($this->parser,'character_data');
3244
3245                         // Parse the XML file.
3246                         if(!xml_parse($this->parser,$xml,true)){
3247                             // Display an error message.
3248                             $err = sprintf('XML error on line %d: %s',
3249                             xml_get_current_line_number($this->parser),
3250                             xml_error_string(xml_get_error_code($this->parser)));
3251                                 $this->debug('parse error: '.$err);
3252                                 $this->errstr = $err;
3253                         } else {
3254                                 $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name);
3255                                 // get final value
3256                                 $this->soapresponse = $this->message[$this->root_struct]['result'];
3257                                 // get header value
3258                                 if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){
3259                                         $this->responseHeaders = $this->message[$this->root_header]['result'];
3260                                 }
3261                                 // resolve hrefs/ids
3262                                 if(sizeof($this->multirefs) > 0){
3263                                         foreach($this->multirefs as $id => $hrefs){
3264                                                 $this->debug('resolving multirefs for id: '.$id);
3265                                                 $idVal = $this->buildVal($this->ids[$id]);
3266                                                 foreach($hrefs as $refPos => $ref){
3267                                                         $this->debug('resolving href at pos '.$refPos);
3268                                                         $this->multirefs[$id][$refPos] = $idVal;
3269                                                 }
3270                                         }
3271                                 }
3272                         }
3273                         xml_parser_free($this->parser);
3274                 } else {
3275                         $this->debug('xml was empty, didn\'t parse!');
3276                         $this->errstr = 'xml was empty, didn\'t parse!';
3277                 }
3278         }
3279
3280         /**
3281         * start-element handler
3282         *
3283         * @param    string $parser XML parser object
3284         * @param    string $name element name
3285         * @param    string $attrs associative array of attributes
3286         * @access   private
3287         */
3288         function start_element($parser, $name, $attrs) {
3289                 // position in a total number of elements, starting from 0
3290                 // update class level pos
3291                 $pos = $this->position++;
3292                 // and set mine
3293                 $this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>'');
3294                 // depth = how many levels removed from root?
3295                 // set mine as current global depth and increment global depth value
3296                 $this->message[$pos]['depth'] = $this->depth++;
3297
3298                 // else add self as child to whoever the current parent is
3299                 if($pos != 0){
3300                         $this->message[$this->parent]['children'] .= '|'.$pos;
3301                 }
3302                 // set my parent
3303                 $this->message[$pos]['parent'] = $this->parent;
3304                 // set self as current parent
3305                 $this->parent = $pos;
3306                 // set self as current value for this depth
3307                 $this->depth_array[$this->depth] = $pos;
3308                 // get element prefix
3309                 if(strpos($name,':')){
3310                         // get ns prefix
3311                         $prefix = substr($name,0,strpos($name,':'));
3312                         // get unqualified name
3313                         $name = substr(strstr($name,':'),1);
3314                 }
3315                 // set status
3316                 if($name == 'Envelope'){
3317                         $this->status = 'envelope';
3318                 } elseif($name == 'Header'){
3319                         $this->root_header = $pos;
3320                         $this->status = 'header';
3321                 } elseif($name == 'Body'){
3322                         $this->status = 'body';
3323                         $this->body_position = $pos;
3324                 // set method
3325                 } elseif($this->status == 'body' && $pos == ($this->body_position+1)){
3326                         $this->status = 'method';
3327                         $this->root_struct_name = $name;
3328                         $this->root_struct = $pos;
3329                         $this->message[$pos]['type'] = 'struct';
3330                         $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
3331                 }
3332                 // set my status
3333                 $this->message[$pos]['status'] = $this->status;
3334                 // set name
3335                 $this->message[$pos]['name'] = htmlspecialchars($name);
3336                 // set attrs
3337                 $this->message[$pos]['attrs'] = $attrs;
3338
3339                 // loop through atts, logging ns and type declarations
3340         $attstr = '';
3341                 foreach($attrs as $key => $value){
3342                 $key_prefix = $this->getPrefix($key);
3343                         $key_localpart = $this->getLocalPart($key);
3344                         // if ns declarations, add to class level array of valid namespaces
3345             if($key_prefix == 'xmlns'){
3346                                 if(ereg('^http://www.w3.org/[0-9]{4}/XMLSchema$',$value)){
3347                                         $this->XMLSchemaVersion = $value;
3348                                         $this->namespaces['xsd'] = $this->XMLSchemaVersion;
3349                                         $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance';
3350                                 }
3351                 $this->namespaces[$key_localpart] = $value;
3352                                 // set method namespace
3353                                 if($name == $this->root_struct_name){
3354                                         $this->methodNamespace = $value;
3355                                 }
3356                         // if it's a type declaration, set type
3357             } elseif($key_localpart == 'type'){
3358                 $value_prefix = $this->getPrefix($value);
3359                 $value_localpart = $this->getLocalPart($value);
3360                                 $this->message[$pos]['type'] = $value_localpart;
3361                                 $this->message[$pos]['typePrefix'] = $value_prefix;
3362                 if(isset($this->namespaces[$value_prefix])){
3363                         $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
3364                 } else if(isset($attrs['xmlns:'.$value_prefix])) {
3365                                         $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix];
3366                 }
3367                                 // should do something here with the namespace of specified type?
3368                         } elseif($key_localpart == 'arrayType'){
3369                                 $this->message[$pos]['type'] = 'array';
3370                                 /* do arrayType ereg here
3371                                 [1]    arrayTypeValue    ::=    atype asize
3372                                 [2]    atype    ::=    QName rank*
3373                                 [3]    rank    ::=    '[' (',')* ']'
3374                                 [4]    asize    ::=    '[' length~ ']'
3375                                 [5]    length    ::=    nextDimension* Digit+
3376                                 [6]    nextDimension    ::=    Digit+ ','
3377                                 */
3378                                 $expr = '([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]';
3379                                 if(ereg($expr,$value,$regs)){
3380                                         $this->message[$pos]['typePrefix'] = $regs[1];
3381                                         $this->message[$pos]['arraySize'] = $regs[3];
3382                                         $this->message[$pos]['arrayCols'] = $regs[4];
3383                                 }
3384                         }
3385                         // log id
3386                         if($key == 'id'){
3387                                 $this->ids[$value] = $pos;
3388                         }
3389                         // root
3390                         if($key_localpart == 'root' && $value == 1){
3391                                 $this->status = 'method';
3392                                 $this->root_struct_name = $name;
3393                                 $this->root_struct = $pos;
3394                                 $this->debug("found root struct $this->root_struct_name, pos $pos");
3395                         }
3396             // for doclit
3397             $attstr .= " $key=\"$value\"";
3398                 }
3399         // get namespace - must be done after namespace atts are processed
3400                 if(isset($prefix)){
3401                         $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
3402                         $this->default_namespace = $this->namespaces[$prefix];
3403                 } else {
3404                         $this->message[$pos]['namespace'] = $this->default_namespace;
3405                 }
3406         if($this->status == 'header'){
3407                 $this->responseHeaders .= "<$name$attstr>";
3408         } elseif($this->root_struct_name != ''){
3409                 $this->document .= "<$name$attstr>";
3410         }
3411         }
3412
3413         /**
3414         * end-element handler
3415         *
3416         * @param    string $parser XML parser object
3417         * @param    string $name element name
3418         * @access   private
3419         */
3420         function end_element($parser, $name) {
3421                 // position of current element is equal to the last value left in depth_array for my depth
3422                 $pos = $this->depth_array[$this->depth--];
3423
3424         // get element prefix
3425                 if(strpos($name,':')){
3426                         // get ns prefix
3427                         $prefix = substr($name,0,strpos($name,':'));
3428                         // get unqualified name
3429                         $name = substr(strstr($name,':'),1);
3430                 }
3431
3432                 // build to native type
3433                 if(isset($this->body_position) && $pos > $this->body_position){
3434                         // deal w/ multirefs
3435                         if(isset($this->message[$pos]['attrs']['href'])){
3436                                 // get id
3437                                 $id = substr($this->message[$pos]['attrs']['href'],1);
3438                                 // add placeholder to href array
3439                                 $this->multirefs[$id][$pos] = "placeholder";
3440                                 // add set a reference to it as the result value
3441                                 $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
3442             // build complex values
3443                         } elseif($this->message[$pos]['children'] != ""){
3444                                 $this->message[$pos]['result'] = $this->buildVal($pos);
3445                         } else {
3446                 $this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
3447                                 if(is_numeric($this->message[$pos]['cdata']) ){
3448                         if( strpos($this->message[$pos]['cdata'],'.') ){
3449                                 $this->message[$pos]['result'] = doubleval($this->message[$pos]['cdata']);
3450                     } else {
3451                         $this->message[$pos]['result'] = intval($this->message[$pos]['cdata']);
3452                     }
3453                 } else {
3454                         $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
3455                 }
3456                         }
3457                 }
3458
3459                 // switch status
3460                 if($pos == $this->root_struct){
3461                         $this->status = 'body';
3462                 } elseif($name == 'Body'){
3463                         $this->status = 'header';
3464                  } elseif($name == 'Header'){
3465                         $this->status = 'envelope';
3466                 } elseif($name == 'Envelope'){
3467                         //
3468                 }
3469                 // set parent back to my parent
3470                 $this->parent = $this->message[$pos]['parent'];
3471         // for doclit
3472         if($this->status == 'header'){
3473                 $this->responseHeaders .= "</$name>";
3474         } elseif($pos >= $this->root_struct){
3475                 $this->document .= "</$name>";
3476         }
3477         }
3478
3479         /**
3480         * element content handler
3481         *
3482         * @param    string $parser XML parser object
3483         * @param    string $data element content
3484         * @access   private
3485         */
3486         function character_data($parser, $data){
3487                 $pos = $this->depth_array[$this->depth];
3488                 if ($this->xml_encoding=='UTF-8'){
3489                         $data = utf8_decode($data);
3490                 }
3491         $this->message[$pos]['cdata'] .= $data;
3492         // for doclit
3493         if($this->status == 'header'){
3494                 $this->responseHeaders .= $data;
3495         } else {
3496                 $this->document .= $data;
3497         }
3498         }
3499
3500         /**
3501         * get the parsed message
3502         *
3503         * @return       mixed
3504         * @access   public
3505         */
3506         function get_response(){
3507                 return $this->soapresponse;
3508         }
3509
3510         /**
3511         * get the parsed headers
3512         *
3513         * @return       string XML or empty if no headers
3514         * @access   public
3515         */
3516         function getHeaders(){
3517             return $this->responseHeaders;
3518         }
3519
3520         /**
3521         * decodes entities
3522         *
3523         * @param    string $text string to translate
3524         * @access   private
3525         */
3526         function decode_entities($text){
3527                 foreach($this->entities as $entity => $encoded){
3528                         $text = str_replace($encoded,$entity,$text);
3529                 }
3530                 return $text;
3531         }
3532
3533         /**
3534         * builds response structures for compound values (arrays/structs)
3535         *
3536         * @param    string $pos position in node tree
3537         * @access   private
3538         */
3539         function buildVal($pos){
3540                 if(!isset($this->message[$pos]['type'])){
3541                         $this->message[$pos]['type'] = '';
3542                 }
3543                 $this->debug('inside buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']);
3544                 // if there are children...
3545                 if($this->message[$pos]['children'] != ''){
3546                         $children = explode('|',$this->message[$pos]['children']);
3547                         array_shift($children); // knock off empty
3548                         // md array
3549                         if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != ''){
3550                 $r=0; // rowcount
3551                 $c=0; // colcount
3552                 foreach($children as $child_pos){
3553                                         $this->debug("got an MD array element: $r, $c");
3554                                         $params[$r][] = $this->message[$child_pos]['result'];
3555                                     $c++;
3556                                     if($c == $this->message[$pos]['arrayCols']){
3557                                         $c = 0;
3558                                                 $r++;
3559                                     }
3560                 }
3561             // array
3562                         } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array'){
3563                 $this->debug('adding array '.$this->message[$pos]['name']);
3564                 foreach($children as $child_pos){
3565                         $params[] = &$this->message[$child_pos]['result'];
3566                 }
3567             // apache Map type: java hashtable
3568             } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){
3569                 foreach($children as $child_pos){
3570                         $kv = explode("|",$this->message[$child_pos]['children']);
3571                         $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
3572                 }
3573             // generic compound type
3574             //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
3575             } else {
3576                 // is array or struct? better way to do this probably
3577                 foreach($children as $child_pos){
3578                         if(isset($keys) && isset($keys[$this->message[$child_pos]['name']])){
3579                                 $struct = 1;
3580                                 break;
3581                         }
3582                         $keys[$this->message[$child_pos]['name']] = 1;
3583                 }
3584                 //
3585                 foreach($children as $child_pos){
3586                         if(isset($struct)){
3587                                 $params[] = &$this->message[$child_pos]['result'];
3588                         } else {
3589                                         $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
3590                         }
3591                 }
3592                         }
3593                         return is_array($params) ? $params : array();
3594                 } else {
3595                 $this->debug('no children');
3596             if(strpos($this->message[$pos]['cdata'],'&')){
3597                         return  strtr($this->message[$pos]['cdata'],array_flip($this->entities));
3598             } else {
3599                 return $this->message[$pos]['cdata'];
3600             }
3601                 }
3602         }
3603 }
3604
3605
3606
3607 ?><?php
3608
3609
3610
3611 /**
3612 *
3613 * soapclient higher level class for easy usage.
3614 *
3615 * usage:
3616 *
3617 * // instantiate client with server info
3618 * $soapclient = new soapclient( string path [ ,boolean wsdl] );
3619 *
3620 * // call method, get results
3621 * echo $soapclient->call( string methodname [ ,array parameters] );
3622 *
3623 * // bye bye client
3624 * unset($soapclient);
3625 *
3626 * @author   Dietrich Ayala <dietrich@ganx4.com>
3627 * @version  v 0.6.3
3628 * @access   public
3629 */
3630 class soapclient extends nusoap_base  {
3631
3632     var $username = '';
3633     var $password = '';
3634     var $requestHeaders = false;
3635     var $responseHeaders;
3636     var $endpoint;
3637     var $error_str = false;
3638     var $proxyhost = '';
3639     var $proxyport = '';
3640     var $xml_encoding = '';
3641     var $http_encoding = false;
3642     var $timeout = 0;
3643     var $endpointType = '';
3644     var $persistentConnection = false;
3645     var $defaultRpcParams = false;
3646         
3647     /**
3648      * fault related variables
3649      *
3650      * @var      fault
3651      * @var      faultcode
3652      * @var      faultstring
3653      * @var      faultdetail
3654      * @access   public
3655      */
3656     var $fault, $faultcode, $faultstring, $faultdetail;
3657
3658     /**
3659      * constructor
3660      *
3661      * @param    string $endpoint SOAP server or WSDL URL
3662      * @param    bool $wsdl optional, set to true if using WSDL
3663      * @param   int $portName optional portName in WSDL document
3664      * @access   public
3665      */
3666     function soapclient($endpoint,$wsdl = false){
3667         $this->endpoint = $endpoint;
3668
3669         // make values
3670         if ($wsdl){
3671             $this->endpointType = 'wsdl';
3672             $this->wsdlFile = $this->endpoint;
3673                         
3674             // instantiate wsdl object and parse wsdl file
3675             $this->debug('instantiating wsdl class with doc: '.$endpoint);
3676             $this->wsdl =& new wsdl($this->wsdlFile,$this->proxyhost,$this->proxyport);
3677             $this->debug("wsdl debug: \n".$this->wsdl->debug_str);
3678             $this->wsdl->debug_str = '';
3679             // catch errors
3680             if($errstr = $this->wsdl->getError()){
3681                 $this->debug('got wsdl error: '.$errstr);
3682                 $this->setError('wsdl error: '.$errstr);
3683             } elseif($this->operations = $this->wsdl->getOperations()){
3684                 $this->debug( 'got '.count($this->operations).' operations from wsdl '.$this->wsdlFile);
3685             } else {
3686                 $this->debug( 'getOperations returned false');
3687                 $this->setError('no operations defined in the WSDL document!');
3688             }
3689         }
3690     }
3691
3692     /**
3693      * calls method, returns PHP native type
3694      *
3695      * @param    string $method SOAP server URL or path
3696      * @param    array $params array of parameters, can be associative or not
3697      * @param   string $namespace optional method namespace
3698      * @param   string $soapAction optional SOAPAction value
3699      * @param   boolean $headers optional array of soapval objects for headers
3700      * @param   boolean $rpcParams optional treat params as RPC for use="literal"
3701      *                   This can be used on a per-call basis to overrider defaultRpcParams.
3702      * @return  mixed
3703      * @access   public
3704      */
3705     function call($operation,$params=array(),$namespace='',$soapAction='',$headers=false,$rpcParams=null){
3706         $this->operation = $operation;
3707         $this->fault = false;
3708         $this->error_str = '';
3709         $this->request = '';
3710         $this->response = '';
3711         $this->faultstring = '';
3712         $this->faultcode = '';
3713         $this->opData = array();
3714                 
3715         $this->debug("call: $operation, $params, $namespace, $soapAction, $headers, $rpcParams");
3716         $this->debug("endpointType: $this->endpointType");
3717         // if wsdl, get operation data and process parameters
3718         if($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)){
3719
3720             $this->opData = $opData;
3721             foreach($opData as $key => $value){
3722                 $this->debug("$key -> $value");
3723             }
3724             $soapAction = $opData['soapAction'];
3725             $this->endpoint = $opData['endpoint'];
3726             $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : 'http://testuri.org';
3727             $style = $opData['style'];
3728             // add ns to ns array
3729             if($namespace != '' && !isset($this->wsdl->namespaces[$namespace])){
3730                 $this->wsdl->namespaces['nu'] = $namespace;
3731             }
3732             // serialize payload
3733                         
3734             if($opData['input']['use'] == 'literal') {
3735                 if (is_null($rpcParams)) {
3736                     $rpcParams = $this->defaultRpcParams;
3737                 }
3738                 if ($rpcParams) {
3739                     $this->debug("serializing literal params for operation $operation");
3740                     $payload = $this->wsdl->serializeRPCParameters($operation,'input',$params);
3741                     $defaultNamespace = $this->wsdl->wsdl_info['targetNamespace'];
3742                 } else {
3743                     $this->debug("serializing literal document for operation $operation");
3744                     $payload = is_array($params) ? array_shift($params) : $params;
3745                 }
3746             } else {
3747                 $this->debug("serializing encoded params for operation $operation");
3748                 $payload = "<".$this->wsdl->getPrefixFromNamespace($namespace).":$operation>".
3749                     $this->wsdl->serializeRPCParameters($operation,'input',$params).
3750                     '</'.$this->wsdl->getPrefixFromNamespace($namespace).":$operation>";
3751             }
3752             $this->debug('payload size: '.strlen($payload));
3753             // serialize envelope
3754             $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,$this->wsdl->usedNamespaces,$style);
3755             $this->debug("wsdl debug: \n".$this->wsdl->debug_str);
3756             $this->wsdl->debug_str = '';
3757         } elseif($this->endpointType == 'wsdl') {
3758             $this->setError( 'operation '.$operation.' not present.');
3759             $this->debug("operation '$operation' not present.");
3760             $this->debug("wsdl debug: \n".$this->wsdl->debug_str);
3761             return false;
3762             // no wsdl
3763         } else {
3764             // make message
3765             if(!isset($style)){
3766                 $style = 'rpc';
3767             }
3768             if($namespace == ''){
3769                 $namespace = 'http://testuri.org';
3770                 $this->wsdl->namespaces['ns1'] = $namespace;
3771             }
3772             // serialize envelope
3773             $payload = '';
3774             foreach($params as $k => $v){
3775                 $payload .= $this->serialize_val($v,$k);
3776             }
3777             $payload = "<ns1:$operation xmlns:ns1=\"$namespace\">\n".$payload."</ns1:$operation>\n";
3778             $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders);
3779         }
3780         $this->debug("endpoint: $this->endpoint, soapAction: $soapAction, namespace: $namespace");
3781         // send
3782         $this->debug('sending msg (len: '.strlen($soapmsg).") w/ soapaction '$soapAction'...");
3783         $return = $this->send($soapmsg,$soapAction,$this->timeout);
3784         if($errstr = $this->getError()){
3785             $this->debug('Error: '.$errstr);
3786             return false;
3787         } else {
3788             $this->return = $return;
3789             $this->debug('sent message successfully and got a(n) '.gettype($return).' back');
3790             
3791             // fault?
3792             if(is_array($return) && isset($return['faultcode'])){
3793                 $this->debug('got fault');
3794                 $this->setError($return['faultcode'].': '.$return['faultstring']);
3795                 $this->fault = true;
3796                 foreach($return as $k => $v){
3797                     $this->$k = $v;
3798                     $this->debug("$k = $v<br>");
3799                 }
3800                 return $return;
3801             } else {
3802                 // array of return values
3803                 if(is_array($return)){
3804                     // multiple 'out' parameters
3805                     if(sizeof($return) > 1){
3806                         return $return;
3807                     }
3808                     // single 'out' parameter
3809                     return array_shift($return);
3810                     // nothing returned (ie, echoVoid)
3811                 } else {
3812                     return "";
3813                 }
3814             }
3815         }
3816     }
3817
3818         /**
3819         * get available data pertaining to an operation
3820         *
3821         * @param    string $operation operation name
3822         * @return       array array of data pertaining to the operation
3823         * @access   public
3824         */
3825         function getOperationData($operation){
3826                 if(isset($this->operations[$operation])){
3827                         return $this->operations[$operation];
3828                 }
3829                 $this->debug("No data for operation: $operation");
3830         }
3831
3832     /**
3833     * send the SOAP message
3834     *
3835     * Note: if the operation has multiple return values
3836     * the return value of this method will be an array
3837     * of those values.
3838     *
3839         * @param    string $msg a SOAPx4 soapmsg object
3840         * @param    string $soapaction SOAPAction value
3841         * @param    integer $timeout set timeout in seconds
3842         * @return       mixed native PHP types.
3843         * @access   private
3844         */
3845         function send($msg, $soapaction = '', $timeout=0) {
3846                 // detect transport
3847                 switch(true){
3848                         // http(s)
3849                         case ereg('^http',$this->endpoint):
3850                                 $this->debug('transporting via HTTP');
3851                                 if($this->persistentConnection && is_object($this->persistentConnection)){
3852                                         $http =& $this->persistentConnection;
3853                                 } else {
3854                                         $http = new soap_transport_http($this->endpoint);
3855                                         // pass encoding into transport layer, so appropriate http headers are sent
3856                                         $http->soap_defencoding = $this->soap_defencoding;
3857                                 }
3858                                 $http->setSOAPAction($soapaction);
3859                                 if($this->proxyhost && $this->proxyport){
3860                                         $http->setProxy($this->proxyhost,$this->proxyport);
3861                                 }
3862                                 if($this->username != '' && $this->password != '') {
3863                                         $http->setCredentials($this->username,$this->password);
3864                                 }
3865                                 if($this->http_encoding != ''){
3866                                         $http->setEncoding($this->http_encoding);
3867                                 }
3868                                 $this->debug('sending message, length: '.strlen($msg));
3869                                 if(ereg('^http:',$this->endpoint)){
3870                                 //if(strpos($this->endpoint,'http:')){
3871                                         $response = $http->send($msg,$timeout);
3872                                 } elseif(ereg('^https',$this->endpoint)){
3873                                 //} elseif(strpos($this->endpoint,'https:')){
3874                                         //if(phpversion() == '4.3.0-dev'){
3875                                                 //$response = $http->send($msg,$timeout);
3876                                 //$this->request = $http->outgoing_payload;
3877                                                 //$this->response = $http->incoming_payload;
3878                                         //} else
3879                                         if (extension_loaded('curl')) {
3880                                                 $response = $http->sendHTTPS($msg,$timeout);
3881                                         } else {
3882                                                 $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
3883                                         }                                                               
3884                                 } else {
3885                                         $this->setError('no http/s in endpoint url');
3886                                 }
3887                                 $this->request = $http->outgoing_payload;
3888                                 $this->response = $http->incoming_payload;
3889                                 $this->debug("transport debug data...\n".$http->debug_str);
3890                                 // save transport object if using persistent connections
3891                                 if($this->persistentConnection && !is_object($this->persistentConnection)){
3892                                         $this->persistentConnection = $http;
3893                                 }
3894                                 if($err = $http->getError()){
3895                                         $this->setError('HTTP Error: '.$err);
3896                                         return false;
3897                                 } elseif($this->getError()){
3898                                         return false;
3899                                 } else {
3900                                         $this->debug('got response, length: '.strlen($response));
3901                                         return $this->parseResponse($response);
3902                                 }
3903                         break;
3904                         default:
3905                                 $this->setError('no transport found, or selected transport is not yet supported!');
3906                         return false;
3907                         break;
3908                 }
3909         }
3910
3911         /**
3912         * processes SOAP message returned from server
3913         *
3914         * @param        string unprocessed response data from server
3915         * @return       mixed value of the message, decoded into a PHP type
3916         * @access   private
3917         */
3918     function parseResponse($data) {
3919         $this->debug('Entering parseResponse(), about to create soap_parser instance');
3920         $parser = new soap_parser($data,$this->xml_encoding,$this->operation);
3921         // if parse errors
3922         if($errstr = $parser->getError()){
3923             $this->setError( $errstr);
3924             // destroy the parser object
3925             unset($parser);
3926             return false;
3927         } else {
3928             // get SOAP headers
3929             $this->responseHeaders = $parser->getHeaders();
3930             // get decoded message
3931             $return = $parser->get_response();
3932             // add parser debug data to our debug
3933             $this->debug($parser->debug_str);
3934             // add document for doclit support
3935             $this->document = $parser->document;
3936             // destroy the parser object
3937             unset($parser);
3938             // return decode message
3939             return $return;
3940         }
3941     }
3942
3943     /**
3944      * set the SOAP headers
3945      *
3946      * @param    $headers string XML
3947      * @access   public
3948      */
3949     function setHeaders($headers){
3950         $this->requestHeaders = $headers;
3951     }
3952
3953     /**
3954      * get the response headers
3955      *
3956      * @return   mixed object SOAPx4 soapval object or empty if no headers
3957      * @access   public
3958      */
3959     function getHeaders(){
3960         if($this->responseHeaders != '') {
3961             return $this->responseHeaders;
3962         }
3963     }
3964
3965         /**
3966          * set proxy info here
3967          *
3968          * @param    string $proxyhost
3969          * @param    string $proxyport
3970          * @access   public
3971          */
3972         function setHTTPProxy($proxyhost, $proxyport) {
3973             $this->proxyhost = $proxyhost;
3974             $this->proxyport = $proxyport;
3975         }
3976
3977         /**
3978          * if authenticating, set user credentials here
3979          *
3980          * @param    string $username
3981          * @param    string $password
3982          * @access   public
3983          */
3984         function setCredentials($username, $password) {
3985             $this->username = $username;
3986             $this->password = $password;
3987         }
3988         
3989         /**
3990          * use HTTP encoding
3991          *
3992          * @param    string $enc
3993          * @access   public
3994          */
3995         function setHTTPEncoding($enc='gzip, deflate'){
3996             $this->http_encoding = $enc;
3997         }
3998         
3999         /**
4000          * use HTTP persistent connections if possible
4001          *
4002          * @access   public
4003          */
4004         function useHTTPPersistentConnection(){
4005             $this->persistentConnection = true;
4006         }
4007         
4008         /**
4009          * gets the default RPC parameter setting.
4010          * If true, default is that call params are like RPC even for document style.
4011          * Each call() can override this value.
4012          *
4013          * @access public
4014          */
4015         function getDefaultRpcParams() {
4016             return $this->defaultRpcParams;
4017         }
4018
4019         /**
4020          * sets the default RPC parameter setting.
4021          * If true, default is that call params are like RPC even for document style
4022          * Each call() can override this value.
4023          *
4024          * @param    boolean $rpcParams
4025          * @access public
4026          */
4027         function setDefaultRpcParams($rpcParams) {
4028             $this->defaultRpcParams = $rpcParams;
4029         }
4030         
4031         /**
4032          * dynamically creates proxy class, allowing user to directly call methods from wsdl
4033          *
4034          * @return   object soap_proxy object
4035          * @access   public
4036          */
4037         function getProxy(){
4038             $evalStr = '';
4039             foreach($this->operations as $operation => $opData){
4040                 if($operation != ''){
4041                     // create param string
4042                     $paramStr = '';
4043                     if(sizeof($opData['input']['parts']) > 0){
4044                         foreach($opData['input']['parts'] as $name => $type){
4045                             $paramStr .= "\$$name,";
4046                         }
4047                         $paramStr = substr($paramStr,0,strlen($paramStr)-1);
4048                     }
4049                     $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
4050                     $evalStr .= "function $operation ($paramStr){
4051                                         // load params into array
4052                                         \$params = array($paramStr);
4053                                         return \$this->call('$operation',\$params,'".$opData['namespace']."','".$opData['soapAction']."');
4054                                 }";
4055                     unset($paramStr);
4056                 }
4057             }
4058             $r = rand();
4059             $evalStr = 'class soap_proxy_'.$r.' extends soapclient {
4060                                 '.$evalStr.'
4061                         }';
4062             //print "proxy class:<pre>$evalStr</pre>";
4063             // eval the class
4064             eval($evalStr);
4065             // instantiate proxy object
4066             eval("\$proxy = new soap_proxy_$r('');");
4067             // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
4068             $proxy->endpointType = 'wsdl';
4069             $proxy->wsdlFile = $this->wsdlFile;
4070             $proxy->wsdl = $this->wsdl;
4071             $proxy->operations = $this->operations;
4072             $proxy->defaultRpcParams = $this->defaultRpcParams;
4073             return $proxy;
4074         }
4075 }
4076
4077 // Local Variables:
4078 // mode: php
4079 // tab-width: 8
4080 // c-basic-offset: 4
4081 // c-hanging-comment-ender-p: nil
4082 // indent-tabs-mode: nil
4083 // End:
4084 ?>