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