Stub file for Gujarati, includes numeral conversion table
[lhc/web/wiklou.git] / soap / nusoap.php
1 <?php
2
3 /*
4 $Id$
5
6 NuSOAP - Web Services Toolkit for PHP
7
8 Copyright (c) 2002 NuSphere Corporation
9
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2.1 of the License, or (at your option) any later version.
14
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
19
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
24 If you have any questions or comments, please email:
25
26 Dietrich Ayala
27 dietrich@ganx4.com
28 http://dietrich.ganx4.com/nusoap
29
30 NuSphere Corporation
31 http://www.nusphere.com
32
33 */
34
35 /* load classes
36
37 // necessary classes
38 require_once('class.soapclient.php');
39 require_once('class.soap_val.php');
40 require_once('class.soap_parser.php');
41 require_once('class.soap_fault.php');
42
43 // transport classes
44 require_once('class.soap_transport_http.php');
45
46 // optional add-on classes
47 require_once('class.xmlschema.php');
48 require_once('class.wsdl.php');
49
50 // server class
51 require_once('class.soap_server.php');*/
52
53 /**
54 *
55 * nusoap_base
56 *
57 * @author Dietrich Ayala <dietrich@ganx4.com>
58 * @version $Id$
59 * @access public
60 */
61 class nusoap_base {
62
63 var $title = 'NuSOAP';
64 var $version = '0.6.7';
65 var $revision = '$Revision$';
66 var $error_str = false;
67 var $debug_str = '';
68 // toggles automatic encoding of special characters as entities
69 // (should always be true, I think)
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 charset encoding for outgoing messages
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 var $usedNamespaces = array();
102
103 /**
104 * load types into typemap array
105 * is this legacy yet?
106 * no, this is used by the xmlschema class to verify type => namespace mappings.
107 * @var typemap
108 * @access public
109 */
110 var $typemap = array(
111 'http://www.w3.org/2001/XMLSchema' => 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' => array(
121 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
122 'float'=>'double','dateTime'=>'string',
123 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
124 'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
125 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
126 'http://xml.apache.org/xml-soap' => array('Map')
127 );
128
129 /**
130 * entities to convert
131 *
132 * @var xmlEntities
133 * @access public
134 */
135 var $xmlEntities = array('quot' => '"','amp' => '&',
136 'lt' => '<','gt' => '>','apos' => "'");
137
138 /**
139 * adds debug data to the class level debug string
140 *
141 * @param string $string debug data
142 * @access private
143 */
144 function debug($string){
145 $this->debug_str .= get_class($this).": $string\n";
146 }
147
148 /**
149 * expands entities, e.g. changes '<' to '&lt;'.
150 *
151 * @param string $val The string in which to expand entities.
152 * @access private
153 */
154 function expandEntities($val) {
155 if ($this->charencoding) {
156 $val = str_replace('&', '&amp;', $val);
157 $val = str_replace("'", '&apos;', $val);
158 $val = str_replace('"', '&quot;', $val);
159 $val = str_replace('<', '&lt;', $val);
160 $val = str_replace('>', '&gt;', $val);
161 }
162 return $val;
163 }
164
165 /**
166 * returns error string if present
167 *
168 * @return boolean $string error string
169 * @access public
170 */
171 function getError(){
172 if($this->error_str != ''){
173 return $this->error_str;
174 }
175 return false;
176 }
177
178 /**
179 * sets error string
180 *
181 * @return boolean $string error string
182 * @access private
183 */
184 function setError($str){
185 $this->error_str = $str;
186 }
187
188 /**
189 * detect if array is a simple array or a struct (associative array)
190 *
191 * @param $val The PHP array
192 * @return string (arraySimple|arrayStruct)
193 * @access private
194 */
195 function isArraySimpleOrStruct($val) {
196 $keyList = array_keys($val);
197 foreach ($keyList as $keyListValue) {
198 if (!is_int($keyListValue)) {
199 return 'arrayStruct';
200 }
201 }
202 return 'arraySimple';
203 }
204
205 /**
206 * serializes PHP values in accordance w/ section 5. Type information is
207 * not serialized if $use == 'literal'.
208 *
209 * @return string
210 * @access public
211 */
212 function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded'){
213 if(is_object($val) && get_class($val) == 'soapval'){
214 return $val->serialize($use);
215 }
216 $this->debug( "in serialize_val: $val, $name, $type, $name_ns, $type_ns, $attributes, $use");
217 // if no name, use item
218 $name = (!$name|| is_numeric($name)) ? 'soapVal' : $name;
219 // if name has ns, add ns prefix to name
220 $xmlns = '';
221 if($name_ns){
222 $prefix = 'nu'.rand(1000,9999);
223 $name = $prefix.':'.$name;
224 $xmlns .= " xmlns:$prefix=\"$name_ns\"";
225 }
226 // if type is prefixed, create type prefix
227 if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
228 // need to fix this. shouldn't default to xsd if no ns specified
229 // w/o checking against typemap
230 $type_prefix = 'xsd';
231 } elseif($type_ns){
232 $type_prefix = 'ns'.rand(1000,9999);
233 $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
234 }
235 // serialize attributes if present
236 $atts = '';
237 if($attributes){
238 foreach($attributes as $k => $v){
239 $atts .= " $k=\"$v\"";
240 }
241 }
242 // serialize if an xsd built-in primitive type
243 if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
244 if (is_bool($val)) {
245 if ($type == 'boolean') {
246 $val = $val ? 'true' : 'false';
247 } elseif (! $val) {
248 $val = 0;
249 }
250 } else if (is_string($val)) {
251 $val = $this->expandEntities($val);
252 }
253 if ($use == 'literal') {
254 return "<$name$xmlns>$val</$name>";
255 } else {
256 return "<$name$xmlns xsi:type=\"xsd:$type\">$val</$name>";
257 }
258 }
259 // detect type and serialize
260 $xml = '';
261 switch(true) {
262 case ($type == '' && is_null($val)):
263 if ($use == 'literal') {
264 // TODO: depends on nillable
265 $xml .= "<$name$xmlns/>";
266 } else {
267 $xml .= "<$name$xmlns xsi:nil=\"true\"/>";
268 }
269 break;
270 case (is_bool($val) || $type == 'boolean'):
271 if ($type == 'boolean') {
272 $val = $val ? 'true' : 'false';
273 } elseif (! $val) {
274 $val = 0;
275 }
276 if ($use == 'literal') {
277 $xml .= "<$name$xmlns $atts>$val</$name>";
278 } else {
279 $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
280 }
281 break;
282 case (is_int($val) || is_long($val) || $type == 'int'):
283 if ($use == 'literal') {
284 $xml .= "<$name$xmlns $atts>$val</$name>";
285 } else {
286 $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
287 }
288 break;
289 case (is_float($val)|| is_double($val) || $type == 'float'):
290 if ($use == 'literal') {
291 $xml .= "<$name$xmlns $atts>$val</$name>";
292 } else {
293 $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
294 }
295 break;
296 case (is_string($val) || $type == 'string'):
297 $val = $this->expandEntities($val);
298 if ($use == 'literal') {
299 $xml .= "<$name$xmlns $atts>$val</$name>";
300 } else {
301 $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
302 }
303 break;
304 case is_object($val):
305 $name = get_class($val);
306 foreach(get_object_vars($val) as $k => $v){
307 $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
308 }
309 $xml .= '<'.$name.'>'.$pXml.'</'.$name.'>';
310 break;
311 break;
312 case (is_array($val) || $type):
313 // detect if struct or array
314 $valueType = $this->isArraySimpleOrStruct($val);
315 if($valueType=='arraySimple' || ereg('^ArrayOf',$type)){
316 $i = 0;
317 if(is_array($val) && count($val)> 0){
318 foreach($val as $v){
319 if(is_object($v) && get_class($v) == 'soapval'){
320 $tt_ns = $v->type_ns;
321 $tt = $v->type;
322 } elseif (is_array($v)) {
323 $tt = $this->isArraySimpleOrStruct($v);
324 } else {
325 $tt = gettype($v);
326 }
327 $array_types[$tt] = 1;
328 $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
329 ++$i;
330 }
331 if(count($array_types) > 1){
332 $array_typename = 'xsd:ur-type';
333 } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
334 if ($tt == 'integer') {
335 $tt = 'int';
336 }
337 $array_typename = 'xsd:'.$tt;
338 } elseif(isset($tt) && $tt == 'arraySimple'){
339 $array_typename = 'SOAP-ENC:Array';
340 } elseif(isset($tt) && $tt == 'arrayStruct'){
341 $array_typename = 'unnamed_struct_use_soapval';
342 } else {
343 // if type is prefixed, create type prefix
344 if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){
345 $array_typename = 'xsd:' . $tt;
346 } elseif ($tt_ns) {
347 $tt_prefix = 'ns' . rand(1000, 9999);
348 $array_typename = "$tt_prefix:$tt";
349 $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
350 } else {
351 $array_typename = $tt;
352 }
353 }
354 $array_type = $i;
355 if ($use == 'literal') {
356 $type_str = '';
357 } else if (isset($type) && isset($type_prefix)) {
358 $type_str = " xsi:type=\"$type_prefix:$type\"";
359 } else {
360 $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
361 }
362 // empty array
363 } else {
364 if ($use == 'literal') {
365 $type_str = '';
366 } else if (isset($type) && isset($type_prefix)) {
367 $type_str = " xsi:type=\"$type_prefix:$type\"";
368 } else {
369 $type_str = " xsi:type=\"SOAP-ENC:Array\"";
370 }
371 }
372 $xml = "<$name$xmlns$type_str$atts>".$xml."</$name>";
373 } else {
374 // got a struct
375 if(isset($type) && isset($type_prefix)){
376 $type_str = " xsi:type=\"$type_prefix:$type\"";
377 } else {
378 $type_str = '';
379 }
380 if ($use == 'literal') {
381 $xml .= "<$name$xmlns $atts>";
382 } else {
383 $xml .= "<$name$xmlns$type_str$atts>";
384 }
385 foreach($val as $k => $v){
386 // Apache Map
387 if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
388 $xml .= '<item>';
389 $xml .= $this->serialize_val($k,'key',false,false,false,false,$use);
390 $xml .= $this->serialize_val($v,'value',false,false,false,false,$use);
391 $xml .= '</item>';
392 } else {
393 $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
394 }
395 }
396 $xml .= "</$name>";
397 }
398 break;
399 default:
400 $xml .= 'not detected, got '.gettype($val).' for '.$val;
401 break;
402 }
403 return $xml;
404 }
405
406 /**
407 * serialize message
408 *
409 * @param string body
410 * @param string headers optional
411 * @param array namespaces optional
412 * @param string style optional (rpc|document)
413 * @param string use optional (encoded|literal)
414 * @return string message
415 * @access public
416 */
417 function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded'){
418 // TODO: add an option to automatically run utf8_encode on $body and $headers
419 // if $this->soap_defencoding is UTF-8. Not doing this automatically allows
420 // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
421
422 // serialize namespaces
423 $ns_string = '';
424 foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
425 $ns_string .= " xmlns:$k=\"$v\"";
426 }
427 if($style == 'rpc' && $use == 'encoded') {
428 $ns_string = ' SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' . $ns_string;
429 }
430
431 // serialize headers
432 if($headers){
433 $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
434 }
435 // serialize envelope
436 return
437 '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
438 '<SOAP-ENV:Envelope'.$ns_string.">".
439 $headers.
440 "<SOAP-ENV:Body>".
441 $body.
442 "</SOAP-ENV:Body>".
443 "</SOAP-ENV:Envelope>";
444 }
445
446 function formatDump($str){
447 $str = htmlspecialchars($str);
448 return nl2br($str);
449 }
450
451 /**
452 * contracts a qualified name
453 *
454 * @param string $string qname
455 * @return string contracted qname
456 * @access private
457 */
458 function contractQname($qname){
459 // get element namespace
460 //$this->xdebug("Contract $qname");
461 if (strrpos($qname, ':')) {
462 // get unqualified name
463 $name = substr($qname, strrpos($qname, ':') + 1);
464 // get ns
465 $ns = substr($qname, 0, strrpos($qname, ':'));
466 $p = $this->getPrefixFromNamespace($ns);
467 if ($p) {
468 return $p . ':' . $name;
469 }
470 return $qname;
471 } else {
472 return $qname;
473 }
474 }
475
476 /**
477 * expands a qualified name
478 *
479 * @param string $string qname
480 * @return string expanded qname
481 * @access private
482 */
483 function expandQname($qname){
484 // get element prefix
485 if(strpos($qname,':') && !ereg('^http://',$qname)){
486 // get unqualified name
487 $name = substr(strstr($qname,':'),1);
488 // get ns prefix
489 $prefix = substr($qname,0,strpos($qname,':'));
490 if(isset($this->namespaces[$prefix])){
491 return $this->namespaces[$prefix].':'.$name;
492 } else {
493 return $qname;
494 }
495 } else {
496 return $qname;
497 }
498 }
499
500 /**
501 * returns the local part of a prefixed string
502 * returns the original string, if not prefixed
503 *
504 * @param string
505 * @return string
506 * @access public
507 */
508 function getLocalPart($str){
509 if($sstr = strrchr($str,':')){
510 // get unqualified name
511 return substr( $sstr, 1 );
512 } else {
513 return $str;
514 }
515 }
516
517 /**
518 * returns the prefix part of a prefixed string
519 * returns false, if not prefixed
520 *
521 * @param string
522 * @return mixed
523 * @access public
524 */
525 function getPrefix($str){
526 if($pos = strrpos($str,':')){
527 // get prefix
528 return substr($str,0,$pos);
529 }
530 return false;
531 }
532
533 /**
534 * pass it a prefix, it returns a namespace
535 * returns false if no namespace registered with the given prefix
536 *
537 * @param string
538 * @return mixed
539 * @access public
540 */
541 function getNamespaceFromPrefix($prefix){
542 if (isset($this->namespaces[$prefix])) {
543 return $this->namespaces[$prefix];
544 }
545 //$this->setError("No namespace registered for prefix '$prefix'");
546 return false;
547 }
548
549 /**
550 * returns the prefix for a given namespace (or prefix)
551 * or false if no prefixes registered for the given namespace
552 *
553 * @param string
554 * @return mixed
555 * @access public
556 */
557 function getPrefixFromNamespace($ns) {
558 foreach ($this->namespaces as $p => $n) {
559 if ($ns == $n || $ns == $p) {
560 $this->usedNamespaces[$p] = $n;
561 return $p;
562 }
563 }
564 return false;
565 }
566
567 function varDump($data) {
568 ob_start();
569 var_dump($data);
570 $ret_val = ob_get_contents();
571 ob_end_clean();
572 return $ret_val;
573 }
574 }
575
576 // XML Schema Datatype Helper Functions
577
578 //xsd:dateTime helpers
579
580 /**
581 * convert unix timestamp to ISO 8601 compliant date string
582 *
583 * @param string $timestamp Unix time stamp
584 * @access public
585 */
586 function timestamp_to_iso8601($timestamp,$utc=true){
587 $datestr = date('Y-m-d\TH:i:sO',$timestamp);
588 if($utc){
589 $eregStr =
590 '([0-9]{4})-'. // centuries & years CCYY-
591 '([0-9]{2})-'. // months MM-
592 '([0-9]{2})'. // days DD
593 'T'. // separator T
594 '([0-9]{2}):'. // hours hh:
595 '([0-9]{2}):'. // minutes mm:
596 '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
597 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
598
599 if(ereg($eregStr,$datestr,$regs)){
600 return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
601 }
602 return false;
603 } else {
604 return $datestr;
605 }
606 }
607
608 /**
609 * convert ISO 8601 compliant date string to unix timestamp
610 *
611 * @param string $datestr ISO 8601 compliant date string
612 * @access public
613 */
614 function iso8601_to_timestamp($datestr){
615 $eregStr =
616 '([0-9]{4})-'. // centuries & years CCYY-
617 '([0-9]{2})-'. // months MM-
618 '([0-9]{2})'. // days DD
619 'T'. // separator T
620 '([0-9]{2}):'. // hours hh:
621 '([0-9]{2}):'. // minutes mm:
622 '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
623 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
624 if(ereg($eregStr,$datestr,$regs)){
625 // not utc
626 if($regs[8] != 'Z'){
627 $op = substr($regs[8],0,1);
628 $h = substr($regs[8],1,2);
629 $m = substr($regs[8],strlen($regs[8])-2,2);
630 if($op == '-'){
631 $regs[4] = $regs[4] + $h;
632 $regs[5] = $regs[5] + $m;
633 } elseif($op == '+'){
634 $regs[4] = $regs[4] - $h;
635 $regs[5] = $regs[5] - $m;
636 }
637 }
638 return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
639 } else {
640 return false;
641 }
642 }
643
644 function usleepWindows($usec)
645 {
646 $start = gettimeofday();
647
648 do
649 {
650 $stop = gettimeofday();
651 $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
652 + $stop['usec'] - $start['usec'];
653 }
654 while ($timePassed < $usec);
655 }
656
657 ?><?php
658
659
660
661 /**
662 * soap_fault class, allows for creation of faults
663 * mainly used for returning faults from deployed functions
664 * in a server instance.
665 * @author Dietrich Ayala <dietrich@ganx4.com>
666 * @version $Id$
667 * @access public
668 */
669 class soap_fault extends nusoap_base {
670
671 var $faultcode;
672 var $faultactor;
673 var $faultstring;
674 var $faultdetail;
675
676 /**
677 * constructor
678 *
679 * @param string $faultcode (client | server)
680 * @param string $faultactor only used when msg routed between multiple actors
681 * @param string $faultstring human readable error message
682 * @param string $faultdetail
683 */
684 function soap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
685 $this->faultcode = $faultcode;
686 $this->faultactor = $faultactor;
687 $this->faultstring = $faultstring;
688 $this->faultdetail = $faultdetail;
689 }
690
691 /**
692 * serialize a fault
693 *
694 * @access public
695 */
696 function serialize(){
697 $ns_string = '';
698 foreach($this->namespaces as $k => $v){
699 $ns_string .= "\n xmlns:$k=\"$v\"";
700 }
701 $return_msg =
702 '<?xml version="1.0" encoding="'.$this->soap_defencoding.'"?>'.
703 '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
704 '<SOAP-ENV:Body>'.
705 '<SOAP-ENV:Fault>'.
706 '<faultcode>'.$this->expandEntities($this->faultcode).'</faultcode>'.
707 '<faultactor>'.$this->expandEntities($this->faultactor).'</faultactor>'.
708 '<faultstring>'.$this->expandEntities($this->faultstring).'</faultstring>'.
709 '<detail>'.$this->serialize_val($this->faultdetail).'</detail>'.
710 '</SOAP-ENV:Fault>'.
711 '</SOAP-ENV:Body>'.
712 '</SOAP-ENV:Envelope>';
713 return $return_msg;
714 }
715 }
716
717
718
719 ?><?php
720
721
722
723 /**
724 * parses an XML Schema, allows access to it's data, other utility methods
725 * no validation... yet.
726 * very experimental and limited. As is discussed on XML-DEV, I'm one of the people
727 * that just doesn't have time to read the spec(s) thoroughly, and just have a couple of trusty
728 * tutorials I refer to :)
729 *
730 * @author Dietrich Ayala <dietrich@ganx4.com>
731 * @version $Id$
732 * @access public
733 */
734 class XMLSchema extends nusoap_base {
735
736 // files
737 var $schema = '';
738 var $xml = '';
739 // namespaces
740 var $enclosingNamespaces;
741 // schema info
742 var $schemaInfo = array();
743 var $schemaTargetNamespace = '';
744 // types, elements, attributes defined by the schema
745 var $attributes = array();
746 var $complexTypes = array();
747 var $currentComplexType = false;
748 var $elements = array();
749 var $currentElement = false;
750 var $simpleTypes = array();
751 var $currentSimpleType = false;
752 // imports
753 var $imports = array();
754 // parser vars
755 var $parser;
756 var $position = 0;
757 var $depth = 0;
758 var $depth_array = array();
759 var $message = array();
760 var $defaultNamespace = array();
761
762 /**
763 * constructor
764 *
765 * @param string $schema schema document URI
766 * @param string $xml xml document URI
767 * @param string $namespaces namespaces defined in enclosing XML
768 * @access public
769 */
770 function XMLSchema($schema='',$xml='',$namespaces=array()){
771
772 $this->debug('xmlschema class instantiated, inside constructor');
773 // files
774 $this->schema = $schema;
775 $this->xml = $xml;
776
777 // namespaces
778 $this->enclosingNamespaces = $namespaces;
779 $this->namespaces = array_merge($this->namespaces, $namespaces);
780
781 // parse schema file
782 if($schema != ''){
783 $this->debug('initial schema file: '.$schema);
784 $this->parseFile($schema, 'schema');
785 }
786
787 // parse xml file
788 if($xml != ''){
789 $this->debug('initial xml file: '.$xml);
790 $this->parseFile($xml, 'xml');
791 }
792
793 }
794
795 /**
796 * parse an XML file
797 *
798 * @param string $xml, path/URL to XML file
799 * @param string $type, (schema | xml)
800 * @return boolean
801 * @access public
802 */
803 function parseFile($xml,$type){
804 // parse xml file
805 if($xml != ""){
806 $xmlStr = @join("",@file($xml));
807 if($xmlStr == ""){
808 $msg = 'Error reading XML from '.$xml;
809 $this->setError($msg);
810 $this->debug($msg);
811 return false;
812 } else {
813 $this->debug("parsing $xml");
814 $this->parseString($xmlStr,$type);
815 $this->debug("done parsing $xml");
816 return true;
817 }
818 }
819 return false;
820 }
821
822 /**
823 * parse an XML string
824 *
825 * @param string $xml path or URL
826 * @param string $type, (schema|xml)
827 * @access private
828 */
829 function parseString($xml,$type){
830 // parse xml string
831 if($xml != ""){
832
833 // Create an XML parser.
834 $this->parser = xml_parser_create();
835 // Set the options for parsing the XML data.
836 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
837
838 // Set the object for the parser.
839 xml_set_object($this->parser, $this);
840
841 // Set the element handlers for the parser.
842 if($type == "schema"){
843 xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
844 xml_set_character_data_handler($this->parser,'schemaCharacterData');
845 } elseif($type == "xml"){
846 xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
847 xml_set_character_data_handler($this->parser,'xmlCharacterData');
848 }
849
850 // Parse the XML file.
851 if(!xml_parse($this->parser,$xml,true)){
852 // Display an error message.
853 $errstr = sprintf('XML error parsing XML schema on line %d: %s',
854 xml_get_current_line_number($this->parser),
855 xml_error_string(xml_get_error_code($this->parser))
856 );
857 $this->debug($errstr);
858 $this->debug("XML payload:\n" . $xml);
859 $this->setError($errstr);
860 }
861
862 xml_parser_free($this->parser);
863 } else{
864 $this->debug('no xml passed to parseString()!!');
865 $this->setError('no xml passed to parseString()!!');
866 }
867 }
868
869 /**
870 * start-element handler
871 *
872 * @param string $parser XML parser object
873 * @param string $name element name
874 * @param string $attrs associative array of attributes
875 * @access private
876 */
877 function schemaStartElement($parser, $name, $attrs) {
878
879 // position in the total number of elements, starting from 0
880 $pos = $this->position++;
881 $depth = $this->depth++;
882 // set self as current value for this depth
883 $this->depth_array[$depth] = $pos;
884 $this->message[$pos] = array('cdata' => '');
885 if ($depth > 0) {
886 $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
887 } else {
888 $this->defaultNamespace[$pos] = false;
889 }
890
891 // get element prefix
892 if($prefix = $this->getPrefix($name)){
893 // get unqualified name
894 $name = $this->getLocalPart($name);
895 } else {
896 $prefix = '';
897 }
898
899 // loop thru attributes, expanding, and registering namespace declarations
900 if(count($attrs) > 0){
901 foreach($attrs as $k => $v){
902 // if ns declarations, add to class level array of valid namespaces
903 if(ereg("^xmlns",$k)){
904 //$this->xdebug("$k: $v");
905 //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
906 if($ns_prefix = substr(strrchr($k,':'),1)){
907 //$this->xdebug("Add namespace[$ns_prefix] = $v");
908 $this->namespaces[$ns_prefix] = $v;
909 } else {
910 $this->defaultNamespace[$pos] = $v;
911 if (! $this->getPrefixFromNamespace($v)) {
912 $this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
913 }
914 }
915 if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema'){
916 $this->XMLSchemaVersion = $v;
917 $this->namespaces['xsi'] = $v.'-instance';
918 }
919 }
920 }
921 foreach($attrs as $k => $v){
922 // expand each attribute
923 $k = strpos($k,':') ? $this->expandQname($k) : $k;
924 $v = strpos($v,':') ? $this->expandQname($v) : $v;
925 $eAttrs[$k] = $v;
926 }
927 $attrs = $eAttrs;
928 } else {
929 $attrs = array();
930 }
931 // find status, register data
932 switch($name){
933 case 'all':
934 case 'choice':
935 case 'sequence':
936 //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
937 $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
938 if($name == 'all' || $name == 'sequence'){
939 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
940 }
941 break;
942 case 'attribute':
943 //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
944 $this->xdebug("parsing attribute " . $this->varDump($attrs));
945 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
946 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
947 if (!strpos($v, ':')) {
948 // no namespace in arrayType attribute value...
949 if ($this->defaultNamespace[$pos]) {
950 // ...so use the default
951 $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
952 }
953 }
954 }
955 if(isset($attrs['name'])){
956 $this->attributes[$attrs['name']] = $attrs;
957 $aname = $attrs['name'];
958 } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
959 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
960 $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
961 } else {
962 $aname = '';
963 }
964 } elseif(isset($attrs['ref'])){
965 $aname = $attrs['ref'];
966 $this->attributes[$attrs['ref']] = $attrs;
967 }
968
969 if(isset($this->currentComplexType)){
970 $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
971 } elseif(isset($this->currentElement)){
972 $this->elements[$this->currentElement]['attrs'][$aname] = $attrs;
973 }
974 // arrayType attribute
975 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
976 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
977 $prefix = $this->getPrefix($aname);
978 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
979 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
980 } else {
981 $v = '';
982 }
983 if(strpos($v,'[,]')){
984 $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
985 }
986 $v = substr($v,0,strpos($v,'[')); // clip the []
987 if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
988 $v = $this->XMLSchemaVersion.':'.$v;
989 }
990 $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
991 }
992 break;
993 case 'complexType':
994 if(isset($attrs['name'])){
995 $this->xdebug('processing named complexType '.$attrs['name']);
996 $this->currentElement = false;
997 $this->currentComplexType = $attrs['name'];
998 $this->complexTypes[$this->currentComplexType] = $attrs;
999 $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1000 if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
1001 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1002 } else {
1003 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1004 }
1005 }else{
1006 $this->xdebug('processing unnamed complexType for element '.$this->currentElement);
1007 $this->currentComplexType = $this->currentElement . '_ContainedType';
1008 $this->currentElement = false;
1009 $this->complexTypes[$this->currentComplexType] = $attrs;
1010 $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1011 if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
1012 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1013 } else {
1014 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1015 }
1016 }
1017 break;
1018 case 'element':
1019 // elements defined as part of a complex type should
1020 // not really be added to $this->elements, but for some
1021 // reason, they are
1022 if(isset($attrs['type'])){
1023 $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']);
1024 $this->currentElement = $attrs['name'];
1025 $this->elements[ $attrs['name'] ] = $attrs;
1026 $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1027 if (!isset($this->elements[ $attrs['name'] ]['form'])) {
1028 $this->elements[ $attrs['name'] ]['form'] = $this->schemaInfo['elementFormDefault'];
1029 }
1030 $ename = $attrs['name'];
1031 } elseif(isset($attrs['ref'])){
1032 $ename = $attrs['ref'];
1033 } else {
1034 $this->xdebug("processing untyped element ".$attrs['name']);
1035 $this->currentElement = $attrs['name'];
1036 $this->elements[ $attrs['name'] ] = $attrs;
1037 $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1038 $this->elements[ $attrs['name'] ]['type'] = $this->schemaTargetNamespace . ':' . $attrs['name'] . '_ContainedType';
1039 if (!isset($this->elements[ $attrs['name'] ]['form'])) {
1040 $this->elements[ $attrs['name'] ]['form'] = $this->schemaInfo['elementFormDefault'];
1041 }
1042 }
1043 if(isset($ename) && $this->currentComplexType){
1044 $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
1045 }
1046 break;
1047 // we ignore enumeration values
1048 //case 'enumeration':
1049 //break;
1050 case 'import':
1051 if (isset($attrs['schemaLocation'])) {
1052 //$this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
1053 $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
1054 } else {
1055 //$this->xdebug('import namespace ' . $attrs['namespace']);
1056 $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
1057 if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
1058 $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
1059 }
1060 }
1061 break;
1062 case 'restriction':
1063 //$this->xdebug("in restriction for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
1064 if($this->currentElement){
1065 $this->elements[$this->currentElement]['type'] = $attrs['base'];
1066 } elseif($this->currentSimpleType){
1067 $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
1068 } elseif($this->currentComplexType){
1069 $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
1070 if(strstr($attrs['base'],':') == ':Array'){
1071 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1072 }
1073 }
1074 break;
1075 case 'schema':
1076 $this->schemaInfo = $attrs;
1077 $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
1078 if (isset($attrs['targetNamespace'])) {
1079 $this->schemaTargetNamespace = $attrs['targetNamespace'];
1080 }
1081 if (!isset($attrs['elementFormDefault'])) {
1082 $this->schemaInfo['elementFormDefault'] = 'unqualified';
1083 }
1084 break;
1085 case 'simpleType':
1086 if(isset($attrs['name'])){
1087 $this->xdebug("processing simpleType for name " . $attrs['name']);
1088 $this->currentSimpleType = $attrs['name'];
1089 $this->simpleTypes[ $attrs['name'] ] = $attrs;
1090 $this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType';
1091 $this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar';
1092 } else {
1093 //echo 'not parsing: '.$name;
1094 //var_dump($attrs);
1095 }
1096 break;
1097 default:
1098 //$this->xdebug("do not have anything to do for element $name");
1099 }
1100 }
1101
1102 /**
1103 * end-element handler
1104 *
1105 * @param string $parser XML parser object
1106 * @param string $name element name
1107 * @access private
1108 */
1109 function schemaEndElement($parser, $name) {
1110 // bring depth down a notch
1111 $this->depth--;
1112 // position of current element is equal to the last value left in depth_array for my depth
1113 if(isset($this->depth_array[$this->depth])){
1114 $pos = $this->depth_array[$this->depth];
1115 }
1116 // move on...
1117 if($name == 'complexType'){
1118 $this->currentComplexType = false;
1119 $this->currentElement = false;
1120 }
1121 if($name == 'element'){
1122 $this->currentElement = false;
1123 }
1124 if($name == 'simpleType'){
1125 $this->currentSimpleType = false;
1126 }
1127 }
1128
1129 /**
1130 * element content handler
1131 *
1132 * @param string $parser XML parser object
1133 * @param string $data element content
1134 * @access private
1135 */
1136 function schemaCharacterData($parser, $data){
1137 $pos = $this->depth_array[$this->depth - 1];
1138 $this->message[$pos]['cdata'] .= $data;
1139 }
1140
1141 /**
1142 * serialize the schema
1143 *
1144 * @access public
1145 */
1146 function serializeSchema(){
1147
1148 $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
1149 $xml = '';
1150 // imports
1151 if (sizeof($this->imports) > 0) {
1152 foreach($this->imports as $ns => $list) {
1153 foreach ($list as $ii) {
1154 if ($ii['location'] != '') {
1155 $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
1156 } else {
1157 $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
1158 }
1159 }
1160 }
1161 }
1162 // complex types
1163 foreach($this->complexTypes as $typeName => $attrs){
1164 $contentStr = '';
1165 // serialize child elements
1166 if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){
1167 foreach($attrs['elements'] as $element => $eParts){
1168 if(isset($eParts['ref'])){
1169 $contentStr .= " <$schemaPrefix:element ref=\"$element\"/>\n";
1170 } else {
1171 $contentStr .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"/>\n";
1172 }
1173 }
1174 }
1175 // attributes
1176 if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){
1177 foreach($attrs['attrs'] as $attr => $aParts){
1178 $contentStr .= " <$schemaPrefix:attribute ref=\"".$this->contractQName($aParts['ref']).'"';
1179 if(isset($aParts['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
1180 $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
1181 $contentStr .= ' wsdl:arrayType="'.$this->contractQName($aParts['http://schemas.xmlsoap.org/wsdl/:arrayType']).'"';
1182 }
1183 $contentStr .= "/>\n";
1184 }
1185 }
1186 // if restriction
1187 if( isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
1188 $contentStr = " <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr." </$schemaPrefix:restriction>\n";
1189 }
1190 // compositor obviates complex/simple content
1191 if(isset($attrs['compositor']) && ($attrs['compositor'] != '')){
1192 $contentStr = " <$schemaPrefix:$attrs[compositor]>\n".$contentStr." </$schemaPrefix:$attrs[compositor]>\n";
1193 }
1194 // complex or simple content
1195 elseif((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){
1196 $contentStr = " <$schemaPrefix:complexContent>\n".$contentStr." </$schemaPrefix:complexContent>\n";
1197 }
1198 // finalize complex type
1199 if($contentStr != ''){
1200 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n";
1201 } else {
1202 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
1203 }
1204 $xml .= $contentStr;
1205 }
1206 // simple types
1207 if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){
1208 foreach($this->simpleTypes as $typeName => $attr){
1209 $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n <restriction base=\"".$this->contractQName($eParts['type'])."\"/>\n </$schemaPrefix:simpleType>";
1210 }
1211 }
1212 // elements
1213 if(isset($this->elements) && count($this->elements) > 0){
1214 foreach($this->elements as $element => $eParts){
1215 $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n";
1216 }
1217 }
1218 // attributes
1219 if(isset($this->attributes) && count($this->attributes) > 0){
1220 foreach($this->attributes as $attr => $aParts){
1221 $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>";
1222 }
1223 }
1224 // finish 'er up
1225 $el = "<$schemaPrefix:schema targetNamespace=\"$this->schemaTargetNamespace\"\n";
1226 foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
1227 $el .= " xmlns:$nsp=\"$ns\"\n";
1228 }
1229 $xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n";
1230 return $xml;
1231 }
1232
1233 /**
1234 * adds debug data to the clas level debug string
1235 *
1236 * @param string $string debug data
1237 * @access private
1238 */
1239 function xdebug($string){
1240 $this->debug('<' . $this->schemaTargetNamespace . '> '.$string);
1241 }
1242
1243 /**
1244 * get the PHP type of a user defined type in the schema
1245 * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1246 * returns false if no type exists, or not w/ the given namespace
1247 * else returns a string that is either a native php type, or 'struct'
1248 *
1249 * @param string $type, name of defined type
1250 * @param string $ns, namespace of type
1251 * @return mixed
1252 * @access public
1253 */
1254 function getPHPType($type,$ns){
1255 if(isset($this->typemap[$ns][$type])){
1256 //print "found type '$type' and ns $ns in typemap<br>";
1257 return $this->typemap[$ns][$type];
1258 } elseif(isset($this->complexTypes[$type])){
1259 //print "getting type '$type' and ns $ns from complexTypes array<br>";
1260 return $this->complexTypes[$type]['phpType'];
1261 }
1262 return false;
1263 }
1264
1265 /**
1266 * returns an array of information about a given type
1267 * returns false if no type exists by the given name
1268 *
1269 * typeDef = array(
1270 * 'elements' => array(), // refs to elements array
1271 * 'restrictionBase' => '',
1272 * 'phpType' => '',
1273 * 'order' => '(sequence|all)',
1274 * 'attrs' => array() // refs to attributes array
1275 * )
1276 *
1277 * @param string
1278 * @return mixed
1279 * @access public
1280 */
1281 function getTypeDef($type){
1282 //$this->debug("in getTypeDef for type $type");
1283 if(isset($this->complexTypes[$type])){
1284 $this->xdebug("in getTypeDef, found complexType $type");
1285 return $this->complexTypes[$type];
1286 } elseif(isset($this->simpleTypes[$type])){
1287 $this->xdebug("in getTypeDef, found simpleType $type");
1288 if (!isset($this->simpleTypes[$type]['phpType'])) {
1289 // get info for type to tack onto the simple type
1290 // TODO: can this ever really apply (i.e. what is a simpleType really?)
1291 $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1);
1292 $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':'));
1293 $etype = $this->getTypeDef($uqType);
1294 if ($etype) {
1295 if (isset($etype['phpType'])) {
1296 $this->simpleTypes[$type]['phpType'] = $etype['phpType'];
1297 }
1298 if (isset($etype['elements'])) {
1299 $this->simpleTypes[$type]['elements'] = $etype['elements'];
1300 }
1301 }
1302 }
1303 return $this->simpleTypes[$type];
1304 } elseif(isset($this->elements[$type])){
1305 $this->xdebug("in getTypeDef, found element $type");
1306 if (!isset($this->elements[$type]['phpType'])) {
1307 // get info for type to tack onto the element
1308 $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1);
1309 $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':'));
1310 $etype = $this->getTypeDef($uqType);
1311 if ($etype) {
1312 if (isset($etype['phpType'])) {
1313 $this->elements[$type]['phpType'] = $etype['phpType'];
1314 }
1315 if (isset($etype['elements'])) {
1316 $this->elements[$type]['elements'] = $etype['elements'];
1317 }
1318 } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
1319 $this->elements[$type]['phpType'] = 'scalar';
1320 }
1321 }
1322 return $this->elements[$type];
1323 } elseif(isset($this->attributes[$type])){
1324 $this->xdebug("in getTypeDef, found attribute $type");
1325 return $this->attributes[$type];
1326 }
1327 $this->xdebug("in getTypeDef, did not find $type");
1328 return false;
1329 }
1330
1331 /**
1332 * returns a sample serialization of a given type, or false if no type by the given name
1333 *
1334 * @param string $type, name of type
1335 * @return mixed
1336 * @access public
1337 */
1338 function serializeTypeDef($type){
1339 //print "in sTD() for type $type<br>";
1340 if($typeDef = $this->getTypeDef($type)){
1341 $str .= '<'.$type;
1342 if(is_array($typeDef['attrs'])){
1343 foreach($attrs as $attName => $data){
1344 $str .= " $attName=\"{type = ".$data['type']."}\"";
1345 }
1346 }
1347 $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
1348 if(count($typeDef['elements']) > 0){
1349 $str .= ">";
1350 foreach($typeDef['elements'] as $element => $eData){
1351 $str .= $this->serializeTypeDef($element);
1352 }
1353 $str .= "</$type>";
1354 } elseif($typeDef['typeClass'] == 'element') {
1355 $str .= "></$type>";
1356 } else {
1357 $str .= "/>";
1358 }
1359 return $str;
1360 }
1361 return false;
1362 }
1363
1364 /**
1365 * returns HTML form elements that allow a user
1366 * to enter values for creating an instance of the given type.
1367 *
1368 * @param string $name, name for type instance
1369 * @param string $type, name of type
1370 * @return string
1371 * @access public
1372 */
1373 function typeToForm($name,$type){
1374 // get typedef
1375 if($typeDef = $this->getTypeDef($type)){
1376 // if struct
1377 if($typeDef['phpType'] == 'struct'){
1378 $buffer .= '<table>';
1379 foreach($typeDef['elements'] as $child => $childDef){
1380 $buffer .= "
1381 <tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>
1382 <td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
1383 }
1384 $buffer .= '</table>';
1385 // if array
1386 } elseif($typeDef['phpType'] == 'array'){
1387 $buffer .= '<table>';
1388 for($i=0;$i < 3; $i++){
1389 $buffer .= "
1390 <tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
1391 <td><input type='text' name='parameters[".$name."][]'></td></tr>";
1392 }
1393 $buffer .= '</table>';
1394 // if scalar
1395 } else {
1396 $buffer .= "<input type='text' name='parameters[$name]'>";
1397 }
1398 } else {
1399 $buffer .= "<input type='text' name='parameters[$name]'>";
1400 }
1401 return $buffer;
1402 }
1403
1404 /**
1405 * adds a complex type to the schema
1406 *
1407 * example: array
1408 *
1409 * addType(
1410 * 'ArrayOfstring',
1411 * 'complexType',
1412 * 'array',
1413 * '',
1414 * 'SOAP-ENC:Array',
1415 * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1416 * 'xsd:string'
1417 * );
1418 *
1419 * example: PHP associative array ( SOAP Struct )
1420 *
1421 * addType(
1422 * 'SOAPStruct',
1423 * 'complexType',
1424 * 'struct',
1425 * 'all',
1426 * array('myVar'=> array('name'=>'myVar','type'=>'string')
1427 * );
1428 *
1429 * @param name
1430 * @param typeClass (complexType|simpleType|attribute)
1431 * @param phpType: currently supported are array and struct (php assoc array)
1432 * @param compositor (all|sequence|choice)
1433 * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1434 * @param elements = array ( name = array(name=>'',type=>'') )
1435 * @param attrs = array(
1436 * array(
1437 * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1438 * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1439 * )
1440 * )
1441 * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1442 *
1443 */
1444 function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
1445 $this->complexTypes[$name] = array(
1446 'name' => $name,
1447 'typeClass' => $typeClass,
1448 'phpType' => $phpType,
1449 'compositor'=> $compositor,
1450 'restrictionBase' => $restrictionBase,
1451 'elements' => $elements,
1452 'attrs' => $attrs,
1453 'arrayType' => $arrayType
1454 );
1455
1456 $this->xdebug("addComplexType $name: " . $this->varDump($this->complexTypes[$name]));
1457 }
1458
1459 /**
1460 * adds a simple type to the schema
1461 *
1462 * @param name
1463 * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1464 * @param typeClass (simpleType)
1465 * @param phpType: (scalar)
1466 * @see xmlschema
1467 *
1468 */
1469 function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar') {
1470 $this->simpleTypes[$name] = array(
1471 'name' => $name,
1472 'typeClass' => $typeClass,
1473 'phpType' => $phpType,
1474 'type' => $restrictionBase
1475 );
1476
1477 $this->xdebug("addSimpleType $name: " . $this->varDump($this->simpleTypes[$name]));
1478 }
1479 }
1480
1481
1482
1483 ?><?php
1484
1485
1486
1487 /**
1488 * for creating serializable abstractions of native PHP types
1489 * NOTE: this is only really used when WSDL is not available.
1490 *
1491 * @author Dietrich Ayala <dietrich@ganx4.com>
1492 * @version $Id$
1493 * @access public
1494 */
1495 class soapval extends nusoap_base {
1496 /**
1497 * constructor
1498 *
1499 * @param string $name optional name
1500 * @param string $type optional type name
1501 * @param mixed $value optional value
1502 * @param string $namespace optional namespace of value
1503 * @param string $type_namespace optional namespace of type
1504 * @param array $attributes associative array of attributes to add to element serialization
1505 * @access public
1506 */
1507 function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
1508 $this->name = $name;
1509 $this->value = $value;
1510 $this->type = $type;
1511 $this->element_ns = $element_ns;
1512 $this->type_ns = $type_ns;
1513 $this->attributes = $attributes;
1514 }
1515
1516 /**
1517 * return serialized value
1518 *
1519 * @return string XML data
1520 * @access private
1521 */
1522 function serialize($use='encoded') {
1523 return $this->serialize_val($this->value,$this->name,$this->type,$this->element_ns,$this->type_ns,$this->attributes,$use);
1524 }
1525
1526 /**
1527 * decodes a soapval object into a PHP native type
1528 *
1529 * @param object $soapval optional SOAPx4 soapval object, else uses self
1530 * @return mixed
1531 * @access public
1532 */
1533 function decode(){
1534 return $this->value;
1535 }
1536 }
1537
1538
1539
1540 ?><?php
1541
1542
1543
1544 /**
1545 * transport class for sending/receiving data via HTTP and HTTPS
1546 * NOTE: PHP must be compiled with the CURL extension for HTTPS support
1547 *
1548 * @author Dietrich Ayala <dietrich@ganx4.com>
1549 * @version $Id$
1550 * @access public
1551 */
1552 class soap_transport_http extends nusoap_base {
1553
1554 var $url = '';
1555 var $uri = '';
1556 var $scheme = '';
1557 var $host = '';
1558 var $port = '';
1559 var $path = '';
1560 var $request_method = 'POST';
1561 var $protocol_version = '1.0';
1562 var $encoding = '';
1563 var $outgoing_headers = array();
1564 var $incoming_headers = array();
1565 var $outgoing_payload = '';
1566 var $incoming_payload = '';
1567 var $useSOAPAction = true;
1568 var $persistentConnection = false;
1569 var $ch = false; // cURL handle
1570 var $username;
1571 var $password;
1572
1573 /**
1574 * constructor
1575 */
1576 function soap_transport_http($url){
1577 $this->url = $url;
1578
1579 $u = parse_url($url);
1580 foreach($u as $k => $v){
1581 $this->debug("$k = $v");
1582 $this->$k = $v;
1583 }
1584
1585 // add any GET params to path
1586 if(isset($u['query']) && $u['query'] != ''){
1587 $this->path .= '?' . $u['query'];
1588 }
1589
1590 // set default port
1591 if(!isset($u['port'])){
1592 if($u['scheme'] == 'https'){
1593 $this->port = 443;
1594 } else {
1595 $this->port = 80;
1596 }
1597 }
1598
1599 $this->uri = $this->path;
1600
1601 // build headers
1602 ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
1603 $this->outgoing_headers['User-Agent'] = $this->title.'/'.$this->version.' ('.$rev[1].')';
1604 if (!isset($u['port'])) {
1605 $this->outgoing_headers['Host'] = $this->host;
1606 } else {
1607 $this->outgoing_headers['Host'] = $this->host.':'.$this->port;
1608 }
1609
1610 if (isset($u['user']) && $u['user'] != '') {
1611 $this->setCredentials($u['user'], isset($u['pass']) ? $u['pass'] : '');
1612 }
1613 }
1614
1615 function connect($connection_timeout=0,$response_timeout=30){
1616 // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
1617 // "regular" socket.
1618 // TODO: disabled for now because OpenSSL must be *compiled* in (not just
1619 // loaded), and until PHP5 stream_get_wrappers is not available.
1620 // if ($this->scheme == 'https') {
1621 // if (version_compare(phpversion(), '4.3.0') >= 0) {
1622 // if (extension_loaded('openssl')) {
1623 // $this->scheme = 'ssl';
1624 // $this->debug('Using SSL over OpenSSL');
1625 // }
1626 // }
1627 // }
1628 $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
1629 if ($this->scheme == 'http' || $this->scheme == 'ssl') {
1630 // use persistent connection
1631 if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){
1632 if (!feof($this->fp)) {
1633 $this->debug('Re-use persistent connection');
1634 return true;
1635 }
1636 fclose($this->fp);
1637 $this->debug('Closed persistent connection at EOF');
1638 }
1639
1640 // munge host if using OpenSSL
1641 if ($this->scheme == 'ssl') {
1642 $host = 'ssl://' . $this->host;
1643 } else {
1644 $host = $this->host;
1645 }
1646 $this->debug('calling fsockopen with host ' . $host);
1647
1648 // open socket
1649 if($connection_timeout > 0){
1650 $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout);
1651 } else {
1652 $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str);
1653 }
1654
1655 // test pointer
1656 if(!$this->fp) {
1657 $msg = 'Couldn\'t open socket connection to server ' . $this->url;
1658 if ($this->errno) {
1659 $msg .= ', Error ('.$this->errno.'): '.$this->error_str;
1660 } else {
1661 $msg .= ' prior to connect(). This is often a problem looking up the host name.';
1662 }
1663 $this->debug($msg);
1664 $this->setError($msg);
1665 return false;
1666 }
1667
1668 // set response timeout
1669 socket_set_timeout( $this->fp, $response_timeout);
1670
1671 $this->debug('socket connected');
1672 return true;
1673 } else if ($this->scheme == 'https') {
1674 if (!extension_loaded('curl')) {
1675 $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
1676 return false;
1677 }
1678 $this->debug('connect using https');
1679 // init CURL
1680 $this->ch = curl_init();
1681 // set url
1682 $hostURL = ($this->port != '') ? "https://$this->host:$this->port" : "https://$this->host";
1683 // add path
1684 $hostURL .= $this->path;
1685 curl_setopt($this->ch, CURLOPT_URL, $hostURL);
1686 // ask for headers in the response output
1687 curl_setopt($this->ch, CURLOPT_HEADER, 1);
1688 // ask for the response output as the return value
1689 curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
1690 // encode
1691 // We manage this ourselves through headers and encoding
1692 // if(function_exists('gzuncompress')){
1693 // curl_setopt($this->ch, CURLOPT_ENCODING, 'deflate');
1694 // }
1695 // persistent connection
1696 if ($this->persistentConnection) {
1697 // The way we send data, we cannot use persistent connections, since
1698 // there will be some "junk" at the end of our request.
1699 //curl_setopt($this->ch, CURL_HTTP_VERSION_1_1, true);
1700 $this->persistentConnection = false;
1701 $this->outgoing_headers['Connection'] = 'close';
1702 }
1703 // set timeout (NOTE: cURL does not have separate connection and response timeouts)
1704 if ($connection_timeout != 0) {
1705 curl_setopt($this->ch, CURLOPT_TIMEOUT, $connection_timeout);
1706 }
1707
1708 // recent versions of cURL turn on peer/host checking by default,
1709 // while PHP binaries are not compiled with a default location for the
1710 // CA cert bundle, so disable peer/host checking.
1711 //curl_setopt($this->ch, CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
1712 curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 0);
1713 curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 0);
1714
1715 /*
1716 TODO: support client certificates (thanks Tobias Boes)
1717 curl_setopt($this->ch, CURLOPT_CAINFO, '$pathToPemFiles/rootca.pem');
1718 curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 1);
1719 curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 1);
1720 curl_setopt($this->ch, CURLOPT_SSLCERT, '$pathToPemFiles/mycert.pem');
1721 curl_setopt($this->ch, CURLOPT_SSLKEY, '$pathToPemFiles/mykey.pem');
1722 */
1723 $this->debug('cURL connection set up');
1724 return true;
1725 } else {
1726 $this->setError('Unknown scheme ' . $this->scheme);
1727 $this->debug('Unknown scheme ' . $this->scheme);
1728 return false;
1729 }
1730 }
1731
1732 /**
1733 * send the SOAP message via HTTP
1734 *
1735 * @param string $data message data
1736 * @param integer $timeout set connection timeout in seconds
1737 * @param integer $response_timeout set response timeout in seconds
1738 * @return string data
1739 * @access public
1740 */
1741 function send($data, $timeout=0, $response_timeout=30) {
1742
1743 $this->debug('entered send() with data of length: '.strlen($data));
1744
1745 $this->tryagain = true;
1746 $tries = 0;
1747 while ($this->tryagain) {
1748 $this->tryagain = false;
1749 if ($tries++ < 2) {
1750 // make connnection
1751 if (!$this->connect($timeout, $response_timeout)){
1752 return false;
1753 }
1754
1755 // send request
1756 if (!$this->sendRequest($data)){
1757 return false;
1758 }
1759
1760 // get response
1761 $respdata = $this->getResponse();
1762 } else {
1763 $this->setError('Too many tries to get an OK response');
1764 }
1765 }
1766 $this->debug('end of send()');
1767 return $respdata;
1768 }
1769
1770
1771 /**
1772 * send the SOAP message via HTTPS 1.0 using CURL
1773 *
1774 * @param string $msg message data
1775 * @param integer $timeout set connection timeout in seconds
1776 * @param integer $response_timeout set response timeout in seconds
1777 * @return string data
1778 * @access public
1779 */
1780 function sendHTTPS($data, $timeout=0, $response_timeout=30) {
1781 return $this->send($data, $timeout, $response_timeout);
1782 }
1783
1784 /**
1785 * if authenticating, set user credentials here
1786 *
1787 * @param string $username
1788 * @param string $password
1789 * @param string $authtype
1790 * @param array $digestRequest
1791 * @access public
1792 */
1793 function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array()) {
1794 global $_SERVER;
1795
1796 $this->debug("Set credentials for authtype $authtype");
1797 // cf. RFC 2617
1798 if ($authtype == 'basic') {
1799 $this->outgoing_headers['Authorization'] = 'Basic '.base64_encode($username.':'.$password);
1800 } elseif ($authtype == 'digest') {
1801 if (isset($digestRequest['nonce'])) {
1802 $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
1803
1804 // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
1805
1806 // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
1807 $A1 = $username. ':' . $digestRequest['realm'] . ':' . $password;
1808
1809 // H(A1) = MD5(A1)
1810 $HA1 = md5($A1);
1811
1812 // A2 = Method ":" digest-uri-value
1813 $A2 = 'POST:' . $this->uri;
1814
1815 // H(A2)
1816 $HA2 = md5($A2);
1817
1818 // KD(secret, data) = H(concat(secret, ":", data))
1819 // if qop == auth:
1820 // request-digest = <"> < KD ( H(A1), unq(nonce-value)
1821 // ":" nc-value
1822 // ":" unq(cnonce-value)
1823 // ":" unq(qop-value)
1824 // ":" H(A2)
1825 // ) <">
1826 // if qop is missing,
1827 // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
1828
1829 $unhashedDigest = '';
1830 $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
1831 $cnonce = $nonce;
1832 if ($digestRequest['qop'] != '') {
1833 $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
1834 } else {
1835 $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
1836 }
1837
1838 $hashedDigest = md5($unhashedDigest);
1839
1840 $this->outgoing_headers['Authorization'] = 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->uri . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"';
1841 }
1842 }
1843 $this->username = $username;
1844 $this->password = $password;
1845 $this->authtype = $authtype;
1846 $this->digestRequest = $digestRequest;
1847
1848 if (isset($this->outgoing_headers['Authorization'])) {
1849 $this->debug('Authorization header set: ' . substr($this->outgoing_headers['Authorization'], 0, 12) . '...');
1850 } else {
1851 $this->debug('Authorization header not set');
1852 }
1853 }
1854
1855 /**
1856 * set the soapaction value
1857 *
1858 * @param string $soapaction
1859 * @access public
1860 */
1861 function setSOAPAction($soapaction) {
1862 $this->outgoing_headers['SOAPAction'] = '"' . $soapaction . '"';
1863 }
1864
1865 /**
1866 * use http encoding
1867 *
1868 * @param string $enc encoding style. supported values: gzip, deflate, or both
1869 * @access public
1870 */
1871 function setEncoding($enc='gzip, deflate'){
1872 $this->protocol_version = '1.1';
1873 $this->outgoing_headers['Accept-Encoding'] = $enc;
1874 $this->outgoing_headers['Connection'] = 'close';
1875 $this->persistentConnection = false;
1876 set_magic_quotes_runtime(0);
1877 // deprecated
1878 $this->encoding = $enc;
1879 }
1880
1881 /**
1882 * set proxy info here
1883 *
1884 * @param string $proxyhost
1885 * @param string $proxyport
1886 * @param string $proxyusername
1887 * @param string $proxypassword
1888 * @access public
1889 */
1890 function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
1891 $this->uri = $this->url;
1892 $this->host = $proxyhost;
1893 $this->port = $proxyport;
1894 if ($proxyusername != '' && $proxypassword != '') {
1895 $this->outgoing_headers['Proxy-Authorization'] = ' Basic '.base64_encode($proxyusername.':'.$proxypassword);
1896 }
1897 }
1898
1899 /**
1900 * decode a string that is encoded w/ "chunked' transfer encoding
1901 * as defined in RFC2068 19.4.6
1902 *
1903 * @param string $buffer
1904 * @param string $lb
1905 * @returns string
1906 * @access public
1907 */
1908 function decodeChunked($buffer, $lb){
1909 // length := 0
1910 $length = 0;
1911 $new = '';
1912
1913 // read chunk-size, chunk-extension (if any) and CRLF
1914 // get the position of the linebreak
1915 $chunkend = strpos($buffer, $lb);
1916 if ($chunkend == FALSE) {
1917 $this->debug('no linebreak found in decodeChunked');
1918 return $new;
1919 }
1920 $temp = substr($buffer,0,$chunkend);
1921 $chunk_size = hexdec( trim($temp) );
1922 $chunkstart = $chunkend + strlen($lb);
1923 // while (chunk-size > 0) {
1924 while ($chunk_size > 0) {
1925 $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
1926 $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size);
1927
1928 // Just in case we got a broken connection
1929 if ($chunkend == FALSE) {
1930 $chunk = substr($buffer,$chunkstart);
1931 // append chunk-data to entity-body
1932 $new .= $chunk;
1933 $length += strlen($chunk);
1934 break;
1935 }
1936
1937 // read chunk-data and CRLF
1938 $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
1939 // append chunk-data to entity-body
1940 $new .= $chunk;
1941 // length := length + chunk-size
1942 $length += strlen($chunk);
1943 // read chunk-size and CRLF
1944 $chunkstart = $chunkend + strlen($lb);
1945
1946 $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb);
1947 if ($chunkend == FALSE) {
1948 break; //Just in case we got a broken connection
1949 }
1950 $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
1951 $chunk_size = hexdec( trim($temp) );
1952 $chunkstart = $chunkend;
1953 }
1954 return $new;
1955 }
1956
1957 /*
1958 * Writes payload, including HTTP headers, to $this->outgoing_payload.
1959 */
1960 function buildPayload($data) {
1961 // add content-length header
1962 $this->outgoing_headers['Content-Length'] = strlen($data);
1963
1964 // start building outgoing payload:
1965 $this->outgoing_payload = "$this->request_method $this->uri HTTP/$this->protocol_version\r\n";
1966
1967 // loop thru headers, serializing
1968 foreach($this->outgoing_headers as $k => $v){
1969 $this->outgoing_payload .= $k.': '.$v."\r\n";
1970 }
1971
1972 // header/body separator
1973 $this->outgoing_payload .= "\r\n";
1974
1975 // add data
1976 $this->outgoing_payload .= $data;
1977 }
1978
1979 function sendRequest($data){
1980 // build payload
1981 $this->buildPayload($data);
1982
1983 if ($this->scheme == 'http' || $this->scheme == 'ssl') {
1984 // send payload
1985 if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
1986 $this->setError('couldn\'t write message data to socket');
1987 $this->debug('couldn\'t write message data to socket');
1988 return false;
1989 }
1990 $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
1991 return true;
1992 } else if ($this->scheme == 'https') {
1993 // set payload
1994 // TODO: cURL does say this should only be the verb, and in fact it
1995 // turns out that the URI and HTTP version are appended to this, which
1996 // some servers refuse to work with
1997 //curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
1998 foreach($this->outgoing_headers as $k => $v){
1999 $curl_headers[] = "$k: $v";
2000 }
2001 curl_setopt($this->ch, CURLOPT_HTTPHEADER, $curl_headers);
2002 if ($this->request_method == "POST") {
2003 curl_setopt($this->ch, CURLOPT_POST, 1);
2004 curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data);
2005 } else {
2006 }
2007 $this->debug('set cURL payload');
2008 return true;
2009 }
2010 }
2011
2012 function getResponse(){
2013 $this->incoming_payload = '';
2014
2015 if ($this->scheme == 'http' || $this->scheme == 'ssl') {
2016 // loop until headers have been retrieved
2017 $data = '';
2018 while (!isset($lb)){
2019
2020 // We might EOF during header read.
2021 if(feof($this->fp)) {
2022 $this->incoming_payload = $data;
2023 $this->debug('found no headers before EOF after length ' . strlen($data));
2024 $this->debug("received before EOF:\n" . $data);
2025 $this->setError('server failed to send headers');
2026 return false;
2027 }
2028
2029 $tmp = fgets($this->fp, 256);
2030 $tmplen = strlen($tmp);
2031 $this->debug("read line of $tmplen bytes: " . trim($tmp));
2032
2033 if ($tmplen == 0) {
2034 $this->incoming_payload = $data;
2035 $this->debug('socket read of headers timed out after length ' . strlen($data));
2036 $this->debug("read before timeout:\n" . $data);
2037 $this->setError('socket read of headers timed out');
2038 return false;
2039 }
2040
2041 $data .= $tmp;
2042 $pos = strpos($data,"\r\n\r\n");
2043 if($pos > 1){
2044 $lb = "\r\n";
2045 } else {
2046 $pos = strpos($data,"\n\n");
2047 if($pos > 1){
2048 $lb = "\n";
2049 }
2050 }
2051 // remove 100 header
2052 if(isset($lb) && ereg('^HTTP/1.1 100',$data)){
2053 unset($lb);
2054 $data = '';
2055 }//
2056 }
2057 // store header data
2058 $this->incoming_payload .= $data;
2059 $this->debug('found end of headers after length ' . strlen($data));
2060 // process headers
2061 $header_data = trim(substr($data,0,$pos));
2062 $header_array = explode($lb,$header_data);
2063 $this->incoming_headers = array();
2064 foreach($header_array as $header_line){
2065 $arr = explode(':',$header_line, 2);
2066 if(count($arr) > 1){
2067 $header_name = strtolower(trim($arr[0]));
2068 $this->incoming_headers[$header_name] = trim($arr[1]);
2069 } else if (isset($header_name)) {
2070 $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
2071 }
2072 }
2073
2074 // loop until msg has been received
2075 if (isset($this->incoming_headers['content-length'])) {
2076 $content_length = $this->incoming_headers['content-length'];
2077 $chunked = false;
2078 $this->debug("want to read content of length $content_length");
2079 } else {
2080 $content_length = 2147483647;
2081 if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
2082 $chunked = true;
2083 $this->debug("want to read chunked content");
2084 } else {
2085 $chunked = false;
2086 $this->debug("want to read content to EOF");
2087 }
2088 }
2089 $data = '';
2090 do {
2091 if ($chunked) {
2092 $tmp = fgets($this->fp, 256);
2093 $tmplen = strlen($tmp);
2094 $this->debug("read chunk line of $tmplen bytes");
2095 if ($tmplen == 0) {
2096 $this->incoming_payload = $data;
2097 $this->debug('socket read of chunk length timed out after length ' . strlen($data));
2098 $this->debug("read before timeout:\n" . $data);
2099 $this->setError('socket read of chunk length timed out');
2100 return false;
2101 }
2102 $content_length = hexdec(trim($tmp));
2103 $this->debug("chunk length $content_length");
2104 }
2105 $strlen = 0;
2106 while (($strlen < $content_length) && (!feof($this->fp))) {
2107 $readlen = min(8192, $content_length - $strlen);
2108 $tmp = fread($this->fp, $readlen);
2109 $tmplen = strlen($tmp);
2110 $this->debug("read buffer of $tmplen bytes");
2111 if (($tmplen == 0) && (!feof($this->fp))) {
2112 $this->incoming_payload = $data;
2113 $this->debug('socket read of body timed out after length ' . strlen($data));
2114 $this->debug("read before timeout:\n" . $data);
2115 $this->setError('socket read of body timed out');
2116 return false;
2117 }
2118 $strlen += $tmplen;
2119 $data .= $tmp;
2120 }
2121 if ($chunked && ($content_length > 0)) {
2122 $tmp = fgets($this->fp, 256);
2123 $tmplen = strlen($tmp);
2124 $this->debug("read chunk terminator of $tmplen bytes");
2125 if ($tmplen == 0) {
2126 $this->incoming_payload = $data;
2127 $this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
2128 $this->debug("read before timeout:\n" . $data);
2129 $this->setError('socket read of chunk terminator timed out');
2130 return false;
2131 }
2132 }
2133 } while ($chunked && ($content_length > 0) && (!feof($this->fp)));
2134 if (feof($this->fp)) {
2135 $this->debug('read to EOF');
2136 }
2137 $this->debug('read body of length ' . strlen($data));
2138 $this->incoming_payload .= $data;
2139 $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server');
2140
2141 // close filepointer
2142 if(
2143 (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') ||
2144 (! $this->persistentConnection) || feof($this->fp)){
2145 fclose($this->fp);
2146 $this->fp = false;
2147 $this->debug('closed socket');
2148 }
2149
2150 // connection was closed unexpectedly
2151 if($this->incoming_payload == ''){
2152 $this->setError('no response from server');
2153 return false;
2154 }
2155
2156 // decode transfer-encoding
2157 // if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
2158 // if(!$data = $this->decodeChunked($data, $lb)){
2159 // $this->setError('Decoding of chunked data failed');
2160 // return false;
2161 // }
2162 //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
2163 // set decoded payload
2164 // $this->incoming_payload = $header_data.$lb.$lb.$data;
2165 // }
2166
2167 } else if ($this->scheme == 'https') {
2168 // send and receive
2169 $this->debug('send and receive with cURL');
2170 $this->incoming_payload = curl_exec($this->ch);
2171 $data = $this->incoming_payload;
2172
2173 $cErr = curl_error($this->ch);
2174 if ($cErr != '') {
2175 $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'<br>';
2176 foreach(curl_getinfo($this->ch) as $k => $v){
2177 $err .= "$k: $v<br>";
2178 }
2179 $this->debug($err);
2180 $this->setError($err);
2181 curl_close($this->ch);
2182 return false;
2183 } else {
2184 //echo '<pre>';
2185 //var_dump(curl_getinfo($this->ch));
2186 //echo '</pre>';
2187 }
2188 // close curl
2189 $this->debug('No cURL error, closing cURL');
2190 curl_close($this->ch);
2191
2192 // remove 100 header
2193 if (ereg('^HTTP/1.1 100',$data)) {
2194 if ($pos = strpos($data,"\r\n\r\n")) {
2195 $data = ltrim(substr($data,$pos));
2196 } elseif($pos = strpos($data,"\n\n") ) {
2197 $data = ltrim(substr($data,$pos));
2198 }
2199 }
2200
2201 // separate content from HTTP headers
2202 if ($pos = strpos($data,"\r\n\r\n")) {
2203 $lb = "\r\n";
2204 } elseif( $pos = strpos($data,"\n\n")) {
2205 $lb = "\n";
2206 } else {
2207 $this->debug('no proper separation of headers and document');
2208 $this->setError('no proper separation of headers and document');
2209 return false;
2210 }
2211 $header_data = trim(substr($data,0,$pos));
2212 $header_array = explode($lb,$header_data);
2213 $data = ltrim(substr($data,$pos));
2214 $this->debug('found proper separation of headers and document');
2215 $this->debug('cleaned data, stringlen: '.strlen($data));
2216 // clean headers
2217 foreach ($header_array as $header_line) {
2218 $arr = explode(':',$header_line,2);
2219 if (count($arr) > 1) {
2220 $this->incoming_headers[strtolower(trim($arr[0]))] = trim($arr[1]);
2221 }
2222 }
2223 }
2224
2225 // see if we need to resend the request with http digest authentication
2226 if (isset($this->incoming_headers['www-authenticate']) && strstr($header_array[0], '401 Unauthorized')) {
2227 $this->debug('Got 401 Unauthorized with WWW-Authenticate: ' . $this->incoming_headers['www-authenticate']);
2228 if (substr("Digest ", $this->incoming_headers['www-authenticate'])) {
2229 $this->debug('Server wants digest authentication');
2230 // remove "Digest " from our elements
2231 $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
2232
2233 // parse elements into array
2234 $digestElements = explode(',', $digestString);
2235 foreach ($digestElements as $val) {
2236 $tempElement = explode('=', trim($val));
2237 $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
2238 }
2239
2240 // should have (at least) qop, realm, nonce
2241 if (isset($digestRequest['nonce'])) {
2242 $this->setCredentials($this->username, $this->password, 'digest', $digestRequest);
2243 $this->tryagain = true;
2244 return false;
2245 }
2246 }
2247 $this->debug('HTTP authentication failed');
2248 $this->setError('HTTP authentication failed');
2249 return false;
2250 }
2251
2252 // decode content-encoding
2253 if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){
2254 if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){
2255 // if decoding works, use it. else assume data wasn't gzencoded
2256 if(function_exists('gzuncompress')){
2257 //$timer->setMarker('starting decoding of gzip/deflated content');
2258 if($this->incoming_headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)){
2259 $data = $degzdata;
2260 } elseif($this->incoming_headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))){ // do our best
2261 $data = $degzdata;
2262 } else {
2263 $this->setError('Errors occurred when trying to decode the data');
2264 }
2265 //$timer->setMarker('finished decoding of gzip/deflated content');
2266 //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
2267 // set decoded payload
2268 $this->incoming_payload = $header_data.$lb.$lb.$data;
2269 } else {
2270 $this->setError('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
2271 }
2272 }
2273 }
2274
2275 if(strlen($data) == 0){
2276 $this->debug('no data after headers!');
2277 $this->setError('no data present after HTTP headers');
2278 return false;
2279 }
2280
2281 return $data;
2282 }
2283
2284 function setContentType($type, $charset = false) {
2285 $this->outgoing_headers['Content-Type'] = $type . ($charset ? '; charset=' . $charset : '');
2286 }
2287
2288 function usePersistentConnection(){
2289 if (isset($this->outgoing_headers['Accept-Encoding'])) {
2290 return false;
2291 }
2292 $this->protocol_version = '1.1';
2293 $this->persistentConnection = true;
2294 $this->outgoing_headers['Connection'] = 'Keep-Alive';
2295 return true;
2296 }
2297 }
2298
2299 ?><?php
2300
2301
2302
2303 /**
2304 *
2305 * soap_server allows the user to create a SOAP server
2306 * that is capable of receiving messages and returning responses
2307 *
2308 * NOTE: WSDL functionality is experimental
2309 *
2310 * @author Dietrich Ayala <dietrich@ganx4.com>
2311 * @version $Id$
2312 * @access public
2313 */
2314 class soap_server extends nusoap_base {
2315 var $headers = array(); // HTTP headers of request
2316 var $request = ''; // HTTP request
2317 var $requestHeaders = ''; // SOAP headers from request (incomplete namespace resolution) (text)
2318 var $document = ''; // SOAP body request portion (incomplete namespace resolution) (text)
2319 var $requestSOAP = ''; // SOAP payload for request (text)
2320 var $methodURI = ''; // requested method namespace URI
2321 var $methodname = ''; // name of method requested
2322 var $methodparams = array(); // method parameters from request
2323 var $xml_encoding = ''; // character set encoding of incoming (request) messages
2324 var $SOAPAction = ''; // SOAP Action from request
2325
2326 var $outgoing_headers = array();// HTTP headers of response
2327 var $response = ''; // HTTP response
2328 var $responseHeaders = ''; // SOAP headers for response (text)
2329 var $responseSOAP = ''; // SOAP payload for response (text)
2330 var $methodreturn = false; // method return to place in response
2331 var $methodreturnisliteralxml = false; // whether $methodreturn is a string of literal XML
2332 var $fault = false; // SOAP fault for response
2333 var $result = 'successful'; // text indication of result (for debugging)
2334
2335 var $operations = array(); // assoc array of operations => opData
2336 var $wsdl = false; // wsdl instance
2337 var $externalWSDLURL = false; // URL for WSDL
2338 var $debug_flag = false; // whether to append debug to response as XML comment
2339
2340 /**
2341 * constructor
2342 * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
2343 *
2344 * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
2345 * @access public
2346 */
2347 function soap_server($wsdl=false){
2348
2349 // turn on debugging?
2350 global $debug;
2351 global $_REQUEST;
2352 global $_SERVER;
2353 global $HTTP_SERVER_VARS;
2354
2355 if (isset($debug)) {
2356 $this->debug_flag = $debug;
2357 } else if (isset($_REQUEST['debug'])) {
2358 $this->debug_flag = $_REQUEST['debug'];
2359 } else if (isset($_SERVER['QUERY_STRING'])) {
2360 $qs = explode('&', $_SERVER['QUERY_STRING']);
2361 foreach ($qs as $v) {
2362 if (substr($v, 0, 6) == 'debug=') {
2363 $this->debug_flag = substr($v, 6);
2364 }
2365 }
2366 } else if (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
2367 $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
2368 foreach ($qs as $v) {
2369 if (substr($v, 0, 6) == 'debug=') {
2370 $this->debug_flag = substr($v, 6);
2371 }
2372 }
2373 }
2374
2375 // wsdl
2376 if($wsdl){
2377 if (is_object($wsdl) && is_a($wsdl, 'wsdl')) {
2378 $this->wsdl = $wsdl;
2379 $this->externalWSDLURL = $this->wsdl->wsdl;
2380 $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
2381 } else {
2382 $this->debug('Create wsdl from ' . $wsdl);
2383 $this->wsdl = new wsdl($wsdl);
2384 $this->externalWSDLURL = $wsdl;
2385 }
2386 $this->debug("wsdl...\n" . $this->wsdl->debug_str);
2387 $this->wsdl->debug_str = '';
2388 if($err = $this->wsdl->getError()){
2389 die('WSDL ERROR: '.$err);
2390 }
2391 }
2392 }
2393
2394 /**
2395 * processes request and returns response
2396 *
2397 * @param string $data usually is the value of $HTTP_RAW_POST_DATA
2398 * @access public
2399 */
2400 function service($data){
2401 global $QUERY_STRING;
2402 if(isset($_SERVER['QUERY_STRING'])){
2403 $qs = $_SERVER['QUERY_STRING'];
2404 } elseif(isset($GLOBALS['QUERY_STRING'])){
2405 $qs = $GLOBALS['QUERY_STRING'];
2406 } elseif(isset($QUERY_STRING) && $QUERY_STRING != ''){
2407 $qs = $QUERY_STRING;
2408 }
2409
2410 if(isset($qs) && ereg('wsdl', $qs) ){
2411 // This is a request for WSDL
2412 if($this->externalWSDLURL){
2413 if (strpos($this->externalWSDLURL,"://")!==false) { // assume URL
2414 header('Location: '.$this->externalWSDLURL);
2415 } else { // assume file
2416 header("Content-Type: text/xml\r\n");
2417 $fp = fopen($this->externalWSDLURL, 'r');
2418 fpassthru($fp);
2419 }
2420 } else {
2421 header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
2422 print $this->wsdl->serialize();
2423 }
2424 } elseif($data == '' && $this->wsdl){
2425 // print web interface
2426 print $this->webDescription();
2427 } else {
2428 // handle the request
2429 $this->parse_request($data);
2430 if (! $this->fault) {
2431 $this->invoke_method();
2432 }
2433 if (! $this->fault) {
2434 $this->serialize_return();
2435 }
2436 $this->send_response();
2437 }
2438 }
2439
2440 /**
2441 * parses HTTP request headers.
2442 *
2443 * The following fields are set by this function (when successful)
2444 *
2445 * headers
2446 * request
2447 * xml_encoding
2448 * SOAPAction
2449 *
2450 * @access private
2451 */
2452 function parse_http_headers() {
2453 global $HTTP_SERVER_VARS;
2454 global $_SERVER;
2455
2456 $this->request = '';
2457 if(function_exists('getallheaders')){
2458 $this->headers = getallheaders();
2459 foreach($this->headers as $k=>$v){
2460 $this->request .= "$k: $v\r\n";
2461 $this->debug("$k: $v");
2462 }
2463 // get SOAPAction header
2464 if(isset($this->headers['SOAPAction'])){
2465 $this->SOAPAction = str_replace('"','',$this->headers['SOAPAction']);
2466 }
2467 // get the character encoding of the incoming request
2468 if(strpos($this->headers['Content-Type'],'=')){
2469 $enc = str_replace('"','',substr(strstr($this->headers["Content-Type"],'='),1));
2470 if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
2471 $this->xml_encoding = strtoupper($enc);
2472 } else {
2473 $this->xml_encoding = 'US-ASCII';
2474 }
2475 } else {
2476 // should be US-ASCII, but for XML, let's be pragmatic and admit UTF-8 is most common
2477 $this->xml_encoding = 'UTF-8';
2478 }
2479 } elseif(isset($_SERVER) && is_array($_SERVER)){
2480 foreach ($_SERVER as $k => $v) {
2481 if (substr($k, 0, 5) == 'HTTP_') {
2482 $k = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($k, 5)))));
2483 } else {
2484 $k = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $k))));
2485 }
2486 if ($k == 'Soapaction') {
2487 // get SOAPAction header
2488 $k = 'SOAPAction';
2489 $v = str_replace('"', '', $v);
2490 $v = str_replace('\\', '', $v);
2491 $this->SOAPAction = $v;
2492 } else if ($k == 'Content-Type') {
2493 // get the character encoding of the incoming request
2494 if (strpos($v, '=')) {
2495 $enc = substr(strstr($v, '='), 1);
2496 $enc = str_replace('"', '', $enc);
2497 $enc = str_replace('\\', '', $enc);
2498 if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
2499 $this->xml_encoding = strtoupper($enc);
2500 } else {
2501 $this->xml_encoding = 'US-ASCII';
2502 }
2503 } else {
2504 // should be US-ASCII, but for XML, let's be pragmatic and admit UTF-8 is most common
2505 $this->xml_encoding = 'UTF-8';
2506 }
2507 }
2508 $this->headers[$k] = $v;
2509 $this->request .= "$k: $v\r\n";
2510 $this->debug("$k: $v");
2511 }
2512 } elseif (is_array($HTTP_SERVER_VARS)) {
2513 foreach ($HTTP_SERVER_VARS as $k => $v) {
2514 if (substr($k, 0, 5) == 'HTTP_') {
2515 $k = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($k, 5)))));
2516 if ($k == 'Soapaction') {
2517 // get SOAPAction header
2518 $k = 'SOAPAction';
2519 $v = str_replace('"', '', $v);
2520 $v = str_replace('\\', '', $v);
2521 $this->SOAPAction = $v;
2522 } else if ($k == 'Content-Type') {
2523 // get the character encoding of the incoming request
2524 if (strpos($v, '=')) {
2525 $enc = substr(strstr($v, '='), 1);
2526 $enc = str_replace('"', '', $enc);
2527 $enc = str_replace('\\', '', $enc);
2528 if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) {
2529 $this->xml_encoding = strtoupper($enc);
2530 } else {
2531 $this->xml_encoding = 'US-ASCII';
2532 }
2533 } else {
2534 // should be US-ASCII, but for XML, let's be pragmatic and admit UTF-8 is most common
2535 $this->xml_encoding = 'UTF-8';
2536 }
2537 }
2538 $this->headers[$k] = $v;
2539 $this->request .= "$k: $v\r\n";
2540 $this->debug("$k: $v");
2541 }
2542 }
2543 }
2544 }
2545
2546 /**
2547 * parses a request
2548 *
2549 * The following fields are set by this function (when successful)
2550 *
2551 * headers
2552 * request
2553 * xml_encoding
2554 * SOAPAction
2555 * request
2556 * requestSOAP
2557 * methodURI
2558 * methodname
2559 * methodparams
2560 * requestHeaders
2561 * document
2562 *
2563 * This sets the fault field on error
2564 *
2565 * @param string $data XML string
2566 * @access private
2567 */
2568 function parse_request($data='') {
2569 $this->debug('entering parse_request() on '.date('H:i Y-m-d'));
2570 $this->parse_http_headers();
2571 $this->debug('got character encoding: '.$this->xml_encoding);
2572 // uncompress if necessary
2573 if (isset($this->headers['Content-Encoding']) && $this->headers['Content-Encoding'] != '') {
2574 $this->debug('got content encoding: ' . $this->headers['Content-Encoding']);
2575 if ($this->headers['Content-Encoding'] == 'deflate' || $this->headers['Content-Encoding'] == 'gzip') {
2576 // if decoding works, use it. else assume data wasn't gzencoded
2577 if (function_exists('gzuncompress')) {
2578 if ($this->headers['Content-Encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) {
2579 $data = $degzdata;
2580 } elseif ($this->headers['Content-Encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
2581 $data = $degzdata;
2582 } else {
2583 $this->fault('Server', 'Errors occurred when trying to decode the data');
2584 return;
2585 }
2586 } else {
2587 $this->fault('Server', 'This Server does not support compressed data');
2588 return;
2589 }
2590 }
2591 }
2592 $this->request .= "\r\n".$data;
2593 $this->requestSOAP = $data;
2594 // parse response, get soap parser obj
2595 $parser = new soap_parser($data,$this->xml_encoding);
2596 // parser debug
2597 $this->debug("parser debug: \n".$parser->debug_str);
2598 // if fault occurred during message parsing
2599 if($err = $parser->getError()){
2600 $this->result = 'fault: error in msg parsing: '.$err;
2601 $this->fault('Server',"error in msg parsing:\n".$err);
2602 // else successfully parsed request into soapval object
2603 } else {
2604 // get/set methodname
2605 $this->methodURI = $parser->root_struct_namespace;
2606 $this->methodname = $parser->root_struct_name;
2607 $this->debug('method name: '.$this->methodname);
2608 $this->debug('calling parser->get_response()');
2609 $this->methodparams = $parser->get_response();
2610 // get SOAP headers
2611 $this->requestHeaders = $parser->getHeaders();
2612 // add document for doclit support
2613 $this->document = $parser->document;
2614 }
2615 $this->debug('leaving parse_request() on '.date('H:i Y-m-d'));
2616 }
2617
2618 /**
2619 * invokes a PHP function for the requested SOAP method
2620 *
2621 * The following fields are set by this function (when successful)
2622 *
2623 * methodreturn
2624 *
2625 * Note that the PHP function that is called may also set the following
2626 * fields to affect the response sent to the client
2627 *
2628 * responseHeaders
2629 * outgoing_headers
2630 *
2631 * This sets the fault field on error
2632 *
2633 * @access private
2634 */
2635 function invoke_method() {
2636 $this->debug('entering invoke_method');
2637 // does method exist?
2638 if(!function_exists($this->methodname)){
2639 // "method not found" fault here
2640 $this->debug("method '$this->methodname' not found!");
2641 $this->result = 'fault: method not found';
2642 $this->fault('Server',"method '$this->methodname' not defined in service");
2643 return;
2644 }
2645 if($this->wsdl){
2646 if(!$this->opData = $this->wsdl->getOperationData($this->methodname)){
2647 //if(
2648 $this->fault('Server',"Operation '$this->methodname' is not defined in the WSDL for this service");
2649 return;
2650 }
2651 $this->debug('opData is ' . $this->varDump($this->opData));
2652 }
2653 $this->debug("method '$this->methodname' exists");
2654 // evaluate message, getting back parameters
2655 // verify that request parameters match the method's signature
2656 if(! $this->verify_method($this->methodname,$this->methodparams)){
2657 // debug
2658 $this->debug('ERROR: request not verified against method signature');
2659 $this->result = 'fault: request failed validation against method signature';
2660 // return fault
2661 $this->fault('Server',"Operation '$this->methodname' not defined in service.");
2662 return;
2663 }
2664
2665 // if there are parameters to pass
2666 $this->debug('params var dump '.$this->varDump($this->methodparams));
2667 if($this->methodparams){
2668 $this->debug("calling '$this->methodname' with params");
2669 if (! function_exists('call_user_func_array')) {
2670 $this->debug('calling method using eval()');
2671 $funcCall = $this->methodname.'(';
2672 foreach($this->methodparams as $param) {
2673 $funcCall .= "\"$param\",";
2674 }
2675 $funcCall = substr($funcCall, 0, -1).')';
2676 $this->debug('function call:<br>'.$funcCall);
2677 @eval("\$this->methodreturn = $funcCall;");
2678 } else {
2679 $this->debug('calling method using call_user_func_array()');
2680 $this->methodreturn = call_user_func_array("$this->methodname",$this->methodparams);
2681 }
2682 } else {
2683 // call method w/ no parameters
2684 $this->debug("calling $this->methodname w/ no params");
2685 $m = $this->methodname;
2686 $this->methodreturn = @$m();
2687 }
2688 $this->debug('methodreturn var dump'.$this->varDump($this->methodreturn));
2689 $this->debug("leaving invoke_method: called method $this->methodname, received $this->methodreturn of type ".gettype($this->methodreturn));
2690 }
2691
2692 /**
2693 * serializes the return value from a PHP function into a full SOAP Envelope
2694 *
2695 * The following fields are set by this function (when successful)
2696 *
2697 * responseSOAP
2698 *
2699 * This sets the fault field on error
2700 *
2701 * @access private
2702 */
2703 function serialize_return() {
2704 $this->debug("Entering serialize_return");
2705 // if we got nothing back. this might be ok (echoVoid)
2706 if(isset($this->methodreturn) && ($this->methodreturn != '' || is_bool($this->methodreturn))) {
2707 // if fault
2708 if(get_class($this->methodreturn) == 'soap_fault'){
2709 $this->debug('got a fault object from method');
2710 $this->fault = $this->methodreturn;
2711 return;
2712 } elseif ($this->methodreturnisliteralxml) {
2713 $return_val = $this->methodreturn;
2714 // returned value(s)
2715 } else {
2716 $this->debug('got a(n) '.gettype($this->methodreturn).' from method');
2717 $this->debug('serializing return value');
2718 if($this->wsdl){
2719 // weak attempt at supporting multiple output params
2720 if(sizeof($this->opData['output']['parts']) > 1){
2721 $opParams = $this->methodreturn;
2722 } else {
2723 // TODO: is this really necessary?
2724 $opParams = array($this->methodreturn);
2725 }
2726 $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
2727 if($errstr = $this->wsdl->getError()){
2728 $this->debug('got wsdl error: '.$errstr);
2729 $this->fault('Server', 'got wsdl error: '.$errstr);
2730 return;
2731 }
2732 } else {
2733 $return_val = $this->serialize_val($this->methodreturn, 'return');
2734 }
2735 }
2736 $this->debug('return val: '.$this->varDump($return_val));
2737 } else {
2738 $return_val = '';
2739 $this->debug('got no response from method');
2740 }
2741 $this->debug('serializing response');
2742 if ($this->wsdl) {
2743 if ($this->opData['style'] == 'rpc') {
2744 $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
2745 } else {
2746 $payload = $return_val;
2747 }
2748 } else {
2749 $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
2750 }
2751 $this->result = 'successful';
2752 if($this->wsdl){
2753 //if($this->debug_flag){
2754 $this->debug("WSDL debug data:\n".$this->wsdl->debug_str);
2755 // }
2756 // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
2757 $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style']);
2758 } else {
2759 $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders);
2760 }
2761 $this->debug("Leaving serialize_return");
2762 }
2763
2764 /**
2765 * sends an HTTP response
2766 *
2767 * The following fields are set by this function (when successful)
2768 *
2769 * outgoing_headers
2770 * response
2771 *
2772 * @access private
2773 */
2774 function send_response() {
2775 $this->debug('Enter send_response');
2776 if ($this->fault) {
2777 $payload = $this->fault->serialize();
2778 $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
2779 $this->outgoing_headers[] = "Status: 500 Internal Server Error";
2780 } else {
2781 $payload = $this->responseSOAP;
2782 // Some combinations of PHP+Web server allow the Status
2783 // to come through as a header. Since OK is the default
2784 // just do nothing.
2785 // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
2786 // $this->outgoing_headers[] = "Status: 200 OK";
2787 }
2788 // add debug data if in debug mode
2789 if(isset($this->debug_flag) && $this->debug_flag){
2790 while (strpos($this->debug_str, '--')) {
2791 $this->debug_str = str_replace('--', '- -', $this->debug_str);
2792 }
2793 $payload .= "<!--\n" . $this->debug_str . "\n-->";
2794 }
2795 $this->outgoing_headers[] = "Server: $this->title Server v$this->version";
2796 ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev);
2797 $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")";
2798 // Let the Web server decide about this
2799 //$this->outgoing_headers[] = "Connection: Close\r\n";
2800 $this->outgoing_headers[] = "Content-Type: text/xml; charset=$this->soap_defencoding";
2801 //begin code to compress payload - by John
2802 if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['Accept-Encoding'])) {
2803 if (strstr($this->headers['Accept-Encoding'], 'deflate')) {
2804 if (function_exists('gzcompress')) {
2805 if (isset($this->debug_flag) && $this->debug_flag) {
2806 $payload .= "<!-- Content being deflated -->";
2807 }
2808 $this->outgoing_headers[] = "Content-Encoding: deflate";
2809 $payload = gzcompress($payload);
2810 } else {
2811 if (isset($this->debug_flag) && $this->debug_flag) {
2812 $payload .= "<!-- Content will not be deflated: no gzcompress -->";
2813 }
2814 }
2815 } else if (strstr($this->headers['Accept-Encoding'], 'gzip')) {
2816 if (function_exists('gzencode')) {
2817 if (isset($this->debug_flag) && $this->debug_flag) {
2818 $payload .= "<!-- Content being gzipped -->";
2819 }
2820 $this->outgoing_headers[] = "Content-Encoding: gzip";
2821 $payload = gzencode($payload);
2822 } else {
2823 if (isset($this->debug_flag) && $this->debug_flag) {
2824 $payload .= "<!-- Content will not be gzipped: no gzencode -->";
2825 }
2826 }
2827 }
2828 }
2829 //end code
2830 $this->outgoing_headers[] = "Content-Length: ".strlen($payload);
2831 reset($this->outgoing_headers);
2832 foreach($this->outgoing_headers as $hdr){
2833 header($hdr, false);
2834 }
2835 $this->response = join("\r\n",$this->outgoing_headers)."\r\n".$payload;
2836 print $payload;
2837 }
2838
2839 /**
2840 * takes the value that was created by parsing the request
2841 * and compares to the method's signature, if available.
2842 *
2843 * @param mixed
2844 * @return boolean
2845 * @access private
2846 */
2847 function verify_method($operation,$request){
2848 if(isset($this->wsdl) && is_object($this->wsdl)){
2849 if($this->wsdl->getOperationData($operation)){
2850 return true;
2851 }
2852 } elseif(isset($this->operations[$operation])){
2853 return true;
2854 }
2855 return false;
2856 }
2857
2858 /**
2859 * add a method to the dispatch map
2860 *
2861 * @param string $methodname
2862 * @param string $in array of input values
2863 * @param string $out array of output values
2864 * @access public
2865 */
2866 function add_to_map($methodname,$in,$out){
2867 $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
2868 }
2869
2870 /**
2871 * register a service with the server
2872 *
2873 * @param string $methodname
2874 * @param string $in assoc array of input values: key = param name, value = param type
2875 * @param string $out assoc array of output values: key = param name, value = param type
2876 * @param string $namespace
2877 * @param string $soapaction
2878 * @param string $style optional (rpc|document)
2879 * @param string $use optional (encoded|literal)
2880 * @param string $documentation optional Description to include in WSDL
2881 * @access public
2882 */
2883 function register($name,$in=false,$out=false,$namespace=false,$soapaction=false,$style=false,$use=false,$documentation=''){
2884 if($this->externalWSDLURL){
2885 die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
2886 }
2887 if(false == $in) {
2888 }
2889 if(false == $out) {
2890 }
2891 if(false == $namespace) {
2892 }
2893 if(false == $soapaction) {
2894 $SERVER_NAME = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : $GLOBALS['SERVER_NAME'];
2895 $SCRIPT_NAME = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : $GLOBALS['SCRIPT_NAME'];
2896 $soapaction = "http://$SERVER_NAME$SCRIPT_NAME/$name";
2897 }
2898 if(false == $style) {
2899 $style = "rpc";
2900 }
2901 if(false == $use) {
2902 $use = "encoded";
2903 }
2904
2905 $this->operations[$name] = array(
2906 'name' => $name,
2907 'in' => $in,
2908 'out' => $out,
2909 'namespace' => $namespace,
2910 'soapaction' => $soapaction,
2911 'style' => $style);
2912 if($this->wsdl){
2913 $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use,$documentation);
2914 }
2915 return true;
2916 }
2917
2918 /**
2919 * create a fault. this also acts as a flag to the server that a fault has occured.
2920 *
2921 * @param string faultcode
2922 * @param string faultstring
2923 * @param string faultactor
2924 * @param string faultdetail
2925 * @access public
2926 */
2927 function fault($faultcode,$faultstring,$faultactor='',$faultdetail=''){
2928 $this->fault = new soap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
2929 }
2930
2931 /**
2932 * prints html description of services
2933 *
2934 * @access private
2935 */
2936 function webDescription(){
2937 $b = '
2938 <html><head><title>NuSOAP: '.$this->wsdl->serviceName.'</title>
2939 <style type="text/css">
2940 body { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
2941 p { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
2942 pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
2943 ul { margin-top: 10px; margin-left: 20px; }
2944 li { list-style-type: none; margin-top: 10px; color: #000000; }
2945 .content{
2946 margin-left: 0px; padding-bottom: 2em; }
2947 .nav {
2948 padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
2949 margin-top: 10px; margin-left: 0px; color: #000000;
2950 background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
2951 .title {
2952 font-family: arial; font-size: 26px; color: #ffffff;
2953 background-color: #999999; width: 105%; margin-left: 0px;
2954 padding-top: 10px; padding-bottom: 10px; padding-left: 15px;}
2955 .hidden {
2956 position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
2957 font-family: arial; overflow: hidden; width: 600;
2958 padding: 20px; font-size: 10px; background-color: #999999;
2959 layer-background-color:#FFFFFF; }
2960 a,a:active { color: charcoal; font-weight: bold; }
2961 a:visited { color: #666666; font-weight: bold; }
2962 a:hover { color: cc3300; font-weight: bold; }
2963 </style>
2964 <script language="JavaScript" type="text/javascript">
2965 <!--
2966 // POP-UP CAPTIONS...
2967 function lib_bwcheck(){ //Browsercheck (needed)
2968 this.ver=navigator.appVersion
2969 this.agent=navigator.userAgent
2970 this.dom=document.getElementById?1:0
2971 this.opera5=this.agent.indexOf("Opera 5")>-1
2972 this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
2973 this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
2974 this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
2975 this.ie=this.ie4||this.ie5||this.ie6
2976 this.mac=this.agent.indexOf("Mac")>-1
2977 this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
2978 this.ns4=(document.layers && !this.dom)?1:0;
2979 this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
2980 return this
2981 }
2982 var bw = new lib_bwcheck()
2983 //Makes crossbrowser object.
2984 function makeObj(obj){
2985 this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
2986 if(!this.evnt) return false
2987 this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
2988 this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
2989 this.writeIt=b_writeIt;
2990 return this
2991 }
2992 // A unit of measure that will be added when setting the position of a layer.
2993 //var px = bw.ns4||window.opera?"":"px";
2994 function b_writeIt(text){
2995 if (bw.ns4){this.wref.write(text);this.wref.close()}
2996 else this.wref.innerHTML = text
2997 }
2998 //Shows the messages
2999 var oDesc;
3000 function popup(divid){
3001 if(oDesc = new makeObj(divid)){
3002 oDesc.css.visibility = "visible"
3003 }
3004 }
3005 function popout(){ // Hides message
3006 if(oDesc) oDesc.css.visibility = "hidden"
3007 }
3008 //-->
3009 </script>
3010 </head>
3011 <body>
3012 <div class=content>
3013 <br><br>
3014 <div class=title>'.$this->wsdl->serviceName.'</div>
3015 <div class=nav>
3016 <p>View the <a href="'.(isset($GLOBALS['PHP_SELF']) ? $GLOBALS['PHP_SELF'] : $_SERVER['PHP_SELF']).'?wsdl">WSDL</a> for the service.
3017 Click on an operation name to view it&apos;s details.</p>
3018 <ul>';
3019 foreach($this->wsdl->getOperations() as $op => $data){
3020 $b .= "<li><a href='#' onclick=\"popup('$op')\">$op</a></li>";
3021 // create hidden div
3022 $b .= "<div id='$op' class='hidden'>
3023 <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
3024 foreach($data as $donnie => $marie){ // loop through opdata
3025 if($donnie == 'input' || $donnie == 'output'){ // show input/output data
3026 $b .= "<font color='white'>".ucfirst($donnie).':</font><br>';
3027 foreach($marie as $captain => $tenille){ // loop through data
3028 if($captain == 'parts'){ // loop thru parts
3029 $b .= "&nbsp;&nbsp;$captain:<br>";
3030 //if(is_array($tenille)){
3031 foreach($tenille as $joanie => $chachi){
3032 $b .= "&nbsp;&nbsp;&nbsp;&nbsp;$joanie: $chachi<br>";
3033 }
3034 //}
3035 } else {
3036 $b .= "&nbsp;&nbsp;$captain: $tenille<br>";
3037 }
3038 }
3039 } else {
3040 $b .= "<font color='white'>".ucfirst($donnie).":</font> $marie<br>";
3041 }
3042 }
3043 $b .= '</div>';
3044 }
3045 $b .= '
3046 <ul>
3047 </div>
3048 </div></body></html>';
3049 return $b;
3050 }
3051
3052 /**
3053 * sets up wsdl object
3054 * this acts as a flag to enable internal WSDL generation
3055 *
3056 * @param string $serviceName, name of the service
3057 * @param string $namespace optional tns namespace
3058 * @param string $endpoint optional URL of service endpoint
3059 * @param string $style optional (rpc|document) WSDL style (also specified by operation)
3060 * @param string $transport optional SOAP transport
3061 * @param string $schemaTargetNamespace optional targetNamespace for service schema
3062 */
3063 function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
3064 {
3065 $SERVER_NAME = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : $GLOBALS['SERVER_NAME'];
3066 $SERVER_PORT = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : $GLOBALS['SERVER_PORT'];
3067 if ($SERVER_PORT == 80) {
3068 $SERVER_PORT = '';
3069 } else {
3070 $SERVER_PORT = ':' . $SERVER_PORT;
3071 }
3072 $SCRIPT_NAME = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : $GLOBALS['SCRIPT_NAME'];
3073 if(false == $namespace) {
3074 $namespace = "http://$SERVER_NAME/soap/$serviceName";
3075 }
3076
3077 if(false == $endpoint) {
3078 if (isset($_SERVER['HTTPS'])) {
3079 $HTTPS = $_SERVER['HTTPS'];
3080 } elseif (isset($GLOBALS['HTTPS'])) {
3081 $HTTPS = $GLOBALS['HTTPS'];
3082 } else {
3083 $HTTPS = '0';
3084 }
3085 if ($HTTPS == '1' || $HTTPS == 'on') {
3086 $SCHEME = 'https';
3087 } else {
3088 $SCHEME = 'http';
3089 }
3090 $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME";
3091 }
3092
3093 if(false == $schemaTargetNamespace) {
3094 $schemaTargetNamespace = $namespace;
3095 }
3096
3097 $this->wsdl = new wsdl;
3098 $this->wsdl->serviceName = $serviceName;
3099 $this->wsdl->endpoint = $endpoint;
3100 $this->wsdl->namespaces['tns'] = $namespace;
3101 $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
3102 $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
3103 if ($schemaTargetNamespace != $namespace) {
3104 $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
3105 }
3106 $this->wsdl->schemas[$schemaTargetNamespace][0] = new xmlschema('', '', $this->wsdl->namespaces);
3107 $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
3108 $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true);
3109 $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true);
3110 $this->wsdl->bindings[$serviceName.'Binding'] = array(
3111 'name'=>$serviceName.'Binding',
3112 'style'=>$style,
3113 'transport'=>$transport,
3114 'portType'=>$serviceName.'PortType');
3115 $this->wsdl->ports[$serviceName.'Port'] = array(
3116 'binding'=>$serviceName.'Binding',
3117 'location'=>$endpoint,
3118 'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');
3119 }
3120 }
3121
3122
3123
3124 ?><?php
3125
3126
3127
3128 /**
3129 * parses a WSDL file, allows access to it's data, other utility methods
3130 *
3131 * @author Dietrich Ayala <dietrich@ganx4.com>
3132 * @version $Id$
3133 * @access public
3134 */
3135 class wsdl extends nusoap_base {
3136 // URL or filename of the root of this WSDL
3137 var $wsdl;
3138 // define internal arrays of bindings, ports, operations, messages, etc.
3139 var $schemas = array();
3140 var $currentSchema;
3141 var $message = array();
3142 var $complexTypes = array();
3143 var $messages = array();
3144 var $currentMessage;
3145 var $currentOperation;
3146 var $portTypes = array();
3147 var $currentPortType;
3148 var $bindings = array();
3149 var $currentBinding;
3150 var $ports = array();
3151 var $currentPort;
3152 var $opData = array();
3153 var $status = '';
3154 var $documentation = false;
3155 var $endpoint = '';
3156 // array of wsdl docs to import
3157 var $import = array();
3158 // parser vars
3159 var $parser;
3160 var $position = 0;
3161 var $depth = 0;
3162 var $depth_array = array();
3163 // for getting wsdl
3164 var $proxyhost = '';
3165 var $proxyport = '';
3166 var $proxyusername = '';
3167 var $proxypassword = '';
3168 var $timeout = 0;
3169 var $response_timeout = 30;
3170
3171 /**
3172 * constructor
3173 *
3174 * @param string $wsdl WSDL document URL
3175 * @param string $proxyhost
3176 * @param string $proxyport
3177 * @param string $proxyusername
3178 * @param string $proxypassword
3179 * @param integer $timeout set the connection timeout
3180 * @param integer $response_timeout set the response timeout
3181 * @access public
3182 */
3183 function wsdl($wsdl = '',$proxyhost=false,$proxyport=false,$proxyusername=false,$proxypassword=false,$timeout=0,$response_timeout=30){
3184 $this->wsdl = $wsdl;
3185 $this->proxyhost = $proxyhost;
3186 $this->proxyport = $proxyport;
3187 $this->proxyusername = $proxyusername;
3188 $this->proxypassword = $proxypassword;
3189 $this->timeout = $timeout;
3190 $this->response_timeout = $response_timeout;
3191
3192 // parse wsdl file
3193 if ($wsdl != "") {
3194 $this->debug('initial wsdl URL: ' . $wsdl);
3195 $this->parseWSDL($wsdl);
3196 }
3197 // imports
3198 // TODO: handle imports more properly, grabbing them in-line and nesting them
3199 $imported_urls = array();
3200 $imported = 1;
3201 while ($imported > 0) {
3202 $imported = 0;
3203 // Schema imports
3204 foreach ($this->schemas as $ns => $list) {
3205 foreach ($list as $xs) {
3206 $wsdlparts = parse_url($this->wsdl); // this is bogusly simple!
3207 foreach ($xs->imports as $ns2 => $list2) {
3208 for ($ii = 0; $ii < count($list2); $ii++) {
3209 if (! $list2[$ii]['loaded']) {
3210 $this->schemas[$ns]->imports[$ns2][$ii]['loaded'] = true;
3211 $url = $list2[$ii]['location'];
3212 if ($url != '') {
3213 $urlparts = parse_url($url);
3214 if (!isset($urlparts['host'])) {
3215 $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] .
3216 substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
3217 }
3218 if (! in_array($url, $imported_urls)) {
3219 $this->parseWSDL($url);
3220 $imported++;
3221 $imported_urls[] = $url;
3222 }
3223 } else {
3224 $this->debug("Unexpected scenario: empty URL for unloaded import");
3225 }
3226 }
3227 }
3228 }
3229 }
3230 }
3231 // WSDL imports
3232 $wsdlparts = parse_url($this->wsdl); // this is bogusly simple!
3233 foreach ($this->import as $ns => $list) {
3234 for ($ii = 0; $ii < count($list); $ii++) {
3235 if (! $list[$ii]['loaded']) {
3236 $this->import[$ns][$ii]['loaded'] = true;
3237 $url = $list[$ii]['location'];
3238 if ($url != '') {
3239 $urlparts = parse_url($url);
3240 if (!isset($urlparts['host'])) {
3241 $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] .
3242 substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
3243 }
3244 if (! in_array($url, $imported_urls)) {
3245 $this->parseWSDL($url);
3246 $imported++;
3247 $imported_urls[] = $url;
3248 }
3249 } else {
3250 $this->debug("Unexpected scenario: empty URL for unloaded import");
3251 }
3252 }
3253 }
3254 }
3255 }
3256 // add new data to operation data
3257 foreach($this->bindings as $binding => $bindingData) {
3258 if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
3259 foreach($bindingData['operations'] as $operation => $data) {
3260 $this->debug('post-parse data gathering for ' . $operation);
3261 $this->bindings[$binding]['operations'][$operation]['input'] =
3262 isset($this->bindings[$binding]['operations'][$operation]['input']) ?
3263 array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) :
3264 $this->portTypes[ $bindingData['portType'] ][$operation]['input'];
3265 $this->bindings[$binding]['operations'][$operation]['output'] =
3266 isset($this->bindings[$binding]['operations'][$operation]['output']) ?
3267 array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) :
3268 $this->portTypes[ $bindingData['portType'] ][$operation]['output'];
3269 if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){
3270 $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ];
3271 }
3272 if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){
3273 $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ];
3274 }
3275 if (isset($bindingData['style'])) {
3276 $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
3277 }
3278 $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
3279 $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : '';
3280 $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
3281 }
3282 }
3283 }
3284 }
3285
3286 /**
3287 * parses the wsdl document
3288 *
3289 * @param string $wsdl path or URL
3290 * @access private
3291 */
3292 function parseWSDL($wsdl = '')
3293 {
3294 if ($wsdl == '') {
3295 $this->debug('no wsdl passed to parseWSDL()!!');
3296 $this->setError('no wsdl passed to parseWSDL()!!');
3297 return false;
3298 }
3299
3300 // parse $wsdl for url format
3301 $wsdl_props = parse_url($wsdl);
3302
3303 if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' || $wsdl_props['scheme'] == 'https')) {
3304 $this->debug('getting WSDL http(s) URL ' . $wsdl);
3305 // get wsdl
3306 $tr = new soap_transport_http($wsdl);
3307 $tr->request_method = 'GET';
3308 $tr->useSOAPAction = false;
3309 if($this->proxyhost && $this->proxyport){
3310 $tr->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
3311 }
3312 if (isset($wsdl_props['user'])) {
3313 $tr->setCredentials($wsdl_props['user'],$wsdl_props['pass']);
3314 }
3315 $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout);
3316 //$this->debug("WSDL request\n" . $tr->outgoing_payload);
3317 //$this->debug("WSDL response\n" . $tr->incoming_payload);
3318 $this->debug("transport debug data...\n" . $tr->debug_str);
3319 // catch errors
3320 if($err = $tr->getError() ){
3321 $errstr = 'HTTP ERROR: '.$err;
3322 $this->debug($errstr);
3323 $this->setError($errstr);
3324 unset($tr);
3325 return false;
3326 }
3327 unset($tr);
3328 } else {
3329 // $wsdl is not http(s), so treat it as a file URL or plain file path
3330 if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'file') && isset($wsdl_props['path'])) {
3331 $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
3332 } else {
3333 $path = $wsdl;
3334 }
3335 $this->debug('getting WSDL file ' . $path);
3336 if ($fp = @fopen($path, 'r')) {
3337 $wsdl_string = '';
3338 while ($data = fread($fp, 32768)) {
3339 $wsdl_string .= $data;
3340 }
3341 fclose($fp);
3342 } else {
3343 $errstr = "Bad path to WSDL file $path";
3344 $this->debug($errstr);
3345 $this->setError($errstr);
3346 return false;
3347 }
3348 }
3349 // end new code added
3350 // Create an XML parser.
3351 $this->parser = xml_parser_create();
3352 // Set the options for parsing the XML data.
3353 // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
3354 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
3355 // Set the object for the parser.
3356 xml_set_object($this->parser, $this);
3357 // Set the element handlers for the parser.
3358 xml_set_element_handler($this->parser, 'start_element', 'end_element');
3359 xml_set_character_data_handler($this->parser, 'character_data');
3360 // Parse the XML file.
3361 if (!xml_parse($this->parser, $wsdl_string, true)) {
3362 // Display an error message.
3363 $errstr = sprintf(
3364 'XML error parsing WSDL from %s on line %d: %s',
3365 $wsdl,
3366 xml_get_current_line_number($this->parser),
3367 xml_error_string(xml_get_error_code($this->parser))
3368 );
3369 $this->debug($errstr);
3370 $this->debug("XML payload:\n" . $wsdl_string);
3371 $this->setError($errstr);
3372 return false;
3373 }
3374 // free the parser
3375 xml_parser_free($this->parser);
3376 // catch wsdl parse errors
3377 if($this->getError()){
3378 return false;
3379 }
3380 return true;
3381 }
3382
3383 /**
3384 * start-element handler
3385 *
3386 * @param string $parser XML parser object
3387 * @param string $name element name
3388 * @param string $attrs associative array of attributes
3389 * @access private
3390 */
3391 function start_element($parser, $name, $attrs)
3392 {
3393 if ($this->status == 'schema') {
3394 $this->currentSchema->schemaStartElement($parser, $name, $attrs);
3395 $this->debug_str .= $this->currentSchema->debug_str;
3396 $this->currentSchema->debug_str = '';
3397 } elseif (ereg('schema$', $name)) {
3398 // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
3399 $this->status = 'schema';
3400 $this->currentSchema = new xmlschema('', '', $this->namespaces);
3401 $this->currentSchema->schemaStartElement($parser, $name, $attrs);
3402 $this->debug_str .= $this->currentSchema->debug_str;
3403 $this->currentSchema->debug_str = '';
3404 } else {
3405 // position in the total number of elements, starting from 0
3406 $pos = $this->position++;
3407 $depth = $this->depth++;
3408 // set self as current value for this depth
3409 $this->depth_array[$depth] = $pos;
3410 $this->message[$pos] = array('cdata' => '');
3411 // get element prefix
3412 if (ereg(':', $name)) {
3413 // get ns prefix
3414 $prefix = substr($name, 0, strpos($name, ':'));
3415 // get ns
3416 $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
3417 // get unqualified name
3418 $name = substr(strstr($name, ':'), 1);
3419 }
3420
3421 if (count($attrs) > 0) {
3422 foreach($attrs as $k => $v) {
3423 // if ns declarations, add to class level array of valid namespaces
3424 if (ereg("^xmlns", $k)) {
3425 if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
3426 $this->namespaces[$ns_prefix] = $v;
3427 } else {
3428 $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
3429 }
3430 if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema') {
3431 $this->XMLSchemaVersion = $v;
3432 $this->namespaces['xsi'] = $v . '-instance';
3433 }
3434 } //
3435 // expand each attribute
3436 $k = strpos($k, ':') ? $this->expandQname($k) : $k;
3437 if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
3438 $v = strpos($v, ':') ? $this->expandQname($v) : $v;
3439 }
3440 $eAttrs[$k] = $v;
3441 }
3442 $attrs = $eAttrs;
3443 } else {
3444 $attrs = array();
3445 }
3446 // find status, register data
3447 switch ($this->status) {
3448 case 'message':
3449 if ($name == 'part') {
3450 if (isset($attrs['type'])) {
3451 $this->debug("msg " . $this->currentMessage . ": found part $attrs[name]: " . implode(',', $attrs));
3452 $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
3453 }
3454 if (isset($attrs['element'])) {
3455 $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'];
3456 }
3457 }
3458 break;
3459 case 'portType':
3460 switch ($name) {
3461 case 'operation':
3462 $this->currentPortOperation = $attrs['name'];
3463 $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
3464 if (isset($attrs['parameterOrder'])) {
3465 $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
3466 }
3467 break;
3468 case 'documentation':
3469 $this->documentation = true;
3470 break;
3471 // merge input/output data
3472 default:
3473 $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
3474 $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
3475 break;
3476 }
3477 break;
3478 case 'binding':
3479 switch ($name) {
3480 case 'binding':
3481 // get ns prefix
3482 if (isset($attrs['style'])) {
3483 $this->bindings[$this->currentBinding]['prefix'] = $prefix;
3484 }
3485 $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
3486 break;
3487 case 'header':
3488 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
3489 break;
3490 case 'operation':
3491 if (isset($attrs['soapAction'])) {
3492 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
3493 }
3494 if (isset($attrs['style'])) {
3495 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
3496 }
3497 if (isset($attrs['name'])) {
3498 $this->currentOperation = $attrs['name'];
3499 $this->debug("current binding operation: $this->currentOperation");
3500 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
3501 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
3502 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
3503 }
3504 break;
3505 case 'input':
3506 $this->opStatus = 'input';
3507 break;
3508 case 'output':
3509 $this->opStatus = 'output';
3510 break;
3511 case 'body':
3512 if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
3513 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
3514 } else {
3515 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
3516 }
3517 break;
3518 }
3519 break;
3520 case 'service':
3521 switch ($name) {
3522 case 'port':
3523 $this->currentPort = $attrs['name'];
3524 $this->debug('current port: ' . $this->currentPort);
3525 $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
3526
3527 break;
3528 case 'address':
3529 $this->ports[$this->currentPort]['location'] = $attrs['location'];
3530 $this->ports[$this->currentPort]['bindingType'] = $namespace;
3531 $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace;
3532 $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location'];
3533 break;
3534 }
3535 break;
3536 }
3537 // set status
3538 switch ($name) {
3539 case 'import':
3540 if (isset($attrs['location'])) {
3541 $this->import[$attrs['namespace']][] = array('location' => $attrs['location'], 'loaded' => false);
3542 $this->debug('parsing import ' . $attrs['namespace']. ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]).')');
3543 } else {
3544 $this->import[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
3545 if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
3546 $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
3547 }
3548 $this->debug('parsing import ' . $attrs['namespace']. ' - [no location] (' . count($this->import[$attrs['namespace']]).')');
3549 }
3550 break;
3551 //wait for schema
3552 //case 'types':
3553 // $this->status = 'schema';
3554 // break;
3555 case 'message':
3556 $this->status = 'message';
3557 $this->messages[$attrs['name']] = array();
3558 $this->currentMessage = $attrs['name'];
3559 break;
3560 case 'portType':
3561 $this->status = 'portType';
3562 $this->portTypes[$attrs['name']] = array();
3563 $this->currentPortType = $attrs['name'];
3564 break;
3565 case "binding":
3566 if (isset($attrs['name'])) {
3567 // get binding name
3568 if (strpos($attrs['name'], ':')) {
3569 $this->currentBinding = $this->getLocalPart($attrs['name']);
3570 } else {
3571 $this->currentBinding = $attrs['name'];
3572 }
3573 $this->status = 'binding';
3574 $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
3575 $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
3576 }
3577 break;
3578 case 'service':
3579 $this->serviceName = $attrs['name'];
3580 $this->status = 'service';
3581 $this->debug('current service: ' . $this->serviceName);
3582 break;
3583 case 'definitions':
3584 foreach ($attrs as $name => $value) {
3585 $this->wsdl_info[$name] = $value;
3586 }
3587 break;
3588 }
3589 }
3590 }
3591
3592 /**
3593 * end-element handler
3594 *
3595 * @param string $parser XML parser object
3596 * @param string $name element name
3597 * @access private
3598 */
3599 function end_element($parser, $name){
3600 // unset schema status
3601 if (/*ereg('types$', $name) ||*/ ereg('schema$', $name)) {
3602 $this->status = "";
3603 $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
3604 }
3605 if ($this->status == 'schema') {
3606 $this->currentSchema->schemaEndElement($parser, $name);
3607 } else {
3608 // bring depth down a notch
3609 $this->depth--;
3610 }
3611 // end documentation
3612 if ($this->documentation) {
3613 //TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
3614 //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
3615 $this->documentation = false;
3616 }
3617 }
3618
3619 /**
3620 * element content handler
3621 *
3622 * @param string $parser XML parser object
3623 * @param string $data element content
3624 * @access private
3625 */
3626 function character_data($parser, $data)
3627 {
3628 $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
3629 if (isset($this->message[$pos]['cdata'])) {
3630 $this->message[$pos]['cdata'] .= $data;
3631 }
3632 if ($this->documentation) {
3633 $this->documentation .= $data;
3634 }
3635 }
3636
3637 function getBindingData($binding)
3638 {
3639 if (is_array($this->bindings[$binding])) {
3640 return $this->bindings[$binding];
3641 }
3642 }
3643
3644 /**
3645 * returns an assoc array of operation names => operation data
3646 *
3647 * @param string $bindingType eg: soap, smtp, dime (only soap is currently supported)
3648 * @return array
3649 * @access public
3650 */
3651 function getOperations($bindingType = 'soap')
3652 {
3653 $ops = array();
3654 if ($bindingType == 'soap') {
3655 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
3656 }
3657 // loop thru ports
3658 foreach($this->ports as $port => $portData) {
3659 // binding type of port matches parameter
3660 if ($portData['bindingType'] == $bindingType) {
3661 //$this->debug("getOperations for port $port");
3662 //$this->debug("port data: " . $this->varDump($portData));
3663 //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ]));
3664 // merge bindings
3665 if (isset($this->bindings[ $portData['binding'] ]['operations'])) {
3666 $ops = array_merge ($ops, $this->bindings[ $portData['binding'] ]['operations']);
3667 }
3668 }
3669 }
3670 return $ops;
3671 }
3672
3673 /**
3674 * returns an associative array of data necessary for calling an operation
3675 *
3676 * @param string $operation , name of operation
3677 * @param string $bindingType , type of binding eg: soap
3678 * @return array
3679 * @access public
3680 */
3681 function getOperationData($operation, $bindingType = 'soap')
3682 {
3683 if ($bindingType == 'soap') {
3684 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
3685 }
3686 // loop thru ports
3687 foreach($this->ports as $port => $portData) {
3688 // binding type of port matches parameter
3689 if ($portData['bindingType'] == $bindingType) {
3690 // get binding
3691 //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
3692 foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) {
3693 if ($operation == $bOperation) {
3694 $opData = $this->bindings[ $portData['binding'] ]['operations'][$operation];
3695 return $opData;
3696 }
3697 }
3698 }
3699 }
3700 }
3701
3702 /**
3703 * returns an array of information about a given type
3704 * returns false if no type exists by the given name
3705 *
3706 * typeDef = array(
3707 * 'elements' => array(), // refs to elements array
3708 * 'restrictionBase' => '',
3709 * 'phpType' => '',
3710 * 'order' => '(sequence|all)',
3711 * 'attrs' => array() // refs to attributes array
3712 * )
3713 *
3714 * @param $type string
3715 * @param $ns string
3716 * @return mixed
3717 * @access public
3718 * @see xmlschema
3719 */
3720 function getTypeDef($type, $ns) {
3721 if ((! $ns) && isset($this->namespaces['tns'])) {
3722 $ns = $this->namespaces['tns'];
3723 }
3724 if (isset($this->schemas[$ns])) {
3725 foreach ($this->schemas[$ns] as $xs) {
3726 $t = $xs->getTypeDef($type);
3727 $this->debug_str .= $xs->debug_str;
3728 $xs->debug_str = '';
3729 if ($t) {
3730 return $t;
3731 }
3732 }
3733 }
3734 return false;
3735 }
3736
3737 /**
3738 * serialize the parsed wsdl
3739 *
3740 * @return string , serialization of WSDL
3741 * @access public
3742 */
3743 function serialize()
3744 {
3745 $xml = '<?xml version="1.0" encoding="ISO-8859-1"?><definitions';
3746 foreach($this->namespaces as $k => $v) {
3747 $xml .= " xmlns:$k=\"$v\"";
3748 }
3749 // 10.9.02 - add poulter fix for wsdl and tns declarations
3750 if (isset($this->namespaces['wsdl'])) {
3751 $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
3752 }
3753 if (isset($this->namespaces['tns'])) {
3754 $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
3755 }
3756 $xml .= '>';
3757 // imports
3758 if (sizeof($this->import) > 0) {
3759 foreach($this->import as $ns => $list) {
3760 foreach ($list as $ii) {
3761 if ($ii['location'] != '') {
3762 $xml .= '<import location="' . $ii['location'] . '" namespace="' . $ns . '" />';
3763 } else {
3764 $xml .= '<import namespace="' . $ns . '" />';
3765 }
3766 }
3767 }
3768 }
3769 // types
3770 if (count($this->schemas)>=1) {
3771 $xml .= '<types>';
3772 foreach ($this->schemas as $ns => $list) {
3773 foreach ($list as $xs) {
3774 $xml .= $xs->serializeSchema();
3775 }
3776 }
3777 $xml .= '</types>';
3778 }
3779 // messages
3780 if (count($this->messages) >= 1) {
3781 foreach($this->messages as $msgName => $msgParts) {
3782 $xml .= '<message name="' . $msgName . '">';
3783 if(is_array($msgParts)){
3784 foreach($msgParts as $partName => $partType) {
3785 // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
3786 if (strpos($partType, ':')) {
3787 $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
3788 } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
3789 // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
3790 $typePrefix = 'xsd';
3791 } else {
3792 foreach($this->typemap as $ns => $types) {
3793 if (isset($types[$partType])) {
3794 $typePrefix = $this->getPrefixFromNamespace($ns);
3795 }
3796 }
3797 if (!isset($typePrefix)) {
3798 die("$partType has no namespace!");
3799 }
3800 }
3801 $xml .= '<part name="' . $partName . '" type="' . $typePrefix . ':' . $this->getLocalPart($partType) . '" />';
3802 }
3803 }
3804 $xml .= '</message>';
3805 }
3806 }
3807 // bindings & porttypes
3808 if (count($this->bindings) >= 1) {
3809 $binding_xml = '';
3810 $portType_xml = '';
3811 foreach($this->bindings as $bindingName => $attrs) {
3812 $binding_xml .= '<binding name="' . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
3813 $binding_xml .= '<soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
3814 $portType_xml .= '<portType name="' . $attrs['portType'] . '">';
3815 foreach($attrs['operations'] as $opName => $opParts) {
3816 $binding_xml .= '<operation name="' . $opName . '">';
3817 $binding_xml .= '<soap:operation soapAction="' . $opParts['soapAction'] . '" style="'. $attrs['style'] . '"/>';
3818 if (isset($opParts['input']['encodingStyle']) && $opParts['input']['encodingStyle'] != '') {
3819 $enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"';
3820 } else {
3821 $enc_style = '';
3822 }
3823 $binding_xml .= '<input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '"' . $enc_style . '/></input>';
3824 if (isset($opParts['output']['encodingStyle']) && $opParts['output']['encodingStyle'] != '') {
3825 $enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"';
3826 } else {
3827 $enc_style = '';
3828 }
3829 $binding_xml .= '<output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '"' . $enc_style . '/></output>';
3830 $binding_xml .= '</operation>';
3831 $portType_xml .= '<operation name="' . $opParts['name'] . '"';
3832 if (isset($opParts['parameterOrder'])) {
3833 $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
3834 }
3835 $portType_xml .= '>';
3836 if(isset($opParts['documentation']) && $opParts['documentation'] != '') {
3837 $portType_xml .= '<documentation>' . htmlspecialchars($opParts['documentation']) . '</documentation>';
3838 }
3839 $portType_xml .= '<input message="tns:' . $opParts['input']['message'] . '"/>';
3840 $portType_xml .= '<output message="tns:' . $opParts['output']['message'] . '"/>';
3841 $portType_xml .= '</operation>';
3842 }
3843 $portType_xml .= '</portType>';
3844 $binding_xml .= '</binding>';
3845 }
3846 $xml .= $portType_xml . $binding_xml;
3847 }
3848 // services
3849 $xml .= '<service name="' . $this->serviceName . '">';
3850 if (count($this->ports) >= 1) {
3851 foreach($this->ports as $pName => $attrs) {
3852 $xml .= '<port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
3853 $xml .= '<soap:address location="' . $attrs['location'] . '"/>';
3854 $xml .= '</port>';
3855 }
3856 }
3857 $xml .= '</service>';
3858 return $xml . '</definitions>';
3859 }
3860
3861 /**
3862 * serialize a PHP value according to a WSDL message definition
3863 *
3864 * TODO
3865 * - multi-ref serialization
3866 * - validate PHP values against type definitions, return errors if invalid
3867 *
3868 * @param string $ type name
3869 * @param mixed $ param value
3870 * @return mixed new param or false if initial value didn't validate
3871 */
3872 function serializeRPCParameters($operation, $direction, $parameters)
3873 {
3874 $this->debug('in serializeRPCParameters with operation '.$operation.', direction '.$direction.' and '.count($parameters).' param(s), and xml schema version ' . $this->XMLSchemaVersion);
3875
3876 if ($direction != 'input' && $direction != 'output') {
3877 $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
3878 $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
3879 return false;
3880 }
3881 if (!$opData = $this->getOperationData($operation)) {
3882 $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
3883 $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
3884 return false;
3885 }
3886 $this->debug($this->varDump($opData));
3887
3888 // Get encoding style for output and set to current
3889 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
3890 if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
3891 $encodingStyle = $opData['output']['encodingStyle'];
3892 $enc_style = $encodingStyle;
3893 }
3894
3895 // set input params
3896 $xml = '';
3897 if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
3898
3899 $use = $opData[$direction]['use'];
3900 $this->debug("use=$use");
3901 $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
3902 if (is_array($parameters)) {
3903 $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
3904 $this->debug('have ' . $parametersArrayType . ' parameters');
3905 foreach($opData[$direction]['parts'] as $name => $type) {
3906 $this->debug('serializing part "'.$name.'" of type "'.$type.'"');
3907 // Track encoding style
3908 if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
3909 $encodingStyle = $opData[$direction]['encodingStyle'];
3910 $enc_style = $encodingStyle;
3911 } else {
3912 $enc_style = false;
3913 }
3914 // NOTE: add error handling here
3915 // if serializeType returns false, then catch global error and fault
3916 if ($parametersArrayType == 'arraySimple') {
3917 $p = array_shift($parameters);
3918 $this->debug('calling serializeType w/indexed param');
3919 $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
3920 } elseif (isset($parameters[$name])) {
3921 $this->debug('calling serializeType w/named param');
3922 $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
3923 } else {
3924 // TODO: only send nillable
3925 $this->debug('calling serializeType w/null param');
3926 $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
3927 }
3928 }
3929 } else {
3930 $this->debug('no parameters passed.');
3931 }
3932 }
3933 return $xml;
3934 }
3935
3936 /**
3937 * serialize a PHP value according to a WSDL message definition
3938 *
3939 * TODO
3940 * - multi-ref serialization
3941 * - validate PHP values against type definitions, return errors if invalid
3942 *
3943 * @param string $ type name
3944 * @param mixed $ param value
3945 * @return mixed new param or false if initial value didn't validate
3946 */
3947 function serializeParameters($operation, $direction, $parameters)
3948 {
3949 $this->debug('in serializeParameters with operation '.$operation.', direction '.$direction.' and '.count($parameters).' param(s), and xml schema version ' . $this->XMLSchemaVersion);
3950
3951 if ($direction != 'input' && $direction != 'output') {
3952 $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
3953 $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
3954 return false;
3955 }
3956 if (!$opData = $this->getOperationData($operation)) {
3957 $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
3958 $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
3959 return false;
3960 }
3961 $this->debug($this->varDump($opData));
3962
3963 // Get encoding style for output and set to current
3964 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
3965 if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
3966 $encodingStyle = $opData['output']['encodingStyle'];
3967 $enc_style = $encodingStyle;
3968 }
3969
3970 // set input params
3971 $xml = '';
3972 if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
3973
3974 $use = $opData[$direction]['use'];
3975 $this->debug("use=$use");
3976 $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
3977 if (is_array($parameters)) {
3978 $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
3979 $this->debug('have ' . $parametersArrayType . ' parameters');
3980 foreach($opData[$direction]['parts'] as $name => $type) {
3981 $this->debug('serializing part "'.$name.'" of type "'.$type.'"');
3982 // Track encoding style
3983 if(isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
3984 $encodingStyle = $opData[$direction]['encodingStyle'];
3985 $enc_style = $encodingStyle;
3986 } else {
3987 $enc_style = false;
3988 }
3989 // NOTE: add error handling here
3990 // if serializeType returns false, then catch global error and fault
3991 if ($parametersArrayType == 'arraySimple') {
3992 $p = array_shift($parameters);
3993 $this->debug('calling serializeType w/indexed param');
3994 $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
3995 } elseif (isset($parameters[$name])) {
3996 $this->debug('calling serializeType w/named param');
3997 $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
3998 } else {
3999 // TODO: only send nillable
4000 $this->debug('calling serializeType w/null param');
4001 $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
4002 }
4003 }
4004 } else {
4005 $this->debug('no parameters passed.');
4006 }
4007 }
4008 return $xml;
4009 }
4010
4011 /**
4012 * serializes a PHP value according a given type definition
4013 *
4014 * @param string $name , name of type (part)
4015 * @param string $type , type of type, heh (type or element)
4016 * @param mixed $value , a native PHP value (parameter value)
4017 * @param string $use , use for part (encoded|literal)
4018 * @param string $encodingStyle , use to add encoding changes to serialisation
4019 * @return string serialization
4020 * @access public
4021 */
4022 function serializeType($name, $type, $value, $use='encoded', $encodingStyle=false)
4023 {
4024 $this->debug("in serializeType: $name, $type, $value, $use, $encodingStyle");
4025 if($use == 'encoded' && $encodingStyle) {
4026 $encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"';
4027 }
4028
4029 // if a soap_val has been supplied, let its type override the WSDL
4030 if (is_object($value) && get_class($value) == 'soapval') {
4031 // TODO: get attributes from soapval?
4032 if ($value->type_ns) {
4033 $type = $value->type_ns . ':' . $value->type;
4034 } else {
4035 $type = $value->type;
4036 }
4037 $value = $value->value;
4038 $forceType = true;
4039 $this->debug("in serializeType: soapval overrides type to $type, value to $value");
4040 } else {
4041 $forceType = false;
4042 }
4043
4044 $xml = '';
4045 if (strpos($type, ':')) {
4046 $uqType = substr($type, strrpos($type, ':') + 1);
4047 $ns = substr($type, 0, strrpos($type, ':'));
4048 $this->debug("got a prefixed type: $uqType, $ns");
4049 if ($this->getNamespaceFromPrefix($ns)) {
4050 $ns = $this->getNamespaceFromPrefix($ns);
4051 $this->debug("expanded prefixed type: $uqType, $ns");
4052 }
4053
4054 if($ns == $this->XMLSchemaVersion){
4055
4056 if (is_null($value)) {
4057 if ($use == 'literal') {
4058 // TODO: depends on nillable
4059 return "<$name/>";
4060 } else {
4061 return "<$name xsi:nil=\"true\"/>";
4062 }
4063 }
4064 if ($uqType == 'boolean' && !$value) {
4065 $value = 'false';
4066 } elseif ($uqType == 'boolean') {
4067 $value = 'true';
4068 }
4069 if ($uqType == 'string' && gettype($value) == 'string') {
4070 $value = $this->expandEntities($value);
4071 }
4072 // it's a scalar
4073 // TODO: what about null/nil values?
4074 // check type isn't a custom type extending xmlschema namespace
4075 if (!$this->getTypeDef($uqType, $ns)) {
4076 if ($use == 'literal') {
4077 if ($forceType) {
4078 return "<$name xsi:type=\"" . $this->getPrefixFromNamespace($this->XMLSchemaVersion) . ":$uqType\">$value</$name>";
4079 } else {
4080 return "<$name>$value</$name>";
4081 }
4082 } else {
4083 return "<$name xsi:type=\"" . $this->getPrefixFromNamespace($this->XMLSchemaVersion) . ":$uqType\"$encodingStyle>$value</$name>";
4084 }
4085 }
4086 } else if ($ns == 'http://xml.apache.org/xml-soap') {
4087 if ($uqType == 'Map') {
4088 $contents = '';
4089 foreach($value as $k => $v) {
4090 $this->debug("serializing map element: key $k, value $v");
4091 $contents .= '<item>';
4092 $contents .= $this->serialize_val($k,'key',false,false,false,false,$use);
4093 $contents .= $this->serialize_val($v,'value',false,false,false,false,$use);
4094 $contents .= '</item>';
4095 }
4096 if ($use == 'literal') {
4097 if ($forceType) {
4098 return "<$name xsi:type=\"" . $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap') . ":$uqType\">$contents</$name>";
4099 } else {
4100 return "<$name>$contents</$name>";
4101 }
4102 } else {
4103 return "<$name xsi:type=\"" . $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap') . ":$uqType\"$encodingStyle>$contents</$name>";
4104 }
4105 }
4106 }
4107 } else {
4108 $this->debug("No namespace for type $type");
4109 $ns = '';
4110 $uqType = $type;
4111 }
4112 if(!$typeDef = $this->getTypeDef($uqType, $ns)){
4113 $this->setError("$type ($uqType) is not a supported type.");
4114 $this->debug("$type ($uqType) is not a supported type.");
4115 return false;
4116 } else {
4117 foreach($typeDef as $k => $v) {
4118 $this->debug("typedef, $k: $v");
4119 }
4120 }
4121 $phpType = $typeDef['phpType'];
4122 $this->debug("serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') );
4123 // if php type == struct, map value to the <all> element names
4124 if ($phpType == 'struct') {
4125 if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') {
4126 $elementName = $uqType;
4127 if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
4128 $elementNS = " xmlns=\"$ns\"";
4129 }
4130 } else {
4131 $elementName = $name;
4132 $elementNS = '';
4133 }
4134 if (is_null($value)) {
4135 if ($use == 'literal') {
4136 // TODO: depends on nillable
4137 return "<$elementName$elementNS/>";
4138 } else {
4139 return "<$elementName$elementNS xsi:nil=\"true\"/>";
4140 }
4141 }
4142 if ($use == 'literal') {
4143 if ($forceType) {
4144 $xml = "<$elementName$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
4145 } else {
4146 $xml = "<$elementName$elementNS>";
4147 }
4148 } else {
4149 $xml = "<$elementName$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>";
4150 }
4151
4152 if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
4153 if (is_array($value)) {
4154 $xvalue = $value;
4155 } elseif (is_object($value)) {
4156 $xvalue = get_object_vars($value);
4157 } else {
4158 $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
4159 $xvalue = array();
4160 }
4161 // toggle whether all elements are present - ideally should validate against schema
4162 if(count($typeDef['elements']) != count($xvalue)){
4163 $optionals = true;
4164 }
4165 foreach($typeDef['elements'] as $eName => $attrs) {
4166 // if user took advantage of a minOccurs=0, then only serialize named parameters
4167 if(isset($optionals) && !isset($xvalue[$eName])){
4168 // do nothing
4169 } else {
4170 // get value
4171 if (isset($xvalue[$eName])) {
4172 $v = $xvalue[$eName];
4173 } else {
4174 $v = null;
4175 }
4176 // TODO: if maxOccurs > 1 (not just unbounded), then allow serialization of an array
4177 if (isset($attrs['maxOccurs']) && $attrs['maxOccurs'] == 'unbounded' && isset($v) && is_array($v) && $this->isArraySimpleOrStruct($v) == 'arraySimple') {
4178 $vv = $v;
4179 foreach ($vv as $k => $v) {
4180 if (isset($attrs['type'])) {
4181 // serialize schema-defined type
4182 $xml .= $this->serializeType($eName, $attrs['type'], $v, $use, $encodingStyle);
4183 } else {
4184 // serialize generic type
4185 $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
4186 $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
4187 }
4188 }
4189 } else {
4190 if (isset($attrs['type'])) {
4191 // serialize schema-defined type
4192 $xml .= $this->serializeType($eName, $attrs['type'], $v, $use, $encodingStyle);
4193 } else {
4194 // serialize generic type
4195 $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
4196 $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
4197 }
4198 }
4199 }
4200 }
4201 } else {
4202 $this->debug("Expected elements for XML Schema type $ns:$uqType");
4203 }
4204 $xml .= "</$elementName>";
4205 } elseif ($phpType == 'array') {
4206 if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
4207 $elementNS = " xmlns=\"$ns\"";
4208 } else {
4209 $elementNS = '';
4210 }
4211 if (is_null($value)) {
4212 if ($use == 'literal') {
4213 // TODO: depends on nillable
4214 return "<$name$elementNS/>";
4215 } else {
4216 return "<$name$elementNS xsi:nil=\"true\"/>";
4217 }
4218 }
4219 if (isset($typeDef['multidimensional'])) {
4220 $nv = array();
4221 foreach($value as $v) {
4222 $cols = ',' . sizeof($v);
4223 $nv = array_merge($nv, $v);
4224 }
4225 $value = $nv;
4226 } else {
4227 $cols = '';
4228 }
4229 if (is_array($value) && sizeof($value) >= 1) {
4230 $rows = sizeof($value);
4231 $contents = '';
4232 foreach($value as $k => $v) {
4233 $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]");
4234 //if (strpos($typeDef['arrayType'], ':') ) {
4235 if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) {
4236 $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
4237 } else {
4238 $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
4239 }
4240 }
4241 $this->debug('contents: '.$this->varDump($contents));
4242 } else {
4243 $rows = 0;
4244 $contents = null;
4245 }
4246 // TODO: for now, an empty value will be serialized as a zero element
4247 // array. Revisit this when coding the handling of null/nil values.
4248 if ($use == 'literal') {
4249 $xml = "<$name$elementNS>"
4250 .$contents
4251 ."</$name>";
4252 } else {
4253 $xml = "<$name$elementNS xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '.
4254 $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
4255 .':arrayType="'
4256 .$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
4257 .":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">"
4258 .$contents
4259 ."</$name>";
4260 }
4261 } elseif ($phpType == 'scalar') {
4262 if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
4263 $elementNS = " xmlns=\"$ns\"";
4264 } else {
4265 $elementNS = '';
4266 }
4267 if ($use == 'literal') {
4268 if ($forceType) {
4269 return "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
4270 } else {
4271 return "<$name$elementNS>$value</$name>";
4272 }
4273 } else {
4274 return "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
4275 }
4276 }
4277 $this->debug('returning: '.$this->varDump($xml));
4278 return $xml;
4279 }
4280
4281 /**
4282 * adds an XML Schema complex type to the WSDL types
4283 *
4284 * @param name
4285 * @param typeClass (complexType|simpleType|attribute)
4286 * @param phpType: currently supported are array and struct (php assoc array)
4287 * @param compositor (all|sequence|choice)
4288 * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
4289 * @param elements = array ( name = array(name=>'',type=>'') )
4290 * @param attrs = array(
4291 * array(
4292 * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
4293 * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
4294 * )
4295 * )
4296 * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
4297 * @see xmlschema
4298 *
4299 */
4300 function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType='') {
4301 if (count($elements) > 0) {
4302 foreach($elements as $n => $e){
4303 // expand each element
4304 foreach ($e as $k => $v) {
4305 $k = strpos($k,':') ? $this->expandQname($k) : $k;
4306 $v = strpos($v,':') ? $this->expandQname($v) : $v;
4307 $ee[$k] = $v;
4308 }
4309 $eElements[$n] = $ee;
4310 }
4311 $elements = $eElements;
4312 }
4313
4314 if (count($attrs) > 0) {
4315 foreach($attrs as $n => $a){
4316 // expand each attribute
4317 foreach ($a as $k => $v) {
4318 $k = strpos($k,':') ? $this->expandQname($k) : $k;
4319 $v = strpos($v,':') ? $this->expandQname($v) : $v;
4320 $aa[$k] = $v;
4321 }
4322 $eAttrs[$n] = $aa;
4323 }
4324 $attrs = $eAttrs;
4325 }
4326
4327 $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
4328 $arrayType = strpos($arrayType,':') ? $this->expandQname($arrayType) : $arrayType;
4329
4330 $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
4331 $this->schemas[$typens][0]->addComplexType($name,$typeClass,$phpType,$compositor,$restrictionBase,$elements,$attrs,$arrayType);
4332 }
4333
4334 /**
4335 * adds an XML Schema simple type to the WSDL types
4336 *
4337 * @param name
4338 * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
4339 * @param typeClass (simpleType)
4340 * @param phpType: (scalar)
4341 * @see xmlschema
4342 *
4343 */
4344 function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar') {
4345 $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
4346
4347 $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
4348 $this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType);
4349 }
4350
4351 /**
4352 * register a service with the server
4353 *
4354 * @param string $methodname
4355 * @param string $in assoc array of input values: key = param name, value = param type
4356 * @param string $out assoc array of output values: key = param name, value = param type
4357 * @param string $namespace optional The namespace for the operation
4358 * @param string $soapaction optional The soapaction for the operation
4359 * @param string $style (rpc|document) optional The style for the operation
4360 * @param string $use (encoded|literal) optional The use for the parameters (cannot mix right now)
4361 * @param string $documentation optional The description to include in the WSDL
4362 * @access public
4363 */
4364 function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = ''){
4365 if ($style == 'rpc' && $use == 'encoded') {
4366 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
4367 } else {
4368 $encodingStyle = '';
4369 }
4370 // get binding
4371 $this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] =
4372 array(
4373 'name' => $name,
4374 'binding' => $this->serviceName . 'Binding',
4375 'endpoint' => $this->endpoint,
4376 'soapAction' => $soapaction,
4377 'style' => $style,
4378 'input' => array(
4379 'use' => $use,
4380 'namespace' => $namespace,
4381 'encodingStyle' => $encodingStyle,
4382 'message' => $name . 'Request',
4383 'parts' => $in),
4384 'output' => array(
4385 'use' => $use,
4386 'namespace' => $namespace,
4387 'encodingStyle' => $encodingStyle,
4388 'message' => $name . 'Response',
4389 'parts' => $out),
4390 'namespace' => $namespace,
4391 'transport' => 'http://schemas.xmlsoap.org/soap/http',
4392 'documentation' => $documentation);
4393 // add portTypes
4394 // add messages
4395 if($in)
4396 {
4397 foreach($in as $pName => $pType)
4398 {
4399 if(strpos($pType,':')) {
4400 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
4401 }
4402 $this->messages[$name.'Request'][$pName] = $pType;
4403 }
4404 } else {
4405 $this->messages[$name.'Request']= '0';
4406 }
4407 if($out)
4408 {
4409 foreach($out as $pName => $pType)
4410 {
4411 if(strpos($pType,':')) {
4412 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
4413 }
4414 $this->messages[$name.'Response'][$pName] = $pType;
4415 }
4416 } else {
4417 $this->messages[$name.'Response']= '0';
4418 }
4419 return true;
4420 }
4421 }
4422 ?><?php
4423
4424
4425
4426 /**
4427 *
4428 * soap_parser class parses SOAP XML messages into native PHP values
4429 *
4430 * @author Dietrich Ayala <dietrich@ganx4.com>
4431 * @version $Id$
4432 * @access public
4433 */
4434 class soap_parser extends nusoap_base {
4435
4436 var $xml = '';
4437 var $xml_encoding = '';
4438 var $method = '';
4439 var $root_struct = '';
4440 var $root_struct_name = '';
4441 var $root_struct_namespace = '';
4442 var $root_header = '';
4443 var $document = ''; // incoming SOAP body (text)
4444 // determines where in the message we are (envelope,header,body,method)
4445 var $status = '';
4446 var $position = 0;
4447 var $depth = 0;
4448 var $default_namespace = '';
4449 var $namespaces = array();
4450 var $message = array();
4451 var $parent = '';
4452 var $fault = false;
4453 var $fault_code = '';
4454 var $fault_str = '';
4455 var $fault_detail = '';
4456 var $depth_array = array();
4457 var $debug_flag = true;
4458 var $soapresponse = NULL;
4459 var $responseHeaders = ''; // incoming SOAP headers (text)
4460 var $body_position = 0;
4461 // for multiref parsing:
4462 // array of id => pos
4463 var $ids = array();
4464 // array of id => hrefs => pos
4465 var $multirefs = array();
4466 // toggle for auto-decoding element content
4467 var $decode_utf8 = true;
4468
4469 /**
4470 * constructor
4471 *
4472 * @param string $xml SOAP message
4473 * @param string $encoding character encoding scheme of message
4474 * @param string $method
4475 * @param string $decode_utf8 whether to decode UTF-8 to ISO-8859-1
4476 * @access public
4477 */
4478 function soap_parser($xml,$encoding='UTF-8',$method='',$decode_utf8=true){
4479 $this->xml = $xml;
4480 $this->xml_encoding = $encoding;
4481 $this->method = $method;
4482 $this->decode_utf8 = $decode_utf8;
4483
4484 // Check whether content has been read.
4485 if(!empty($xml)){
4486 $this->debug('Entering soap_parser(), length='.strlen($xml).', encoding='.$encoding);
4487 // Create an XML parser - why not xml_parser_create_ns?
4488 $this->parser = xml_parser_create($this->xml_encoding);
4489 // Set the options for parsing the XML data.
4490 //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
4491 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
4492 xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding);
4493 // Set the object for the parser.
4494 xml_set_object($this->parser, $this);
4495 // Set the element handlers for the parser.
4496 xml_set_element_handler($this->parser, 'start_element','end_element');
4497 xml_set_character_data_handler($this->parser,'character_data');
4498
4499 // Parse the XML file.
4500 if(!xml_parse($this->parser,$xml,true)){
4501 // Display an error message.
4502 $err = sprintf('XML error parsing SOAP payload on line %d: %s',
4503 xml_get_current_line_number($this->parser),
4504 xml_error_string(xml_get_error_code($this->parser)));
4505 $this->debug($err);
4506 $this->debug("XML payload:\n" . $xml);
4507 $this->setError($err);
4508 } else {
4509 $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name);
4510 // get final value
4511 $this->soapresponse = $this->message[$this->root_struct]['result'];
4512 // get header value: no, because this is documented as XML string
4513 // if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){
4514 // $this->responseHeaders = $this->message[$this->root_header]['result'];
4515 // }
4516 // resolve hrefs/ids
4517 if(sizeof($this->multirefs) > 0){
4518 foreach($this->multirefs as $id => $hrefs){
4519 $this->debug('resolving multirefs for id: '.$id);
4520 $idVal = $this->buildVal($this->ids[$id]);
4521 foreach($hrefs as $refPos => $ref){
4522 $this->debug('resolving href at pos '.$refPos);
4523 $this->multirefs[$id][$refPos] = $idVal;
4524 }
4525 }
4526 }
4527 }
4528 xml_parser_free($this->parser);
4529 } else {
4530 $this->debug('xml was empty, didn\'t parse!');
4531 $this->setError('xml was empty, didn\'t parse!');
4532 }
4533 }
4534
4535 /**
4536 * start-element handler
4537 *
4538 * @param string $parser XML parser object
4539 * @param string $name element name
4540 * @param string $attrs associative array of attributes
4541 * @access private
4542 */
4543 function start_element($parser, $name, $attrs) {
4544 // position in a total number of elements, starting from 0
4545 // update class level pos
4546 $pos = $this->position++;
4547 // and set mine
4548 $this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>'');
4549 // depth = how many levels removed from root?
4550 // set mine as current global depth and increment global depth value
4551 $this->message[$pos]['depth'] = $this->depth++;
4552
4553 // else add self as child to whoever the current parent is
4554 if($pos != 0){
4555 $this->message[$this->parent]['children'] .= '|'.$pos;
4556 }
4557 // set my parent
4558 $this->message[$pos]['parent'] = $this->parent;
4559 // set self as current parent
4560 $this->parent = $pos;
4561 // set self as current value for this depth
4562 $this->depth_array[$this->depth] = $pos;
4563 // get element prefix
4564 if(strpos($name,':')){
4565 // get ns prefix
4566 $prefix = substr($name,0,strpos($name,':'));
4567 // get unqualified name
4568 $name = substr(strstr($name,':'),1);
4569 }
4570 // set status
4571 if($name == 'Envelope'){
4572 $this->status = 'envelope';
4573 } elseif($name == 'Header'){
4574 $this->root_header = $pos;
4575 $this->status = 'header';
4576 } elseif($name == 'Body'){
4577 $this->status = 'body';
4578 $this->body_position = $pos;
4579 // set method
4580 } elseif($this->status == 'body' && $pos == ($this->body_position+1)){
4581 $this->status = 'method';
4582 $this->root_struct_name = $name;
4583 $this->root_struct = $pos;
4584 $this->message[$pos]['type'] = 'struct';
4585 $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
4586 }
4587 // set my status
4588 $this->message[$pos]['status'] = $this->status;
4589 // set name
4590 $this->message[$pos]['name'] = htmlspecialchars($name);
4591 // set attrs
4592 $this->message[$pos]['attrs'] = $attrs;
4593
4594 // loop through atts, logging ns and type declarations
4595 $attstr = '';
4596 foreach($attrs as $key => $value){
4597 $key_prefix = $this->getPrefix($key);
4598 $key_localpart = $this->getLocalPart($key);
4599 // if ns declarations, add to class level array of valid namespaces
4600 if($key_prefix == 'xmlns'){
4601 if(ereg('^http://www.w3.org/[0-9]{4}/XMLSchema$',$value)){
4602 $this->XMLSchemaVersion = $value;
4603 $this->namespaces['xsd'] = $this->XMLSchemaVersion;
4604 $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance';
4605 }
4606 $this->namespaces[$key_localpart] = $value;
4607 // set method namespace
4608 if($name == $this->root_struct_name){
4609 $this->methodNamespace = $value;
4610 }
4611 // if it's a type declaration, set type
4612 } elseif($key_localpart == 'type'){
4613 $value_prefix = $this->getPrefix($value);
4614 $value_localpart = $this->getLocalPart($value);
4615 $this->message[$pos]['type'] = $value_localpart;
4616 $this->message[$pos]['typePrefix'] = $value_prefix;
4617 if(isset($this->namespaces[$value_prefix])){
4618 $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
4619 } else if(isset($attrs['xmlns:'.$value_prefix])) {
4620 $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix];
4621 }
4622 // should do something here with the namespace of specified type?
4623 } elseif($key_localpart == 'arrayType'){
4624 $this->message[$pos]['type'] = 'array';
4625 /* do arrayType ereg here
4626 [1] arrayTypeValue ::= atype asize
4627 [2] atype ::= QName rank*
4628 [3] rank ::= '[' (',')* ']'
4629 [4] asize ::= '[' length~ ']'
4630 [5] length ::= nextDimension* Digit+
4631 [6] nextDimension ::= Digit+ ','
4632 */
4633 $expr = '([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]';
4634 if(ereg($expr,$value,$regs)){
4635 $this->message[$pos]['typePrefix'] = $regs[1];
4636 $this->message[$pos]['arrayTypePrefix'] = $regs[1];
4637 if (isset($this->namespaces[$regs[1]])) {
4638 $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]];
4639 } else if (isset($attrs['xmlns:'.$regs[1]])) {
4640 $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:'.$regs[1]];
4641 }
4642 $this->message[$pos]['arrayType'] = $regs[2];
4643 $this->message[$pos]['arraySize'] = $regs[3];
4644 $this->message[$pos]['arrayCols'] = $regs[4];
4645 }
4646 }
4647 // log id
4648 if($key == 'id'){
4649 $this->ids[$value] = $pos;
4650 }
4651 // root
4652 if($key_localpart == 'root' && $value == 1){
4653 $this->status = 'method';
4654 $this->root_struct_name = $name;
4655 $this->root_struct = $pos;
4656 $this->debug("found root struct $this->root_struct_name, pos $pos");
4657 }
4658 // for doclit
4659 $attstr .= " $key=\"$value\"";
4660 }
4661 // get namespace - must be done after namespace atts are processed
4662 if(isset($prefix)){
4663 $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
4664 $this->default_namespace = $this->namespaces[$prefix];
4665 } else {
4666 $this->message[$pos]['namespace'] = $this->default_namespace;
4667 }
4668 if($this->status == 'header'){
4669 if ($this->root_header != $pos) {
4670 $this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
4671 }
4672 } elseif($this->root_struct_name != ''){
4673 $this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
4674 }
4675 }
4676
4677 /**
4678 * end-element handler
4679 *
4680 * @param string $parser XML parser object
4681 * @param string $name element name
4682 * @access private
4683 */
4684 function end_element($parser, $name) {
4685 // position of current element is equal to the last value left in depth_array for my depth
4686 $pos = $this->depth_array[$this->depth--];
4687
4688 // get element prefix
4689 if(strpos($name,':')){
4690 // get ns prefix
4691 $prefix = substr($name,0,strpos($name,':'));
4692 // get unqualified name
4693 $name = substr(strstr($name,':'),1);
4694 }
4695
4696 // build to native type
4697 if(isset($this->body_position) && $pos > $this->body_position){
4698 // deal w/ multirefs
4699 if(isset($this->message[$pos]['attrs']['href'])){
4700 // get id
4701 $id = substr($this->message[$pos]['attrs']['href'],1);
4702 // add placeholder to href array
4703 $this->multirefs[$id][$pos] = 'placeholder';
4704 // add set a reference to it as the result value
4705 $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
4706 // build complex values
4707 } elseif($this->message[$pos]['children'] != ''){
4708
4709 // if result has already been generated (struct/array
4710 if(!isset($this->message[$pos]['result'])){
4711 $this->message[$pos]['result'] = $this->buildVal($pos);
4712 }
4713
4714 // set value of simple type
4715 } else {
4716 //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
4717 if (isset($this->message[$pos]['type'])) {
4718 $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
4719 } else {
4720 $parent = $this->message[$pos]['parent'];
4721 if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
4722 $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
4723 } else {
4724 $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
4725 }
4726 }
4727
4728 /* add value to parent's result, if parent is struct/array
4729 $parent = $this->message[$pos]['parent'];
4730 if($this->message[$parent]['type'] != 'map'){
4731 if(strtolower($this->message[$parent]['type']) == 'array'){
4732 $this->message[$parent]['result'][] = $this->message[$pos]['result'];
4733 } else {
4734 $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result'];
4735 }
4736 }
4737 */
4738 }
4739 }
4740
4741 // for doclit
4742 if($this->status == 'header'){
4743 if ($this->root_header != $pos) {
4744 $this->responseHeaders .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
4745 }
4746 } elseif($pos >= $this->root_struct){
4747 $this->document .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
4748 }
4749 // switch status
4750 if($pos == $this->root_struct){
4751 $this->status = 'body';
4752 $this->root_struct_namespace = $this->message[$pos]['namespace'];
4753 } elseif($name == 'Body'){
4754 $this->status = 'envelope';
4755 } elseif($name == 'Header'){
4756 $this->status = 'envelope';
4757 } elseif($name == 'Envelope'){
4758 //
4759 }
4760 // set parent back to my parent
4761 $this->parent = $this->message[$pos]['parent'];
4762 }
4763
4764 /**
4765 * element content handler
4766 *
4767 * @param string $parser XML parser object
4768 * @param string $data element content
4769 * @access private
4770 */
4771 function character_data($parser, $data){
4772 $pos = $this->depth_array[$this->depth];
4773 if ($this->xml_encoding=='UTF-8'){
4774 // TODO: add an option to disable this for folks who want
4775 // raw UTF-8 that, e.g., might not map to iso-8859-1
4776 // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
4777 if($this->decode_utf8){
4778 $data = utf8_decode($data);
4779 }
4780 }
4781 $this->message[$pos]['cdata'] .= $data;
4782 // for doclit
4783 if($this->status == 'header'){
4784 $this->responseHeaders .= $data;
4785 } else {
4786 $this->document .= $data;
4787 }
4788 }
4789
4790 /**
4791 * get the parsed message
4792 *
4793 * @return mixed
4794 * @access public
4795 */
4796 function get_response(){
4797 return $this->soapresponse;
4798 }
4799
4800 /**
4801 * get the parsed headers
4802 *
4803 * @return string XML or empty if no headers
4804 * @access public
4805 */
4806 function getHeaders(){
4807 return $this->responseHeaders;
4808 }
4809
4810 /**
4811 * decodes entities
4812 *
4813 * @param string $text string to translate
4814 * @access private
4815 */
4816 function decode_entities($text){
4817 foreach($this->entities as $entity => $encoded){
4818 $text = str_replace($encoded,$entity,$text);
4819 }
4820 return $text;
4821 }
4822
4823 /**
4824 * decodes simple types into PHP variables
4825 *
4826 * @param string $value value to decode
4827 * @param string $type XML type to decode
4828 * @param string $typens XML type namespace to decode
4829 * @access private
4830 */
4831 function decodeSimple($value, $type, $typens) {
4832 // TODO: use the namespace!
4833 if ((!isset($type)) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') {
4834 return (string) $value;
4835 }
4836 if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') {
4837 return (int) $value;
4838 }
4839 if ($type == 'float' || $type == 'double' || $type == 'decimal') {
4840 return (double) $value;
4841 }
4842 if ($type == 'boolean') {
4843 if (strtolower($value) == 'false' || strtolower($value) == 'f') {
4844 return false;
4845 }
4846 return (boolean) $value;
4847 }
4848 if ($type == 'base64' || $type == 'base64Binary') {
4849 return base64_decode($value);
4850 }
4851 // obscure numeric types
4852 if ($type == 'nonPositiveInteger' || $type == 'negativeInteger'
4853 || $type == 'nonNegativeInteger' || $type == 'positiveInteger'
4854 || $type == 'unsignedInt'
4855 || $type == 'unsignedShort' || $type == 'unsignedByte') {
4856 return (int) $value;
4857 }
4858 // everything else
4859 return (string) $value;
4860 }
4861
4862 /**
4863 * builds response structures for compound values (arrays/structs)
4864 *
4865 * @param string $pos position in node tree
4866 * @access private
4867 */
4868 function buildVal($pos){
4869 if(!isset($this->message[$pos]['type'])){
4870 $this->message[$pos]['type'] = '';
4871 }
4872 $this->debug('inside buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']);
4873 // if there are children...
4874 if($this->message[$pos]['children'] != ''){
4875 $children = explode('|',$this->message[$pos]['children']);
4876 array_shift($children); // knock off empty
4877 // md array
4878 if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != ''){
4879 $r=0; // rowcount
4880 $c=0; // colcount
4881 foreach($children as $child_pos){
4882 $this->debug("got an MD array element: $r, $c");
4883 $params[$r][] = $this->message[$child_pos]['result'];
4884 $c++;
4885 if($c == $this->message[$pos]['arrayCols']){
4886 $c = 0;
4887 $r++;
4888 }
4889 }
4890 // array
4891 } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array'){
4892 $this->debug('adding array '.$this->message[$pos]['name']);
4893 foreach($children as $child_pos){
4894 $params[] = &$this->message[$child_pos]['result'];
4895 }
4896 // apache Map type: java hashtable
4897 } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){
4898 foreach($children as $child_pos){
4899 $kv = explode("|",$this->message[$child_pos]['children']);
4900 $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
4901 }
4902 // generic compound type
4903 //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
4904 } else {
4905 // Apache Vector type: treat as an array
4906 if ($this->message[$pos]['type'] == 'Vector' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
4907 $notstruct = 1;
4908 } else {
4909 // is array or struct?
4910 foreach($children as $child_pos){
4911 if(isset($keys) && isset($keys[$this->message[$child_pos]['name']])){
4912 $notstruct = 1;
4913 break;
4914 }
4915 $keys[$this->message[$child_pos]['name']] = 1;
4916 }
4917 }
4918 //
4919 foreach($children as $child_pos){
4920 if(isset($notstruct)){
4921 $params[] = &$this->message[$child_pos]['result'];
4922 } else {
4923 if (isset($params[$this->message[$child_pos]['name']])) {
4924 // de-serialize repeated element name into an array
4925 if (!is_array($params[$this->message[$child_pos]['name']])) {
4926 $params[$this->message[$child_pos]['name']] = array($params[$this->message[$child_pos]['name']]);
4927 }
4928 $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result'];
4929 } else {
4930 $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
4931 }
4932 }
4933 }
4934 }
4935 return is_array($params) ? $params : array();
4936 } else {
4937 $this->debug('no children');
4938 if(strpos($this->message[$pos]['cdata'],'&')){
4939 return strtr($this->message[$pos]['cdata'],array_flip($this->entities));
4940 } else {
4941 return $this->message[$pos]['cdata'];
4942 }
4943 }
4944 }
4945 }
4946
4947
4948
4949 ?><?php
4950
4951
4952
4953 /**
4954 *
4955 * soapclient higher level class for easy usage.
4956 *
4957 * usage:
4958 *
4959 * // instantiate client with server info
4960 * $soapclient = new soapclient( string path [ ,boolean wsdl] );
4961 *
4962 * // call method, get results
4963 * echo $soapclient->call( string methodname [ ,array parameters] );
4964 *
4965 * // bye bye client
4966 * unset($soapclient);
4967 *
4968 * @author Dietrich Ayala <dietrich@ganx4.com>
4969 * @version $Id$
4970 * @access public
4971 */
4972 class soapclient extends nusoap_base {
4973
4974 var $username = '';
4975 var $password = '';
4976 var $authtype = '';
4977 var $requestHeaders = false; // SOAP headers in request (text)
4978 var $responseHeaders = ''; // SOAP headers from response (incomplete namespace resolution) (text)
4979 var $document = ''; // SOAP body response portion (incomplete namespace resolution) (text)
4980 var $endpoint;
4981 var $error_str = false;
4982 var $proxyhost = '';
4983 var $proxyport = '';
4984 var $proxyusername = '';
4985 var $proxypassword = '';
4986 var $xml_encoding = ''; // character set encoding of incoming (response) messages
4987 var $http_encoding = false;
4988 var $timeout = 0; // HTTP connection timeout
4989 var $response_timeout = 30; // HTTP response timeout
4990 var $endpointType = '';
4991 var $persistentConnection = false;
4992 var $defaultRpcParams = false; // This is no longer used
4993 var $request = ''; // HTTP request
4994 var $response = ''; // HTTP response
4995 var $responseData = ''; // SOAP payload of response
4996 // toggles whether the parser decodes element content w/ utf8_decode()
4997 var $decode_utf8 = true;
4998
4999 /**
5000 * fault related variables
5001 *
5002 * @var fault
5003 * @var faultcode
5004 * @var faultstring
5005 * @var faultdetail
5006 * @access public
5007 */
5008 var $fault, $faultcode, $faultstring, $faultdetail;
5009
5010 /**
5011 * constructor
5012 *
5013 * @param mixed $endpoint SOAP server or WSDL URL (string), or wsdl instance (object)
5014 * @param bool $wsdl optional, set to true if using WSDL
5015 * @param int $portName optional portName in WSDL document
5016 * @param string $proxyhost
5017 * @param string $proxyport
5018 * @param string $proxyusername
5019 * @param string $proxypassword
5020 * @param integer $timeout set the connection timeout
5021 * @param integer $response_timeout set the response timeout
5022 * @access public
5023 */
5024 function soapclient($endpoint,$wsdl = false,$proxyhost = false,$proxyport = false,$proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30){
5025 $this->endpoint = $endpoint;
5026 $this->proxyhost = $proxyhost;
5027 $this->proxyport = $proxyport;
5028 $this->proxyusername = $proxyusername;
5029 $this->proxypassword = $proxypassword;
5030 $this->timeout = $timeout;
5031 $this->response_timeout = $response_timeout;
5032
5033 // make values
5034 if($wsdl){
5035 $this->endpointType = 'wsdl';
5036 if (is_object($endpoint) && is_a($endpoint, 'wsdl')) {
5037 $this->wsdl = $endpoint;
5038 $this->endpoint = $this->wsdl->wsdl;
5039 $this->wsdlFile = $this->endpoint;
5040 $this->debug('existing wsdl instance created from ' . $this->endpoint);
5041 } else {
5042 $this->wsdlFile = $this->endpoint;
5043
5044 // instantiate wsdl object and parse wsdl file
5045 $this->debug('instantiating wsdl class with doc: '.$endpoint);
5046 $this->wsdl =& new wsdl($this->wsdlFile,$this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword,$this->timeout,$this->response_timeout);
5047 }
5048 $this->debug("wsdl debug...\n".$this->wsdl->debug_str);
5049 $this->wsdl->debug_str = '';
5050 // catch errors
5051 if($errstr = $this->wsdl->getError()){
5052 $this->debug('got wsdl error: '.$errstr);
5053 $this->setError('wsdl error: '.$errstr);
5054 } elseif($this->operations = $this->wsdl->getOperations()){
5055 $this->debug( 'got '.count($this->operations).' operations from wsdl '.$this->wsdlFile);
5056 } else {
5057 $this->debug( 'getOperations returned false');
5058 $this->setError('no operations defined in the WSDL document!');
5059 }
5060 }
5061 }
5062
5063 /**
5064 * calls method, returns PHP native type
5065 *
5066 * @param string $method SOAP server URL or path
5067 * @param array $params An array, associative or simple, of the parameters
5068 * for the method call, or a string that is the XML
5069 * for the call. For rpc style, this call will
5070 * wrap the XML in a tag named after the method, as
5071 * well as the SOAP Envelope and Body. For document
5072 * style, this will only wrap with the Envelope and Body.
5073 * IMPORTANT: when using an array with document style,
5074 * in which case there
5075 * is really one parameter, the root of the fragment
5076 * used in the call, which encloses what programmers
5077 * normally think of parameters. A parameter array
5078 * *must* include the wrapper.
5079 * @param string $namespace optional method namespace (WSDL can override)
5080 * @param string $soapAction optional SOAPAction value (WSDL can override)
5081 * @param boolean $headers optional array of soapval objects for headers
5082 * @param boolean $rpcParams optional no longer used
5083 * @param string $style optional (rpc|document) the style to use when serializing parameters (WSDL can override)
5084 * @param string $use optional (encoded|literal) the use when serializing parameters (WSDL can override)
5085 * @return mixed
5086 * @access public
5087 */
5088 function call($operation,$params=array(),$namespace='',$soapAction='',$headers=false,$rpcParams=null,$style='rpc',$use='encoded'){
5089 $this->operation = $operation;
5090 $this->fault = false;
5091 $this->error_str = '';
5092 $this->request = '';
5093 $this->response = '';
5094 $this->responseData = '';
5095 $this->faultstring = '';
5096 $this->faultcode = '';
5097 $this->opData = array();
5098
5099 $this->debug("call: $operation, $params, $namespace, $soapAction, $headers, $style, $use; endpointType: $this->endpointType");
5100 if ($headers) {
5101 $this->requestHeaders = $headers;
5102 }
5103 // serialize parameters
5104 if($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)){
5105 // use WSDL for operation
5106 $this->opData = $opData;
5107 foreach($opData as $key => $value){
5108 $this->debug("$key -> $value");
5109 }
5110 if (isset($opData['soapAction'])) {
5111 $soapAction = $opData['soapAction'];
5112 }
5113 $this->endpoint = $opData['endpoint'];
5114 $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : ($namespace != '' ? $namespace : 'http://testuri.org');
5115 $style = $opData['style'];
5116 $use = $opData['input']['use'];
5117 // add ns to ns array
5118 if($namespace != '' && !isset($this->wsdl->namespaces[$namespace])){
5119 $this->wsdl->namespaces['nu'] = $namespace;
5120 }
5121 $nsPrefix = $this->wsdl->getPrefixFromNamespace($namespace);
5122 // serialize payload
5123 if (is_string($params)) {
5124 $this->debug("serializing param string for WSDL operation $operation");
5125 $payload = $params;
5126 } elseif (is_array($params)) {
5127 $this->debug("serializing param array for WSDL operation $operation");
5128 $payload = $this->wsdl->serializeRPCParameters($operation,'input',$params);
5129 } else {
5130 $this->debug('params must be array or string');
5131 $this->setError('params must be array or string');
5132 return false;
5133 }
5134 $usedNamespaces = $this->wsdl->usedNamespaces;
5135 // Partial fix for multiple encoding styles in the same function call
5136 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5137 if (isset($opData['output']['encodingStyle']) && $encodingStyle != $opData['output']['encodingStyle']) {
5138 $methodEncodingStyle = ' SOAP-ENV:encodingStyle="' . $opData['output']['encodingStyle'] . '"';
5139 } else {
5140 $methodEncodingStyle = '';
5141 }
5142 $this->debug("wsdl debug: \n".$this->wsdl->debug_str);
5143 $this->wsdl->debug_str = '';
5144 if ($errstr = $this->wsdl->getError()) {
5145 $this->debug('got wsdl error: '.$errstr);
5146 $this->setError('wsdl error: '.$errstr);
5147 return false;
5148 }
5149 } elseif($this->endpointType == 'wsdl') {
5150 // operation not in WSDL
5151 $this->setError( 'operation '.$operation.' not present.');
5152 $this->debug("operation '$operation' not present.");
5153 $this->debug("wsdl debug: \n".$this->wsdl->debug_str);
5154 $this->wsdl->debug_str = '';
5155 return false;
5156 } else {
5157 // no WSDL
5158 if($namespace == ''){
5159 $namespace = 'http://testuri.org';
5160 }
5161 //$this->namespaces['ns1'] = $namespace;
5162 $nsPrefix = 'ns1';
5163 // serialize
5164 $payload = '';
5165 if (is_string($params)) {
5166 $this->debug("serializing param string for operation $operation");
5167 $payload = $params;
5168 } elseif (is_array($params)) {
5169 $this->debug("serializing param array for operation $operation");
5170 foreach($params as $k => $v){
5171 $payload .= $this->serialize_val($v,$k,false,false,false,false,$use);
5172 }
5173 } else {
5174 $this->debug('params must be array or string');
5175 $this->setError('params must be array or string');
5176 return false;
5177 }
5178 $usedNamespaces = array();
5179 $methodEncodingStyle = '';
5180 }
5181 // wrap RPC calls with method element
5182 if ($style == 'rpc') {
5183 if ($use == 'literal') {
5184 $this->debug("wrapping RPC request with literal method element");
5185 $payload = "<$operation xmlns=\"$namespace\">" . $payload . "</$operation>";
5186 } else {
5187 $this->debug("wrapping RPC request with encoded method element");
5188 $payload = "<$nsPrefix:$operation$methodEncodingStyle xmlns:$nsPrefix=\"$namespace\">" .
5189 $payload .
5190 "</$nsPrefix:$operation>";
5191 }
5192 }
5193 // serialize envelope
5194 $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,$usedNamespaces,$style,$use);
5195 $this->debug("endpoint: $this->endpoint, soapAction: $soapAction, namespace: $namespace, style: $style, use: $use");
5196 $this->debug('SOAP message length: ' . strlen($soapmsg) . ' contents: ' . substr($soapmsg, 0, 1000));
5197 // send
5198 $return = $this->send($this->getHTTPBody($soapmsg),$soapAction,$this->timeout,$this->response_timeout);
5199 if($errstr = $this->getError()){
5200 $this->debug('Error: '.$errstr);
5201 return false;
5202 } else {
5203 $this->return = $return;
5204 $this->debug('sent message successfully and got a(n) '.gettype($return).' back');
5205
5206 // fault?
5207 if(is_array($return) && isset($return['faultcode'])){
5208 $this->debug('got fault');
5209 $this->setError($return['faultcode'].': '.$return['faultstring']);
5210 $this->fault = true;
5211 foreach($return as $k => $v){
5212 $this->$k = $v;
5213 $this->debug("$k = $v<br>");
5214 }
5215 return $return;
5216 } else {
5217 // array of return values
5218 if(is_array($return)){
5219 // multiple 'out' parameters
5220 if(sizeof($return) > 1){
5221 return $return;
5222 }
5223 // single 'out' parameter
5224 return array_shift($return);
5225 // nothing returned (ie, echoVoid)
5226 } else {
5227 return "";
5228 }
5229 }
5230 }
5231 }
5232
5233 /**
5234 * get available data pertaining to an operation
5235 *
5236 * @param string $operation operation name
5237 * @return array array of data pertaining to the operation
5238 * @access public
5239 */
5240 function getOperationData($operation){
5241 if(isset($this->operations[$operation])){
5242 return $this->operations[$operation];
5243 }
5244 $this->debug("No data for operation: $operation");
5245 }
5246
5247 /**
5248 * send the SOAP message
5249 *
5250 * Note: if the operation has multiple return values
5251 * the return value of this method will be an array
5252 * of those values.
5253 *
5254 * @param string $msg a SOAPx4 soapmsg object
5255 * @param string $soapaction SOAPAction value
5256 * @param integer $timeout set connection timeout in seconds
5257 * @param integer $response_timeout set response timeout in seconds
5258 * @return mixed native PHP types.
5259 * @access private
5260 */
5261 function send($msg, $soapaction = '', $timeout=0, $response_timeout=30) {
5262 // detect transport
5263 switch(true){
5264 // http(s)
5265 case ereg('^http',$this->endpoint):
5266 $this->debug('transporting via HTTP');
5267 if($this->persistentConnection == true && is_object($this->persistentConnection)){
5268 $http =& $this->persistentConnection;
5269 } else {
5270 $http = new soap_transport_http($this->endpoint);
5271 if ($this->persistentConnection) {
5272 $http->usePersistentConnection();
5273 }
5274 }
5275 $http->setContentType($this->getHTTPContentType(), $this->getHTTPContentTypeCharset());
5276 $http->setSOAPAction($soapaction);
5277 if($this->proxyhost && $this->proxyport){
5278 $http->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
5279 }
5280 if($this->username != '' && $this->password != '') {
5281 $http->setCredentials($this->username, $this->password, $this->authtype);
5282 }
5283 if($this->http_encoding != ''){
5284 $http->setEncoding($this->http_encoding);
5285 }
5286 $this->debug('sending message, length: '.strlen($msg));
5287 if(ereg('^http:',$this->endpoint)){
5288 //if(strpos($this->endpoint,'http:')){
5289 $this->responseData = $http->send($msg,$timeout,$response_timeout);
5290 } elseif(ereg('^https',$this->endpoint)){
5291 //} elseif(strpos($this->endpoint,'https:')){
5292 //if(phpversion() == '4.3.0-dev'){
5293 //$response = $http->send($msg,$timeout,$response_timeout);
5294 //$this->request = $http->outgoing_payload;
5295 //$this->response = $http->incoming_payload;
5296 //} else
5297 if (extension_loaded('curl')) {
5298 $this->responseData = $http->sendHTTPS($msg,$timeout,$response_timeout);
5299 } else {
5300 $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
5301 }
5302 } else {
5303 $this->setError('no http/s in endpoint url');
5304 }
5305 $this->request = $http->outgoing_payload;
5306 $this->response = $http->incoming_payload;
5307 $this->debug("transport debug data...\n".$http->debug_str);
5308
5309 // save transport object if using persistent connections
5310 if ($this->persistentConnection) {
5311 $http->debug_str = '';
5312 if (!is_object($this->persistentConnection)) {
5313 $this->persistentConnection = $http;
5314 }
5315 }
5316
5317 if($err = $http->getError()){
5318 $this->setError('HTTP Error: '.$err);
5319 return false;
5320 } elseif($this->getError()){
5321 return false;
5322 } else {
5323 $this->debug('got response, length: '. strlen($this->responseData).' type: '.$http->incoming_headers['content-type']);
5324 return $this->parseResponse($http->incoming_headers, $this->responseData);
5325 }
5326 break;
5327 default:
5328 $this->setError('no transport found, or selected transport is not yet supported!');
5329 return false;
5330 break;
5331 }
5332 }
5333
5334 /**
5335 * processes SOAP message returned from server
5336 *
5337 * @param array $headers The HTTP headers
5338 * @param string $data unprocessed response data from server
5339 * @return mixed value of the message, decoded into a PHP type
5340 * @access protected
5341 */
5342 function parseResponse($headers, $data) {
5343 $this->debug('Entering parseResponse() for data of length ' . strlen($data) . ' and type ' . $headers['content-type']);
5344 if (!strstr($headers['content-type'], 'text/xml')) {
5345 $this->setError('Response not of type text/xml');
5346 return false;
5347 }
5348 if (strpos($headers['content-type'], '=')) {
5349 $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
5350 $this->debug('Got response encoding: ' . $enc);
5351 if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
5352 $this->xml_encoding = strtoupper($enc);
5353 } else {
5354 $this->xml_encoding = 'US-ASCII';
5355 }
5356 } else {
5357 // should be US-ASCII, but for XML, let's be pragmatic and admit UTF-8 is most common
5358 $this->xml_encoding = 'UTF-8';
5359 }
5360 $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating soap_parser');
5361 $parser = new soap_parser($data,$this->xml_encoding,$this->operation,$this->decode_utf8);
5362 // add parser debug data to our debug
5363 $this->debug($parser->debug_str);
5364 // if parse errors
5365 if($errstr = $parser->getError()){
5366 $this->setError( $errstr);
5367 // destroy the parser object
5368 unset($parser);
5369 return false;
5370 } else {
5371 // get SOAP headers
5372 $this->responseHeaders = $parser->getHeaders();
5373 // get decoded message
5374 $return = $parser->get_response();
5375 // add document for doclit support
5376 $this->document = $parser->document;
5377 // destroy the parser object
5378 unset($parser);
5379 // return decode message
5380 return $return;
5381 }
5382 }
5383
5384 /**
5385 * set the SOAP headers
5386 *
5387 * @param $headers string XML
5388 * @access public
5389 */
5390 function setHeaders($headers){
5391 $this->requestHeaders = $headers;
5392 }
5393
5394 /**
5395 * get the response headers
5396 *
5397 * @return mixed object SOAPx4 soapval object or empty if no headers
5398 * @access public
5399 */
5400 function getHeaders(){
5401 if($this->responseHeaders != '') {
5402 return $this->responseHeaders;
5403 }
5404 }
5405
5406 /**
5407 * set proxy info here
5408 *
5409 * @param string $proxyhost
5410 * @param string $proxyport
5411 * @param string $proxyusername
5412 * @param string $proxypassword
5413 * @access public
5414 */
5415 function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
5416 $this->proxyhost = $proxyhost;
5417 $this->proxyport = $proxyport;
5418 $this->proxyusername = $proxyusername;
5419 $this->proxypassword = $proxypassword;
5420 }
5421
5422 /**
5423 * if authenticating, set user credentials here
5424 *
5425 * @param string $username
5426 * @param string $password
5427 * @param string $authtype (basic|digest)
5428 * @access public
5429 */
5430 function setCredentials($username, $password, $authtype = 'basic') {
5431 $this->username = $username;
5432 $this->password = $password;
5433 $this->authtype = $authtype;
5434 }
5435
5436 /**
5437 * use HTTP encoding
5438 *
5439 * @param string $enc
5440 * @access public
5441 */
5442 function setHTTPEncoding($enc='gzip, deflate'){
5443 $this->http_encoding = $enc;
5444 }
5445
5446 /**
5447 * use HTTP persistent connections if possible
5448 *
5449 * @access public
5450 */
5451 function useHTTPPersistentConnection(){
5452 $this->persistentConnection = true;
5453 }
5454
5455 /**
5456 * gets the default RPC parameter setting.
5457 * If true, default is that call params are like RPC even for document style.
5458 * Each call() can override this value.
5459 *
5460 * This is no longer used.
5461 *
5462 * @access public
5463 * @deprecated
5464 */
5465 function getDefaultRpcParams() {
5466 return $this->defaultRpcParams;
5467 }
5468
5469 /**
5470 * sets the default RPC parameter setting.
5471 * If true, default is that call params are like RPC even for document style
5472 * Each call() can override this value.
5473 *
5474 * @param boolean $rpcParams
5475 * @access public
5476 */
5477 function setDefaultRpcParams($rpcParams) {
5478 $this->defaultRpcParams = $rpcParams;
5479 }
5480
5481 /**
5482 * dynamically creates proxy class, allowing user to directly call methods from wsdl
5483 *
5484 * @return object soap_proxy object
5485 * @access public
5486 */
5487 function getProxy(){
5488 $evalStr = '';
5489 foreach($this->operations as $operation => $opData){
5490 if($operation != ''){
5491 // create param string
5492 $paramStr = '';
5493 if(sizeof($opData['input']['parts']) > 0){
5494 foreach($opData['input']['parts'] as $name => $type){
5495 $paramStr .= "\$$name,";
5496 }
5497 $paramStr = substr($paramStr,0,strlen($paramStr)-1);
5498 }
5499 $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
5500 $evalStr .= "function $operation ($paramStr){
5501 // load params into array
5502 \$params = array($paramStr);
5503 return \$this->call('$operation',\$params,'".$opData['namespace']."','".(isset($opData['soapAction']) ? $opData['soapAction'] : '')."');
5504 }";
5505 unset($paramStr);
5506 }
5507 }
5508 $r = rand();
5509 $evalStr = 'class soap_proxy_'.$r.' extends soapclient {
5510 '.$evalStr.'
5511 }';
5512 //print "proxy class:<pre>$evalStr</pre>";
5513 // eval the class
5514 eval($evalStr);
5515 // instantiate proxy object
5516 eval("\$proxy = new soap_proxy_$r('');");
5517 // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
5518 $proxy->endpointType = 'wsdl';
5519 $proxy->wsdlFile = $this->wsdlFile;
5520 $proxy->wsdl = $this->wsdl;
5521 $proxy->operations = $this->operations;
5522 $proxy->defaultRpcParams = $this->defaultRpcParams;
5523 // transfer other state
5524 $proxy->username = $this->username;
5525 $proxy->password = $this->password;
5526 $proxy->proxyhost = $this->proxyhost;
5527 $proxy->proxyport = $this->proxyport;
5528 $proxy->proxyusername = $this->proxyusername;
5529 $proxy->proxypassword = $this->proxypassword;
5530 $proxy->timeout = $this->timeout;
5531 $proxy->response_timeout = $this->response_timeout;
5532 $proxy->http_encoding = $this->http_encoding;
5533 $proxy->persistentConnection = $this->persistentConnection;
5534 return $proxy;
5535 }
5536
5537 /**
5538 * gets the HTTP body for the current request.
5539 *
5540 * @param string $soapmsg The SOAP payload
5541 * @return string The HTTP body, which includes the SOAP payload
5542 * @access protected
5543 */
5544 function getHTTPBody($soapmsg) {
5545 return $soapmsg;
5546 }
5547
5548 /**
5549 * gets the HTTP content type for the current request.
5550 *
5551 * Note: getHTTPBody must be called before this.
5552 *
5553 * @return string the HTTP content type for the current request.
5554 * @access protected
5555 */
5556 function getHTTPContentType() {
5557 return 'text/xml';
5558 }
5559
5560 /**
5561 * gets the HTTP content type charset for the current request.
5562 * returns false for non-text content types.
5563 *
5564 * Note: getHTTPBody must be called before this.
5565 *
5566 * @return string the HTTP content type charset for the current request.
5567 * @access protected
5568 */
5569 function getHTTPContentTypeCharset() {
5570 return $this->soap_defencoding;
5571 }
5572
5573 /*
5574 * whether or not parser should decode utf8 element content
5575 *
5576 * @return always returns true
5577 * @access public
5578 */
5579 function decodeUTF8($bool){
5580 $this->decode_utf8 = $bool;
5581 return true;
5582 }
5583 }
5584 ?>