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