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