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