Initial import.
[siap.git] / nusoap / lib / nusoap.php
CommitLineData
696f20d5
MS
1<?php
2
3/*
4$Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
5
6NuSOAP - Web Services Toolkit for PHP
7
8Copyright (c) 2002 NuSphere Corporation
9
10This library is free software; you can redistribute it and/or
11modify it under the terms of the GNU Lesser General Public
12License as published by the Free Software Foundation; either
13version 2.1 of the License, or (at your option) any later version.
14
15This library is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18Lesser General Public License for more details.
19
20You should have received a copy of the GNU Lesser General Public
21License along with this library; if not, write to the Free Software
22Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
24The NuSOAP project home is:
25http://sourceforge.net/projects/nusoap/
26
27The primary support for NuSOAP is the Help forum on the project home page.
28
29If you have any questions or comments, please email:
30
31Dietrich Ayala
32dietrich@ganx4.com
33http://dietrich.ganx4.com/nusoap
34
35NuSphere Corporation
36http://www.nusphere.com
37
38*/
39
40/*
41 * Some of the standards implmented in whole or part by NuSOAP:
42 *
43 * SOAP 1.1 (http://www.w3.org/TR/2000/NOTE-SOAP-20000508/)
44 * WSDL 1.1 (http://www.w3.org/TR/2001/NOTE-wsdl-20010315)
45 * SOAP Messages With Attachments (http://www.w3.org/TR/SOAP-attachments)
46 * XML 1.0 (http://www.w3.org/TR/2006/REC-xml-20060816/)
47 * Namespaces in XML 1.0 (http://www.w3.org/TR/2006/REC-xml-names-20060816/)
48 * XML Schema 1.0 (http://www.w3.org/TR/xmlschema-0/)
49 * RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
50 * RFC 2068 Hypertext Transfer Protocol -- HTTP/1.1
51 * RFC 2617 HTTP Authentication: Basic and Digest Access Authentication
52 */
53
54/* load classes
55
56// necessary classes
57require_once('class.soapclient.php');
58require_once('class.soap_val.php');
59require_once('class.soap_parser.php');
60require_once('class.soap_fault.php');
61
62// transport classes
63require_once('class.soap_transport_http.php');
64
65// optional add-on classes
66require_once('class.xmlschema.php');
67require_once('class.wsdl.php');
68
69// server class
70require_once('class.soap_server.php');*/
71
72// class variable emulation
73// cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
74$GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = 9;
75
76/**
77*
78* nusoap_base
79*
80* @author Dietrich Ayala <dietrich@ganx4.com>
81* @author Scott Nichol <snichol@users.sourceforge.net>
82* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
83* @access public
84*/
85class nusoap_base {
86 /**
87 * Identification for HTTP headers.
88 *
89 * @var string
90 * @access private
91 */
92 var $title = 'NuSOAP';
93 /**
94 * Version for HTTP headers.
95 *
96 * @var string
97 * @access private
98 */
99 var $version = '0.9.5';
100 /**
101 * CVS revision for HTTP headers.
102 *
103 * @var string
104 * @access private
105 */
106 var $revision = '$Revision: 1.123 $';
107 /**
108 * Current error string (manipulated by getError/setError)
109 *
110 * @var string
111 * @access private
112 */
113 var $error_str = '';
114 /**
115 * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
116 *
117 * @var string
118 * @access private
119 */
120 var $debug_str = '';
121 /**
122 * toggles automatic encoding of special characters as entities
123 * (should always be true, I think)
124 *
125 * @var boolean
126 * @access private
127 */
128 var $charencoding = true;
129 /**
130 * the debug level for this instance
131 *
132 * @var integer
133 * @access private
134 */
135 var $debugLevel;
136
137 /**
138 * set schema version
139 *
140 * @var string
141 * @access public
142 */
143 var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
144
145 /**
146 * charset encoding for outgoing messages
147 *
148 * @var string
149 * @access public
150 */
151 var $soap_defencoding = 'ISO-8859-1';
152 //var $soap_defencoding = 'UTF-8';
153
154 /**
155 * namespaces in an array of prefix => uri
156 *
157 * this is "seeded" by a set of constants, but it may be altered by code
158 *
159 * @var array
160 * @access public
161 */
162 var $namespaces = array(
163 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
164 'xsd' => 'http://www.w3.org/2001/XMLSchema',
165 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
166 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/'
167 );
168
169 /**
170 * namespaces used in the current context, e.g. during serialization
171 *
172 * @var array
173 * @access private
174 */
175 var $usedNamespaces = array();
176
177 /**
178 * XML Schema types in an array of uri => (array of xml type => php type)
179 * is this legacy yet?
180 * no, this is used by the nusoap_xmlschema class to verify type => namespace mappings.
181 * @var array
182 * @access public
183 */
184 var $typemap = array(
185 'http://www.w3.org/2001/XMLSchema' => array(
186 'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
187 'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
188 'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
189 // abstract "any" types
190 'anyType'=>'string','anySimpleType'=>'string',
191 // derived datatypes
192 'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
193 'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
194 'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
195 'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
196 'http://www.w3.org/2000/10/XMLSchema' => array(
197 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
198 'float'=>'double','dateTime'=>'string',
199 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
200 'http://www.w3.org/1999/XMLSchema' => array(
201 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
202 'float'=>'double','dateTime'=>'string',
203 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
204 'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
205 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
206 'http://xml.apache.org/xml-soap' => array('Map')
207 );
208
209 /**
210 * XML entities to convert
211 *
212 * @var array
213 * @access public
214 * @deprecated
215 * @see expandEntities
216 */
217 var $xmlEntities = array('quot' => '"','amp' => '&',
218 'lt' => '<','gt' => '>','apos' => "'");
219
220 /**
221 * constructor
222 *
223 * @access public
224 */
225 function nusoap_base() {
226 $this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'];
227 }
228
229 /**
230 * gets the global debug level, which applies to future instances
231 *
232 * @return integer Debug level 0-9, where 0 turns off
233 * @access public
234 */
235 function getGlobalDebugLevel() {
236 return $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'];
237 }
238
239 /**
240 * sets the global debug level, which applies to future instances
241 *
242 * @param int $level Debug level 0-9, where 0 turns off
243 * @access public
244 */
245 function setGlobalDebugLevel($level) {
246 $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = $level;
247 }
248
249 /**
250 * gets the debug level for this instance
251 *
252 * @return int Debug level 0-9, where 0 turns off
253 * @access public
254 */
255 function getDebugLevel() {
256 return $this->debugLevel;
257 }
258
259 /**
260 * sets the debug level for this instance
261 *
262 * @param int $level Debug level 0-9, where 0 turns off
263 * @access public
264 */
265 function setDebugLevel($level) {
266 $this->debugLevel = $level;
267 }
268
269 /**
270 * adds debug data to the instance debug string with formatting
271 *
272 * @param string $string debug data
273 * @access private
274 */
275 function debug($string){
276 if ($this->debugLevel > 0) {
277 $this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n");
278 }
279 }
280
281 /**
282 * adds debug data to the instance debug string without formatting
283 *
284 * @param string $string debug data
285 * @access public
286 */
287 function appendDebug($string){
288 if ($this->debugLevel > 0) {
289 // it would be nice to use a memory stream here to use
290 // memory more efficiently
291 $this->debug_str .= $string;
292 }
293 }
294
295 /**
296 * clears the current debug data for this instance
297 *
298 * @access public
299 */
300 function clearDebug() {
301 // it would be nice to use a memory stream here to use
302 // memory more efficiently
303 $this->debug_str = '';
304 }
305
306 /**
307 * gets the current debug data for this instance
308 *
309 * @return debug data
310 * @access public
311 */
312 function &getDebug() {
313 // it would be nice to use a memory stream here to use
314 // memory more efficiently
315 return $this->debug_str;
316 }
317
318 /**
319 * gets the current debug data for this instance as an XML comment
320 * this may change the contents of the debug data
321 *
322 * @return debug data as an XML comment
323 * @access public
324 */
325 function &getDebugAsXMLComment() {
326 // it would be nice to use a memory stream here to use
327 // memory more efficiently
328 while (strpos($this->debug_str, '--')) {
329 $this->debug_str = str_replace('--', '- -', $this->debug_str);
330 }
331 $ret = "<!--\n" . $this->debug_str . "\n-->";
332 return $ret;
333 }
334
335 /**
336 * expands entities, e.g. changes '<' to '&lt;'.
337 *
338 * @param string $val The string in which to expand entities.
339 * @access private
340 */
341 function expandEntities($val) {
342 if ($this->charencoding) {
343 $val = str_replace('&', '&amp;', $val);
344 $val = str_replace("'", '&apos;', $val);
345 $val = str_replace('"', '&quot;', $val);
346 $val = str_replace('<', '&lt;', $val);
347 $val = str_replace('>', '&gt;', $val);
348 }
349 return $val;
350 }
351
352 /**
353 * returns error string if present
354 *
355 * @return mixed error string or false
356 * @access public
357 */
358 function getError(){
359 if($this->error_str != ''){
360 return $this->error_str;
361 }
362 return false;
363 }
364
365 /**
366 * sets error string
367 *
368 * @return boolean $string error string
369 * @access private
370 */
371 function setError($str){
372 $this->error_str = $str;
373 }
374
375 /**
376 * detect if array is a simple array or a struct (associative array)
377 *
378 * @param mixed $val The PHP array
379 * @return string (arraySimple|arrayStruct)
380 * @access private
381 */
382 function isArraySimpleOrStruct($val) {
383 $keyList = array_keys($val);
384 foreach ($keyList as $keyListValue) {
385 if (!is_int($keyListValue)) {
386 return 'arrayStruct';
387 }
388 }
389 return 'arraySimple';
390 }
391
392 /**
393 * serializes PHP values in accordance w/ section 5. Type information is
394 * not serialized if $use == 'literal'.
395 *
396 * @param mixed $val The value to serialize
397 * @param string $name The name (local part) of the XML element
398 * @param string $type The XML schema type (local part) for the element
399 * @param string $name_ns The namespace for the name of the XML element
400 * @param string $type_ns The namespace for the type of the element
401 * @param array $attributes The attributes to serialize as name=>value pairs
402 * @param string $use The WSDL "use" (encoded|literal)
403 * @param boolean $soapval Whether this is called from soapval.
404 * @return string The serialized element, possibly with child elements
405 * @access public
406 */
407 function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded',$soapval=false) {
408 $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use, soapval=$soapval");
409 $this->appendDebug('value=' . $this->varDump($val));
410 $this->appendDebug('attributes=' . $this->varDump($attributes));
411
412 if (is_object($val) && get_class($val) == 'soapval' && (! $soapval)) {
413 $this->debug("serialize_val: serialize soapval");
414 $xml = $val->serialize($use);
415 $this->appendDebug($val->getDebug());
416 $val->clearDebug();
417 $this->debug("serialize_val of soapval returning $xml");
418 return $xml;
419 }
420 // force valid name if necessary
421 if (is_numeric($name)) {
422 $name = '__numeric_' . $name;
423 } elseif (! $name) {
424 $name = 'noname';
425 }
426 // if name has ns, add ns prefix to name
427 $xmlns = '';
428 if($name_ns){
429 $prefix = 'nu'.rand(1000,9999);
430 $name = $prefix.':'.$name;
431 $xmlns .= " xmlns:$prefix=\"$name_ns\"";
432 }
433 // if type is prefixed, create type prefix
434 if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
435 // need to fix this. shouldn't default to xsd if no ns specified
436 // w/o checking against typemap
437 $type_prefix = 'xsd';
438 } elseif($type_ns){
439 $type_prefix = 'ns'.rand(1000,9999);
440 $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
441 }
442 // serialize attributes if present
443 $atts = '';
444 if($attributes){
445 foreach($attributes as $k => $v){
446 $atts .= " $k=\"".$this->expandEntities($v).'"';
447 }
448 }
449 // serialize null value
450 if (is_null($val)) {
451 $this->debug("serialize_val: serialize null");
452 if ($use == 'literal') {
453 // TODO: depends on minOccurs
454 $xml = "<$name$xmlns$atts/>";
455 $this->debug("serialize_val returning $xml");
456 return $xml;
457 } else {
458 if (isset($type) && isset($type_prefix)) {
459 $type_str = " xsi:type=\"$type_prefix:$type\"";
460 } else {
461 $type_str = '';
462 }
463 $xml = "<$name$xmlns$type_str$atts xsi:nil=\"true\"/>";
464 $this->debug("serialize_val returning $xml");
465 return $xml;
466 }
467 }
468 // serialize if an xsd built-in primitive type
469 if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
470 $this->debug("serialize_val: serialize xsd built-in primitive type");
471 if (is_bool($val)) {
472 if ($type == 'boolean') {
473 $val = $val ? 'true' : 'false';
474 } elseif (! $val) {
475 $val = 0;
476 }
477 } else if (is_string($val)) {
478 $val = $this->expandEntities($val);
479 }
480 if ($use == 'literal') {
481 $xml = "<$name$xmlns$atts>$val</$name>";
482 $this->debug("serialize_val returning $xml");
483 return $xml;
484 } else {
485 $xml = "<$name$xmlns xsi:type=\"xsd:$type\"$atts>$val</$name>";
486 $this->debug("serialize_val returning $xml");
487 return $xml;
488 }
489 }
490 // detect type and serialize
491 $xml = '';
492 switch(true) {
493 case (is_bool($val) || $type == 'boolean'):
494 $this->debug("serialize_val: serialize boolean");
495 if ($type == 'boolean') {
496 $val = $val ? 'true' : 'false';
497 } elseif (! $val) {
498 $val = 0;
499 }
500 if ($use == 'literal') {
501 $xml .= "<$name$xmlns$atts>$val</$name>";
502 } else {
503 $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
504 }
505 break;
506 case (is_int($val) || is_long($val) || $type == 'int'):
507 $this->debug("serialize_val: serialize int");
508 if ($use == 'literal') {
509 $xml .= "<$name$xmlns$atts>$val</$name>";
510 } else {
511 $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
512 }
513 break;
514 case (is_float($val)|| is_double($val) || $type == 'float'):
515 $this->debug("serialize_val: serialize float");
516 if ($use == 'literal') {
517 $xml .= "<$name$xmlns$atts>$val</$name>";
518 } else {
519 $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
520 }
521 break;
522 case (is_string($val) || $type == 'string'):
523 $this->debug("serialize_val: serialize string");
524 $val = $this->expandEntities($val);
525 if ($use == 'literal') {
526 $xml .= "<$name$xmlns$atts>$val</$name>";
527 } else {
528 $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
529 }
530 break;
531 case is_object($val):
532 $this->debug("serialize_val: serialize object");
533 if (get_class($val) == 'soapval') {
534 $this->debug("serialize_val: serialize soapval object");
535 $pXml = $val->serialize($use);
536 $this->appendDebug($val->getDebug());
537 $val->clearDebug();
538 } else {
539 if (! $name) {
540 $name = get_class($val);
541 $this->debug("In serialize_val, used class name $name as element name");
542 } else {
543 $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
544 }
545 foreach(get_object_vars($val) as $k => $v){
546 $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
547 }
548 }
549 if(isset($type) && isset($type_prefix)){
550 $type_str = " xsi:type=\"$type_prefix:$type\"";
551 } else {
552 $type_str = '';
553 }
554 if ($use == 'literal') {
555 $xml .= "<$name$xmlns$atts>$pXml</$name>";
556 } else {
557 $xml .= "<$name$xmlns$type_str$atts>$pXml</$name>";
558 }
559 break;
560 break;
561 case (is_array($val) || $type):
562 // detect if struct or array
563 $valueType = $this->isArraySimpleOrStruct($val);
564 if($valueType=='arraySimple' || preg_match('/^ArrayOf/',$type)){
565 $this->debug("serialize_val: serialize array");
566 $i = 0;
567 if(is_array($val) && count($val)> 0){
568 foreach($val as $v){
569 if(is_object($v) && get_class($v) == 'soapval'){
570 $tt_ns = $v->type_ns;
571 $tt = $v->type;
572 } elseif (is_array($v)) {
573 $tt = $this->isArraySimpleOrStruct($v);
574 } else {
575 $tt = gettype($v);
576 }
577 $array_types[$tt] = 1;
578 // TODO: for literal, the name should be $name
579 $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
580 ++$i;
581 }
582 if(count($array_types) > 1){
583 $array_typename = 'xsd:anyType';
584 } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
585 if ($tt == 'integer') {
586 $tt = 'int';
587 }
588 $array_typename = 'xsd:'.$tt;
589 } elseif(isset($tt) && $tt == 'arraySimple'){
590 $array_typename = 'SOAP-ENC:Array';
591 } elseif(isset($tt) && $tt == 'arrayStruct'){
592 $array_typename = 'unnamed_struct_use_soapval';
593 } else {
594 // if type is prefixed, create type prefix
595 if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){
596 $array_typename = 'xsd:' . $tt;
597 } elseif ($tt_ns) {
598 $tt_prefix = 'ns' . rand(1000, 9999);
599 $array_typename = "$tt_prefix:$tt";
600 $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
601 } else {
602 $array_typename = $tt;
603 }
604 }
605 $array_type = $i;
606 if ($use == 'literal') {
607 $type_str = '';
608 } else if (isset($type) && isset($type_prefix)) {
609 $type_str = " xsi:type=\"$type_prefix:$type\"";
610 } else {
611 $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
612 }
613 // empty array
614 } else {
615 if ($use == 'literal') {
616 $type_str = '';
617 } else if (isset($type) && isset($type_prefix)) {
618 $type_str = " xsi:type=\"$type_prefix:$type\"";
619 } else {
620 $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\"";
621 }
622 }
623 // TODO: for array in literal, there is no wrapper here
624 $xml = "<$name$xmlns$type_str$atts>".$xml."</$name>";
625 } else {
626 // got a struct
627 $this->debug("serialize_val: serialize struct");
628 if(isset($type) && isset($type_prefix)){
629 $type_str = " xsi:type=\"$type_prefix:$type\"";
630 } else {
631 $type_str = '';
632 }
633 if ($use == 'literal') {
634 $xml .= "<$name$xmlns$atts>";
635 } else {
636 $xml .= "<$name$xmlns$type_str$atts>";
637 }
638 foreach($val as $k => $v){
639 // Apache Map
640 if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
641 $xml .= '<item>';
642 $xml .= $this->serialize_val($k,'key',false,false,false,false,$use);
643 $xml .= $this->serialize_val($v,'value',false,false,false,false,$use);
644 $xml .= '</item>';
645 } else {
646 $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
647 }
648 }
649 $xml .= "</$name>";
650 }
651 break;
652 default:
653 $this->debug("serialize_val: serialize unknown");
654 $xml .= 'not detected, got '.gettype($val).' for '.$val;
655 break;
656 }
657 $this->debug("serialize_val returning $xml");
658 return $xml;
659 }
660
661 /**
662 * serializes a message
663 *
664 * @param string $body the XML of the SOAP body
665 * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
666 * @param array $namespaces optional the namespaces used in generating the body and headers
667 * @param string $style optional (rpc|document)
668 * @param string $use optional (encoded|literal)
669 * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
670 * @return string the message
671 * @access public
672 */
673 function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded',$encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'){
674 // TODO: add an option to automatically run utf8_encode on $body and $headers
675 // if $this->soap_defencoding is UTF-8. Not doing this automatically allows
676 // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
677
678 $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
679 $this->debug("headers:");
680 $this->appendDebug($this->varDump($headers));
681 $this->debug("namespaces:");
682 $this->appendDebug($this->varDump($namespaces));
683
684 // serialize namespaces
685 $ns_string = '';
686 foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
687 $ns_string .= " xmlns:$k=\"$v\"";
688 }
689 if($encodingStyle) {
690 $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
691 }
692
693 // serialize headers
694 if($headers){
695 if (is_array($headers)) {
696 $xml = '';
697 foreach ($headers as $k => $v) {
698 if (is_object($v) && get_class($v) == 'soapval') {
699 $xml .= $this->serialize_val($v, false, false, false, false, false, $use);
700 } else {
701 $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
702 }
703 }
704 $headers = $xml;
705 $this->debug("In serializeEnvelope, serialized array of headers to $headers");
706 }
707 $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
708 }
709 // serialize envelope
710 return
711 '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
712 '<SOAP-ENV:Envelope'.$ns_string.">".
713 $headers.
714 "<SOAP-ENV:Body>".
715 $body.
716 "</SOAP-ENV:Body>".
717 "</SOAP-ENV:Envelope>";
718 }
719
720 /**
721 * formats a string to be inserted into an HTML stream
722 *
723 * @param string $str The string to format
724 * @return string The formatted string
725 * @access public
726 * @deprecated
727 */
728 function formatDump($str){
729 $str = htmlspecialchars($str);
730 return nl2br($str);
731 }
732
733 /**
734 * contracts (changes namespace to prefix) a qualified name
735 *
736 * @param string $qname qname
737 * @return string contracted qname
738 * @access private
739 */
740 function contractQname($qname){
741 // get element namespace
742 //$this->xdebug("Contract $qname");
743 if (strrpos($qname, ':')) {
744 // get unqualified name
745 $name = substr($qname, strrpos($qname, ':') + 1);
746 // get ns
747 $ns = substr($qname, 0, strrpos($qname, ':'));
748 $p = $this->getPrefixFromNamespace($ns);
749 if ($p) {
750 return $p . ':' . $name;
751 }
752 return $qname;
753 } else {
754 return $qname;
755 }
756 }
757
758 /**
759 * expands (changes prefix to namespace) a qualified name
760 *
761 * @param string $qname qname
762 * @return string expanded qname
763 * @access private
764 */
765 function expandQname($qname){
766 // get element prefix
767 if(strpos($qname,':') && !preg_match('/^http:\/\//',$qname)){
768 // get unqualified name
769 $name = substr(strstr($qname,':'),1);
770 // get ns prefix
771 $prefix = substr($qname,0,strpos($qname,':'));
772 if(isset($this->namespaces[$prefix])){
773 return $this->namespaces[$prefix].':'.$name;
774 } else {
775 return $qname;
776 }
777 } else {
778 return $qname;
779 }
780 }
781
782 /**
783 * returns the local part of a prefixed string
784 * returns the original string, if not prefixed
785 *
786 * @param string $str The prefixed string
787 * @return string The local part
788 * @access public
789 */
790 function getLocalPart($str){
791 if($sstr = strrchr($str,':')){
792 // get unqualified name
793 return substr( $sstr, 1 );
794 } else {
795 return $str;
796 }
797 }
798
799 /**
800 * returns the prefix part of a prefixed string
801 * returns false, if not prefixed
802 *
803 * @param string $str The prefixed string
804 * @return mixed The prefix or false if there is no prefix
805 * @access public
806 */
807 function getPrefix($str){
808 if($pos = strrpos($str,':')){
809 // get prefix
810 return substr($str,0,$pos);
811 }
812 return false;
813 }
814
815 /**
816 * pass it a prefix, it returns a namespace
817 *
818 * @param string $prefix The prefix
819 * @return mixed The namespace, false if no namespace has the specified prefix
820 * @access public
821 */
822 function getNamespaceFromPrefix($prefix){
823 if (isset($this->namespaces[$prefix])) {
824 return $this->namespaces[$prefix];
825 }
826 //$this->setError("No namespace registered for prefix '$prefix'");
827 return false;
828 }
829
830 /**
831 * returns the prefix for a given namespace (or prefix)
832 * or false if no prefixes registered for the given namespace
833 *
834 * @param string $ns The namespace
835 * @return mixed The prefix, false if the namespace has no prefixes
836 * @access public
837 */
838 function getPrefixFromNamespace($ns) {
839 foreach ($this->namespaces as $p => $n) {
840 if ($ns == $n || $ns == $p) {
841 $this->usedNamespaces[$p] = $n;
842 return $p;
843 }
844 }
845 return false;
846 }
847
848 /**
849 * returns the time in ODBC canonical form with microseconds
850 *
851 * @return string The time in ODBC canonical form with microseconds
852 * @access public
853 */
854 function getmicrotime() {
855 if (function_exists('gettimeofday')) {
856 $tod = gettimeofday();
857 $sec = $tod['sec'];
858 $usec = $tod['usec'];
859 } else {
860 $sec = time();
861 $usec = 0;
862 }
863 return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
864 }
865
866 /**
867 * Returns a string with the output of var_dump
868 *
869 * @param mixed $data The variable to var_dump
870 * @return string The output of var_dump
871 * @access public
872 */
873 function varDump($data) {
874 ob_start();
875 var_dump($data);
876 $ret_val = ob_get_contents();
877 ob_end_clean();
878 return $ret_val;
879 }
880
881 /**
882 * represents the object as a string
883 *
884 * @return string
885 * @access public
886 */
887 function __toString() {
888 return $this->varDump($this);
889 }
890}
891
892// XML Schema Datatype Helper Functions
893
894//xsd:dateTime helpers
895
896/**
897* convert unix timestamp to ISO 8601 compliant date string
898*
899* @param int $timestamp Unix time stamp
900* @param boolean $utc Whether the time stamp is UTC or local
901* @return mixed ISO 8601 date string or false
902* @access public
903*/
904function timestamp_to_iso8601($timestamp,$utc=true){
905 $datestr = date('Y-m-d\TH:i:sO',$timestamp);
906 $pos = strrpos($datestr, "+");
907 if ($pos === FALSE) {
908 $pos = strrpos($datestr, "-");
909 }
910 if ($pos !== FALSE) {
911 if (strlen($datestr) == $pos + 5) {
912 $datestr = substr($datestr, 0, $pos + 3) . ':' . substr($datestr, -2);
913 }
914 }
915 if($utc){
916 $pattern = '/'.
917 '([0-9]{4})-'. // centuries & years CCYY-
918 '([0-9]{2})-'. // months MM-
919 '([0-9]{2})'. // days DD
920 'T'. // separator T
921 '([0-9]{2}):'. // hours hh:
922 '([0-9]{2}):'. // minutes mm:
923 '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
924 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'. // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
925 '/';
926
927 if(preg_match($pattern,$datestr,$regs)){
928 return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
929 }
930 return false;
931 } else {
932 return $datestr;
933 }
934}
935
936/**
937* convert ISO 8601 compliant date string to unix timestamp
938*
939* @param string $datestr ISO 8601 compliant date string
940* @return mixed Unix timestamp (int) or false
941* @access public
942*/
943function iso8601_to_timestamp($datestr){
944 $pattern = '/'.
945 '([0-9]{4})-'. // centuries & years CCYY-
946 '([0-9]{2})-'. // months MM-
947 '([0-9]{2})'. // days DD
948 'T'. // separator T
949 '([0-9]{2}):'. // hours hh:
950 '([0-9]{2}):'. // minutes mm:
951 '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
952 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'. // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
953 '/';
954 if(preg_match($pattern,$datestr,$regs)){
955 // not utc
956 if($regs[8] != 'Z'){
957 $op = substr($regs[8],0,1);
958 $h = substr($regs[8],1,2);
959 $m = substr($regs[8],strlen($regs[8])-2,2);
960 if($op == '-'){
961 $regs[4] = $regs[4] + $h;
962 $regs[5] = $regs[5] + $m;
963 } elseif($op == '+'){
964 $regs[4] = $regs[4] - $h;
965 $regs[5] = $regs[5] - $m;
966 }
967 }
968 return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
969// return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
970 } else {
971 return false;
972 }
973}
974
975/**
976* sleeps some number of microseconds
977*
978* @param string $usec the number of microseconds to sleep
979* @access public
980* @deprecated
981*/
982function usleepWindows($usec)
983{
984 $start = gettimeofday();
985
986 do
987 {
988 $stop = gettimeofday();
989 $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
990 + $stop['usec'] - $start['usec'];
991 }
992 while ($timePassed < $usec);
993}
994
995?><?php
996
997
998
999/**
1000* Contains information for a SOAP fault.
1001* Mainly used for returning faults from deployed functions
1002* in a server instance.
1003* @author Dietrich Ayala <dietrich@ganx4.com>
1004* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
1005* @access public
1006*/
1007class nusoap_fault extends nusoap_base {
1008 /**
1009 * The fault code (client|server)
1010 * @var string
1011 * @access private
1012 */
1013 var $faultcode;
1014 /**
1015 * The fault actor
1016 * @var string
1017 * @access private
1018 */
1019 var $faultactor;
1020 /**
1021 * The fault string, a description of the fault
1022 * @var string
1023 * @access private
1024 */
1025 var $faultstring;
1026 /**
1027 * The fault detail, typically a string or array of string
1028 * @var mixed
1029 * @access private
1030 */
1031 var $faultdetail;
1032
1033 /**
1034 * constructor
1035 *
1036 * @param string $faultcode (SOAP-ENV:Client | SOAP-ENV:Server)
1037 * @param string $faultactor only used when msg routed between multiple actors
1038 * @param string $faultstring human readable error message
1039 * @param mixed $faultdetail detail, typically a string or array of string
1040 */
1041 function nusoap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
1042 parent::nusoap_base();
1043 $this->faultcode = $faultcode;
1044 $this->faultactor = $faultactor;
1045 $this->faultstring = $faultstring;
1046 $this->faultdetail = $faultdetail;
1047 }
1048
1049 /**
1050 * serialize a fault
1051 *
1052 * @return string The serialization of the fault instance.
1053 * @access public
1054 */
1055 function serialize(){
1056 $ns_string = '';
1057 foreach($this->namespaces as $k => $v){
1058 $ns_string .= "\n xmlns:$k=\"$v\"";
1059 }
1060 $return_msg =
1061 '<?xml version="1.0" encoding="'.$this->soap_defencoding.'"?>'.
1062 '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
1063 '<SOAP-ENV:Body>'.
1064 '<SOAP-ENV:Fault>'.
1065 $this->serialize_val($this->faultcode, 'faultcode').
1066 $this->serialize_val($this->faultactor, 'faultactor').
1067 $this->serialize_val($this->faultstring, 'faultstring').
1068 $this->serialize_val($this->faultdetail, 'detail').
1069 '</SOAP-ENV:Fault>'.
1070 '</SOAP-ENV:Body>'.
1071 '</SOAP-ENV:Envelope>';
1072 return $return_msg;
1073 }
1074}
1075
1076/**
1077 * Backward compatibility
1078 */
1079class soap_fault extends nusoap_fault {
1080}
1081
1082?><?php
1083
1084
1085
1086/**
1087* parses an XML Schema, allows access to it's data, other utility methods.
1088* imperfect, no validation... yet, but quite functional.
1089*
1090* @author Dietrich Ayala <dietrich@ganx4.com>
1091* @author Scott Nichol <snichol@users.sourceforge.net>
1092* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
1093* @access public
1094*/
1095class nusoap_xmlschema extends nusoap_base {
1096
1097 // files
1098 var $schema = '';
1099 var $xml = '';
1100 // namespaces
1101 var $enclosingNamespaces;
1102 // schema info
1103 var $schemaInfo = array();
1104 var $schemaTargetNamespace = '';
1105 // types, elements, attributes defined by the schema
1106 var $attributes = array();
1107 var $complexTypes = array();
1108 var $complexTypeStack = array();
1109 var $currentComplexType = null;
1110 var $elements = array();
1111 var $elementStack = array();
1112 var $currentElement = null;
1113 var $simpleTypes = array();
1114 var $simpleTypeStack = array();
1115 var $currentSimpleType = null;
1116 // imports
1117 var $imports = array();
1118 // parser vars
1119 var $parser;
1120 var $position = 0;
1121 var $depth = 0;
1122 var $depth_array = array();
1123 var $message = array();
1124 var $defaultNamespace = array();
1125
1126 /**
1127 * constructor
1128 *
1129 * @param string $schema schema document URI
1130 * @param string $xml xml document URI
1131 * @param string $namespaces namespaces defined in enclosing XML
1132 * @access public
1133 */
1134 function nusoap_xmlschema($schema='',$xml='',$namespaces=array()){
1135 parent::nusoap_base();
1136 $this->debug('nusoap_xmlschema class instantiated, inside constructor');
1137 // files
1138 $this->schema = $schema;
1139 $this->xml = $xml;
1140
1141 // namespaces
1142 $this->enclosingNamespaces = $namespaces;
1143 $this->namespaces = array_merge($this->namespaces, $namespaces);
1144
1145 // parse schema file
1146 if($schema != ''){
1147 $this->debug('initial schema file: '.$schema);
1148 $this->parseFile($schema, 'schema');
1149 }
1150
1151 // parse xml file
1152 if($xml != ''){
1153 $this->debug('initial xml file: '.$xml);
1154 $this->parseFile($xml, 'xml');
1155 }
1156
1157 }
1158
1159 /**
1160 * parse an XML file
1161 *
1162 * @param string $xml path/URL to XML file
1163 * @param string $type (schema | xml)
1164 * @return boolean
1165 * @access public
1166 */
1167 function parseFile($xml,$type){
1168 // parse xml file
1169 if($xml != ""){
1170 $xmlStr = @join("",@file($xml));
1171 if($xmlStr == ""){
1172 $msg = 'Error reading XML from '.$xml;
1173 $this->setError($msg);
1174 $this->debug($msg);
1175 return false;
1176 } else {
1177 $this->debug("parsing $xml");
1178 $this->parseString($xmlStr,$type);
1179 $this->debug("done parsing $xml");
1180 return true;
1181 }
1182 }
1183 return false;
1184 }
1185
1186 /**
1187 * parse an XML string
1188 *
1189 * @param string $xml path or URL
1190 * @param string $type (schema|xml)
1191 * @access private
1192 */
1193 function parseString($xml,$type){
1194 // parse xml string
1195 if($xml != ""){
1196
1197 // Create an XML parser.
1198 $this->parser = xml_parser_create();
1199 // Set the options for parsing the XML data.
1200 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
1201
1202 // Set the object for the parser.
1203 xml_set_object($this->parser, $this);
1204
1205 // Set the element handlers for the parser.
1206 if($type == "schema"){
1207 xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
1208 xml_set_character_data_handler($this->parser,'schemaCharacterData');
1209 } elseif($type == "xml"){
1210 xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
1211 xml_set_character_data_handler($this->parser,'xmlCharacterData');
1212 }
1213
1214 // Parse the XML file.
1215 if(!xml_parse($this->parser,$xml,true)){
1216 // Display an error message.
1217 $errstr = sprintf('XML error parsing XML schema on line %d: %s',
1218 xml_get_current_line_number($this->parser),
1219 xml_error_string(xml_get_error_code($this->parser))
1220 );
1221 $this->debug($errstr);
1222 $this->debug("XML payload:\n" . $xml);
1223 $this->setError($errstr);
1224 }
1225
1226 xml_parser_free($this->parser);
1227 } else{
1228 $this->debug('no xml passed to parseString()!!');
1229 $this->setError('no xml passed to parseString()!!');
1230 }
1231 }
1232
1233 /**
1234 * gets a type name for an unnamed type
1235 *
1236 * @param string Element name
1237 * @return string A type name for an unnamed type
1238 * @access private
1239 */
1240 function CreateTypeName($ename) {
1241 $scope = '';
1242 for ($i = 0; $i < count($this->complexTypeStack); $i++) {
1243 $scope .= $this->complexTypeStack[$i] . '_';
1244 }
1245 return $scope . $ename . '_ContainedType';
1246 }
1247
1248 /**
1249 * start-element handler
1250 *
1251 * @param string $parser XML parser object
1252 * @param string $name element name
1253 * @param string $attrs associative array of attributes
1254 * @access private
1255 */
1256 function schemaStartElement($parser, $name, $attrs) {
1257
1258 // position in the total number of elements, starting from 0
1259 $pos = $this->position++;
1260 $depth = $this->depth++;
1261 // set self as current value for this depth
1262 $this->depth_array[$depth] = $pos;
1263 $this->message[$pos] = array('cdata' => '');
1264 if ($depth > 0) {
1265 $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
1266 } else {
1267 $this->defaultNamespace[$pos] = false;
1268 }
1269
1270 // get element prefix
1271 if($prefix = $this->getPrefix($name)){
1272 // get unqualified name
1273 $name = $this->getLocalPart($name);
1274 } else {
1275 $prefix = '';
1276 }
1277
1278 // loop thru attributes, expanding, and registering namespace declarations
1279 if(count($attrs) > 0){
1280 foreach($attrs as $k => $v){
1281 // if ns declarations, add to class level array of valid namespaces
1282 if(preg_match('/^xmlns/',$k)){
1283 //$this->xdebug("$k: $v");
1284 //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
1285 if($ns_prefix = substr(strrchr($k,':'),1)){
1286 //$this->xdebug("Add namespace[$ns_prefix] = $v");
1287 $this->namespaces[$ns_prefix] = $v;
1288 } else {
1289 $this->defaultNamespace[$pos] = $v;
1290 if (! $this->getPrefixFromNamespace($v)) {
1291 $this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
1292 }
1293 }
1294 if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema'){
1295 $this->XMLSchemaVersion = $v;
1296 $this->namespaces['xsi'] = $v.'-instance';
1297 }
1298 }
1299 }
1300 foreach($attrs as $k => $v){
1301 // expand each attribute
1302 $k = strpos($k,':') ? $this->expandQname($k) : $k;
1303 $v = strpos($v,':') ? $this->expandQname($v) : $v;
1304 $eAttrs[$k] = $v;
1305 }
1306 $attrs = $eAttrs;
1307 } else {
1308 $attrs = array();
1309 }
1310 // find status, register data
1311 switch($name){
1312 case 'all': // (optional) compositor content for a complexType
1313 case 'choice':
1314 case 'group':
1315 case 'sequence':
1316 //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement");
1317 $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
1318 //if($name == 'all' || $name == 'sequence'){
1319 // $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1320 //}
1321 break;
1322 case 'attribute': // complexType attribute
1323 //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
1324 $this->xdebug("parsing attribute:");
1325 $this->appendDebug($this->varDump($attrs));
1326 if (!isset($attrs['form'])) {
1327 // TODO: handle globals
1328 $attrs['form'] = $this->schemaInfo['attributeFormDefault'];
1329 }
1330 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1331 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1332 if (!strpos($v, ':')) {
1333 // no namespace in arrayType attribute value...
1334 if ($this->defaultNamespace[$pos]) {
1335 // ...so use the default
1336 $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1337 }
1338 }
1339 }
1340 if(isset($attrs['name'])){
1341 $this->attributes[$attrs['name']] = $attrs;
1342 $aname = $attrs['name'];
1343 } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
1344 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
1345 $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1346 } else {
1347 $aname = '';
1348 }
1349 } elseif(isset($attrs['ref'])){
1350 $aname = $attrs['ref'];
1351 $this->attributes[$attrs['ref']] = $attrs;
1352 }
1353
1354 if($this->currentComplexType){ // This should *always* be
1355 $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
1356 }
1357 // arrayType attribute
1358 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
1359 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1360 $prefix = $this->getPrefix($aname);
1361 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
1362 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
1363 } else {
1364 $v = '';
1365 }
1366 if(strpos($v,'[,]')){
1367 $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
1368 }
1369 $v = substr($v,0,strpos($v,'[')); // clip the []
1370 if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
1371 $v = $this->XMLSchemaVersion.':'.$v;
1372 }
1373 $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
1374 }
1375 break;
1376 case 'complexContent': // (optional) content for a complexType
1377 $this->xdebug("do nothing for element $name");
1378 break;
1379 case 'complexType':
1380 array_push($this->complexTypeStack, $this->currentComplexType);
1381 if(isset($attrs['name'])){
1382 // TODO: what is the scope of named complexTypes that appear
1383 // nested within other c complexTypes?
1384 $this->xdebug('processing named complexType '.$attrs['name']);
1385 //$this->currentElement = false;
1386 $this->currentComplexType = $attrs['name'];
1387 $this->complexTypes[$this->currentComplexType] = $attrs;
1388 $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1389 // This is for constructs like
1390 // <complexType name="ListOfString" base="soap:Array">
1391 // <sequence>
1392 // <element name="string" type="xsd:string"
1393 // minOccurs="0" maxOccurs="unbounded" />
1394 // </sequence>
1395 // </complexType>
1396 if(isset($attrs['base']) && preg_match('/:Array$/',$attrs['base'])){
1397 $this->xdebug('complexType is unusual array');
1398 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1399 } else {
1400 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1401 }
1402 } else {
1403 $name = $this->CreateTypeName($this->currentElement);
1404 $this->xdebug('processing unnamed complexType for element ' . $this->currentElement . ' named ' . $name);
1405 $this->currentComplexType = $name;
1406 //$this->currentElement = false;
1407 $this->complexTypes[$this->currentComplexType] = $attrs;
1408 $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
1409 // This is for constructs like
1410 // <complexType name="ListOfString" base="soap:Array">
1411 // <sequence>
1412 // <element name="string" type="xsd:string"
1413 // minOccurs="0" maxOccurs="unbounded" />
1414 // </sequence>
1415 // </complexType>
1416 if(isset($attrs['base']) && preg_match('/:Array$/',$attrs['base'])){
1417 $this->xdebug('complexType is unusual array');
1418 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1419 } else {
1420 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
1421 }
1422 }
1423 $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'false';
1424 break;
1425 case 'element':
1426 array_push($this->elementStack, $this->currentElement);
1427 if (!isset($attrs['form'])) {
1428 if ($this->currentComplexType) {
1429 $attrs['form'] = $this->schemaInfo['elementFormDefault'];
1430 } else {
1431 // global
1432 $attrs['form'] = 'qualified';
1433 }
1434 }
1435 if(isset($attrs['type'])){
1436 $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']);
1437 if (! $this->getPrefix($attrs['type'])) {
1438 if ($this->defaultNamespace[$pos]) {
1439 $attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type'];
1440 $this->xdebug('used default namespace to make type ' . $attrs['type']);
1441 }
1442 }
1443 // This is for constructs like
1444 // <complexType name="ListOfString" base="soap:Array">
1445 // <sequence>
1446 // <element name="string" type="xsd:string"
1447 // minOccurs="0" maxOccurs="unbounded" />
1448 // </sequence>
1449 // </complexType>
1450 if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') {
1451 $this->xdebug('arrayType for unusual array is ' . $attrs['type']);
1452 $this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type'];
1453 }
1454 $this->currentElement = $attrs['name'];
1455 $ename = $attrs['name'];
1456 } elseif(isset($attrs['ref'])){
1457 $this->xdebug("processing element as ref to ".$attrs['ref']);
1458 $this->currentElement = "ref to ".$attrs['ref'];
1459 $ename = $this->getLocalPart($attrs['ref']);
1460 } else {
1461 $type = $this->CreateTypeName($this->currentComplexType . '_' . $attrs['name']);
1462 $this->xdebug("processing untyped element " . $attrs['name'] . ' type ' . $type);
1463 $this->currentElement = $attrs['name'];
1464 $attrs['type'] = $this->schemaTargetNamespace . ':' . $type;
1465 $ename = $attrs['name'];
1466 }
1467 if (isset($ename) && $this->currentComplexType) {
1468 $this->xdebug("add element $ename to complexType $this->currentComplexType");
1469 $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
1470 } elseif (!isset($attrs['ref'])) {
1471 $this->xdebug("add element $ename to elements array");
1472 $this->elements[ $attrs['name'] ] = $attrs;
1473 $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
1474 }
1475 break;
1476 case 'enumeration': // restriction value list member
1477 $this->xdebug('enumeration ' . $attrs['value']);
1478 if ($this->currentSimpleType) {
1479 $this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value'];
1480 } elseif ($this->currentComplexType) {
1481 $this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value'];
1482 }
1483 break;
1484 case 'extension': // simpleContent or complexContent type extension
1485 $this->xdebug('extension ' . $attrs['base']);
1486 if ($this->currentComplexType) {
1487 $ns = $this->getPrefix($attrs['base']);
1488 if ($ns == '') {
1489 $this->complexTypes[$this->currentComplexType]['extensionBase'] = $this->schemaTargetNamespace . ':' . $attrs['base'];
1490 } else {
1491 $this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base'];
1492 }
1493 } else {
1494 $this->xdebug('no current complexType to set extensionBase');
1495 }
1496 break;
1497 case 'import':
1498 if (isset($attrs['schemaLocation'])) {
1499 $this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']);
1500 $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
1501 } else {
1502 $this->xdebug('import namespace ' . $attrs['namespace']);
1503 $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
1504 if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
1505 $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
1506 }
1507 }
1508 break;
1509 case 'include':
1510 if (isset($attrs['schemaLocation'])) {
1511 $this->xdebug('include into namespace ' . $this->schemaTargetNamespace . ' from ' . $attrs['schemaLocation']);
1512 $this->imports[$this->schemaTargetNamespace][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
1513 } else {
1514 $this->xdebug('ignoring invalid XML Schema construct: include without schemaLocation attribute');
1515 }
1516 break;
1517 case 'list': // simpleType value list
1518 $this->xdebug("do nothing for element $name");
1519 break;
1520 case 'restriction': // simpleType, simpleContent or complexContent value restriction
1521 $this->xdebug('restriction ' . $attrs['base']);
1522 if($this->currentSimpleType){
1523 $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
1524 } elseif($this->currentComplexType){
1525 $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
1526 if(strstr($attrs['base'],':') == ':Array'){
1527 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
1528 }
1529 }
1530 break;
1531 case 'schema':
1532 $this->schemaInfo = $attrs;
1533 $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
1534 if (isset($attrs['targetNamespace'])) {
1535 $this->schemaTargetNamespace = $attrs['targetNamespace'];
1536 }
1537 if (!isset($attrs['elementFormDefault'])) {
1538 $this->schemaInfo['elementFormDefault'] = 'unqualified';
1539 }
1540 if (!isset($attrs['attributeFormDefault'])) {
1541 $this->schemaInfo['attributeFormDefault'] = 'unqualified';
1542 }
1543 break;
1544 case 'simpleContent': // (optional) content for a complexType
1545 if ($this->currentComplexType) { // This should *always* be
1546 $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'true';
1547 } else {
1548 $this->xdebug("do nothing for element $name because there is no current complexType");
1549 }
1550 break;
1551 case 'simpleType':
1552 array_push($this->simpleTypeStack, $this->currentSimpleType);
1553 if(isset($attrs['name'])){
1554 $this->xdebug("processing simpleType for name " . $attrs['name']);
1555 $this->currentSimpleType = $attrs['name'];
1556 $this->simpleTypes[ $attrs['name'] ] = $attrs;
1557 $this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType';
1558 $this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar';
1559 } else {
1560 $name = $this->CreateTypeName($this->currentComplexType . '_' . $this->currentElement);
1561 $this->xdebug('processing unnamed simpleType for element ' . $this->currentElement . ' named ' . $name);
1562 $this->currentSimpleType = $name;
1563 //$this->currentElement = false;
1564 $this->simpleTypes[$this->currentSimpleType] = $attrs;
1565 $this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar';
1566 }
1567 break;
1568 case 'union': // simpleType type list
1569 $this->xdebug("do nothing for element $name");
1570 break;
1571 default:
1572 $this->xdebug("do not have any logic to process element $name");
1573 }
1574 }
1575
1576 /**
1577 * end-element handler
1578 *
1579 * @param string $parser XML parser object
1580 * @param string $name element name
1581 * @access private
1582 */
1583 function schemaEndElement($parser, $name) {
1584 // bring depth down a notch
1585 $this->depth--;
1586 // position of current element is equal to the last value left in depth_array for my depth
1587 if(isset($this->depth_array[$this->depth])){
1588 $pos = $this->depth_array[$this->depth];
1589 }
1590 // get element prefix
1591 if ($prefix = $this->getPrefix($name)){
1592 // get unqualified name
1593 $name = $this->getLocalPart($name);
1594 } else {
1595 $prefix = '';
1596 }
1597 // move on...
1598 if($name == 'complexType'){
1599 $this->xdebug('done processing complexType ' . ($this->currentComplexType ? $this->currentComplexType : '(unknown)'));
1600 $this->xdebug($this->varDump($this->complexTypes[$this->currentComplexType]));
1601 $this->currentComplexType = array_pop($this->complexTypeStack);
1602 //$this->currentElement = false;
1603 }
1604 if($name == 'element'){
1605 $this->xdebug('done processing element ' . ($this->currentElement ? $this->currentElement : '(unknown)'));
1606 $this->currentElement = array_pop($this->elementStack);
1607 }
1608 if($name == 'simpleType'){
1609 $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ? $this->currentSimpleType : '(unknown)'));
1610 $this->xdebug($this->varDump($this->simpleTypes[$this->currentSimpleType]));
1611 $this->currentSimpleType = array_pop($this->simpleTypeStack);
1612 }
1613 }
1614
1615 /**
1616 * element content handler
1617 *
1618 * @param string $parser XML parser object
1619 * @param string $data element content
1620 * @access private
1621 */
1622 function schemaCharacterData($parser, $data){
1623 $pos = $this->depth_array[$this->depth - 1];
1624 $this->message[$pos]['cdata'] .= $data;
1625 }
1626
1627 /**
1628 * serialize the schema
1629 *
1630 * @access public
1631 */
1632 function serializeSchema(){
1633
1634 $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
1635 $xml = '';
1636 // imports
1637 if (sizeof($this->imports) > 0) {
1638 foreach($this->imports as $ns => $list) {
1639 foreach ($list as $ii) {
1640 if ($ii['location'] != '') {
1641 $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
1642 } else {
1643 $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
1644 }
1645 }
1646 }
1647 }
1648 // complex types
1649 foreach($this->complexTypes as $typeName => $attrs){
1650 $contentStr = '';
1651 // serialize child elements
1652 if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){
1653 foreach($attrs['elements'] as $element => $eParts){
1654 if(isset($eParts['ref'])){
1655 $contentStr .= " <$schemaPrefix:element ref=\"$element\"/>\n";
1656 } else {
1657 $contentStr .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"";
1658 foreach ($eParts as $aName => $aValue) {
1659 // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable
1660 if ($aName != 'name' && $aName != 'type') {
1661 $contentStr .= " $aName=\"$aValue\"";
1662 }
1663 }
1664 $contentStr .= "/>\n";
1665 }
1666 }
1667 // compositor wraps elements
1668 if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) {
1669 $contentStr = " <$schemaPrefix:$attrs[compositor]>\n".$contentStr." </$schemaPrefix:$attrs[compositor]>\n";
1670 }
1671 }
1672 // attributes
1673 if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){
1674 foreach($attrs['attrs'] as $attr => $aParts){
1675 $contentStr .= " <$schemaPrefix:attribute";
1676 foreach ($aParts as $a => $v) {
1677 if ($a == 'ref' || $a == 'type') {
1678 $contentStr .= " $a=\"".$this->contractQName($v).'"';
1679 } elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') {
1680 $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
1681 $contentStr .= ' wsdl:arrayType="'.$this->contractQName($v).'"';
1682 } else {
1683 $contentStr .= " $a=\"$v\"";
1684 }
1685 }
1686 $contentStr .= "/>\n";
1687 }
1688 }
1689 // if restriction
1690 if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
1691 $contentStr = " <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr." </$schemaPrefix:restriction>\n";
1692 // complex or simple content
1693 if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){
1694 $contentStr = " <$schemaPrefix:complexContent>\n".$contentStr." </$schemaPrefix:complexContent>\n";
1695 }
1696 }
1697 // finalize complex type
1698 if($contentStr != ''){
1699 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n";
1700 } else {
1701 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
1702 }
1703 $xml .= $contentStr;
1704 }
1705 // simple types
1706 if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){
1707 foreach($this->simpleTypes as $typeName => $eParts){
1708 $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n <$schemaPrefix:restriction base=\"".$this->contractQName($eParts['type'])."\">\n";
1709 if (isset($eParts['enumeration'])) {
1710 foreach ($eParts['enumeration'] as $e) {
1711 $xml .= " <$schemaPrefix:enumeration value=\"$e\"/>\n";
1712 }
1713 }
1714 $xml .= " </$schemaPrefix:restriction>\n </$schemaPrefix:simpleType>";
1715 }
1716 }
1717 // elements
1718 if(isset($this->elements) && count($this->elements) > 0){
1719 foreach($this->elements as $element => $eParts){
1720 $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n";
1721 }
1722 }
1723 // attributes
1724 if(isset($this->attributes) && count($this->attributes) > 0){
1725 foreach($this->attributes as $attr => $aParts){
1726 $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>";
1727 }
1728 }
1729 // finish 'er up
1730 $attr = '';
1731 foreach ($this->schemaInfo as $k => $v) {
1732 if ($k == 'elementFormDefault' || $k == 'attributeFormDefault') {
1733 $attr .= " $k=\"$v\"";
1734 }
1735 }
1736 $el = "<$schemaPrefix:schema$attr targetNamespace=\"$this->schemaTargetNamespace\"\n";
1737 foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
1738 $el .= " xmlns:$nsp=\"$ns\"";
1739 }
1740 $xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n";
1741 return $xml;
1742 }
1743
1744 /**
1745 * adds debug data to the clas level debug string
1746 *
1747 * @param string $string debug data
1748 * @access private
1749 */
1750 function xdebug($string){
1751 $this->debug('<' . $this->schemaTargetNamespace . '> '.$string);
1752 }
1753
1754 /**
1755 * get the PHP type of a user defined type in the schema
1756 * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
1757 * returns false if no type exists, or not w/ the given namespace
1758 * else returns a string that is either a native php type, or 'struct'
1759 *
1760 * @param string $type name of defined type
1761 * @param string $ns namespace of type
1762 * @return mixed
1763 * @access public
1764 * @deprecated
1765 */
1766 function getPHPType($type,$ns){
1767 if(isset($this->typemap[$ns][$type])){
1768 //print "found type '$type' and ns $ns in typemap<br>";
1769 return $this->typemap[$ns][$type];
1770 } elseif(isset($this->complexTypes[$type])){
1771 //print "getting type '$type' and ns $ns from complexTypes array<br>";
1772 return $this->complexTypes[$type]['phpType'];
1773 }
1774 return false;
1775 }
1776
1777 /**
1778 * returns an associative array of information about a given type
1779 * returns false if no type exists by the given name
1780 *
1781 * For a complexType typeDef = array(
1782 * 'restrictionBase' => '',
1783 * 'phpType' => '',
1784 * 'compositor' => '(sequence|all)',
1785 * 'elements' => array(), // refs to elements array
1786 * 'attrs' => array() // refs to attributes array
1787 * ... and so on (see addComplexType)
1788 * )
1789 *
1790 * For simpleType or element, the array has different keys.
1791 *
1792 * @param string $type
1793 * @return mixed
1794 * @access public
1795 * @see addComplexType
1796 * @see addSimpleType
1797 * @see addElement
1798 */
1799 function getTypeDef($type){
1800 //$this->debug("in getTypeDef for type $type");
1801 if (substr($type, -1) == '^') {
1802 $is_element = 1;
1803 $type = substr($type, 0, -1);
1804 } else {
1805 $is_element = 0;
1806 }
1807
1808 if((! $is_element) && isset($this->complexTypes[$type])){
1809 $this->xdebug("in getTypeDef, found complexType $type");
1810 return $this->complexTypes[$type];
1811 } elseif((! $is_element) && isset($this->simpleTypes[$type])){
1812 $this->xdebug("in getTypeDef, found simpleType $type");
1813 if (!isset($this->simpleTypes[$type]['phpType'])) {
1814 // get info for type to tack onto the simple type
1815 // TODO: can this ever really apply (i.e. what is a simpleType really?)
1816 $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1);
1817 $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':'));
1818 $etype = $this->getTypeDef($uqType);
1819 if ($etype) {
1820 $this->xdebug("in getTypeDef, found type for simpleType $type:");
1821 $this->xdebug($this->varDump($etype));
1822 if (isset($etype['phpType'])) {
1823 $this->simpleTypes[$type]['phpType'] = $etype['phpType'];
1824 }
1825 if (isset($etype['elements'])) {
1826 $this->simpleTypes[$type]['elements'] = $etype['elements'];
1827 }
1828 }
1829 }
1830 return $this->simpleTypes[$type];
1831 } elseif(isset($this->elements[$type])){
1832 $this->xdebug("in getTypeDef, found element $type");
1833 if (!isset($this->elements[$type]['phpType'])) {
1834 // get info for type to tack onto the element
1835 $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1);
1836 $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':'));
1837 $etype = $this->getTypeDef($uqType);
1838 if ($etype) {
1839 $this->xdebug("in getTypeDef, found type for element $type:");
1840 $this->xdebug($this->varDump($etype));
1841 if (isset($etype['phpType'])) {
1842 $this->elements[$type]['phpType'] = $etype['phpType'];
1843 }
1844 if (isset($etype['elements'])) {
1845 $this->elements[$type]['elements'] = $etype['elements'];
1846 }
1847 if (isset($etype['extensionBase'])) {
1848 $this->elements[$type]['extensionBase'] = $etype['extensionBase'];
1849 }
1850 } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
1851 $this->xdebug("in getTypeDef, element $type is an XSD type");
1852 $this->elements[$type]['phpType'] = 'scalar';
1853 }
1854 }
1855 return $this->elements[$type];
1856 } elseif(isset($this->attributes[$type])){
1857 $this->xdebug("in getTypeDef, found attribute $type");
1858 return $this->attributes[$type];
1859 } elseif (preg_match('/_ContainedType$/', $type)) {
1860 $this->xdebug("in getTypeDef, have an untyped element $type");
1861 $typeDef['typeClass'] = 'simpleType';
1862 $typeDef['phpType'] = 'scalar';
1863 $typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string';
1864 return $typeDef;
1865 }
1866 $this->xdebug("in getTypeDef, did not find $type");
1867 return false;
1868 }
1869
1870 /**
1871 * returns a sample serialization of a given type, or false if no type by the given name
1872 *
1873 * @param string $type name of type
1874 * @return mixed
1875 * @access public
1876 * @deprecated
1877 */
1878 function serializeTypeDef($type){
1879 //print "in sTD() for type $type<br>";
1880 if($typeDef = $this->getTypeDef($type)){
1881 $str .= '<'.$type;
1882 if(is_array($typeDef['attrs'])){
1883 foreach($typeDef['attrs'] as $attName => $data){
1884 $str .= " $attName=\"{type = ".$data['type']."}\"";
1885 }
1886 }
1887 $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
1888 if(count($typeDef['elements']) > 0){
1889 $str .= ">";
1890 foreach($typeDef['elements'] as $element => $eData){
1891 $str .= $this->serializeTypeDef($element);
1892 }
1893 $str .= "</$type>";
1894 } elseif($typeDef['typeClass'] == 'element') {
1895 $str .= "></$type>";
1896 } else {
1897 $str .= "/>";
1898 }
1899 return $str;
1900 }
1901 return false;
1902 }
1903
1904 /**
1905 * returns HTML form elements that allow a user
1906 * to enter values for creating an instance of the given type.
1907 *
1908 * @param string $name name for type instance
1909 * @param string $type name of type
1910 * @return string
1911 * @access public
1912 * @deprecated
1913 */
1914 function typeToForm($name,$type){
1915 // get typedef
1916 if($typeDef = $this->getTypeDef($type)){
1917 // if struct
1918 if($typeDef['phpType'] == 'struct'){
1919 $buffer .= '<table>';
1920 foreach($typeDef['elements'] as $child => $childDef){
1921 $buffer .= "
1922 <tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>
1923 <td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
1924 }
1925 $buffer .= '</table>';
1926 // if array
1927 } elseif($typeDef['phpType'] == 'array'){
1928 $buffer .= '<table>';
1929 for($i=0;$i < 3; $i++){
1930 $buffer .= "
1931 <tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
1932 <td><input type='text' name='parameters[".$name."][]'></td></tr>";
1933 }
1934 $buffer .= '</table>';
1935 // if scalar
1936 } else {
1937 $buffer .= "<input type='text' name='parameters[$name]'>";
1938 }
1939 } else {
1940 $buffer .= "<input type='text' name='parameters[$name]'>";
1941 }
1942 return $buffer;
1943 }
1944
1945 /**
1946 * adds a complex type to the schema
1947 *
1948 * example: array
1949 *
1950 * addType(
1951 * 'ArrayOfstring',
1952 * 'complexType',
1953 * 'array',
1954 * '',
1955 * 'SOAP-ENC:Array',
1956 * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
1957 * 'xsd:string'
1958 * );
1959 *
1960 * example: PHP associative array ( SOAP Struct )
1961 *
1962 * addType(
1963 * 'SOAPStruct',
1964 * 'complexType',
1965 * 'struct',
1966 * 'all',
1967 * array('myVar'=> array('name'=>'myVar','type'=>'string')
1968 * );
1969 *
1970 * @param name
1971 * @param typeClass (complexType|simpleType|attribute)
1972 * @param phpType: currently supported are array and struct (php assoc array)
1973 * @param compositor (all|sequence|choice)
1974 * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
1975 * @param elements = array ( name = array(name=>'',type=>'') )
1976 * @param attrs = array(
1977 * array(
1978 * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
1979 * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
1980 * )
1981 * )
1982 * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
1983 * @access public
1984 * @see getTypeDef
1985 */
1986 function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
1987 $this->complexTypes[$name] = array(
1988 'name' => $name,
1989 'typeClass' => $typeClass,
1990 'phpType' => $phpType,
1991 'compositor'=> $compositor,
1992 'restrictionBase' => $restrictionBase,
1993 'elements' => $elements,
1994 'attrs' => $attrs,
1995 'arrayType' => $arrayType
1996 );
1997
1998 $this->xdebug("addComplexType $name:");
1999 $this->appendDebug($this->varDump($this->complexTypes[$name]));
2000 }
2001
2002 /**
2003 * adds a simple type to the schema
2004 *
2005 * @param string $name
2006 * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
2007 * @param string $typeClass (should always be simpleType)
2008 * @param string $phpType (should always be scalar)
2009 * @param array $enumeration array of values
2010 * @access public
2011 * @see nusoap_xmlschema
2012 * @see getTypeDef
2013 */
2014 function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
2015 $this->simpleTypes[$name] = array(
2016 'name' => $name,
2017 'typeClass' => $typeClass,
2018 'phpType' => $phpType,
2019 'type' => $restrictionBase,
2020 'enumeration' => $enumeration
2021 );
2022
2023 $this->xdebug("addSimpleType $name:");
2024 $this->appendDebug($this->varDump($this->simpleTypes[$name]));
2025 }
2026
2027 /**
2028 * adds an element to the schema
2029 *
2030 * @param array $attrs attributes that must include name and type
2031 * @see nusoap_xmlschema
2032 * @access public
2033 */
2034 function addElement($attrs) {
2035 if (! $this->getPrefix($attrs['type'])) {
2036 $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type'];
2037 }
2038 $this->elements[ $attrs['name'] ] = $attrs;
2039 $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
2040
2041 $this->xdebug("addElement " . $attrs['name']);
2042 $this->appendDebug($this->varDump($this->elements[ $attrs['name'] ]));
2043 }
2044}
2045
2046/**
2047 * Backward compatibility
2048 */
2049class XMLSchema extends nusoap_xmlschema {
2050}
2051
2052?><?php
2053
2054
2055
2056/**
2057* For creating serializable abstractions of native PHP types. This class
2058* allows element name/namespace, XSD type, and XML attributes to be
2059* associated with a value. This is extremely useful when WSDL is not
2060* used, but is also useful when WSDL is used with polymorphic types, including
2061* xsd:anyType and user-defined types.
2062*
2063* @author Dietrich Ayala <dietrich@ganx4.com>
2064* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
2065* @access public
2066*/
2067class soapval extends nusoap_base {
2068 /**
2069 * The XML element name
2070 *
2071 * @var string
2072 * @access private
2073 */
2074 var $name;
2075 /**
2076 * The XML type name (string or false)
2077 *
2078 * @var mixed
2079 * @access private
2080 */
2081 var $type;
2082 /**
2083 * The PHP value
2084 *
2085 * @var mixed
2086 * @access private
2087 */
2088 var $value;
2089 /**
2090 * The XML element namespace (string or false)
2091 *
2092 * @var mixed
2093 * @access private
2094 */
2095 var $element_ns;
2096 /**
2097 * The XML type namespace (string or false)
2098 *
2099 * @var mixed
2100 * @access private
2101 */
2102 var $type_ns;
2103 /**
2104 * The XML element attributes (array or false)
2105 *
2106 * @var mixed
2107 * @access private
2108 */
2109 var $attributes;
2110
2111 /**
2112 * constructor
2113 *
2114 * @param string $name optional name
2115 * @param mixed $type optional type name
2116 * @param mixed $value optional value
2117 * @param mixed $element_ns optional namespace of value
2118 * @param mixed $type_ns optional namespace of type
2119 * @param mixed $attributes associative array of attributes to add to element serialization
2120 * @access public
2121 */
2122 function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
2123 parent::nusoap_base();
2124 $this->name = $name;
2125 $this->type = $type;
2126 $this->value = $value;
2127 $this->element_ns = $element_ns;
2128 $this->type_ns = $type_ns;
2129 $this->attributes = $attributes;
2130 }
2131
2132 /**
2133 * return serialized value
2134 *
2135 * @param string $use The WSDL use value (encoded|literal)
2136 * @return string XML data
2137 * @access public
2138 */
2139 function serialize($use='encoded') {
2140 return $this->serialize_val($this->value, $this->name, $this->type, $this->element_ns, $this->type_ns, $this->attributes, $use, true);
2141 }
2142
2143 /**
2144 * decodes a soapval object into a PHP native type
2145 *
2146 * @return mixed
2147 * @access public
2148 */
2149 function decode(){
2150 return $this->value;
2151 }
2152}
2153
2154
2155
2156?><?php
2157
2158
2159
2160/**
2161* transport class for sending/receiving data via HTTP and HTTPS
2162* NOTE: PHP must be compiled with the CURL extension for HTTPS support
2163*
2164* @author Dietrich Ayala <dietrich@ganx4.com>
2165* @author Scott Nichol <snichol@users.sourceforge.net>
2166* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
2167* @access public
2168*/
2169class soap_transport_http extends nusoap_base {
2170
2171 var $url = '';
2172 var $uri = '';
2173 var $digest_uri = '';
2174 var $scheme = '';
2175 var $host = '';
2176 var $port = '';
2177 var $path = '';
2178 var $request_method = 'POST';
2179 var $protocol_version = '1.0';
2180 var $encoding = '';
2181 var $outgoing_headers = array();
2182 var $incoming_headers = array();
2183 var $incoming_cookies = array();
2184 var $outgoing_payload = '';
2185 var $incoming_payload = '';
2186 var $response_status_line; // HTTP response status line
2187 var $useSOAPAction = true;
2188 var $persistentConnection = false;
2189 var $ch = false; // cURL handle
2190 var $ch_options = array(); // cURL custom options
2191 var $use_curl = false; // force cURL use
2192 var $proxy = null; // proxy information (associative array)
2193 var $username = '';
2194 var $password = '';
2195 var $authtype = '';
2196 var $digestRequest = array();
2197 var $certRequest = array(); // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional)
2198 // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem'
2199 // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem'
2200 // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem'
2201 // passphrase: SSL key password/passphrase
2202 // certpassword: SSL certificate password
2203 // verifypeer: default is 1
2204 // verifyhost: default is 1
2205
2206 /**
2207 * constructor
2208 *
2209 * @param string $url The URL to which to connect
2210 * @param array $curl_options User-specified cURL options
2211 * @param boolean $use_curl Whether to try to force cURL use
2212 * @access public
2213 */
2214 function soap_transport_http($url, $curl_options = NULL, $use_curl = false){
2215 parent::nusoap_base();
2216 $this->debug("ctor url=$url use_curl=$use_curl curl_options:");
2217 $this->appendDebug($this->varDump($curl_options));
2218 $this->setURL($url);
2219 if (is_array($curl_options)) {
2220 $this->ch_options = $curl_options;
2221 }
2222 $this->use_curl = $use_curl;
2223 preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
2224 $this->setHeader('User-Agent', $this->title.'/'.$this->version.' ('.$rev[1].')');
2225 }
2226
2227 /**
2228 * sets a cURL option
2229 *
2230 * @param mixed $option The cURL option (always integer?)
2231 * @param mixed $value The cURL option value
2232 * @access private
2233 */
2234 function setCurlOption($option, $value) {
2235 $this->debug("setCurlOption option=$option, value=");
2236 $this->appendDebug($this->varDump($value));
2237 curl_setopt($this->ch, $option, $value);
2238 }
2239
2240 /**
2241 * sets an HTTP header
2242 *
2243 * @param string $name The name of the header
2244 * @param string $value The value of the header
2245 * @access private
2246 */
2247 function setHeader($name, $value) {
2248 $this->outgoing_headers[$name] = $value;
2249 $this->debug("set header $name: $value");
2250 }
2251
2252 /**
2253 * unsets an HTTP header
2254 *
2255 * @param string $name The name of the header
2256 * @access private
2257 */
2258 function unsetHeader($name) {
2259 if (isset($this->outgoing_headers[$name])) {
2260 $this->debug("unset header $name");
2261 unset($this->outgoing_headers[$name]);
2262 }
2263 }
2264
2265 /**
2266 * sets the URL to which to connect
2267 *
2268 * @param string $url The URL to which to connect
2269 * @access private
2270 */
2271 function setURL($url) {
2272 $this->url = $url;
2273
2274 $u = parse_url($url);
2275 foreach($u as $k => $v){
2276 $this->debug("parsed URL $k = $v");
2277 $this->$k = $v;
2278 }
2279
2280 // add any GET params to path
2281 if(isset($u['query']) && $u['query'] != ''){
2282 $this->path .= '?' . $u['query'];
2283 }
2284
2285 // set default port
2286 if(!isset($u['port'])){
2287 if($u['scheme'] == 'https'){
2288 $this->port = 443;
2289 } else {
2290 $this->port = 80;
2291 }
2292 }
2293
2294 $this->uri = $this->path;
2295 $this->digest_uri = $this->uri;
2296
2297 // build headers
2298 if (!isset($u['port'])) {
2299 $this->setHeader('Host', $this->host);
2300 } else {
2301 $this->setHeader('Host', $this->host.':'.$this->port);
2302 }
2303
2304 if (isset($u['user']) && $u['user'] != '') {
2305 $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : '');
2306 }
2307 }
2308
2309 /**
2310 * gets the I/O method to use
2311 *
2312 * @return string I/O method to use (socket|curl|unknown)
2313 * @access private
2314 */
2315 function io_method() {
2316 if ($this->use_curl || ($this->scheme == 'https') || ($this->scheme == 'http' && $this->authtype == 'ntlm') || ($this->scheme == 'http' && is_array($this->proxy) && $this->proxy['authtype'] == 'ntlm'))
2317 return 'curl';
2318 if (($this->scheme == 'http' || $this->scheme == 'ssl') && $this->authtype != 'ntlm' && (!is_array($this->proxy) || $this->proxy['authtype'] != 'ntlm'))
2319 return 'socket';
2320 return 'unknown';
2321 }
2322
2323 /**
2324 * establish an HTTP connection
2325 *
2326 * @param integer $timeout set connection timeout in seconds
2327 * @param integer $response_timeout set response timeout in seconds
2328 * @return boolean true if connected, false if not
2329 * @access private
2330 */
2331 function connect($connection_timeout=0,$response_timeout=30){
2332 // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like
2333 // "regular" socket.
2334 // TODO: disabled for now because OpenSSL must be *compiled* in (not just
2335 // loaded), and until PHP5 stream_get_wrappers is not available.
2336// if ($this->scheme == 'https') {
2337// if (version_compare(phpversion(), '4.3.0') >= 0) {
2338// if (extension_loaded('openssl')) {
2339// $this->scheme = 'ssl';
2340// $this->debug('Using SSL over OpenSSL');
2341// }
2342// }
2343// }
2344 $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port");
2345 if ($this->io_method() == 'socket') {
2346 if (!is_array($this->proxy)) {
2347 $host = $this->host;
2348 $port = $this->port;
2349 } else {
2350 $host = $this->proxy['host'];
2351 $port = $this->proxy['port'];
2352 }
2353
2354 // use persistent connection
2355 if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){
2356 if (!feof($this->fp)) {
2357 $this->debug('Re-use persistent connection');
2358 return true;
2359 }
2360 fclose($this->fp);
2361 $this->debug('Closed persistent connection at EOF');
2362 }
2363
2364 // munge host if using OpenSSL
2365 if ($this->scheme == 'ssl') {
2366 $host = 'ssl://' . $host;
2367 }
2368 $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout);
2369
2370 // open socket
2371 if($connection_timeout > 0){
2372 $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout);
2373 } else {
2374 $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str);
2375 }
2376
2377 // test pointer
2378 if(!$this->fp) {
2379 $msg = 'Couldn\'t open socket connection to server ' . $this->url;
2380 if ($this->errno) {
2381 $msg .= ', Error ('.$this->errno.'): '.$this->error_str;
2382 } else {
2383 $msg .= ' prior to connect(). This is often a problem looking up the host name.';
2384 }
2385 $this->debug($msg);
2386 $this->setError($msg);
2387 return false;
2388 }
2389
2390 // set response timeout
2391 $this->debug('set response timeout to ' . $response_timeout);
2392 socket_set_timeout( $this->fp, $response_timeout);
2393
2394 $this->debug('socket connected');
2395 return true;
2396 } else if ($this->io_method() == 'curl') {
2397 if (!extension_loaded('curl')) {
2398// $this->setError('cURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
2399 $this->setError('The PHP cURL Extension is required for HTTPS or NLTM. You will need to re-build or update your PHP to include cURL or change php.ini to load the PHP cURL extension.');
2400 return false;
2401 }
2402 // Avoid warnings when PHP does not have these options
2403 if (defined('CURLOPT_CONNECTIONTIMEOUT'))
2404 $CURLOPT_CONNECTIONTIMEOUT = CURLOPT_CONNECTIONTIMEOUT;
2405 else
2406 $CURLOPT_CONNECTIONTIMEOUT = 78;
2407 if (defined('CURLOPT_HTTPAUTH'))
2408 $CURLOPT_HTTPAUTH = CURLOPT_HTTPAUTH;
2409 else
2410 $CURLOPT_HTTPAUTH = 107;
2411 if (defined('CURLOPT_PROXYAUTH'))
2412 $CURLOPT_PROXYAUTH = CURLOPT_PROXYAUTH;
2413 else
2414 $CURLOPT_PROXYAUTH = 111;
2415 if (defined('CURLAUTH_BASIC'))
2416 $CURLAUTH_BASIC = CURLAUTH_BASIC;
2417 else
2418 $CURLAUTH_BASIC = 1;
2419 if (defined('CURLAUTH_DIGEST'))
2420 $CURLAUTH_DIGEST = CURLAUTH_DIGEST;
2421 else
2422 $CURLAUTH_DIGEST = 2;
2423 if (defined('CURLAUTH_NTLM'))
2424 $CURLAUTH_NTLM = CURLAUTH_NTLM;
2425 else
2426 $CURLAUTH_NTLM = 8;
2427
2428 $this->debug('connect using cURL');
2429 // init CURL
2430 $this->ch = curl_init();
2431 // set url
2432 $hostURL = ($this->port != '') ? "$this->scheme://$this->host:$this->port" : "$this->scheme://$this->host";
2433 // add path
2434 $hostURL .= $this->path;
2435 $this->setCurlOption(CURLOPT_URL, $hostURL);
2436 // follow location headers (re-directs)
2437 if (ini_get('safe_mode') || ini_get('open_basedir')) {
2438 $this->debug('safe_mode or open_basedir set, so do not set CURLOPT_FOLLOWLOCATION');
2439 $this->debug('safe_mode = ');
2440 $this->appendDebug($this->varDump(ini_get('safe_mode')));
2441 $this->debug('open_basedir = ');
2442 $this->appendDebug($this->varDump(ini_get('open_basedir')));
2443 } else {
2444 $this->setCurlOption(CURLOPT_FOLLOWLOCATION, 1);
2445 }
2446 // ask for headers in the response output
2447 $this->setCurlOption(CURLOPT_HEADER, 1);
2448 // ask for the response output as the return value
2449 $this->setCurlOption(CURLOPT_RETURNTRANSFER, 1);
2450 // encode
2451 // We manage this ourselves through headers and encoding
2452// if(function_exists('gzuncompress')){
2453// $this->setCurlOption(CURLOPT_ENCODING, 'deflate');
2454// }
2455 // persistent connection
2456 if ($this->persistentConnection) {
2457 // I believe the following comment is now bogus, having applied to
2458 // the code when it used CURLOPT_CUSTOMREQUEST to send the request.
2459 // The way we send data, we cannot use persistent connections, since
2460 // there will be some "junk" at the end of our request.
2461 //$this->setCurlOption(CURL_HTTP_VERSION_1_1, true);
2462 $this->persistentConnection = false;
2463 $this->setHeader('Connection', 'close');
2464 }
2465 // set timeouts
2466 if ($connection_timeout != 0) {
2467 $this->setCurlOption($CURLOPT_CONNECTIONTIMEOUT, $connection_timeout);
2468 }
2469 if ($response_timeout != 0) {
2470 $this->setCurlOption(CURLOPT_TIMEOUT, $response_timeout);
2471 }
2472
2473 if ($this->scheme == 'https') {
2474 $this->debug('set cURL SSL verify options');
2475 // recent versions of cURL turn on peer/host checking by default,
2476 // while PHP binaries are not compiled with a default location for the
2477 // CA cert bundle, so disable peer/host checking.
2478 //$this->setCurlOption(CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt');
2479 $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 0);
2480 $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 0);
2481
2482 // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo)
2483 if ($this->authtype == 'certificate') {
2484 $this->debug('set cURL certificate options');
2485 if (isset($this->certRequest['cainfofile'])) {
2486 $this->setCurlOption(CURLOPT_CAINFO, $this->certRequest['cainfofile']);
2487 }
2488 if (isset($this->certRequest['verifypeer'])) {
2489 $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']);
2490 } else {
2491 $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 1);
2492 }
2493 if (isset($this->certRequest['verifyhost'])) {
2494 $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']);
2495 } else {
2496 $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 1);
2497 }
2498 if (isset($this->certRequest['sslcertfile'])) {
2499 $this->setCurlOption(CURLOPT_SSLCERT, $this->certRequest['sslcertfile']);
2500 }
2501 if (isset($this->certRequest['sslkeyfile'])) {
2502 $this->setCurlOption(CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']);
2503 }
2504 if (isset($this->certRequest['passphrase'])) {
2505 $this->setCurlOption(CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']);
2506 }
2507 if (isset($this->certRequest['certpassword'])) {
2508 $this->setCurlOption(CURLOPT_SSLCERTPASSWD, $this->certRequest['certpassword']);
2509 }
2510 }
2511 }
2512 if ($this->authtype && ($this->authtype != 'certificate')) {
2513 if ($this->username) {
2514 $this->debug('set cURL username/password');
2515 $this->setCurlOption(CURLOPT_USERPWD, "$this->username:$this->password");
2516 }
2517 if ($this->authtype == 'basic') {
2518 $this->debug('set cURL for Basic authentication');
2519 $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_BASIC);
2520 }
2521 if ($this->authtype == 'digest') {
2522 $this->debug('set cURL for digest authentication');
2523 $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_DIGEST);
2524 }
2525 if ($this->authtype == 'ntlm') {
2526 $this->debug('set cURL for NTLM authentication');
2527 $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_NTLM);
2528 }
2529 }
2530 if (is_array($this->proxy)) {
2531 $this->debug('set cURL proxy options');
2532 if ($this->proxy['port'] != '') {
2533 $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host'].':'.$this->proxy['port']);
2534 } else {
2535 $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host']);
2536 }
2537 if ($this->proxy['username'] || $this->proxy['password']) {
2538 $this->debug('set cURL proxy authentication options');
2539 $this->setCurlOption(CURLOPT_PROXYUSERPWD, $this->proxy['username'].':'.$this->proxy['password']);
2540 if ($this->proxy['authtype'] == 'basic') {
2541 $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_BASIC);
2542 }
2543 if ($this->proxy['authtype'] == 'ntlm') {
2544 $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_NTLM);
2545 }
2546 }
2547 }
2548 $this->debug('cURL connection set up');
2549 return true;
2550 } else {
2551 $this->setError('Unknown scheme ' . $this->scheme);
2552 $this->debug('Unknown scheme ' . $this->scheme);
2553 return false;
2554 }
2555 }
2556
2557 /**
2558 * sends the SOAP request and gets the SOAP response via HTTP[S]
2559 *
2560 * @param string $data message data
2561 * @param integer $timeout set connection timeout in seconds
2562 * @param integer $response_timeout set response timeout in seconds
2563 * @param array $cookies cookies to send
2564 * @return string data
2565 * @access public
2566 */
2567 function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) {
2568
2569 $this->debug('entered send() with data of length: '.strlen($data));
2570
2571 $this->tryagain = true;
2572 $tries = 0;
2573 while ($this->tryagain) {
2574 $this->tryagain = false;
2575 if ($tries++ < 2) {
2576 // make connnection
2577 if (!$this->connect($timeout, $response_timeout)){
2578 return false;
2579 }
2580
2581 // send request
2582 if (!$this->sendRequest($data, $cookies)){
2583 return false;
2584 }
2585
2586 // get response
2587 $respdata = $this->getResponse();
2588 } else {
2589 $this->setError("Too many tries to get an OK response ($this->response_status_line)");
2590 }
2591 }
2592 $this->debug('end of send()');
2593 return $respdata;
2594 }
2595
2596
2597 /**
2598 * sends the SOAP request and gets the SOAP response via HTTPS using CURL
2599 *
2600 * @param string $data message data
2601 * @param integer $timeout set connection timeout in seconds
2602 * @param integer $response_timeout set response timeout in seconds
2603 * @param array $cookies cookies to send
2604 * @return string data
2605 * @access public
2606 * @deprecated
2607 */
2608 function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) {
2609 return $this->send($data, $timeout, $response_timeout, $cookies);
2610 }
2611
2612 /**
2613 * if authenticating, set user credentials here
2614 *
2615 * @param string $username
2616 * @param string $password
2617 * @param string $authtype (basic|digest|certificate|ntlm)
2618 * @param array $digestRequest (keys must be nonce, nc, realm, qop)
2619 * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
2620 * @access public
2621 */
2622 function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) {
2623 $this->debug("setCredentials username=$username authtype=$authtype digestRequest=");
2624 $this->appendDebug($this->varDump($digestRequest));
2625 $this->debug("certRequest=");
2626 $this->appendDebug($this->varDump($certRequest));
2627 // cf. RFC 2617
2628 if ($authtype == 'basic') {
2629 $this->setHeader('Authorization', 'Basic '.base64_encode(str_replace(':','',$username).':'.$password));
2630 } elseif ($authtype == 'digest') {
2631 if (isset($digestRequest['nonce'])) {
2632 $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1;
2633
2634 // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html)
2635
2636 // A1 = unq(username-value) ":" unq(realm-value) ":" passwd
2637 $A1 = $username. ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password;
2638
2639 // H(A1) = MD5(A1)
2640 $HA1 = md5($A1);
2641
2642 // A2 = Method ":" digest-uri-value
2643 $A2 = $this->request_method . ':' . $this->digest_uri;
2644
2645 // H(A2)
2646 $HA2 = md5($A2);
2647
2648 // KD(secret, data) = H(concat(secret, ":", data))
2649 // if qop == auth:
2650 // request-digest = <"> < KD ( H(A1), unq(nonce-value)
2651 // ":" nc-value
2652 // ":" unq(cnonce-value)
2653 // ":" unq(qop-value)
2654 // ":" H(A2)
2655 // ) <">
2656 // if qop is missing,
2657 // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <">
2658
2659 $unhashedDigest = '';
2660 $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : '';
2661 $cnonce = $nonce;
2662 if ($digestRequest['qop'] != '') {
2663 $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2;
2664 } else {
2665 $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2;
2666 }
2667
2668 $hashedDigest = md5($unhashedDigest);
2669
2670 $opaque = '';
2671 if (isset($digestRequest['opaque'])) {
2672 $opaque = ', opaque="' . $digestRequest['opaque'] . '"';
2673 }
2674
2675 $this->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . $opaque . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"');
2676 }
2677 } elseif ($authtype == 'certificate') {
2678 $this->certRequest = $certRequest;
2679 $this->debug('Authorization header not set for certificate');
2680 } elseif ($authtype == 'ntlm') {
2681 // do nothing
2682 $this->debug('Authorization header not set for ntlm');
2683 }
2684 $this->username = $username;
2685 $this->password = $password;
2686 $this->authtype = $authtype;
2687 $this->digestRequest = $digestRequest;
2688 }
2689
2690 /**
2691 * set the soapaction value
2692 *
2693 * @param string $soapaction
2694 * @access public
2695 */
2696 function setSOAPAction($soapaction) {
2697 $this->setHeader('SOAPAction', '"' . $soapaction . '"');
2698 }
2699
2700 /**
2701 * use http encoding
2702 *
2703 * @param string $enc encoding style. supported values: gzip, deflate, or both
2704 * @access public
2705 */
2706 function setEncoding($enc='gzip, deflate') {
2707 if (function_exists('gzdeflate')) {
2708 $this->protocol_version = '1.1';
2709 $this->setHeader('Accept-Encoding', $enc);
2710 if (!isset($this->outgoing_headers['Connection'])) {
2711 $this->setHeader('Connection', 'close');
2712 $this->persistentConnection = false;
2713 }
2714 // deprecated as of PHP 5.3.0
2715 //set_magic_quotes_runtime(0);
2716 $this->encoding = $enc;
2717 }
2718 }
2719
2720 /**
2721 * set proxy info here
2722 *
2723 * @param string $proxyhost use an empty string to remove proxy
2724 * @param string $proxyport
2725 * @param string $proxyusername
2726 * @param string $proxypassword
2727 * @param string $proxyauthtype (basic|ntlm)
2728 * @access public
2729 */
2730 function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 'basic') {
2731 if ($proxyhost) {
2732 $this->proxy = array(
2733 'host' => $proxyhost,
2734 'port' => $proxyport,
2735 'username' => $proxyusername,
2736 'password' => $proxypassword,
2737 'authtype' => $proxyauthtype
2738 );
2739 if ($proxyusername != '' && $proxypassword != '' && $proxyauthtype = 'basic') {
2740 $this->setHeader('Proxy-Authorization', ' Basic '.base64_encode($proxyusername.':'.$proxypassword));
2741 }
2742 } else {
2743 $this->debug('remove proxy');
2744 $proxy = null;
2745 unsetHeader('Proxy-Authorization');
2746 }
2747 }
2748
2749
2750 /**
2751 * Test if the given string starts with a header that is to be skipped.
2752 * Skippable headers result from chunked transfer and proxy requests.
2753 *
2754 * @param string $data The string to check.
2755 * @returns boolean Whether a skippable header was found.
2756 * @access private
2757 */
2758 function isSkippableCurlHeader(&$data) {
2759 $skipHeaders = array( 'HTTP/1.1 100',
2760 'HTTP/1.0 301',
2761 'HTTP/1.1 301',
2762 'HTTP/1.0 302',
2763 'HTTP/1.1 302',
2764 'HTTP/1.0 401',
2765 'HTTP/1.1 401',
2766 'HTTP/1.0 200 Connection established');
2767 foreach ($skipHeaders as $hd) {
2768 $prefix = substr($data, 0, strlen($hd));
2769 if ($prefix == $hd) return true;
2770 }
2771
2772 return false;
2773 }
2774
2775 /**
2776 * decode a string that is encoded w/ "chunked' transfer encoding
2777 * as defined in RFC2068 19.4.6
2778 *
2779 * @param string $buffer
2780 * @param string $lb
2781 * @returns string
2782 * @access public
2783 * @deprecated
2784 */
2785 function decodeChunked($buffer, $lb){
2786 // length := 0
2787 $length = 0;
2788 $new = '';
2789
2790 // read chunk-size, chunk-extension (if any) and CRLF
2791 // get the position of the linebreak
2792 $chunkend = strpos($buffer, $lb);
2793 if ($chunkend == FALSE) {
2794 $this->debug('no linebreak found in decodeChunked');
2795 return $new;
2796 }
2797 $temp = substr($buffer,0,$chunkend);
2798 $chunk_size = hexdec( trim($temp) );
2799 $chunkstart = $chunkend + strlen($lb);
2800 // while (chunk-size > 0) {
2801 while ($chunk_size > 0) {
2802 $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size");
2803 $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size);
2804
2805 // Just in case we got a broken connection
2806 if ($chunkend == FALSE) {
2807 $chunk = substr($buffer,$chunkstart);
2808 // append chunk-data to entity-body
2809 $new .= $chunk;
2810 $length += strlen($chunk);
2811 break;
2812 }
2813
2814 // read chunk-data and CRLF
2815 $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2816 // append chunk-data to entity-body
2817 $new .= $chunk;
2818 // length := length + chunk-size
2819 $length += strlen($chunk);
2820 // read chunk-size and CRLF
2821 $chunkstart = $chunkend + strlen($lb);
2822
2823 $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb);
2824 if ($chunkend == FALSE) {
2825 break; //Just in case we got a broken connection
2826 }
2827 $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
2828 $chunk_size = hexdec( trim($temp) );
2829 $chunkstart = $chunkend;
2830 }
2831 return $new;
2832 }
2833
2834 /**
2835 * Writes the payload, including HTTP headers, to $this->outgoing_payload.
2836 *
2837 * @param string $data HTTP body
2838 * @param string $cookie_str data for HTTP Cookie header
2839 * @return void
2840 * @access private
2841 */
2842 function buildPayload($data, $cookie_str = '') {
2843 // Note: for cURL connections, $this->outgoing_payload is ignored,
2844 // as is the Content-Length header, but these are still created as
2845 // debugging guides.
2846
2847 // add content-length header
2848 if ($this->request_method != 'GET') {
2849 $this->setHeader('Content-Length', strlen($data));
2850 }
2851
2852 // start building outgoing payload:
2853 if ($this->proxy) {
2854 $uri = $this->url;
2855 } else {
2856 $uri = $this->uri;
2857 }
2858 $req = "$this->request_method $uri HTTP/$this->protocol_version";
2859 $this->debug("HTTP request: $req");
2860 $this->outgoing_payload = "$req\r\n";
2861
2862 // loop thru headers, serializing
2863 foreach($this->outgoing_headers as $k => $v){
2864 $hdr = $k.': '.$v;
2865 $this->debug("HTTP header: $hdr");
2866 $this->outgoing_payload .= "$hdr\r\n";
2867 }
2868
2869 // add any cookies
2870 if ($cookie_str != '') {
2871 $hdr = 'Cookie: '.$cookie_str;
2872 $this->debug("HTTP header: $hdr");
2873 $this->outgoing_payload .= "$hdr\r\n";
2874 }
2875
2876 // header/body separator
2877 $this->outgoing_payload .= "\r\n";
2878
2879 // add data
2880 $this->outgoing_payload .= $data;
2881 }
2882
2883 /**
2884 * sends the SOAP request via HTTP[S]
2885 *
2886 * @param string $data message data
2887 * @param array $cookies cookies to send
2888 * @return boolean true if OK, false if problem
2889 * @access private
2890 */
2891 function sendRequest($data, $cookies = NULL) {
2892 // build cookie string
2893 $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https')));
2894
2895 // build payload
2896 $this->buildPayload($data, $cookie_str);
2897
2898 if ($this->io_method() == 'socket') {
2899 // send payload
2900 if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
2901 $this->setError('couldn\'t write message data to socket');
2902 $this->debug('couldn\'t write message data to socket');
2903 return false;
2904 }
2905 $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload));
2906 return true;
2907 } else if ($this->io_method() == 'curl') {
2908 // set payload
2909 // cURL does say this should only be the verb, and in fact it
2910 // turns out that the URI and HTTP version are appended to this, which
2911 // some servers refuse to work with (so we no longer use this method!)
2912 //$this->setCurlOption(CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
2913 $curl_headers = array();
2914 foreach($this->outgoing_headers as $k => $v){
2915 if ($k == 'Connection' || $k == 'Content-Length' || $k == 'Host' || $k == 'Authorization' || $k == 'Proxy-Authorization') {
2916 $this->debug("Skip cURL header $k: $v");
2917 } else {
2918 $curl_headers[] = "$k: $v";
2919 }
2920 }
2921 if ($cookie_str != '') {
2922 $curl_headers[] = 'Cookie: ' . $cookie_str;
2923 }
2924 $this->setCurlOption(CURLOPT_HTTPHEADER, $curl_headers);
2925 $this->debug('set cURL HTTP headers');
2926 if ($this->request_method == "POST") {
2927 $this->setCurlOption(CURLOPT_POST, 1);
2928 $this->setCurlOption(CURLOPT_POSTFIELDS, $data);
2929 $this->debug('set cURL POST data');
2930 } else {
2931 }
2932 // insert custom user-set cURL options
2933 foreach ($this->ch_options as $key => $val) {
2934 $this->setCurlOption($key, $val);
2935 }
2936
2937 $this->debug('set cURL payload');
2938 return true;
2939 }
2940 }
2941
2942 /**
2943 * gets the SOAP response via HTTP[S]
2944 *
2945 * @return string the response (also sets member variables like incoming_payload)
2946 * @access private
2947 */
2948 function getResponse(){
2949 $this->incoming_payload = '';
2950
2951 if ($this->io_method() == 'socket') {
2952 // loop until headers have been retrieved
2953 $data = '';
2954 while (!isset($lb)){
2955
2956 // We might EOF during header read.
2957 if(feof($this->fp)) {
2958 $this->incoming_payload = $data;
2959 $this->debug('found no headers before EOF after length ' . strlen($data));
2960 $this->debug("received before EOF:\n" . $data);
2961 $this->setError('server failed to send headers');
2962 return false;
2963 }
2964
2965 $tmp = fgets($this->fp, 256);
2966 $tmplen = strlen($tmp);
2967 $this->debug("read line of $tmplen bytes: " . trim($tmp));
2968
2969 if ($tmplen == 0) {
2970 $this->incoming_payload = $data;
2971 $this->debug('socket read of headers timed out after length ' . strlen($data));
2972 $this->debug("read before timeout: " . $data);
2973 $this->setError('socket read of headers timed out');
2974 return false;
2975 }
2976
2977 $data .= $tmp;
2978 $pos = strpos($data,"\r\n\r\n");
2979 if($pos > 1){
2980 $lb = "\r\n";
2981 } else {
2982 $pos = strpos($data,"\n\n");
2983 if($pos > 1){
2984 $lb = "\n";
2985 }
2986 }
2987 // remove 100 headers
2988 if (isset($lb) && preg_match('/^HTTP\/1.1 100/',$data)) {
2989 unset($lb);
2990 $data = '';
2991 }//
2992 }
2993 // store header data
2994 $this->incoming_payload .= $data;
2995 $this->debug('found end of headers after length ' . strlen($data));
2996 // process headers
2997 $header_data = trim(substr($data,0,$pos));
2998 $header_array = explode($lb,$header_data);
2999 $this->incoming_headers = array();
3000 $this->incoming_cookies = array();
3001 foreach($header_array as $header_line){
3002 $arr = explode(':',$header_line, 2);
3003 if(count($arr) > 1){
3004 $header_name = strtolower(trim($arr[0]));
3005 $this->incoming_headers[$header_name] = trim($arr[1]);
3006 if ($header_name == 'set-cookie') {
3007 // TODO: allow multiple cookies from parseCookie
3008 $cookie = $this->parseCookie(trim($arr[1]));
3009 if ($cookie) {
3010 $this->incoming_cookies[] = $cookie;
3011 $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
3012 } else {
3013 $this->debug('did not find cookie in ' . trim($arr[1]));
3014 }
3015 }
3016 } else if (isset($header_name)) {
3017 // append continuation line to previous header
3018 $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
3019 }
3020 }
3021
3022 // loop until msg has been received
3023 if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') {
3024 $content_length = 2147483647; // ignore any content-length header
3025 $chunked = true;
3026 $this->debug("want to read chunked content");
3027 } elseif (isset($this->incoming_headers['content-length'])) {
3028 $content_length = $this->incoming_headers['content-length'];
3029 $chunked = false;
3030 $this->debug("want to read content of length $content_length");
3031 } else {
3032 $content_length = 2147483647;
3033 $chunked = false;
3034 $this->debug("want to read content to EOF");
3035 }
3036 $data = '';
3037 do {
3038 if ($chunked) {
3039 $tmp = fgets($this->fp, 256);
3040 $tmplen = strlen($tmp);
3041 $this->debug("read chunk line of $tmplen bytes");
3042 if ($tmplen == 0) {
3043 $this->incoming_payload = $data;
3044 $this->debug('socket read of chunk length timed out after length ' . strlen($data));
3045 $this->debug("read before timeout:\n" . $data);
3046 $this->setError('socket read of chunk length timed out');
3047 return false;
3048 }
3049 $content_length = hexdec(trim($tmp));
3050 $this->debug("chunk length $content_length");
3051 }
3052 $strlen = 0;
3053 while (($strlen < $content_length) && (!feof($this->fp))) {
3054 $readlen = min(8192, $content_length - $strlen);
3055 $tmp = fread($this->fp, $readlen);
3056 $tmplen = strlen($tmp);
3057 $this->debug("read buffer of $tmplen bytes");
3058 if (($tmplen == 0) && (!feof($this->fp))) {
3059 $this->incoming_payload = $data;
3060 $this->debug('socket read of body timed out after length ' . strlen($data));
3061 $this->debug("read before timeout:\n" . $data);
3062 $this->setError('socket read of body timed out');
3063 return false;
3064 }
3065 $strlen += $tmplen;
3066 $data .= $tmp;
3067 }
3068 if ($chunked && ($content_length > 0)) {
3069 $tmp = fgets($this->fp, 256);
3070 $tmplen = strlen($tmp);
3071 $this->debug("read chunk terminator of $tmplen bytes");
3072 if ($tmplen == 0) {
3073 $this->incoming_payload = $data;
3074 $this->debug('socket read of chunk terminator timed out after length ' . strlen($data));
3075 $this->debug("read before timeout:\n" . $data);
3076 $this->setError('socket read of chunk terminator timed out');
3077 return false;
3078 }
3079 }
3080 } while ($chunked && ($content_length > 0) && (!feof($this->fp)));
3081 if (feof($this->fp)) {
3082 $this->debug('read to EOF');
3083 }
3084 $this->debug('read body of length ' . strlen($data));
3085 $this->incoming_payload .= $data;
3086 $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server');
3087
3088 // close filepointer
3089 if(
3090 (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') ||
3091 (! $this->persistentConnection) || feof($this->fp)){
3092 fclose($this->fp);
3093 $this->fp = false;
3094 $this->debug('closed socket');
3095 }
3096
3097 // connection was closed unexpectedly
3098 if($this->incoming_payload == ''){
3099 $this->setError('no response from server');
3100 return false;
3101 }
3102
3103 // decode transfer-encoding
3104// if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){
3105// if(!$data = $this->decodeChunked($data, $lb)){
3106// $this->setError('Decoding of chunked data failed');
3107// return false;
3108// }
3109 //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
3110 // set decoded payload
3111// $this->incoming_payload = $header_data.$lb.$lb.$data;
3112// }
3113
3114 } else if ($this->io_method() == 'curl') {
3115 // send and receive
3116 $this->debug('send and receive with cURL');
3117 $this->incoming_payload = curl_exec($this->ch);
3118 $data = $this->incoming_payload;
3119
3120 $cErr = curl_error($this->ch);
3121 if ($cErr != '') {
3122 $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'<br>';
3123 // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE
3124 foreach(curl_getinfo($this->ch) as $k => $v){
3125 $err .= "$k: $v<br>";
3126 }
3127 $this->debug($err);
3128 $this->setError($err);
3129 curl_close($this->ch);
3130 return false;
3131 } else {
3132 //echo '<pre>';
3133 //var_dump(curl_getinfo($this->ch));
3134 //echo '</pre>';
3135 }
3136 // close curl
3137 $this->debug('No cURL error, closing cURL');
3138 curl_close($this->ch);
3139
3140 // try removing skippable headers
3141 $savedata = $data;
3142 while ($this->isSkippableCurlHeader($data)) {
3143 $this->debug("Found HTTP header to skip");
3144 if ($pos = strpos($data,"\r\n\r\n")) {
3145 $data = ltrim(substr($data,$pos));
3146 } elseif($pos = strpos($data,"\n\n") ) {
3147 $data = ltrim(substr($data,$pos));
3148 }
3149 }
3150
3151 if ($data == '') {
3152 // have nothing left; just remove 100 header(s)
3153 $data = $savedata;
3154 while (preg_match('/^HTTP\/1.1 100/',$data)) {
3155 if ($pos = strpos($data,"\r\n\r\n")) {
3156 $data = ltrim(substr($data,$pos));
3157 } elseif($pos = strpos($data,"\n\n") ) {
3158 $data = ltrim(substr($data,$pos));
3159 }
3160 }
3161 }
3162
3163 // separate content from HTTP headers
3164 if ($pos = strpos($data,"\r\n\r\n")) {
3165 $lb = "\r\n";
3166 } elseif( $pos = strpos($data,"\n\n")) {
3167 $lb = "\n";
3168 } else {
3169 $this->debug('no proper separation of headers and document');
3170 $this->setError('no proper separation of headers and document');
3171 return false;
3172 }
3173 $header_data = trim(substr($data,0,$pos));
3174 $header_array = explode($lb,$header_data);
3175 $data = ltrim(substr($data,$pos));
3176 $this->debug('found proper separation of headers and document');
3177 $this->debug('cleaned data, stringlen: '.strlen($data));
3178 // clean headers
3179 foreach ($header_array as $header_line) {
3180 $arr = explode(':',$header_line,2);
3181 if(count($arr) > 1){
3182 $header_name = strtolower(trim($arr[0]));
3183 $this->incoming_headers[$header_name] = trim($arr[1]);
3184 if ($header_name == 'set-cookie') {
3185 // TODO: allow multiple cookies from parseCookie
3186 $cookie = $this->parseCookie(trim($arr[1]));
3187 if ($cookie) {
3188 $this->incoming_cookies[] = $cookie;
3189 $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']);
3190 } else {
3191 $this->debug('did not find cookie in ' . trim($arr[1]));
3192 }
3193 }
3194 } else if (isset($header_name)) {
3195 // append continuation line to previous header
3196 $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line;
3197 }
3198 }
3199 }
3200
3201 $this->response_status_line = $header_array[0];
3202 $arr = explode(' ', $this->response_status_line, 3);
3203 $http_version = $arr[0];
3204 $http_status = intval($arr[1]);
3205 $http_reason = count($arr) > 2 ? $arr[2] : '';
3206
3207 // see if we need to resend the request with http digest authentication
3208 if (isset($this->incoming_headers['location']) && ($http_status == 301 || $http_status == 302)) {
3209 $this->debug("Got $http_status $http_reason with Location: " . $this->incoming_headers['location']);
3210 $this->setURL($this->incoming_headers['location']);
3211 $this->tryagain = true;
3212 return false;
3213 }
3214
3215 // see if we need to resend the request with http digest authentication
3216 if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) {
3217 $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']);
3218 if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) {
3219 $this->debug('Server wants digest authentication');
3220 // remove "Digest " from our elements
3221 $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']);
3222
3223 // parse elements into array
3224 $digestElements = explode(',', $digestString);
3225 foreach ($digestElements as $val) {
3226 $tempElement = explode('=', trim($val), 2);
3227 $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]);
3228 }
3229
3230 // should have (at least) qop, realm, nonce
3231 if (isset($digestRequest['nonce'])) {
3232 $this->setCredentials($this->username, $this->password, 'digest', $digestRequest);
3233 $this->tryagain = true;
3234 return false;
3235 }
3236 }
3237 $this->debug('HTTP authentication failed');
3238 $this->setError('HTTP authentication failed');
3239 return false;
3240 }
3241
3242 if (
3243 ($http_status >= 300 && $http_status <= 307) ||
3244 ($http_status >= 400 && $http_status <= 417) ||
3245 ($http_status >= 501 && $http_status <= 505)
3246 ) {
3247 $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)");
3248 return false;
3249 }
3250
3251 // decode content-encoding
3252 if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){
3253 if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){
3254 // if decoding works, use it. else assume data wasn't gzencoded
3255 if(function_exists('gzinflate')){
3256 //$timer->setMarker('starting decoding of gzip/deflated content');
3257 // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress)
3258 // this means there are no Zlib headers, although there should be
3259 $this->debug('The gzinflate function exists');
3260 $datalen = strlen($data);
3261 if ($this->incoming_headers['content-encoding'] == 'deflate') {
3262 if ($degzdata = @gzinflate($data)) {
3263 $data = $degzdata;
3264 $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes');
3265 if (strlen($data) < $datalen) {
3266 // test for the case that the payload has been compressed twice
3267 $this->debug('The inflated payload is smaller than the gzipped one; try again');
3268 if ($degzdata = @gzinflate($data)) {
3269 $data = $degzdata;
3270 $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes');
3271 }
3272 }
3273 } else {
3274 $this->debug('Error using gzinflate to inflate the payload');
3275 $this->setError('Error using gzinflate to inflate the payload');
3276 }
3277 } elseif ($this->incoming_headers['content-encoding'] == 'gzip') {
3278 if ($degzdata = @gzinflate(substr($data, 10))) { // do our best
3279 $data = $degzdata;
3280 $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes');
3281 if (strlen($data) < $datalen) {
3282 // test for the case that the payload has been compressed twice
3283 $this->debug('The un-gzipped payload is smaller than the gzipped one; try again');
3284 if ($degzdata = @gzinflate(substr($data, 10))) {
3285 $data = $degzdata;
3286 $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes');
3287 }
3288 }
3289 } else {
3290 $this->debug('Error using gzinflate to un-gzip the payload');
3291 $this->setError('Error using gzinflate to un-gzip the payload');
3292 }
3293 }
3294 //$timer->setMarker('finished decoding of gzip/deflated content');
3295 //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
3296 // set decoded payload
3297 $this->incoming_payload = $header_data.$lb.$lb.$data;
3298 } else {
3299 $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
3300 $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.');
3301 }
3302 } else {
3303 $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
3304 $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']);
3305 }
3306 } else {
3307 $this->debug('No Content-Encoding header');
3308 }
3309
3310 if(strlen($data) == 0){
3311 $this->debug('no data after headers!');
3312 $this->setError('no data present after HTTP headers');
3313 return false;
3314 }
3315
3316 return $data;
3317 }
3318
3319 /**
3320 * sets the content-type for the SOAP message to be sent
3321 *
3322 * @param string $type the content type, MIME style
3323 * @param mixed $charset character set used for encoding (or false)
3324 * @access public
3325 */
3326 function setContentType($type, $charset = false) {
3327 $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : ''));
3328 }
3329
3330 /**
3331 * specifies that an HTTP persistent connection should be used
3332 *
3333 * @return boolean whether the request was honored by this method.
3334 * @access public
3335 */
3336 function usePersistentConnection(){
3337 if (isset($this->outgoing_headers['Accept-Encoding'])) {
3338 return false;
3339 }
3340 $this->protocol_version = '1.1';
3341 $this->persistentConnection = true;
3342 $this->setHeader('Connection', 'Keep-Alive');
3343 return true;
3344 }
3345
3346 /**
3347 * parse an incoming Cookie into it's parts
3348 *
3349 * @param string $cookie_str content of cookie
3350 * @return array with data of that cookie
3351 * @access private
3352 */
3353 /*
3354 * TODO: allow a Set-Cookie string to be parsed into multiple cookies
3355 */
3356 function parseCookie($cookie_str) {
3357 $cookie_str = str_replace('; ', ';', $cookie_str) . ';';
3358 $data = preg_split('/;/', $cookie_str);
3359 $value_str = $data[0];
3360
3361 $cookie_param = 'domain=';
3362 $start = strpos($cookie_str, $cookie_param);
3363 if ($start > 0) {
3364 $domain = substr($cookie_str, $start + strlen($cookie_param));
3365 $domain = substr($domain, 0, strpos($domain, ';'));
3366 } else {
3367 $domain = '';
3368 }
3369
3370 $cookie_param = 'expires=';
3371 $start = strpos($cookie_str, $cookie_param);
3372 if ($start > 0) {
3373 $expires = substr($cookie_str, $start + strlen($cookie_param));
3374 $expires = substr($expires, 0, strpos($expires, ';'));
3375 } else {
3376 $expires = '';
3377 }
3378
3379 $cookie_param = 'path=';
3380 $start = strpos($cookie_str, $cookie_param);
3381 if ( $start > 0 ) {
3382 $path = substr($cookie_str, $start + strlen($cookie_param));
3383 $path = substr($path, 0, strpos($path, ';'));
3384 } else {
3385 $path = '/';
3386 }
3387
3388 $cookie_param = ';secure;';
3389 if (strpos($cookie_str, $cookie_param) !== FALSE) {
3390 $secure = true;
3391 } else {
3392 $secure = false;
3393 }
3394
3395 $sep_pos = strpos($value_str, '=');
3396
3397 if ($sep_pos) {
3398 $name = substr($value_str, 0, $sep_pos);
3399 $value = substr($value_str, $sep_pos + 1);
3400 $cookie= array( 'name' => $name,
3401 'value' => $value,
3402 'domain' => $domain,
3403 'path' => $path,
3404 'expires' => $expires,
3405 'secure' => $secure
3406 );
3407 return $cookie;
3408 }
3409 return false;
3410 }
3411
3412 /**
3413 * sort out cookies for the current request
3414 *
3415 * @param array $cookies array with all cookies
3416 * @param boolean $secure is the send-content secure or not?
3417 * @return string for Cookie-HTTP-Header
3418 * @access private
3419 */
3420 function getCookiesForRequest($cookies, $secure=false) {
3421 $cookie_str = '';
3422 if ((! is_null($cookies)) && (is_array($cookies))) {
3423 foreach ($cookies as $cookie) {
3424 if (! is_array($cookie)) {
3425 continue;
3426 }
3427 $this->debug("check cookie for validity: ".$cookie['name'].'='.$cookie['value']);
3428 if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
3429 if (strtotime($cookie['expires']) <= time()) {
3430 $this->debug('cookie has expired');
3431 continue;
3432 }
3433 }
3434 if ((isset($cookie['domain'])) && (! empty($cookie['domain']))) {
3435 $domain = preg_quote($cookie['domain']);
3436 if (! preg_match("'.*$domain$'i", $this->host)) {
3437 $this->debug('cookie has different domain');
3438 continue;
3439 }
3440 }
3441 if ((isset($cookie['path'])) && (! empty($cookie['path']))) {
3442 $path = preg_quote($cookie['path']);
3443 if (! preg_match("'^$path.*'i", $this->path)) {
3444 $this->debug('cookie is for a different path');
3445 continue;
3446 }
3447 }
3448 if ((! $secure) && (isset($cookie['secure'])) && ($cookie['secure'])) {
3449 $this->debug('cookie is secure, transport is not');
3450 continue;
3451 }
3452 $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; ';
3453 $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']);
3454 }
3455 }
3456 return $cookie_str;
3457 }
3458}
3459
3460?><?php
3461
3462
3463
3464/**
3465*
3466* nusoap_server allows the user to create a SOAP server
3467* that is capable of receiving messages and returning responses
3468*
3469* @author Dietrich Ayala <dietrich@ganx4.com>
3470* @author Scott Nichol <snichol@users.sourceforge.net>
3471* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
3472* @access public
3473*/
3474class nusoap_server extends nusoap_base {
3475 /**
3476 * HTTP headers of request
3477 * @var array
3478 * @access private
3479 */
3480 var $headers = array();
3481 /**
3482 * HTTP request
3483 * @var string
3484 * @access private
3485 */
3486 var $request = '';
3487 /**
3488 * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
3489 * @var string
3490 * @access public
3491 */
3492 var $requestHeaders = '';
3493 /**
3494 * SOAP Headers from request (parsed)
3495 * @var mixed
3496 * @access public
3497 */
3498 var $requestHeader = NULL;
3499 /**
3500 * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
3501 * @var string
3502 * @access public
3503 */
3504 var $document = '';
3505 /**
3506 * SOAP payload for request (text)
3507 * @var string
3508 * @access public
3509 */
3510 var $requestSOAP = '';
3511 /**
3512 * requested method namespace URI
3513 * @var string
3514 * @access private
3515 */
3516 var $methodURI = '';
3517 /**
3518 * name of method requested
3519 * @var string
3520 * @access private
3521 */
3522 var $methodname = '';
3523 /**
3524 * method parameters from request
3525 * @var array
3526 * @access private
3527 */
3528 var $methodparams = array();
3529 /**
3530 * SOAP Action from request
3531 * @var string
3532 * @access private
3533 */
3534 var $SOAPAction = '';
3535 /**
3536 * character set encoding of incoming (request) messages
3537 * @var string
3538 * @access public
3539 */
3540 var $xml_encoding = '';
3541 /**
3542 * toggles whether the parser decodes element content w/ utf8_decode()
3543 * @var boolean
3544 * @access public
3545 */
3546 var $decode_utf8 = true;
3547
3548 /**
3549 * HTTP headers of response
3550 * @var array
3551 * @access public
3552 */
3553 var $outgoing_headers = array();
3554 /**
3555 * HTTP response
3556 * @var string
3557 * @access private
3558 */
3559 var $response = '';
3560 /**
3561 * SOAP headers for response (text or array of soapval or associative array)
3562 * @var mixed
3563 * @access public
3564 */
3565 var $responseHeaders = '';
3566 /**
3567 * SOAP payload for response (text)
3568 * @var string
3569 * @access private
3570 */
3571 var $responseSOAP = '';
3572 /**
3573 * method return value to place in response
3574 * @var mixed
3575 * @access private
3576 */
3577 var $methodreturn = false;
3578 /**
3579 * whether $methodreturn is a string of literal XML
3580 * @var boolean
3581 * @access public
3582 */
3583 var $methodreturnisliteralxml = false;
3584 /**
3585 * SOAP fault for response (or false)
3586 * @var mixed
3587 * @access private
3588 */
3589 var $fault = false;
3590 /**
3591 * text indication of result (for debugging)
3592 * @var string
3593 * @access private
3594 */
3595 var $result = 'successful';
3596
3597 /**
3598 * assoc array of operations => opData; operations are added by the register()
3599 * method or by parsing an external WSDL definition
3600 * @var array
3601 * @access private
3602 */
3603 var $operations = array();
3604 /**
3605 * wsdl instance (if one)
3606 * @var mixed
3607 * @access private
3608 */
3609 var $wsdl = false;
3610 /**
3611 * URL for WSDL (if one)
3612 * @var mixed
3613 * @access private
3614 */
3615 var $externalWSDLURL = false;
3616 /**
3617 * whether to append debug to response as XML comment
3618 * @var boolean
3619 * @access public
3620 */
3621 var $debug_flag = false;
3622
3623
3624 /**
3625 * constructor
3626 * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
3627 *
3628 * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
3629 * @access public
3630 */
3631 function nusoap_server($wsdl=false){
3632 parent::nusoap_base();
3633 // turn on debugging?
3634 global $debug;
3635 global $HTTP_SERVER_VARS;
3636
3637 if (isset($_SERVER)) {
3638 $this->debug("_SERVER is defined:");
3639 $this->appendDebug($this->varDump($_SERVER));
3640 } elseif (isset($HTTP_SERVER_VARS)) {
3641 $this->debug("HTTP_SERVER_VARS is defined:");
3642 $this->appendDebug($this->varDump($HTTP_SERVER_VARS));
3643 } else {
3644 $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
3645 }
3646
3647 if (isset($debug)) {
3648 $this->debug("In nusoap_server, set debug_flag=$debug based on global flag");
3649 $this->debug_flag = $debug;
3650 } elseif (isset($_SERVER['QUERY_STRING'])) {
3651 $qs = explode('&', $_SERVER['QUERY_STRING']);
3652 foreach ($qs as $v) {
3653 if (substr($v, 0, 6) == 'debug=') {
3654 $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
3655 $this->debug_flag = substr($v, 6);
3656 }
3657 }
3658 } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3659 $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
3660 foreach ($qs as $v) {
3661 if (substr($v, 0, 6) == 'debug=') {
3662 $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
3663 $this->debug_flag = substr($v, 6);
3664 }
3665 }
3666 }
3667
3668 // wsdl
3669 if($wsdl){
3670 $this->debug("In nusoap_server, WSDL is specified");
3671 if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) {
3672 $this->wsdl = $wsdl;
3673 $this->externalWSDLURL = $this->wsdl->wsdl;
3674 $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
3675 } else {
3676 $this->debug('Create wsdl from ' . $wsdl);
3677 $this->wsdl = new wsdl($wsdl);
3678 $this->externalWSDLURL = $wsdl;
3679 }
3680 $this->appendDebug($this->wsdl->getDebug());
3681 $this->wsdl->clearDebug();
3682 if($err = $this->wsdl->getError()){
3683 die('WSDL ERROR: '.$err);
3684 }
3685 }
3686 }
3687
3688 /**
3689 * processes request and returns response
3690 *
3691 * @param string $data usually is the value of $HTTP_RAW_POST_DATA
3692 * @access public
3693 */
3694 function service($data){
3695 global $HTTP_SERVER_VARS;
3696
3697 if (isset($_SERVER['REQUEST_METHOD'])) {
3698 $rm = $_SERVER['REQUEST_METHOD'];
3699 } elseif (isset($HTTP_SERVER_VARS['REQUEST_METHOD'])) {
3700 $rm = $HTTP_SERVER_VARS['REQUEST_METHOD'];
3701 } else {
3702 $rm = '';
3703 }
3704
3705 if (isset($_SERVER['QUERY_STRING'])) {
3706 $qs = $_SERVER['QUERY_STRING'];
3707 } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
3708 $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
3709 } else {
3710 $qs = '';
3711 }
3712 $this->debug("In service, request method=$rm query string=$qs strlen(\$data)=" . strlen($data));
3713
3714 if ($rm == 'POST') {
3715 $this->debug("In service, invoke the request");
3716 $this->parse_request($data);
3717 if (! $this->fault) {
3718 $this->invoke_method();
3719 }
3720 if (! $this->fault) {
3721 $this->serialize_return();
3722 }
3723 $this->send_response();
3724 } elseif (preg_match('/wsdl/', $qs) ){
3725 $this->debug("In service, this is a request for WSDL");
3726 if ($this->externalWSDLURL){
3727 if (strpos($this->externalWSDLURL, "http://") !== false) { // assume URL
3728 $this->debug("In service, re-direct for WSDL");
3729 header('Location: '.$this->externalWSDLURL);
3730 } else { // assume file
3731 $this->debug("In service, use file passthru for WSDL");
3732 header("Content-Type: text/xml\r\n");
3733 $pos = strpos($this->externalWSDLURL, "file://");
3734 if ($pos === false) {
3735 $filename = $this->externalWSDLURL;
3736 } else {
3737 $filename = substr($this->externalWSDLURL, $pos + 7);
3738 }
3739 $fp = fopen($this->externalWSDLURL, 'r');
3740 fpassthru($fp);
3741 }
3742 } elseif ($this->wsdl) {
3743 $this->debug("In service, serialize WSDL");
3744 header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
3745 print $this->wsdl->serialize($this->debug_flag);
3746 if ($this->debug_flag) {
3747 $this->debug('wsdl:');
3748 $this->appendDebug($this->varDump($this->wsdl));
3749 print $this->getDebugAsXMLComment();
3750 }
3751 } else {
3752 $this->debug("In service, there is no WSDL");
3753 header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3754 print "This service does not provide WSDL";
3755 }
3756 } elseif ($this->wsdl) {
3757 $this->debug("In service, return Web description");
3758 print $this->wsdl->webDescription();
3759 } else {
3760 $this->debug("In service, no Web description");
3761 header("Content-Type: text/html; charset=ISO-8859-1\r\n");
3762 print "This service does not provide a Web description";
3763 }
3764 }
3765
3766 /**
3767 * parses HTTP request headers.
3768 *
3769 * The following fields are set by this function (when successful)
3770 *
3771 * headers
3772 * request
3773 * xml_encoding
3774 * SOAPAction
3775 *
3776 * @access private
3777 */
3778 function parse_http_headers() {
3779 global $HTTP_SERVER_VARS;
3780
3781 $this->request = '';
3782 $this->SOAPAction = '';
3783 if(function_exists('getallheaders')){
3784 $this->debug("In parse_http_headers, use getallheaders");
3785 $headers = getallheaders();
3786 foreach($headers as $k=>$v){
3787 $k = strtolower($k);
3788 $this->headers[$k] = $v;
3789 $this->request .= "$k: $v\r\n";
3790 $this->debug("$k: $v");
3791 }
3792 // get SOAPAction header
3793 if(isset($this->headers['soapaction'])){
3794 $this->SOAPAction = str_replace('"','',$this->headers['soapaction']);
3795 }
3796 // get the character encoding of the incoming request
3797 if(isset($this->headers['content-type']) && strpos($this->headers['content-type'],'=')){
3798 $enc = str_replace('"','',substr(strstr($this->headers["content-type"],'='),1));
3799 if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){
3800 $this->xml_encoding = strtoupper($enc);
3801 } else {
3802 $this->xml_encoding = 'US-ASCII';
3803 }
3804 } else {
3805 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3806 $this->xml_encoding = 'ISO-8859-1';
3807 }
3808 } elseif(isset($_SERVER) && is_array($_SERVER)){
3809 $this->debug("In parse_http_headers, use _SERVER");
3810 foreach ($_SERVER as $k => $v) {
3811 if (substr($k, 0, 5) == 'HTTP_') {
3812 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
3813 } else {
3814 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
3815 }
3816 if ($k == 'soapaction') {
3817 // get SOAPAction header
3818 $k = 'SOAPAction';
3819 $v = str_replace('"', '', $v);
3820 $v = str_replace('\\', '', $v);
3821 $this->SOAPAction = $v;
3822 } else if ($k == 'content-type') {
3823 // get the character encoding of the incoming request
3824 if (strpos($v, '=')) {
3825 $enc = substr(strstr($v, '='), 1);
3826 $enc = str_replace('"', '', $enc);
3827 $enc = str_replace('\\', '', $enc);
3828 if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)) {
3829 $this->xml_encoding = strtoupper($enc);
3830 } else {
3831 $this->xml_encoding = 'US-ASCII';
3832 }
3833 } else {
3834 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3835 $this->xml_encoding = 'ISO-8859-1';
3836 }
3837 }
3838 $this->headers[$k] = $v;
3839 $this->request .= "$k: $v\r\n";
3840 $this->debug("$k: $v");
3841 }
3842 } elseif (is_array($HTTP_SERVER_VARS)) {
3843 $this->debug("In parse_http_headers, use HTTP_SERVER_VARS");
3844 foreach ($HTTP_SERVER_VARS as $k => $v) {
3845 if (substr($k, 0, 5) == 'HTTP_') {
3846 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); $k = strtolower(substr($k, 5));
3847 } else {
3848 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); $k = strtolower($k);
3849 }
3850 if ($k == 'soapaction') {
3851 // get SOAPAction header
3852 $k = 'SOAPAction';
3853 $v = str_replace('"', '', $v);
3854 $v = str_replace('\\', '', $v);
3855 $this->SOAPAction = $v;
3856 } else if ($k == 'content-type') {
3857 // get the character encoding of the incoming request
3858 if (strpos($v, '=')) {
3859 $enc = substr(strstr($v, '='), 1);
3860 $enc = str_replace('"', '', $enc);
3861 $enc = str_replace('\\', '', $enc);
3862 if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)) {
3863 $this->xml_encoding = strtoupper($enc);
3864 } else {
3865 $this->xml_encoding = 'US-ASCII';
3866 }
3867 } else {
3868 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
3869 $this->xml_encoding = 'ISO-8859-1';
3870 }
3871 }
3872 $this->headers[$k] = $v;
3873 $this->request .= "$k: $v\r\n";
3874 $this->debug("$k: $v");
3875 }
3876 } else {
3877 $this->debug("In parse_http_headers, HTTP headers not accessible");
3878 $this->setError("HTTP headers not accessible");
3879 }
3880 }
3881
3882 /**
3883 * parses a request
3884 *
3885 * The following fields are set by this function (when successful)
3886 *
3887 * headers
3888 * request
3889 * xml_encoding
3890 * SOAPAction
3891 * request
3892 * requestSOAP
3893 * methodURI
3894 * methodname
3895 * methodparams
3896 * requestHeaders
3897 * document
3898 *
3899 * This sets the fault field on error
3900 *
3901 * @param string $data XML string
3902 * @access private
3903 */
3904 function parse_request($data='') {
3905 $this->debug('entering parse_request()');
3906 $this->parse_http_headers();
3907 $this->debug('got character encoding: '.$this->xml_encoding);
3908 // uncompress if necessary
3909 if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') {
3910 $this->debug('got content encoding: ' . $this->headers['content-encoding']);
3911 if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') {
3912 // if decoding works, use it. else assume data wasn't gzencoded
3913 if (function_exists('gzuncompress')) {
3914 if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) {
3915 $data = $degzdata;
3916 } elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
3917 $data = $degzdata;
3918 } else {
3919 $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data');
3920 return;
3921 }
3922 } else {
3923 $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data');
3924 return;
3925 }
3926 }
3927 }
3928 $this->request .= "\r\n".$data;
3929 $data = $this->parseRequest($this->headers, $data);
3930 $this->requestSOAP = $data;
3931 $this->debug('leaving parse_request');
3932 }
3933
3934 /**
3935 * invokes a PHP function for the requested SOAP method
3936 *
3937 * The following fields are set by this function (when successful)
3938 *
3939 * methodreturn
3940 *
3941 * Note that the PHP function that is called may also set the following
3942 * fields to affect the response sent to the client
3943 *
3944 * responseHeaders
3945 * outgoing_headers
3946 *
3947 * This sets the fault field on error
3948 *
3949 * @access private
3950 */
3951 function invoke_method() {
3952 $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);
3953
3954 //
3955 // if you are debugging in this area of the code, your service uses a class to implement methods,
3956 // you use SOAP RPC, and the client is .NET, please be aware of the following...
3957 // when the .NET wsdl.exe utility generates a proxy, it will remove the '.' or '..' from the
3958 // method name. that is fine for naming the .NET methods. it is not fine for properly constructing
3959 // the XML request and reading the XML response. you need to add the RequestElementName and
3960 // ResponseElementName to the System.Web.Services.Protocols.SoapRpcMethodAttribute that wsdl.exe
3961 // generates for the method. these parameters are used to specify the correct XML element names
3962 // for .NET to use, i.e. the names with the '.' in them.
3963 //
3964 $orig_methodname = $this->methodname;
3965 if ($this->wsdl) {
3966 if ($this->opData = $this->wsdl->getOperationData($this->methodname)) {
3967 $this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
3968 $this->appendDebug('opData=' . $this->varDump($this->opData));
3969 } elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) {
3970 // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
3971 $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
3972 $this->appendDebug('opData=' . $this->varDump($this->opData));
3973 $this->methodname = $this->opData['name'];
3974 } else {
3975 $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
3976 $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
3977 return;
3978 }
3979 } else {
3980 $this->debug('in invoke_method, no WSDL to validate method');
3981 }
3982
3983 // if a . is present in $this->methodname, we see if there is a class in scope,
3984 // which could be referred to. We will also distinguish between two deliminators,
3985 // to allow methods to be called a the class or an instance
3986 if (strpos($this->methodname, '..') > 0) {
3987 $delim = '..';
3988 } else if (strpos($this->methodname, '.') > 0) {
3989 $delim = '.';
3990 } else {
3991 $delim = '';
3992 }
3993 $this->debug("in invoke_method, delim=$delim");
3994
3995 $class = '';
3996 $method = '';
3997 if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1) {
3998 $try_class = substr($this->methodname, 0, strpos($this->methodname, $delim));
3999 if (class_exists($try_class)) {
4000 // get the class and method name
4001 $class = $try_class;
4002 $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim));
4003 $this->debug("in invoke_method, class=$class method=$method delim=$delim");
4004 } else {
4005 $this->debug("in invoke_method, class=$try_class not found");
4006 }
4007 } else {
4008 $try_class = '';
4009 $this->debug("in invoke_method, no class to try");
4010 }
4011
4012 // does method exist?
4013 if ($class == '') {
4014 if (!function_exists($this->methodname)) {
4015 $this->debug("in invoke_method, function '$this->methodname' not found!");
4016 $this->result = 'fault: method not found';
4017 $this->fault('SOAP-ENV:Client',"method '$this->methodname'('$orig_methodname') not defined in service('$try_class' '$delim')");
4018 return;
4019 }
4020 } else {
4021 $method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method;
4022 if (!in_array($method_to_compare, get_class_methods($class))) {
4023 $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
4024 $this->result = 'fault: method not found';
4025 $this->fault('SOAP-ENV:Client',"method '$this->methodname'/'$method_to_compare'('$orig_methodname') not defined in service/'$class'('$try_class' '$delim')");
4026 return;
4027 }
4028 }
4029
4030 // evaluate message, getting back parameters
4031 // verify that request parameters match the method's signature
4032 if(! $this->verify_method($this->methodname,$this->methodparams)){
4033 // debug
4034 $this->debug('ERROR: request not verified against method signature');
4035 $this->result = 'fault: request failed validation against method signature';
4036 // return fault
4037 $this->fault('SOAP-ENV:Client',"Operation '$this->methodname' not defined in service.");
4038 return;
4039 }
4040
4041 // if there are parameters to pass
4042 $this->debug('in invoke_method, params:');
4043 $this->appendDebug($this->varDump($this->methodparams));
4044 $this->debug("in invoke_method, calling '$this->methodname'");
4045 if (!function_exists('call_user_func_array')) {
4046 if ($class == '') {
4047 $this->debug('in invoke_method, calling function using eval()');
4048 $funcCall = "\$this->methodreturn = $this->methodname(";
4049 } else {
4050 if ($delim == '..') {
4051 $this->debug('in invoke_method, calling class method using eval()');
4052 $funcCall = "\$this->methodreturn = ".$class."::".$method."(";
4053 } else {
4054 $this->debug('in invoke_method, calling instance method using eval()');
4055 // generate unique instance name
4056 $instname = "\$inst_".time();
4057 $funcCall = $instname." = new ".$class."(); ";
4058 $funcCall .= "\$this->methodreturn = ".$instname."->".$method."(";
4059 }
4060 }
4061 if ($this->methodparams) {
4062 foreach ($this->methodparams as $param) {
4063 if (is_array($param) || is_object($param)) {
4064 $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
4065 return;
4066 }
4067 $funcCall .= "\"$param\",";
4068 }
4069 $funcCall = substr($funcCall, 0, -1);
4070 }
4071 $funcCall .= ');';
4072 $this->debug('in invoke_method, function call: '.$funcCall);
4073 @eval($funcCall);
4074 } else {
4075 if ($class == '') {
4076 $this->debug('in invoke_method, calling function using call_user_func_array()');
4077 $call_arg = "$this->methodname"; // straight assignment changes $this->methodname to lower case after call_user_func_array()
4078 } elseif ($delim == '..') {
4079 $this->debug('in invoke_method, calling class method using call_user_func_array()');
4080 $call_arg = array ($class, $method);
4081 } else {
4082 $this->debug('in invoke_method, calling instance method using call_user_func_array()');
4083 $instance = new $class ();
4084 $call_arg = array(&$instance, $method);
4085 }
4086 if (is_array($this->methodparams)) {
4087 $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams));
4088 } else {
4089 $this->methodreturn = call_user_func_array($call_arg, array());
4090 }
4091 }
4092 $this->debug('in invoke_method, methodreturn:');
4093 $this->appendDebug($this->varDump($this->methodreturn));
4094 $this->debug("in invoke_method, called method $this->methodname, received data of type ".gettype($this->methodreturn));
4095 }
4096
4097 /**
4098 * serializes the return value from a PHP function into a full SOAP Envelope
4099 *
4100 * The following fields are set by this function (when successful)
4101 *
4102 * responseSOAP
4103 *
4104 * This sets the fault field on error
4105 *
4106 * @access private
4107 */
4108 function serialize_return() {
4109 $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
4110 // if fault
4111 if (isset($this->methodreturn) && is_object($this->methodreturn) && ((get_class($this->methodreturn) == 'soap_fault') || (get_class($this->methodreturn) == 'nusoap_fault'))) {
4112 $this->debug('got a fault object from method');
4113 $this->fault = $this->methodreturn;
4114 return;
4115 } elseif ($this->methodreturnisliteralxml) {
4116 $return_val = $this->methodreturn;
4117 // returned value(s)
4118 } else {
4119 $this->debug('got a(n) '.gettype($this->methodreturn).' from method');
4120 $this->debug('serializing return value');
4121 if($this->wsdl){
4122 if (sizeof($this->opData['output']['parts']) > 1) {
4123 $this->debug('more than one output part, so use the method return unchanged');
4124 $opParams = $this->methodreturn;
4125 } elseif (sizeof($this->opData['output']['parts']) == 1) {
4126 $this->debug('exactly one output part, so wrap the method return in a simple array');
4127 // TODO: verify that it is not already wrapped!
4128 //foreach ($this->opData['output']['parts'] as $name => $type) {
4129 // $this->debug('wrap in element named ' . $name);
4130 //}
4131 $opParams = array($this->methodreturn);
4132 }
4133 $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
4134 $this->appendDebug($this->wsdl->getDebug());
4135 $this->wsdl->clearDebug();
4136 if($errstr = $this->wsdl->getError()){
4137 $this->debug('got wsdl error: '.$errstr);
4138 $this->fault('SOAP-ENV:Server', 'unable to serialize result');
4139 return;
4140 }
4141 } else {
4142 if (isset($this->methodreturn)) {
4143 $return_val = $this->serialize_val($this->methodreturn, 'return');
4144 } else {
4145 $return_val = '';
4146 $this->debug('in absence of WSDL, assume void return for backward compatibility');
4147 }
4148 }
4149 }
4150 $this->debug('return value:');
4151 $this->appendDebug($this->varDump($return_val));
4152
4153 $this->debug('serializing response');
4154 if ($this->wsdl) {
4155 $this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
4156 if ($this->opData['style'] == 'rpc') {
4157 $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
4158 if ($this->opData['output']['use'] == 'literal') {
4159 // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace
4160 if ($this->methodURI) {
4161 $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
4162 } else {
4163 $payload = '<'.$this->methodname.'Response>'.$return_val.'</'.$this->methodname.'Response>';
4164 }
4165 } else {
4166 if ($this->methodURI) {
4167 $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
4168 } else {
4169 $payload = '<'.$this->methodname.'Response>'.$return_val.'</'.$this->methodname.'Response>';
4170 }
4171 }
4172 } else {
4173 $this->debug('style is not rpc for serialization: assume document');
4174 $payload = $return_val;
4175 }
4176 } else {
4177 $this->debug('do not have WSDL for serialization: assume rpc/encoded');
4178 $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>";
4179 }
4180 $this->result = 'successful';
4181 if($this->wsdl){
4182 //if($this->debug_flag){
4183 $this->appendDebug($this->wsdl->getDebug());
4184 // }
4185 if (isset($this->opData['output']['encodingStyle'])) {
4186 $encodingStyle = $this->opData['output']['encodingStyle'];
4187 } else {
4188 $encodingStyle = '';
4189 }
4190 // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
4191 $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style'],$this->opData['output']['use'],$encodingStyle);
4192 } else {
4193 $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders);
4194 }
4195 $this->debug("Leaving serialize_return");
4196 }
4197
4198 /**
4199 * sends an HTTP response
4200 *
4201 * The following fields are set by this function (when successful)
4202 *
4203 * outgoing_headers
4204 * response
4205 *
4206 * @access private
4207 */
4208 function send_response() {
4209 $this->debug('Enter send_response');
4210 if ($this->fault) {
4211 $payload = $this->fault->serialize();
4212 $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
4213 $this->outgoing_headers[] = "Status: 500 Internal Server Error";
4214 } else {
4215 $payload = $this->responseSOAP;
4216 // Some combinations of PHP+Web server allow the Status
4217 // to come through as a header. Since OK is the default
4218 // just do nothing.
4219 // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
4220 // $this->outgoing_headers[] = "Status: 200 OK";
4221 }
4222 // add debug data if in debug mode
4223 if(isset($this->debug_flag) && $this->debug_flag){
4224 $payload .= $this->getDebugAsXMLComment();
4225 }
4226 $this->outgoing_headers[] = "Server: $this->title Server v$this->version";
4227 preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
4228 $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")";
4229 // Let the Web server decide about this
4230 //$this->outgoing_headers[] = "Connection: Close\r\n";
4231 $payload = $this->getHTTPBody($payload);
4232 $type = $this->getHTTPContentType();
4233 $charset = $this->getHTTPContentTypeCharset();
4234 $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
4235 //begin code to compress payload - by John
4236 // NOTE: there is no way to know whether the Web server will also compress
4237 // this data.
4238 if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {
4239 if (strstr($this->headers['accept-encoding'], 'gzip')) {
4240 if (function_exists('gzencode')) {
4241 if (isset($this->debug_flag) && $this->debug_flag) {
4242 $payload .= "<!-- Content being gzipped -->";
4243 }
4244 $this->outgoing_headers[] = "Content-Encoding: gzip";
4245 $payload = gzencode($payload);
4246 } else {
4247 if (isset($this->debug_flag) && $this->debug_flag) {
4248 $payload .= "<!-- Content will not be gzipped: no gzencode -->";
4249 }
4250 }
4251 } elseif (strstr($this->headers['accept-encoding'], 'deflate')) {
4252 // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
4253 // instead of gzcompress output,
4254 // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
4255 if (function_exists('gzdeflate')) {
4256 if (isset($this->debug_flag) && $this->debug_flag) {
4257 $payload .= "<!-- Content being deflated -->";
4258 }
4259 $this->outgoing_headers[] = "Content-Encoding: deflate";
4260 $payload = gzdeflate($payload);
4261 } else {
4262 if (isset($this->debug_flag) && $this->debug_flag) {
4263 $payload .= "<!-- Content will not be deflated: no gzcompress -->";
4264 }
4265 }
4266 }
4267 }
4268 //end code
4269 $this->outgoing_headers[] = "Content-Length: ".strlen($payload);
4270 reset($this->outgoing_headers);
4271 foreach($this->outgoing_headers as $hdr){
4272 header($hdr, false);
4273 }
4274 print $payload;
4275 $this->response = join("\r\n",$this->outgoing_headers)."\r\n\r\n".$payload;
4276 }
4277
4278 /**
4279 * takes the value that was created by parsing the request
4280 * and compares to the method's signature, if available.
4281 *
4282 * @param string $operation The operation to be invoked
4283 * @param array $request The array of parameter values
4284 * @return boolean Whether the operation was found
4285 * @access private
4286 */
4287 function verify_method($operation,$request){
4288 if(isset($this->wsdl) && is_object($this->wsdl)){
4289 if($this->wsdl->getOperationData($operation)){
4290 return true;
4291 }
4292 } elseif(isset($this->operations[$operation])){
4293 return true;
4294 }
4295 return false;
4296 }
4297
4298 /**
4299 * processes SOAP message received from client
4300 *
4301 * @param array $headers The HTTP headers
4302 * @param string $data unprocessed request data from client
4303 * @return mixed value of the message, decoded into a PHP type
4304 * @access private
4305 */
4306 function parseRequest($headers, $data) {
4307 $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' headers:');
4308 $this->appendDebug($this->varDump($headers));
4309 if (!isset($headers['content-type'])) {
4310 $this->setError('Request not of type text/xml (no content-type header)');
4311 return false;
4312 }
4313 if (!strstr($headers['content-type'], 'text/xml')) {
4314 $this->setError('Request not of type text/xml');
4315 return false;
4316 }
4317 if (strpos($headers['content-type'], '=')) {
4318 $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
4319 $this->debug('Got response encoding: ' . $enc);
4320 if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){
4321 $this->xml_encoding = strtoupper($enc);
4322 } else {
4323 $this->xml_encoding = 'US-ASCII';
4324 }
4325 } else {
4326 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
4327 $this->xml_encoding = 'ISO-8859-1';
4328 }
4329 $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
4330 // parse response, get soap parser obj
4331 $parser = new nusoap_parser($data,$this->xml_encoding,'',$this->decode_utf8);
4332 // parser debug
4333 $this->debug("parser debug: \n".$parser->getDebug());
4334 // if fault occurred during message parsing
4335 if($err = $parser->getError()){
4336 $this->result = 'fault: error in msg parsing: '.$err;
4337 $this->fault('SOAP-ENV:Client',"error in msg parsing:\n".$err);
4338 // else successfully parsed request into soapval object
4339 } else {
4340 // get/set methodname
4341 $this->methodURI = $parser->root_struct_namespace;
4342 $this->methodname = $parser->root_struct_name;
4343 $this->debug('methodname: '.$this->methodname.' methodURI: '.$this->methodURI);
4344 $this->debug('calling parser->get_soapbody()');
4345 $this->methodparams = $parser->get_soapbody();
4346 // get SOAP headers
4347 $this->requestHeaders = $parser->getHeaders();
4348 // get SOAP Header
4349 $this->requestHeader = $parser->get_soapheader();
4350 // add document for doclit support
4351 $this->document = $parser->document;
4352 }
4353 }
4354
4355 /**
4356 * gets the HTTP body for the current response.
4357 *
4358 * @param string $soapmsg The SOAP payload
4359 * @return string The HTTP body, which includes the SOAP payload
4360 * @access private
4361 */
4362 function getHTTPBody($soapmsg) {
4363 return $soapmsg;
4364 }
4365
4366 /**
4367 * gets the HTTP content type for the current response.
4368 *
4369 * Note: getHTTPBody must be called before this.
4370 *
4371 * @return string the HTTP content type for the current response.
4372 * @access private
4373 */
4374 function getHTTPContentType() {
4375 return 'text/xml';
4376 }
4377
4378 /**
4379 * gets the HTTP content type charset for the current response.
4380 * returns false for non-text content types.
4381 *
4382 * Note: getHTTPBody must be called before this.
4383 *
4384 * @return string the HTTP content type charset for the current response.
4385 * @access private
4386 */
4387 function getHTTPContentTypeCharset() {
4388 return $this->soap_defencoding;
4389 }
4390
4391 /**
4392 * add a method to the dispatch map (this has been replaced by the register method)
4393 *
4394 * @param string $methodname
4395 * @param string $in array of input values
4396 * @param string $out array of output values
4397 * @access public
4398 * @deprecated
4399 */
4400 function add_to_map($methodname,$in,$out){
4401 $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
4402 }
4403
4404 /**
4405 * register a service function with the server
4406 *
4407 * @param string $name the name of the PHP function, class.method or class..method
4408 * @param array $in assoc array of input values: key = param name, value = param type
4409 * @param array $out assoc array of output values: key = param name, value = param type
4410 * @param mixed $namespace the element namespace for the method or false
4411 * @param mixed $soapaction the soapaction for the method or false
4412 * @param mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
4413 * @param mixed $use optional (encoded|literal) or false
4414 * @param string $documentation optional Description to include in WSDL
4415 * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
4416 * @access public
4417 */
4418 function register($name,$in=array(),$out=array(),$namespace=false,$soapaction=false,$style=false,$use=false,$documentation='',$encodingStyle=''){
4419 global $HTTP_SERVER_VARS;
4420
4421 if($this->externalWSDLURL){
4422 die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
4423 }
4424 if (! $name) {
4425 die('You must specify a name when you register an operation');
4426 }
4427 if (!is_array($in)) {
4428 die('You must provide an array for operation inputs');
4429 }
4430 if (!is_array($out)) {
4431 die('You must provide an array for operation outputs');
4432 }
4433 if(false == $namespace) {
4434 }
4435 if(false == $soapaction) {
4436 if (isset($_SERVER)) {
4437 $SERVER_NAME = $_SERVER['SERVER_NAME'];
4438 $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4439 $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
4440 } elseif (isset($HTTP_SERVER_VARS)) {
4441 $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
4442 $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
4443 $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
4444 } else {
4445 $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
4446 }
4447 if ($HTTPS == '1' || $HTTPS == 'on') {
4448 $SCHEME = 'https';
4449 } else {
4450 $SCHEME = 'http';
4451 }
4452 $soapaction = "$SCHEME://$SERVER_NAME$SCRIPT_NAME/$name";
4453 }
4454 if(false == $style) {
4455 $style = "rpc";
4456 }
4457 if(false == $use) {
4458 $use = "encoded";
4459 }
4460 if ($use == 'encoded' && $encodingStyle == '') {
4461 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
4462 }
4463
4464 $this->operations[$name] = array(
4465 'name' => $name,
4466 'in' => $in,
4467 'out' => $out,
4468 'namespace' => $namespace,
4469 'soapaction' => $soapaction,
4470 'style' => $style);
4471 if($this->wsdl){
4472 $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use,$documentation,$encodingStyle);
4473 }
4474 return true;
4475 }
4476
4477 /**
4478 * Specify a fault to be returned to the client.
4479 * This also acts as a flag to the server that a fault has occured.
4480 *
4481 * @param string $faultcode
4482 * @param string $faultstring
4483 * @param string $faultactor
4484 * @param string $faultdetail
4485 * @access public
4486 */
4487 function fault($faultcode,$faultstring,$faultactor='',$faultdetail=''){
4488 if ($faultdetail == '' && $this->debug_flag) {
4489 $faultdetail = $this->getDebug();
4490 }
4491 $this->fault = new nusoap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
4492 $this->fault->soap_defencoding = $this->soap_defencoding;
4493 }
4494
4495 /**
4496 * Sets up wsdl object.
4497 * Acts as a flag to enable internal WSDL generation
4498 *
4499 * @param string $serviceName, name of the service
4500 * @param mixed $namespace optional 'tns' service namespace or false
4501 * @param mixed $endpoint optional URL of service endpoint or false
4502 * @param string $style optional (rpc|document) WSDL style (also specified by operation)
4503 * @param string $transport optional SOAP transport
4504 * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
4505 */
4506 function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
4507 {
4508 global $HTTP_SERVER_VARS;
4509
4510 if (isset($_SERVER)) {
4511 $SERVER_NAME = $_SERVER['SERVER_NAME'];
4512 $SERVER_PORT = $_SERVER['SERVER_PORT'];
4513 $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
4514 $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
4515 } elseif (isset($HTTP_SERVER_VARS)) {
4516 $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
4517 $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT'];
4518 $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
4519 $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
4520 } else {
4521 $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
4522 }
4523 // If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI)
4524 $colon = strpos($SERVER_NAME,":");
4525 if ($colon) {
4526 $SERVER_NAME = substr($SERVER_NAME, 0, $colon);
4527 }
4528 if ($SERVER_PORT == 80) {
4529 $SERVER_PORT = '';
4530 } else {
4531 $SERVER_PORT = ':' . $SERVER_PORT;
4532 }
4533 if(false == $namespace) {
4534 $namespace = "http://$SERVER_NAME/soap/$serviceName";
4535 }
4536
4537 if(false == $endpoint) {
4538 if ($HTTPS == '1' || $HTTPS == 'on') {
4539 $SCHEME = 'https';
4540 } else {
4541 $SCHEME = 'http';
4542 }
4543 $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME";
4544 }
4545
4546 if(false == $schemaTargetNamespace) {
4547 $schemaTargetNamespace = $namespace;
4548 }
4549
4550 $this->wsdl = new wsdl;
4551 $this->wsdl->serviceName = $serviceName;
4552 $this->wsdl->endpoint = $endpoint;
4553 $this->wsdl->namespaces['tns'] = $namespace;
4554 $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
4555 $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
4556 if ($schemaTargetNamespace != $namespace) {
4557 $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
4558 }
4559 $this->wsdl->schemas[$schemaTargetNamespace][0] = new nusoap_xmlschema('', '', $this->wsdl->namespaces);
4560 if ($style == 'document') {
4561 $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified';
4562 }
4563 $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
4564 $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true);
4565 $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true);
4566 $this->wsdl->bindings[$serviceName.'Binding'] = array(
4567 'name'=>$serviceName.'Binding',
4568 'style'=>$style,
4569 'transport'=>$transport,
4570 'portType'=>$serviceName.'PortType');
4571 $this->wsdl->ports[$serviceName.'Port'] = array(
4572 'binding'=>$serviceName.'Binding',
4573 'location'=>$endpoint,
4574 'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');
4575 }
4576}
4577
4578/**
4579 * Backward compatibility
4580 */
4581class soap_server extends nusoap_server {
4582}
4583
4584?><?php
4585
4586
4587
4588/**
4589* parses a WSDL file, allows access to it's data, other utility methods.
4590* also builds WSDL structures programmatically.
4591*
4592* @author Dietrich Ayala <dietrich@ganx4.com>
4593* @author Scott Nichol <snichol@users.sourceforge.net>
4594* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
4595* @access public
4596*/
4597class wsdl extends nusoap_base {
4598 // URL or filename of the root of this WSDL
4599 var $wsdl;
4600 // define internal arrays of bindings, ports, operations, messages, etc.
4601 var $schemas = array();
4602 var $currentSchema;
4603 var $message = array();
4604 var $complexTypes = array();
4605 var $messages = array();
4606 var $currentMessage;
4607 var $currentOperation;
4608 var $portTypes = array();
4609 var $currentPortType;
4610 var $bindings = array();
4611 var $currentBinding;
4612 var $ports = array();
4613 var $currentPort;
4614 var $opData = array();
4615 var $status = '';
4616 var $documentation = false;
4617 var $endpoint = '';
4618 // array of wsdl docs to import
4619 var $import = array();
4620 // parser vars
4621 var $parser;
4622 var $position = 0;
4623 var $depth = 0;
4624 var $depth_array = array();
4625 // for getting wsdl
4626 var $proxyhost = '';
4627 var $proxyport = '';
4628 var $proxyusername = '';
4629 var $proxypassword = '';
4630 var $timeout = 0;
4631 var $response_timeout = 30;
4632 var $curl_options = array(); // User-specified cURL options
4633 var $use_curl = false; // whether to always try to use cURL
4634 // for HTTP authentication
4635 var $username = ''; // Username for HTTP authentication
4636 var $password = ''; // Password for HTTP authentication
4637 var $authtype = ''; // Type of HTTP authentication
4638 var $certRequest = array(); // Certificate for HTTP SSL authentication
4639
4640 /**
4641 * constructor
4642 *
4643 * @param string $wsdl WSDL document URL
4644 * @param string $proxyhost
4645 * @param string $proxyport
4646 * @param string $proxyusername
4647 * @param string $proxypassword
4648 * @param integer $timeout set the connection timeout
4649 * @param integer $response_timeout set the response timeout
4650 * @param array $curl_options user-specified cURL options
4651 * @param boolean $use_curl try to use cURL
4652 * @access public
4653 */
4654 function wsdl($wsdl = '',$proxyhost=false,$proxyport=false,$proxyusername=false,$proxypassword=false,$timeout=0,$response_timeout=30,$curl_options=null,$use_curl=false){
4655 parent::nusoap_base();
4656 $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
4657 $this->proxyhost = $proxyhost;
4658 $this->proxyport = $proxyport;
4659 $this->proxyusername = $proxyusername;
4660 $this->proxypassword = $proxypassword;
4661 $this->timeout = $timeout;
4662 $this->response_timeout = $response_timeout;
4663 if (is_array($curl_options))
4664 $this->curl_options = $curl_options;
4665 $this->use_curl = $use_curl;
4666 $this->fetchWSDL($wsdl);
4667 }
4668
4669 /**
4670 * fetches the WSDL document and parses it
4671 *
4672 * @access public
4673 */
4674 function fetchWSDL($wsdl) {
4675 $this->debug("parse and process WSDL path=$wsdl");
4676 $this->wsdl = $wsdl;
4677 // parse wsdl file
4678 if ($this->wsdl != "") {
4679 $this->parseWSDL($this->wsdl);
4680 }
4681 // imports
4682 // TODO: handle imports more properly, grabbing them in-line and nesting them
4683 $imported_urls = array();
4684 $imported = 1;
4685 while ($imported > 0) {
4686 $imported = 0;
4687 // Schema imports
4688 foreach ($this->schemas as $ns => $list) {
4689 foreach ($list as $xs) {
4690 $wsdlparts = parse_url($this->wsdl); // this is bogusly simple!
4691 foreach ($xs->imports as $ns2 => $list2) {
4692 for ($ii = 0; $ii < count($list2); $ii++) {
4693 if (! $list2[$ii]['loaded']) {
4694 $this->schemas[$ns]->imports[$ns2][$ii]['loaded'] = true;
4695 $url = $list2[$ii]['location'];
4696 if ($url != '') {
4697 $urlparts = parse_url($url);
4698 if (!isset($urlparts['host'])) {
4699 $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' .$wsdlparts['port'] : '') .
4700 substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
4701 }
4702 if (! in_array($url, $imported_urls)) {
4703 $this->parseWSDL($url);
4704 $imported++;
4705 $imported_urls[] = $url;
4706 }
4707 } else {
4708 $this->debug("Unexpected scenario: empty URL for unloaded import");
4709 }
4710 }
4711 }
4712 }
4713 }
4714 }
4715 // WSDL imports
4716 $wsdlparts = parse_url($this->wsdl); // this is bogusly simple!
4717 foreach ($this->import as $ns => $list) {
4718 for ($ii = 0; $ii < count($list); $ii++) {
4719 if (! $list[$ii]['loaded']) {
4720 $this->import[$ns][$ii]['loaded'] = true;
4721 $url = $list[$ii]['location'];
4722 if ($url != '') {
4723 $urlparts = parse_url($url);
4724 if (!isset($urlparts['host'])) {
4725 $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') .
4726 substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path'];
4727 }
4728 if (! in_array($url, $imported_urls)) {
4729 $this->parseWSDL($url);
4730 $imported++;
4731 $imported_urls[] = $url;
4732 }
4733 } else {
4734 $this->debug("Unexpected scenario: empty URL for unloaded import");
4735 }
4736 }
4737 }
4738 }
4739 }
4740 // add new data to operation data
4741 foreach($this->bindings as $binding => $bindingData) {
4742 if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
4743 foreach($bindingData['operations'] as $operation => $data) {
4744 $this->debug('post-parse data gathering for ' . $operation);
4745 $this->bindings[$binding]['operations'][$operation]['input'] =
4746 isset($this->bindings[$binding]['operations'][$operation]['input']) ?
4747 array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) :
4748 $this->portTypes[ $bindingData['portType'] ][$operation]['input'];
4749 $this->bindings[$binding]['operations'][$operation]['output'] =
4750 isset($this->bindings[$binding]['operations'][$operation]['output']) ?
4751 array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) :
4752 $this->portTypes[ $bindingData['portType'] ][$operation]['output'];
4753 if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){
4754 $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ];
4755 }
4756 if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){
4757 $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ];
4758 }
4759 // Set operation style if necessary, but do not override one already provided
4760 if (isset($bindingData['style']) && !isset($this->bindings[$binding]['operations'][$operation]['style'])) {
4761 $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
4762 }
4763 $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
4764 $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : '';
4765 $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
4766 }
4767 }
4768 }
4769 }
4770
4771 /**
4772 * parses the wsdl document
4773 *
4774 * @param string $wsdl path or URL
4775 * @access private
4776 */
4777 function parseWSDL($wsdl = '') {
4778 $this->debug("parse WSDL at path=$wsdl");
4779
4780 if ($wsdl == '') {
4781 $this->debug('no wsdl passed to parseWSDL()!!');
4782 $this->setError('no wsdl passed to parseWSDL()!!');
4783 return false;
4784 }
4785
4786 // parse $wsdl for url format
4787 $wsdl_props = parse_url($wsdl);
4788
4789 if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' || $wsdl_props['scheme'] == 'https')) {
4790 $this->debug('getting WSDL http(s) URL ' . $wsdl);
4791 // get wsdl
4792 $tr = new soap_transport_http($wsdl, $this->curl_options, $this->use_curl);
4793 $tr->request_method = 'GET';
4794 $tr->useSOAPAction = false;
4795 if($this->proxyhost && $this->proxyport){
4796 $tr->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
4797 }
4798 if ($this->authtype != '') {
4799 $tr->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
4800 }
4801 $tr->setEncoding('gzip, deflate');
4802 $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout);
4803 //$this->debug("WSDL request\n" . $tr->outgoing_payload);
4804 //$this->debug("WSDL response\n" . $tr->incoming_payload);
4805 $this->appendDebug($tr->getDebug());
4806 // catch errors
4807 if($err = $tr->getError() ){
4808 $errstr = 'Getting ' . $wsdl . ' - HTTP ERROR: '.$err;
4809 $this->debug($errstr);
4810 $this->setError($errstr);
4811 unset($tr);
4812 return false;
4813 }
4814 unset($tr);
4815 $this->debug("got WSDL URL");
4816 } else {
4817 // $wsdl is not http(s), so treat it as a file URL or plain file path
4818 if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'file') && isset($wsdl_props['path'])) {
4819 $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path'];
4820 } else {
4821 $path = $wsdl;
4822 }
4823 $this->debug('getting WSDL file ' . $path);
4824 if ($fp = @fopen($path, 'r')) {
4825 $wsdl_string = '';
4826 while ($data = fread($fp, 32768)) {
4827 $wsdl_string .= $data;
4828 }
4829 fclose($fp);
4830 } else {
4831 $errstr = "Bad path to WSDL file $path";
4832 $this->debug($errstr);
4833 $this->setError($errstr);
4834 return false;
4835 }
4836 }
4837 $this->debug('Parse WSDL');
4838 // end new code added
4839 // Create an XML parser.
4840 $this->parser = xml_parser_create();
4841 // Set the options for parsing the XML data.
4842 // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
4843 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
4844 // Set the object for the parser.
4845 xml_set_object($this->parser, $this);
4846 // Set the element handlers for the parser.
4847 xml_set_element_handler($this->parser, 'start_element', 'end_element');
4848 xml_set_character_data_handler($this->parser, 'character_data');
4849 // Parse the XML file.
4850 if (!xml_parse($this->parser, $wsdl_string, true)) {
4851 // Display an error message.
4852 $errstr = sprintf(
4853 'XML error parsing WSDL from %s on line %d: %s',
4854 $wsdl,
4855 xml_get_current_line_number($this->parser),
4856 xml_error_string(xml_get_error_code($this->parser))
4857 );
4858 $this->debug($errstr);
4859 $this->debug("XML payload:\n" . $wsdl_string);
4860 $this->setError($errstr);
4861 return false;
4862 }
4863 // free the parser
4864 xml_parser_free($this->parser);
4865 $this->debug('Parsing WSDL done');
4866 // catch wsdl parse errors
4867 if($this->getError()){
4868 return false;
4869 }
4870 return true;
4871 }
4872
4873 /**
4874 * start-element handler
4875 *
4876 * @param string $parser XML parser object
4877 * @param string $name element name
4878 * @param string $attrs associative array of attributes
4879 * @access private
4880 */
4881 function start_element($parser, $name, $attrs)
4882 {
4883 if ($this->status == 'schema') {
4884 $this->currentSchema->schemaStartElement($parser, $name, $attrs);
4885 $this->appendDebug($this->currentSchema->getDebug());
4886 $this->currentSchema->clearDebug();
4887 } elseif (preg_match('/schema$/', $name)) {
4888 $this->debug('Parsing WSDL schema');
4889 // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
4890 $this->status = 'schema';
4891 $this->currentSchema = new nusoap_xmlschema('', '', $this->namespaces);
4892 $this->currentSchema->schemaStartElement($parser, $name, $attrs);
4893 $this->appendDebug($this->currentSchema->getDebug());
4894 $this->currentSchema->clearDebug();
4895 } else {
4896 // position in the total number of elements, starting from 0
4897 $pos = $this->position++;
4898 $depth = $this->depth++;
4899 // set self as current value for this depth
4900 $this->depth_array[$depth] = $pos;
4901 $this->message[$pos] = array('cdata' => '');
4902 // process attributes
4903 if (count($attrs) > 0) {
4904 // register namespace declarations
4905 foreach($attrs as $k => $v) {
4906 if (preg_match('/^xmlns/',$k)) {
4907 if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
4908 $this->namespaces[$ns_prefix] = $v;
4909 } else {
4910 $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
4911 }
4912 if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema') {
4913 $this->XMLSchemaVersion = $v;
4914 $this->namespaces['xsi'] = $v . '-instance';
4915 }
4916 }
4917 }
4918 // expand each attribute prefix to its namespace
4919 foreach($attrs as $k => $v) {
4920 $k = strpos($k, ':') ? $this->expandQname($k) : $k;
4921 if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
4922 $v = strpos($v, ':') ? $this->expandQname($v) : $v;
4923 }
4924 $eAttrs[$k] = $v;
4925 }
4926 $attrs = $eAttrs;
4927 } else {
4928 $attrs = array();
4929 }
4930 // get element prefix, namespace and name
4931 if (preg_match('/:/', $name)) {
4932 // get ns prefix
4933 $prefix = substr($name, 0, strpos($name, ':'));
4934 // get ns
4935 $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
4936 // get unqualified name
4937 $name = substr(strstr($name, ':'), 1);
4938 }
4939 // process attributes, expanding any prefixes to namespaces
4940 // find status, register data
4941 switch ($this->status) {
4942 case 'message':
4943 if ($name == 'part') {
4944 if (isset($attrs['type'])) {
4945 $this->debug("msg " . $this->currentMessage . ": found part (with type) $attrs[name]: " . implode(',', $attrs));
4946 $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
4947 }
4948 if (isset($attrs['element'])) {
4949 $this->debug("msg " . $this->currentMessage . ": found part (with element) $attrs[name]: " . implode(',', $attrs));
4950 $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'] . '^';
4951 }
4952 }
4953 break;
4954 case 'portType':
4955 switch ($name) {
4956 case 'operation':
4957 $this->currentPortOperation = $attrs['name'];
4958 $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
4959 if (isset($attrs['parameterOrder'])) {
4960 $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
4961 }
4962 break;
4963 case 'documentation':
4964 $this->documentation = true;
4965 break;
4966 // merge input/output data
4967 default:
4968 $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
4969 $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
4970 break;
4971 }
4972 break;
4973 case 'binding':
4974 switch ($name) {
4975 case 'binding':
4976 // get ns prefix
4977 if (isset($attrs['style'])) {
4978 $this->bindings[$this->currentBinding]['prefix'] = $prefix;
4979 }
4980 $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
4981 break;
4982 case 'header':
4983 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
4984 break;
4985 case 'operation':
4986 if (isset($attrs['soapAction'])) {
4987 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
4988 }
4989 if (isset($attrs['style'])) {
4990 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
4991 }
4992 if (isset($attrs['name'])) {
4993 $this->currentOperation = $attrs['name'];
4994 $this->debug("current binding operation: $this->currentOperation");
4995 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
4996 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
4997 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
4998 }
4999 break;
5000 case 'input':
5001 $this->opStatus = 'input';
5002 break;
5003 case 'output':
5004 $this->opStatus = 'output';
5005 break;
5006 case 'body':
5007 if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
5008 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
5009 } else {
5010 $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
5011 }
5012 break;
5013 }
5014 break;
5015 case 'service':
5016 switch ($name) {
5017 case 'port':
5018 $this->currentPort = $attrs['name'];
5019 $this->debug('current port: ' . $this->currentPort);
5020 $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
5021
5022 break;
5023 case 'address':
5024 $this->ports[$this->currentPort]['location'] = $attrs['location'];
5025 $this->ports[$this->currentPort]['bindingType'] = $namespace;
5026 $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace;
5027 $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location'];
5028 break;
5029 }
5030 break;
5031 }
5032 // set status
5033 switch ($name) {
5034 case 'import':
5035 if (isset($attrs['location'])) {
5036 $this->import[$attrs['namespace']][] = array('location' => $attrs['location'], 'loaded' => false);
5037 $this->debug('parsing import ' . $attrs['namespace']. ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]).')');
5038 } else {
5039 $this->import[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
5040 if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
5041 $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
5042 }
5043 $this->debug('parsing import ' . $attrs['namespace']. ' - [no location] (' . count($this->import[$attrs['namespace']]).')');
5044 }
5045 break;
5046 //wait for schema
5047 //case 'types':
5048 // $this->status = 'schema';
5049 // break;
5050 case 'message':
5051 $this->status = 'message';
5052 $this->messages[$attrs['name']] = array();
5053 $this->currentMessage = $attrs['name'];
5054 break;
5055 case 'portType':
5056 $this->status = 'portType';
5057 $this->portTypes[$attrs['name']] = array();
5058 $this->currentPortType = $attrs['name'];
5059 break;
5060 case "binding":
5061 if (isset($attrs['name'])) {
5062 // get binding name
5063 if (strpos($attrs['name'], ':')) {
5064 $this->currentBinding = $this->getLocalPart($attrs['name']);
5065 } else {
5066 $this->currentBinding = $attrs['name'];
5067 }
5068 $this->status = 'binding';
5069 $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
5070 $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
5071 }
5072 break;
5073 case 'service':
5074 $this->serviceName = $attrs['name'];
5075 $this->status = 'service';
5076 $this->debug('current service: ' . $this->serviceName);
5077 break;
5078 case 'definitions':
5079 foreach ($attrs as $name => $value) {
5080 $this->wsdl_info[$name] = $value;
5081 }
5082 break;
5083 }
5084 }
5085 }
5086
5087 /**
5088 * end-element handler
5089 *
5090 * @param string $parser XML parser object
5091 * @param string $name element name
5092 * @access private
5093 */
5094 function end_element($parser, $name){
5095 // unset schema status
5096 if (/*preg_match('/types$/', $name) ||*/ preg_match('/schema$/', $name)) {
5097 $this->status = "";
5098 $this->appendDebug($this->currentSchema->getDebug());
5099 $this->currentSchema->clearDebug();
5100 $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema;
5101 $this->debug('Parsing WSDL schema done');
5102 }
5103 if ($this->status == 'schema') {
5104 $this->currentSchema->schemaEndElement($parser, $name);
5105 } else {
5106 // bring depth down a notch
5107 $this->depth--;
5108 }
5109 // end documentation
5110 if ($this->documentation) {
5111 //TODO: track the node to which documentation should be assigned; it can be a part, message, etc.
5112 //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
5113 $this->documentation = false;
5114 }
5115 }
5116
5117 /**
5118 * element content handler
5119 *
5120 * @param string $parser XML parser object
5121 * @param string $data element content
5122 * @access private
5123 */
5124 function character_data($parser, $data)
5125 {
5126 $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
5127 if (isset($this->message[$pos]['cdata'])) {
5128 $this->message[$pos]['cdata'] .= $data;
5129 }
5130 if ($this->documentation) {
5131 $this->documentation .= $data;
5132 }
5133 }
5134
5135 /**
5136 * if authenticating, set user credentials here
5137 *
5138 * @param string $username
5139 * @param string $password
5140 * @param string $authtype (basic|digest|certificate|ntlm)
5141 * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
5142 * @access public
5143 */
5144 function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) {
5145 $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
5146 $this->appendDebug($this->varDump($certRequest));
5147 $this->username = $username;
5148 $this->password = $password;
5149 $this->authtype = $authtype;
5150 $this->certRequest = $certRequest;
5151 }
5152
5153 function getBindingData($binding)
5154 {
5155 if (is_array($this->bindings[$binding])) {
5156 return $this->bindings[$binding];
5157 }
5158 }
5159
5160 /**
5161 * returns an assoc array of operation names => operation data
5162 *
5163 * @param string $portName WSDL port name
5164 * @param string $bindingType eg: soap, smtp, dime (only soap and soap12 are currently supported)
5165 * @return array
5166 * @access public
5167 */
5168 function getOperations($portName = '', $bindingType = 'soap') {
5169 $ops = array();
5170 if ($bindingType == 'soap') {
5171 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5172 } elseif ($bindingType == 'soap12') {
5173 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5174 } else {
5175 $this->debug("getOperations bindingType $bindingType may not be supported");
5176 }
5177 $this->debug("getOperations for port '$portName' bindingType $bindingType");
5178 // loop thru ports
5179 foreach($this->ports as $port => $portData) {
5180 $this->debug("getOperations checking port $port bindingType " . $portData['bindingType']);
5181 if ($portName == '' || $port == $portName) {
5182 // binding type of port matches parameter
5183 if ($portData['bindingType'] == $bindingType) {
5184 $this->debug("getOperations found port $port bindingType $bindingType");
5185 //$this->debug("port data: " . $this->varDump($portData));
5186 //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ]));
5187 // merge bindings
5188 if (isset($this->bindings[ $portData['binding'] ]['operations'])) {
5189 $ops = array_merge ($ops, $this->bindings[ $portData['binding'] ]['operations']);
5190 }
5191 }
5192 }
5193 }
5194 if (count($ops) == 0) {
5195 $this->debug("getOperations found no operations for port '$portName' bindingType $bindingType");
5196 }
5197 return $ops;
5198 }
5199
5200 /**
5201 * returns an associative array of data necessary for calling an operation
5202 *
5203 * @param string $operation name of operation
5204 * @param string $bindingType type of binding eg: soap, soap12
5205 * @return array
5206 * @access public
5207 */
5208 function getOperationData($operation, $bindingType = 'soap')
5209 {
5210 if ($bindingType == 'soap') {
5211 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5212 } elseif ($bindingType == 'soap12') {
5213 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5214 }
5215 // loop thru ports
5216 foreach($this->ports as $port => $portData) {
5217 // binding type of port matches parameter
5218 if ($portData['bindingType'] == $bindingType) {
5219 // get binding
5220 //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
5221 foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) {
5222 // note that we could/should also check the namespace here
5223 if ($operation == $bOperation) {
5224 $opData = $this->bindings[ $portData['binding'] ]['operations'][$operation];
5225 return $opData;
5226 }
5227 }
5228 }
5229 }
5230 }
5231
5232 /**
5233 * returns an associative array of data necessary for calling an operation
5234 *
5235 * @param string $soapAction soapAction for operation
5236 * @param string $bindingType type of binding eg: soap, soap12
5237 * @return array
5238 * @access public
5239 */
5240 function getOperationDataForSoapAction($soapAction, $bindingType = 'soap') {
5241 if ($bindingType == 'soap') {
5242 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
5243 } elseif ($bindingType == 'soap12') {
5244 $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/';
5245 }
5246 // loop thru ports
5247 foreach($this->ports as $port => $portData) {
5248 // binding type of port matches parameter
5249 if ($portData['bindingType'] == $bindingType) {
5250 // loop through operations for the binding
5251 foreach ($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
5252 if ($opData['soapAction'] == $soapAction) {
5253 return $opData;
5254 }
5255 }
5256 }
5257 }
5258 }
5259
5260 /**
5261 * returns an array of information about a given type
5262 * returns false if no type exists by the given name
5263 *
5264 * typeDef = array(
5265 * 'elements' => array(), // refs to elements array
5266 * 'restrictionBase' => '',
5267 * 'phpType' => '',
5268 * 'order' => '(sequence|all)',
5269 * 'attrs' => array() // refs to attributes array
5270 * )
5271 *
5272 * @param string $type the type
5273 * @param string $ns namespace (not prefix) of the type
5274 * @return mixed
5275 * @access public
5276 * @see nusoap_xmlschema
5277 */
5278 function getTypeDef($type, $ns) {
5279 $this->debug("in getTypeDef: type=$type, ns=$ns");
5280 if ((! $ns) && isset($this->namespaces['tns'])) {
5281 $ns = $this->namespaces['tns'];
5282 $this->debug("in getTypeDef: type namespace forced to $ns");
5283 }
5284 if (!isset($this->schemas[$ns])) {
5285 foreach ($this->schemas as $ns0 => $schema0) {
5286 if (strcasecmp($ns, $ns0) == 0) {
5287 $this->debug("in getTypeDef: replacing schema namespace $ns with $ns0");
5288 $ns = $ns0;
5289 break;
5290 }
5291 }
5292 }
5293 if (isset($this->schemas[$ns])) {
5294 $this->debug("in getTypeDef: have schema for namespace $ns");
5295 for ($i = 0; $i < count($this->schemas[$ns]); $i++) {
5296 $xs = &$this->schemas[$ns][$i];
5297 $t = $xs->getTypeDef($type);
5298 $this->appendDebug($xs->getDebug());
5299 $xs->clearDebug();
5300 if ($t) {
5301 $this->debug("in getTypeDef: found type $type");
5302 if (!isset($t['phpType'])) {
5303 // get info for type to tack onto the element
5304 $uqType = substr($t['type'], strrpos($t['type'], ':') + 1);
5305 $ns = substr($t['type'], 0, strrpos($t['type'], ':'));
5306 $etype = $this->getTypeDef($uqType, $ns);
5307 if ($etype) {
5308 $this->debug("found type for [element] $type:");
5309 $this->debug($this->varDump($etype));
5310 if (isset($etype['phpType'])) {
5311 $t['phpType'] = $etype['phpType'];
5312 }
5313 if (isset($etype['elements'])) {
5314 $t['elements'] = $etype['elements'];
5315 }
5316 if (isset($etype['attrs'])) {
5317 $t['attrs'] = $etype['attrs'];
5318 }
5319 } else {
5320 $this->debug("did not find type for [element] $type");
5321 }
5322 }
5323 return $t;
5324 }
5325 }
5326 $this->debug("in getTypeDef: did not find type $type");
5327 } else {
5328 $this->debug("in getTypeDef: do not have schema for namespace $ns");
5329 }
5330 return false;
5331 }
5332
5333 /**
5334 * prints html description of services
5335 *
5336 * @access private
5337 */
5338 function webDescription(){
5339 global $HTTP_SERVER_VARS;
5340
5341 if (isset($_SERVER)) {
5342 $PHP_SELF = $_SERVER['PHP_SELF'];
5343 } elseif (isset($HTTP_SERVER_VARS)) {
5344 $PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF'];
5345 } else {
5346 $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
5347 }
5348
5349 $b = '
5350 <html><head><title>NuSOAP: '.$this->serviceName.'</title>
5351 <style type="text/css">
5352 body { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
5353 p { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
5354 pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
5355 ul { margin-top: 10px; margin-left: 20px; }
5356 li { list-style-type: none; margin-top: 10px; color: #000000; }
5357 .content{
5358 margin-left: 0px; padding-bottom: 2em; }
5359 .nav {
5360 padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
5361 margin-top: 10px; margin-left: 0px; color: #000000;
5362 background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
5363 .title {
5364 font-family: arial; font-size: 26px; color: #ffffff;
5365 background-color: #999999; width: 100%;
5366 margin-left: 0px; margin-right: 0px;
5367 padding-top: 10px; padding-bottom: 10px;}
5368 .hidden {
5369 position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
5370 font-family: arial; overflow: hidden; width: 600;
5371 padding: 20px; font-size: 10px; background-color: #999999;
5372 layer-background-color:#FFFFFF; }
5373 a,a:active { color: charcoal; font-weight: bold; }
5374 a:visited { color: #666666; font-weight: bold; }
5375 a:hover { color: cc3300; font-weight: bold; }
5376 </style>
5377 <script language="JavaScript" type="text/javascript">
5378 <!--
5379 // POP-UP CAPTIONS...
5380 function lib_bwcheck(){ //Browsercheck (needed)
5381 this.ver=navigator.appVersion
5382 this.agent=navigator.userAgent
5383 this.dom=document.getElementById?1:0
5384 this.opera5=this.agent.indexOf("Opera 5")>-1
5385 this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
5386 this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
5387 this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
5388 this.ie=this.ie4||this.ie5||this.ie6
5389 this.mac=this.agent.indexOf("Mac")>-1
5390 this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
5391 this.ns4=(document.layers && !this.dom)?1:0;
5392 this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
5393 return this
5394 }
5395 var bw = new lib_bwcheck()
5396 //Makes crossbrowser object.
5397 function makeObj(obj){
5398 this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
5399 if(!this.evnt) return false
5400 this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
5401 this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
5402 this.writeIt=b_writeIt;
5403 return this
5404 }
5405 // A unit of measure that will be added when setting the position of a layer.
5406 //var px = bw.ns4||window.opera?"":"px";
5407 function b_writeIt(text){
5408 if (bw.ns4){this.wref.write(text);this.wref.close()}
5409 else this.wref.innerHTML = text
5410 }
5411 //Shows the messages
5412 var oDesc;
5413 function popup(divid){
5414 if(oDesc = new makeObj(divid)){
5415 oDesc.css.visibility = "visible"
5416 }
5417 }
5418 function popout(){ // Hides message
5419 if(oDesc) oDesc.css.visibility = "hidden"
5420 }
5421 //-->
5422 </script>
5423 </head>
5424 <body>
5425 <div class=content>
5426 <br><br>
5427 <div class=title>'.$this->serviceName.'</div>
5428 <div class=nav>
5429 <p>View the <a href="'.$PHP_SELF.'?wsdl">WSDL</a> for the service.
5430 Click on an operation name to view it&apos;s details.</p>
5431 <ul>';
5432 foreach($this->getOperations() as $op => $data){
5433 $b .= "<li><a href='#' onclick=\"popout();popup('$op')\">$op</a></li>";
5434 // create hidden div
5435 $b .= "<div id='$op' class='hidden'>
5436 <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
5437 foreach($data as $donnie => $marie){ // loop through opdata
5438 if($donnie == 'input' || $donnie == 'output'){ // show input/output data
5439 $b .= "<font color='white'>".ucfirst($donnie).':</font><br>';
5440 foreach($marie as $captain => $tenille){ // loop through data
5441 if($captain == 'parts'){ // loop thru parts
5442 $b .= "&nbsp;&nbsp;$captain:<br>";
5443 //if(is_array($tenille)){
5444 foreach($tenille as $joanie => $chachi){
5445 $b .= "&nbsp;&nbsp;&nbsp;&nbsp;$joanie: $chachi<br>";
5446 }
5447 //}
5448 } else {
5449 $b .= "&nbsp;&nbsp;$captain: $tenille<br>";
5450 }
5451 }
5452 } else {
5453 $b .= "<font color='white'>".ucfirst($donnie).":</font> $marie<br>";
5454 }
5455 }
5456 $b .= '</div>';
5457 }
5458 $b .= '
5459 <ul>
5460 </div>
5461 </div></body></html>';
5462 return $b;
5463 }
5464
5465 /**
5466 * serialize the parsed wsdl
5467 *
5468 * @param mixed $debug whether to put debug=1 in endpoint URL
5469 * @return string serialization of WSDL
5470 * @access public
5471 */
5472 function serialize($debug = 0)
5473 {
5474 $xml = '<?xml version="1.0" encoding="ISO-8859-1"?>';
5475 $xml .= "\n<definitions";
5476 foreach($this->namespaces as $k => $v) {
5477 $xml .= " xmlns:$k=\"$v\"";
5478 }
5479 // 10.9.02 - add poulter fix for wsdl and tns declarations
5480 if (isset($this->namespaces['wsdl'])) {
5481 $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
5482 }
5483 if (isset($this->namespaces['tns'])) {
5484 $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
5485 }
5486 $xml .= '>';
5487 // imports
5488 if (sizeof($this->import) > 0) {
5489 foreach($this->import as $ns => $list) {
5490 foreach ($list as $ii) {
5491 if ($ii['location'] != '') {
5492 $xml .= '<import location="' . $ii['location'] . '" namespace="' . $ns . '" />';
5493 } else {
5494 $xml .= '<import namespace="' . $ns . '" />';
5495 }
5496 }
5497 }
5498 }
5499 // types
5500 if (count($this->schemas)>=1) {
5501 $xml .= "\n<types>\n";
5502 foreach ($this->schemas as $ns => $list) {
5503 foreach ($list as $xs) {
5504 $xml .= $xs->serializeSchema();
5505 }
5506 }
5507 $xml .= '</types>';
5508 }
5509 // messages
5510 if (count($this->messages) >= 1) {
5511 foreach($this->messages as $msgName => $msgParts) {
5512 $xml .= "\n<message name=\"" . $msgName . '">';
5513 if(is_array($msgParts)){
5514 foreach($msgParts as $partName => $partType) {
5515 // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
5516 if (strpos($partType, ':')) {
5517 $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
5518 } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
5519 // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
5520 $typePrefix = 'xsd';
5521 } else {
5522 foreach($this->typemap as $ns => $types) {
5523 if (isset($types[$partType])) {
5524 $typePrefix = $this->getPrefixFromNamespace($ns);
5525 }
5526 }
5527 if (!isset($typePrefix)) {
5528 die("$partType has no namespace!");
5529 }
5530 }
5531 $ns = $this->getNamespaceFromPrefix($typePrefix);
5532 $localPart = $this->getLocalPart($partType);
5533 $typeDef = $this->getTypeDef($localPart, $ns);
5534 if ($typeDef['typeClass'] == 'element') {
5535 $elementortype = 'element';
5536 if (substr($localPart, -1) == '^') {
5537 $localPart = substr($localPart, 0, -1);
5538 }
5539 } else {
5540 $elementortype = 'type';
5541 }
5542 $xml .= "\n" . ' <part name="' . $partName . '" ' . $elementortype . '="' . $typePrefix . ':' . $localPart . '" />';
5543 }
5544 }
5545 $xml .= '</message>';
5546 }
5547 }
5548 // bindings & porttypes
5549 if (count($this->bindings) >= 1) {
5550 $binding_xml = '';
5551 $portType_xml = '';
5552 foreach($this->bindings as $bindingName => $attrs) {
5553 $binding_xml .= "\n<binding name=\"" . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
5554 $binding_xml .= "\n" . ' <soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
5555 $portType_xml .= "\n<portType name=\"" . $attrs['portType'] . '">';
5556 foreach($attrs['operations'] as $opName => $opParts) {
5557 $binding_xml .= "\n" . ' <operation name="' . $opName . '">';
5558 $binding_xml .= "\n" . ' <soap:operation soapAction="' . $opParts['soapAction'] . '" style="'. $opParts['style'] . '"/>';
5559 if (isset($opParts['input']['encodingStyle']) && $opParts['input']['encodingStyle'] != '') {
5560 $enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"';
5561 } else {
5562 $enc_style = '';
5563 }
5564 $binding_xml .= "\n" . ' <input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '"' . $enc_style . '/></input>';
5565 if (isset($opParts['output']['encodingStyle']) && $opParts['output']['encodingStyle'] != '') {
5566 $enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"';
5567 } else {
5568 $enc_style = '';
5569 }
5570 $binding_xml .= "\n" . ' <output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '"' . $enc_style . '/></output>';
5571 $binding_xml .= "\n" . ' </operation>';
5572 $portType_xml .= "\n" . ' <operation name="' . $opParts['name'] . '"';
5573 if (isset($opParts['parameterOrder'])) {
5574 $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
5575 }
5576 $portType_xml .= '>';
5577 if(isset($opParts['documentation']) && $opParts['documentation'] != '') {
5578 $portType_xml .= "\n" . ' <documentation>' . htmlspecialchars($opParts['documentation']) . '</documentation>';
5579 }
5580 $portType_xml .= "\n" . ' <input message="tns:' . $opParts['input']['message'] . '"/>';
5581 $portType_xml .= "\n" . ' <output message="tns:' . $opParts['output']['message'] . '"/>';
5582 $portType_xml .= "\n" . ' </operation>';
5583 }
5584 $portType_xml .= "\n" . '</portType>';
5585 $binding_xml .= "\n" . '</binding>';
5586 }
5587 $xml .= $portType_xml . $binding_xml;
5588 }
5589 // services
5590 $xml .= "\n<service name=\"" . $this->serviceName . '">';
5591 if (count($this->ports) >= 1) {
5592 foreach($this->ports as $pName => $attrs) {
5593 $xml .= "\n" . ' <port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
5594 $xml .= "\n" . ' <soap:address location="' . $attrs['location'] . ($debug ? '?debug=1' : '') . '"/>';
5595 $xml .= "\n" . ' </port>';
5596 }
5597 }
5598 $xml .= "\n" . '</service>';
5599 return $xml . "\n</definitions>";
5600 }
5601
5602 /**
5603 * determine whether a set of parameters are unwrapped
5604 * when they are expect to be wrapped, Microsoft-style.
5605 *
5606 * @param string $type the type (element name) of the wrapper
5607 * @param array $parameters the parameter values for the SOAP call
5608 * @return boolean whether they parameters are unwrapped (and should be wrapped)
5609 * @access private
5610 */
5611 function parametersMatchWrapped($type, &$parameters) {
5612 $this->debug("in parametersMatchWrapped type=$type, parameters=");
5613 $this->appendDebug($this->varDump($parameters));
5614
5615 // split type into namespace:unqualified-type
5616 if (strpos($type, ':')) {
5617 $uqType = substr($type, strrpos($type, ':') + 1);
5618 $ns = substr($type, 0, strrpos($type, ':'));
5619 $this->debug("in parametersMatchWrapped: got a prefixed type: $uqType, $ns");
5620 if ($this->getNamespaceFromPrefix($ns)) {
5621 $ns = $this->getNamespaceFromPrefix($ns);
5622 $this->debug("in parametersMatchWrapped: expanded prefixed type: $uqType, $ns");
5623 }
5624 } else {
5625 // TODO: should the type be compared to types in XSD, and the namespace
5626 // set to XSD if the type matches?
5627 $this->debug("in parametersMatchWrapped: No namespace for type $type");
5628 $ns = '';
5629 $uqType = $type;
5630 }
5631
5632 // get the type information
5633 if (!$typeDef = $this->getTypeDef($uqType, $ns)) {
5634 $this->debug("in parametersMatchWrapped: $type ($uqType) is not a supported type.");
5635 return false;
5636 }
5637 $this->debug("in parametersMatchWrapped: found typeDef=");
5638 $this->appendDebug($this->varDump($typeDef));
5639 if (substr($uqType, -1) == '^') {
5640 $uqType = substr($uqType, 0, -1);
5641 }
5642 $phpType = $typeDef['phpType'];
5643 $arrayType = (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '');
5644 $this->debug("in parametersMatchWrapped: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: $arrayType");
5645
5646 // we expect a complexType or element of complexType
5647 if ($phpType != 'struct') {
5648 $this->debug("in parametersMatchWrapped: not a struct");
5649 return false;
5650 }
5651
5652 // see whether the parameter names match the elements
5653 if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
5654 $elements = 0;
5655 $matches = 0;
5656 foreach ($typeDef['elements'] as $name => $attrs) {
5657 if (isset($parameters[$name])) {
5658 $this->debug("in parametersMatchWrapped: have parameter named $name");
5659 $matches++;
5660 } else {
5661 $this->debug("in parametersMatchWrapped: do not have parameter named $name");
5662 }
5663 $elements++;
5664 }
5665
5666 $this->debug("in parametersMatchWrapped: $matches parameter names match $elements wrapped parameter names");
5667 if ($matches == 0) {
5668 return false;
5669 }
5670 return true;
5671 }
5672
5673 // since there are no elements for the type, if the user passed no
5674 // parameters, the parameters match wrapped.
5675 $this->debug("in parametersMatchWrapped: no elements type $ns:$uqType");
5676 return count($parameters) == 0;
5677 }
5678
5679 /**
5680 * serialize PHP values according to a WSDL message definition
5681 * contrary to the method name, this is not limited to RPC
5682 *
5683 * TODO
5684 * - multi-ref serialization
5685 * - validate PHP values against type definitions, return errors if invalid
5686 *
5687 * @param string $operation operation name
5688 * @param string $direction (input|output)
5689 * @param mixed $parameters parameter value(s)
5690 * @param string $bindingType (soap|soap12)
5691 * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
5692 * @access public
5693 */
5694 function serializeRPCParameters($operation, $direction, $parameters, $bindingType = 'soap') {
5695 $this->debug("in serializeRPCParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion, bindingType=$bindingType");
5696 $this->appendDebug('parameters=' . $this->varDump($parameters));
5697
5698 if ($direction != 'input' && $direction != 'output') {
5699 $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5700 $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5701 return false;
5702 }
5703 if (!$opData = $this->getOperationData($operation, $bindingType)) {
5704 $this->debug('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
5705 $this->setError('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType);
5706 return false;
5707 }
5708 $this->debug('in serializeRPCParameters: opData:');
5709 $this->appendDebug($this->varDump($opData));
5710
5711 // Get encoding style for output and set to current
5712 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5713 if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5714 $encodingStyle = $opData['output']['encodingStyle'];
5715 $enc_style = $encodingStyle;
5716 }
5717
5718 // set input params
5719 $xml = '';
5720 if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
5721 $parts = &$opData[$direction]['parts'];
5722 $part_count = sizeof($parts);
5723 $style = $opData['style'];
5724 $use = $opData[$direction]['use'];
5725 $this->debug("have $part_count part(s) to serialize using $style/$use");
5726 if (is_array($parameters)) {
5727 $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5728 $parameter_count = count($parameters);
5729 $this->debug("have $parameter_count parameter(s) provided as $parametersArrayType to serialize");
5730 // check for Microsoft-style wrapped parameters
5731 if ($style == 'document' && $use == 'literal' && $part_count == 1 && isset($parts['parameters'])) {
5732 $this->debug('check whether the caller has wrapped the parameters');
5733 if ($direction == 'output' && $parametersArrayType == 'arraySimple' && $parameter_count == 1) {
5734 // TODO: consider checking here for double-wrapping, when
5735 // service function wraps, then NuSOAP wraps again
5736 $this->debug("change simple array to associative with 'parameters' element");
5737 $parameters['parameters'] = $parameters[0];
5738 unset($parameters[0]);
5739 }
5740 if (($parametersArrayType == 'arrayStruct' || $parameter_count == 0) && !isset($parameters['parameters'])) {
5741 $this->debug('check whether caller\'s parameters match the wrapped ones');
5742 if ($this->parametersMatchWrapped($parts['parameters'], $parameters)) {
5743 $this->debug('wrap the parameters for the caller');
5744 $parameters = array('parameters' => $parameters);
5745 $parameter_count = 1;
5746 }
5747 }
5748 }
5749 foreach ($parts as $name => $type) {
5750 $this->debug("serializing part $name of type $type");
5751 // Track encoding style
5752 if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
5753 $encodingStyle = $opData[$direction]['encodingStyle'];
5754 $enc_style = $encodingStyle;
5755 } else {
5756 $enc_style = false;
5757 }
5758 // NOTE: add error handling here
5759 // if serializeType returns false, then catch global error and fault
5760 if ($parametersArrayType == 'arraySimple') {
5761 $p = array_shift($parameters);
5762 $this->debug('calling serializeType w/indexed param');
5763 $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5764 } elseif (isset($parameters[$name])) {
5765 $this->debug('calling serializeType w/named param');
5766 $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5767 } else {
5768 // TODO: only send nillable
5769 $this->debug('calling serializeType w/null param');
5770 $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
5771 }
5772 }
5773 } else {
5774 $this->debug('no parameters passed.');
5775 }
5776 }
5777 $this->debug("serializeRPCParameters returning: $xml");
5778 return $xml;
5779 }
5780
5781 /**
5782 * serialize a PHP value according to a WSDL message definition
5783 *
5784 * TODO
5785 * - multi-ref serialization
5786 * - validate PHP values against type definitions, return errors if invalid
5787 *
5788 * @param string $operation operation name
5789 * @param string $direction (input|output)
5790 * @param mixed $parameters parameter value(s)
5791 * @return mixed parameters serialized as XML or false on error (e.g. operation not found)
5792 * @access public
5793 * @deprecated
5794 */
5795 function serializeParameters($operation, $direction, $parameters)
5796 {
5797 $this->debug("in serializeParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion");
5798 $this->appendDebug('parameters=' . $this->varDump($parameters));
5799
5800 if ($direction != 'input' && $direction != 'output') {
5801 $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
5802 $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
5803 return false;
5804 }
5805 if (!$opData = $this->getOperationData($operation)) {
5806 $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
5807 $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
5808 return false;
5809 }
5810 $this->debug('opData:');
5811 $this->appendDebug($this->varDump($opData));
5812
5813 // Get encoding style for output and set to current
5814 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
5815 if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) {
5816 $encodingStyle = $opData['output']['encodingStyle'];
5817 $enc_style = $encodingStyle;
5818 }
5819
5820 // set input params
5821 $xml = '';
5822 if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
5823
5824 $use = $opData[$direction]['use'];
5825 $this->debug("use=$use");
5826 $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
5827 if (is_array($parameters)) {
5828 $parametersArrayType = $this->isArraySimpleOrStruct($parameters);
5829 $this->debug('have ' . $parametersArrayType . ' parameters');
5830 foreach($opData[$direction]['parts'] as $name => $type) {
5831 $this->debug('serializing part "'.$name.'" of type "'.$type.'"');
5832 // Track encoding style
5833 if(isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) {
5834 $encodingStyle = $opData[$direction]['encodingStyle'];
5835 $enc_style = $encodingStyle;
5836 } else {
5837 $enc_style = false;
5838 }
5839 // NOTE: add error handling here
5840 // if serializeType returns false, then catch global error and fault
5841 if ($parametersArrayType == 'arraySimple') {
5842 $p = array_shift($parameters);
5843 $this->debug('calling serializeType w/indexed param');
5844 $xml .= $this->serializeType($name, $type, $p, $use, $enc_style);
5845 } elseif (isset($parameters[$name])) {
5846 $this->debug('calling serializeType w/named param');
5847 $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style);
5848 } else {
5849 // TODO: only send nillable
5850 $this->debug('calling serializeType w/null param');
5851 $xml .= $this->serializeType($name, $type, null, $use, $enc_style);
5852 }
5853 }
5854 } else {
5855 $this->debug('no parameters passed.');
5856 }
5857 }
5858 $this->debug("serializeParameters returning: $xml");
5859 return $xml;
5860 }
5861
5862 /**
5863 * serializes a PHP value according a given type definition
5864 *
5865 * @param string $name name of value (part or element)
5866 * @param string $type XML schema type of value (type or element)
5867 * @param mixed $value a native PHP value (parameter value)
5868 * @param string $use use for part (encoded|literal)
5869 * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
5870 * @param boolean $unqualified a kludge for what should be XML namespace form handling
5871 * @return string value serialized as an XML string
5872 * @access private
5873 */
5874 function serializeType($name, $type, $value, $use='encoded', $encodingStyle=false, $unqualified=false)
5875 {
5876 $this->debug("in serializeType: name=$name, type=$type, use=$use, encodingStyle=$encodingStyle, unqualified=" . ($unqualified ? "unqualified" : "qualified"));
5877 $this->appendDebug("value=" . $this->varDump($value));
5878 if($use == 'encoded' && $encodingStyle) {
5879 $encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"';
5880 }
5881
5882 // if a soapval has been supplied, let its type override the WSDL
5883 if (is_object($value) && get_class($value) == 'soapval') {
5884 if ($value->type_ns) {
5885 $type = $value->type_ns . ':' . $value->type;
5886 $forceType = true;
5887 $this->debug("in serializeType: soapval overrides type to $type");
5888 } elseif ($value->type) {
5889 $type = $value->type;
5890 $forceType = true;
5891 $this->debug("in serializeType: soapval overrides type to $type");
5892 } else {
5893 $forceType = false;
5894 $this->debug("in serializeType: soapval does not override type");
5895 }
5896 $attrs = $value->attributes;
5897 $value = $value->value;
5898 $this->debug("in serializeType: soapval overrides value to $value");
5899 if ($attrs) {
5900 if (!is_array($value)) {
5901 $value['!'] = $value;
5902 }
5903 foreach ($attrs as $n => $v) {
5904 $value['!' . $n] = $v;
5905 }
5906 $this->debug("in serializeType: soapval provides attributes");
5907 }
5908 } else {
5909 $forceType = false;
5910 }
5911
5912 $xml = '';
5913 if (strpos($type, ':')) {
5914 $uqType = substr($type, strrpos($type, ':') + 1);
5915 $ns = substr($type, 0, strrpos($type, ':'));
5916 $this->debug("in serializeType: got a prefixed type: $uqType, $ns");
5917 if ($this->getNamespaceFromPrefix($ns)) {
5918 $ns = $this->getNamespaceFromPrefix($ns);
5919 $this->debug("in serializeType: expanded prefixed type: $uqType, $ns");
5920 }
5921
5922 if($ns == $this->XMLSchemaVersion || $ns == 'http://schemas.xmlsoap.org/soap/encoding/'){
5923 $this->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type');
5924 if ($unqualified && $use == 'literal') {
5925 $elementNS = " xmlns=\"\"";
5926 } else {
5927 $elementNS = '';
5928 }
5929 if (is_null($value)) {
5930 if ($use == 'literal') {
5931 // TODO: depends on minOccurs
5932 $xml = "<$name$elementNS/>";
5933 } else {
5934 // TODO: depends on nillable, which should be checked before calling this method
5935 $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
5936 }
5937 $this->debug("in serializeType: returning: $xml");
5938 return $xml;
5939 }
5940 if ($uqType == 'Array') {
5941 // JBoss/Axis does this sometimes
5942 return $this->serialize_val($value, $name, false, false, false, false, $use);
5943 }
5944 if ($uqType == 'boolean') {
5945 if ((is_string($value) && $value == 'false') || (! $value)) {
5946 $value = 'false';
5947 } else {
5948 $value = 'true';
5949 }
5950 }
5951 if ($uqType == 'string' && gettype($value) == 'string') {
5952 $value = $this->expandEntities($value);
5953 }
5954 if (($uqType == 'long' || $uqType == 'unsignedLong') && gettype($value) == 'double') {
5955 $value = sprintf("%.0lf", $value);
5956 }
5957 // it's a scalar
5958 // TODO: what about null/nil values?
5959 // check type isn't a custom type extending xmlschema namespace
5960 if (!$this->getTypeDef($uqType, $ns)) {
5961 if ($use == 'literal') {
5962 if ($forceType) {
5963 $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
5964 } else {
5965 $xml = "<$name$elementNS>$value</$name>";
5966 }
5967 } else {
5968 $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
5969 }
5970 $this->debug("in serializeType: returning: $xml");
5971 return $xml;
5972 }
5973 $this->debug('custom type extends XML Schema or SOAP Encoding namespace (yuck)');
5974 } else if ($ns == 'http://xml.apache.org/xml-soap') {
5975 $this->debug('in serializeType: appears to be Apache SOAP type');
5976 if ($uqType == 'Map') {
5977 $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
5978 if (! $tt_prefix) {
5979 $this->debug('in serializeType: Add namespace for Apache SOAP type');
5980 $tt_prefix = 'ns' . rand(1000, 9999);
5981 $this->namespaces[$tt_prefix] = 'http://xml.apache.org/xml-soap';
5982 // force this to be added to usedNamespaces
5983 $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap');
5984 }
5985 $contents = '';
5986 foreach($value as $k => $v) {
5987 $this->debug("serializing map element: key $k, value $v");
5988 $contents .= '<item>';
5989 $contents .= $this->serialize_val($k,'key',false,false,false,false,$use);
5990 $contents .= $this->serialize_val($v,'value',false,false,false,false,$use);
5991 $contents .= '</item>';
5992 }
5993 if ($use == 'literal') {
5994 if ($forceType) {
5995 $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\">$contents</$name>";
5996 } else {
5997 $xml = "<$name>$contents</$name>";
5998 }
5999 } else {
6000 $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\"$encodingStyle>$contents</$name>";
6001 }
6002 $this->debug("in serializeType: returning: $xml");
6003 return $xml;
6004 }
6005 $this->debug('in serializeType: Apache SOAP type, but only support Map');
6006 }
6007 } else {
6008 // TODO: should the type be compared to types in XSD, and the namespace
6009 // set to XSD if the type matches?
6010 $this->debug("in serializeType: No namespace for type $type");
6011 $ns = '';
6012 $uqType = $type;
6013 }
6014 if(!$typeDef = $this->getTypeDef($uqType, $ns)){
6015 $this->setError("$type ($uqType) is not a supported type.");
6016 $this->debug("in serializeType: $type ($uqType) is not a supported type.");
6017 return false;
6018 } else {
6019 $this->debug("in serializeType: found typeDef");
6020 $this->appendDebug('typeDef=' . $this->varDump($typeDef));
6021 if (substr($uqType, -1) == '^') {
6022 $uqType = substr($uqType, 0, -1);
6023 }
6024 }
6025 if (!isset($typeDef['phpType'])) {
6026 $this->setError("$type ($uqType) has no phpType.");
6027 $this->debug("in serializeType: $type ($uqType) has no phpType.");
6028 return false;
6029 }
6030 $phpType = $typeDef['phpType'];
6031 $this->debug("in serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') );
6032 // if php type == struct, map value to the <all> element names
6033 if ($phpType == 'struct') {
6034 if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') {
6035 $elementName = $uqType;
6036 if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
6037 $elementNS = " xmlns=\"$ns\"";
6038 } else {
6039 $elementNS = " xmlns=\"\"";
6040 }
6041 } else {
6042 $elementName = $name;
6043 if ($unqualified) {
6044 $elementNS = " xmlns=\"\"";
6045 } else {
6046 $elementNS = '';
6047 }
6048 }
6049 if (is_null($value)) {
6050 if ($use == 'literal') {
6051 // TODO: depends on minOccurs and nillable
6052 $xml = "<$elementName$elementNS/>";
6053 } else {
6054 $xml = "<$elementName$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>";
6055 }
6056 $this->debug("in serializeType: returning: $xml");
6057 return $xml;
6058 }
6059 if (is_object($value)) {
6060 $value = get_object_vars($value);
6061 }
6062 if (is_array($value)) {
6063 $elementAttrs = $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType);
6064 if ($use == 'literal') {
6065 if ($forceType) {
6066 $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
6067 } else {
6068 $xml = "<$elementName$elementNS$elementAttrs>";
6069 }
6070 } else {
6071 $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>";
6072 }
6073
6074 if (isset($typeDef['simpleContent']) && $typeDef['simpleContent'] == 'true') {
6075 if (isset($value['!'])) {
6076 $xml .= $value['!'];
6077 $this->debug("in serializeType: serialized simpleContent for type $type");
6078 } else {
6079 $this->debug("in serializeType: no simpleContent to serialize for type $type");
6080 }
6081 } else {
6082 // complexContent
6083 $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle);
6084 }
6085 $xml .= "</$elementName>";
6086 } else {
6087 $this->debug("in serializeType: phpType is struct, but value is not an array");
6088 $this->setError("phpType is struct, but value is not an array: see debug output for details");
6089 $xml = '';
6090 }
6091 } elseif ($phpType == 'array') {
6092 if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
6093 $elementNS = " xmlns=\"$ns\"";
6094 } else {
6095 if ($unqualified) {
6096 $elementNS = " xmlns=\"\"";
6097 } else {
6098 $elementNS = '';
6099 }
6100 }
6101 if (is_null($value)) {
6102 if ($use == 'literal') {
6103 // TODO: depends on minOccurs
6104 $xml = "<$name$elementNS/>";
6105 } else {
6106 $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" .
6107 $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
6108 ":Array\" " .
6109 $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') .
6110 ':arrayType="' .
6111 $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType'])) .
6112 ':' .
6113 $this->getLocalPart($typeDef['arrayType'])."[0]\"/>";
6114 }
6115 $this->debug("in serializeType: returning: $xml");
6116 return $xml;
6117 }
6118 if (isset($typeDef['multidimensional'])) {
6119 $nv = array();
6120 foreach($value as $v) {
6121 $cols = ',' . sizeof($v);
6122 $nv = array_merge($nv, $v);
6123 }
6124 $value = $nv;
6125 } else {
6126 $cols = '';
6127 }
6128 if (is_array($value) && sizeof($value) >= 1) {
6129 $rows = sizeof($value);
6130 $contents = '';
6131 foreach($value as $k => $v) {
6132 $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]");
6133 //if (strpos($typeDef['arrayType'], ':') ) {
6134 if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) {
6135 $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
6136 } else {
6137 $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
6138 }
6139 }
6140 } else {
6141 $rows = 0;
6142 $contents = null;
6143 }
6144 // TODO: for now, an empty value will be serialized as a zero element
6145 // array. Revisit this when coding the handling of null/nil values.
6146 if ($use == 'literal') {
6147 $xml = "<$name$elementNS>"
6148 .$contents
6149 ."</$name>";
6150 } else {
6151 $xml = "<$name$elementNS xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '.
6152 $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
6153 .':arrayType="'
6154 .$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
6155 .":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">"
6156 .$contents
6157 ."</$name>";
6158 }
6159 } elseif ($phpType == 'scalar') {
6160 if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) {
6161 $elementNS = " xmlns=\"$ns\"";
6162 } else {
6163 if ($unqualified) {
6164 $elementNS = " xmlns=\"\"";
6165 } else {
6166 $elementNS = '';
6167 }
6168 }
6169 if ($use == 'literal') {
6170 if ($forceType) {
6171 $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value</$name>";
6172 } else {
6173 $xml = "<$name$elementNS>$value</$name>";
6174 }
6175 } else {
6176 $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value</$name>";
6177 }
6178 }
6179 $this->debug("in serializeType: returning: $xml");
6180 return $xml;
6181 }
6182
6183 /**
6184 * serializes the attributes for a complexType
6185 *
6186 * @param array $typeDef our internal representation of an XML schema type (or element)
6187 * @param mixed $value a native PHP value (parameter value)
6188 * @param string $ns the namespace of the type
6189 * @param string $uqType the local part of the type
6190 * @return string value serialized as an XML string
6191 * @access private
6192 */
6193 function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType) {
6194 $this->debug("serializeComplexTypeAttributes for XML Schema type $ns:$uqType");
6195 $xml = '';
6196 if (isset($typeDef['extensionBase'])) {
6197 $nsx = $this->getPrefix($typeDef['extensionBase']);
6198 $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
6199 if ($this->getNamespaceFromPrefix($nsx)) {
6200 $nsx = $this->getNamespaceFromPrefix($nsx);
6201 }
6202 if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) {
6203 $this->debug("serialize attributes for extension base $nsx:$uqTypex");
6204 $xml .= $this->serializeComplexTypeAttributes($typeDefx, $value, $nsx, $uqTypex);
6205 } else {
6206 $this->debug("extension base $nsx:$uqTypex is not a supported type");
6207 }
6208 }
6209 if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) {
6210 $this->debug("serialize attributes for XML Schema type $ns:$uqType");
6211 if (is_array($value)) {
6212 $xvalue = $value;
6213 } elseif (is_object($value)) {
6214 $xvalue = get_object_vars($value);
6215 } else {
6216 $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
6217 $xvalue = array();
6218 }
6219 foreach ($typeDef['attrs'] as $aName => $attrs) {
6220 if (isset($xvalue['!' . $aName])) {
6221 $xname = '!' . $aName;
6222 $this->debug("value provided for attribute $aName with key $xname");
6223 } elseif (isset($xvalue[$aName])) {
6224 $xname = $aName;
6225 $this->debug("value provided for attribute $aName with key $xname");
6226 } elseif (isset($attrs['default'])) {
6227 $xname = '!' . $aName;
6228 $xvalue[$xname] = $attrs['default'];
6229 $this->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName);
6230 } else {
6231 $xname = '';
6232 $this->debug("no value provided for attribute $aName");
6233 }
6234 if ($xname) {
6235 $xml .= " $aName=\"" . $this->expandEntities($xvalue[$xname]) . "\"";
6236 }
6237 }
6238 } else {
6239 $this->debug("no attributes to serialize for XML Schema type $ns:$uqType");
6240 }
6241 return $xml;
6242 }
6243
6244 /**
6245 * serializes the elements for a complexType
6246 *
6247 * @param array $typeDef our internal representation of an XML schema type (or element)
6248 * @param mixed $value a native PHP value (parameter value)
6249 * @param string $ns the namespace of the type
6250 * @param string $uqType the local part of the type
6251 * @param string $use use for part (encoded|literal)
6252 * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style)
6253 * @return string value serialized as an XML string
6254 * @access private
6255 */
6256 function serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use='encoded', $encodingStyle=false) {
6257 $this->debug("in serializeComplexTypeElements for XML Schema type $ns:$uqType");
6258 $xml = '';
6259 if (isset($typeDef['extensionBase'])) {
6260 $nsx = $this->getPrefix($typeDef['extensionBase']);
6261 $uqTypex = $this->getLocalPart($typeDef['extensionBase']);
6262 if ($this->getNamespaceFromPrefix($nsx)) {
6263 $nsx = $this->getNamespaceFromPrefix($nsx);
6264 }
6265 if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) {
6266 $this->debug("serialize elements for extension base $nsx:$uqTypex");
6267 $xml .= $this->serializeComplexTypeElements($typeDefx, $value, $nsx, $uqTypex, $use, $encodingStyle);
6268 } else {
6269 $this->debug("extension base $nsx:$uqTypex is not a supported type");
6270 }
6271 }
6272 if (isset($typeDef['elements']) && is_array($typeDef['elements'])) {
6273 $this->debug("in serializeComplexTypeElements, serialize elements for XML Schema type $ns:$uqType");
6274 if (is_array($value)) {
6275 $xvalue = $value;
6276 } elseif (is_object($value)) {
6277 $xvalue = get_object_vars($value);
6278 } else {
6279 $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType");
6280 $xvalue = array();
6281 }
6282 // toggle whether all elements are present - ideally should validate against schema
6283 if (count($typeDef['elements']) != count($xvalue)){
6284 $optionals = true;
6285 }
6286 foreach ($typeDef['elements'] as $eName => $attrs) {
6287 if (!isset($xvalue[$eName])) {
6288 if (isset($attrs['default'])) {
6289 $xvalue[$eName] = $attrs['default'];
6290 $this->debug('use default value of ' . $xvalue[$eName] . ' for element ' . $eName);
6291 }
6292 }
6293 // if user took advantage of a minOccurs=0, then only serialize named parameters
6294 if (isset($optionals)
6295 && (!isset($xvalue[$eName]))
6296 && ( (!isset($attrs['nillable'])) || $attrs['nillable'] != 'true')
6297 ){
6298 if (isset($attrs['minOccurs']) && $attrs['minOccurs'] <> '0') {
6299 $this->debug("apparent error: no value provided for element $eName with minOccurs=" . $attrs['minOccurs']);
6300 }
6301 // do nothing
6302 $this->debug("no value provided for complexType element $eName and element is not nillable, so serialize nothing");
6303 } else {
6304 // get value
6305 if (isset($xvalue[$eName])) {
6306 $v = $xvalue[$eName];
6307 } else {
6308 $v = null;
6309 }
6310 if (isset($attrs['form'])) {
6311 $unqualified = ($attrs['form'] == 'unqualified');
6312 } else {
6313 $unqualified = false;
6314 }
6315 if (isset($attrs['maxOccurs']) && ($attrs['maxOccurs'] == 'unbounded' || $attrs['maxOccurs'] > 1) && isset($v) && is_array($v) && $this->isArraySimpleOrStruct($v) == 'arraySimple') {
6316 $vv = $v;
6317 foreach ($vv as $k => $v) {
6318 if (isset($attrs['type']) || isset($attrs['ref'])) {
6319 // serialize schema-defined type
6320 $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6321 } else {
6322 // serialize generic type (can this ever really happen?)
6323 $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
6324 $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
6325 }
6326 }
6327 } else {
6328 if (is_null($v) && isset($attrs['minOccurs']) && $attrs['minOccurs'] == '0') {
6329 // do nothing
6330 } elseif (is_null($v) && isset($attrs['nillable']) && $attrs['nillable'] == 'true') {
6331 // TODO: serialize a nil correctly, but for now serialize schema-defined type
6332 $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6333 } elseif (isset($attrs['type']) || isset($attrs['ref'])) {
6334 // serialize schema-defined type
6335 $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified);
6336 } else {
6337 // serialize generic type (can this ever really happen?)
6338 $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use");
6339 $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use);
6340 }
6341 }
6342 }
6343 }
6344 } else {
6345 $this->debug("no elements to serialize for XML Schema type $ns:$uqType");
6346 }
6347 return $xml;
6348 }
6349
6350 /**
6351 * adds an XML Schema complex type to the WSDL types
6352 *
6353 * @param string $name
6354 * @param string $typeClass (complexType|simpleType|attribute)
6355 * @param string $phpType currently supported are array and struct (php assoc array)
6356 * @param string $compositor (all|sequence|choice)
6357 * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
6358 * @param array $elements e.g. array ( name => array(name=>'',type=>'') )
6359 * @param array $attrs e.g. array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'xsd:string[]'))
6360 * @param string $arrayType as namespace:name (xsd:string)
6361 * @see nusoap_xmlschema
6362 * @access public
6363 */
6364 function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType='') {
6365 if (count($elements) > 0) {
6366 $eElements = array();
6367 foreach($elements as $n => $e){
6368 // expand each element
6369 $ee = array();
6370 foreach ($e as $k => $v) {
6371 $k = strpos($k,':') ? $this->expandQname($k) : $k;
6372 $v = strpos($v,':') ? $this->expandQname($v) : $v;
6373 $ee[$k] = $v;
6374 }
6375 $eElements[$n] = $ee;
6376 }
6377 $elements = $eElements;
6378 }
6379
6380 if (count($attrs) > 0) {
6381 foreach($attrs as $n => $a){
6382 // expand each attribute
6383 foreach ($a as $k => $v) {
6384 $k = strpos($k,':') ? $this->expandQname($k) : $k;
6385 $v = strpos($v,':') ? $this->expandQname($v) : $v;
6386 $aa[$k] = $v;
6387 }
6388 $eAttrs[$n] = $aa;
6389 }
6390 $attrs = $eAttrs;
6391 }
6392
6393 $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
6394 $arrayType = strpos($arrayType,':') ? $this->expandQname($arrayType) : $arrayType;
6395
6396 $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6397 $this->schemas[$typens][0]->addComplexType($name,$typeClass,$phpType,$compositor,$restrictionBase,$elements,$attrs,$arrayType);
6398 }
6399
6400 /**
6401 * adds an XML Schema simple type to the WSDL types
6402 *
6403 * @param string $name
6404 * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
6405 * @param string $typeClass (should always be simpleType)
6406 * @param string $phpType (should always be scalar)
6407 * @param array $enumeration array of values
6408 * @see nusoap_xmlschema
6409 * @access public
6410 */
6411 function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) {
6412 $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase;
6413
6414 $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6415 $this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType, $enumeration);
6416 }
6417
6418 /**
6419 * adds an element to the WSDL types
6420 *
6421 * @param array $attrs attributes that must include name and type
6422 * @see nusoap_xmlschema
6423 * @access public
6424 */
6425 function addElement($attrs) {
6426 $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns'];
6427 $this->schemas[$typens][0]->addElement($attrs);
6428 }
6429
6430 /**
6431 * register an operation with the server
6432 *
6433 * @param string $name operation (method) name
6434 * @param array $in assoc array of input values: key = param name, value = param type
6435 * @param array $out assoc array of output values: key = param name, value = param type
6436 * @param string $namespace optional The namespace for the operation
6437 * @param string $soapaction optional The soapaction for the operation
6438 * @param string $style (rpc|document) optional The style for the operation Note: when 'document' is specified, parameter and return wrappers are created for you automatically
6439 * @param string $use (encoded|literal) optional The use for the parameters (cannot mix right now)
6440 * @param string $documentation optional The description to include in the WSDL
6441 * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
6442 * @access public
6443 */
6444 function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '', $encodingStyle = ''){
6445 if ($use == 'encoded' && $encodingStyle == '') {
6446 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
6447 }
6448
6449 if ($style == 'document') {
6450 $elements = array();
6451 foreach ($in as $n => $t) {
6452 $elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified');
6453 }
6454 $this->addComplexType($name . 'RequestType', 'complexType', 'struct', 'all', '', $elements);
6455 $this->addElement(array('name' => $name, 'type' => $name . 'RequestType'));
6456 $in = array('parameters' => 'tns:' . $name . '^');
6457
6458 $elements = array();
6459 foreach ($out as $n => $t) {
6460 $elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified');
6461 }
6462 $this->addComplexType($name . 'ResponseType', 'complexType', 'struct', 'all', '', $elements);
6463 $this->addElement(array('name' => $name . 'Response', 'type' => $name . 'ResponseType', 'form' => 'qualified'));
6464 $out = array('parameters' => 'tns:' . $name . 'Response' . '^');
6465 }
6466
6467 // get binding
6468 $this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] =
6469 array(
6470 'name' => $name,
6471 'binding' => $this->serviceName . 'Binding',
6472 'endpoint' => $this->endpoint,
6473 'soapAction' => $soapaction,
6474 'style' => $style,
6475 'input' => array(
6476 'use' => $use,
6477 'namespace' => $namespace,
6478 'encodingStyle' => $encodingStyle,
6479 'message' => $name . 'Request',
6480 'parts' => $in),
6481 'output' => array(
6482 'use' => $use,
6483 'namespace' => $namespace,
6484 'encodingStyle' => $encodingStyle,
6485 'message' => $name . 'Response',
6486 'parts' => $out),
6487 'namespace' => $namespace,
6488 'transport' => 'http://schemas.xmlsoap.org/soap/http',
6489 'documentation' => $documentation);
6490 // add portTypes
6491 // add messages
6492 if($in)
6493 {
6494 foreach($in as $pName => $pType)
6495 {
6496 if(strpos($pType,':')) {
6497 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
6498 }
6499 $this->messages[$name.'Request'][$pName] = $pType;
6500 }
6501 } else {
6502 $this->messages[$name.'Request']= '0';
6503 }
6504 if($out)
6505 {
6506 foreach($out as $pName => $pType)
6507 {
6508 if(strpos($pType,':')) {
6509 $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
6510 }
6511 $this->messages[$name.'Response'][$pName] = $pType;
6512 }
6513 } else {
6514 $this->messages[$name.'Response']= '0';
6515 }
6516 return true;
6517 }
6518}
6519?><?php
6520
6521
6522
6523/**
6524*
6525* nusoap_parser class parses SOAP XML messages into native PHP values
6526*
6527* @author Dietrich Ayala <dietrich@ganx4.com>
6528* @author Scott Nichol <snichol@users.sourceforge.net>
6529* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
6530* @access public
6531*/
6532class nusoap_parser extends nusoap_base {
6533
6534 var $xml = '';
6535 var $xml_encoding = '';
6536 var $method = '';
6537 var $root_struct = '';
6538 var $root_struct_name = '';
6539 var $root_struct_namespace = '';
6540 var $root_header = '';
6541 var $document = ''; // incoming SOAP body (text)
6542 // determines where in the message we are (envelope,header,body,method)
6543 var $status = '';
6544 var $position = 0;
6545 var $depth = 0;
6546 var $default_namespace = '';
6547 var $namespaces = array();
6548 var $message = array();
6549 var $parent = '';
6550 var $fault = false;
6551 var $fault_code = '';
6552 var $fault_str = '';
6553 var $fault_detail = '';
6554 var $depth_array = array();
6555 var $debug_flag = true;
6556 var $soapresponse = NULL; // parsed SOAP Body
6557 var $soapheader = NULL; // parsed SOAP Header
6558 var $responseHeaders = ''; // incoming SOAP headers (text)
6559 var $body_position = 0;
6560 // for multiref parsing:
6561 // array of id => pos
6562 var $ids = array();
6563 // array of id => hrefs => pos
6564 var $multirefs = array();
6565 // toggle for auto-decoding element content
6566 var $decode_utf8 = true;
6567
6568 /**
6569 * constructor that actually does the parsing
6570 *
6571 * @param string $xml SOAP message
6572 * @param string $encoding character encoding scheme of message
6573 * @param string $method method for which XML is parsed (unused?)
6574 * @param string $decode_utf8 whether to decode UTF-8 to ISO-8859-1
6575 * @access public
6576 */
6577 function nusoap_parser($xml,$encoding='UTF-8',$method='',$decode_utf8=true){
6578 parent::nusoap_base();
6579 $this->xml = $xml;
6580 $this->xml_encoding = $encoding;
6581 $this->method = $method;
6582 $this->decode_utf8 = $decode_utf8;
6583
6584 // Check whether content has been read.
6585 if(!empty($xml)){
6586 // Check XML encoding
6587 $pos_xml = strpos($xml, '<?xml');
6588 if ($pos_xml !== FALSE) {
6589 $xml_decl = substr($xml, $pos_xml, strpos($xml, '?>', $pos_xml + 2) - $pos_xml + 1);
6590 if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) {
6591 $xml_encoding = $res[1];
6592 if (strtoupper($xml_encoding) != $encoding) {
6593 $err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'";
6594 $this->debug($err);
6595 if ($encoding != 'ISO-8859-1' || strtoupper($xml_encoding) != 'UTF-8') {
6596 $this->setError($err);
6597 return;
6598 }
6599 // when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed
6600 } else {
6601 $this->debug('Charset from HTTP Content-Type matches encoding from XML declaration');
6602 }
6603 } else {
6604 $this->debug('No encoding specified in XML declaration');
6605 }
6606 } else {
6607 $this->debug('No XML declaration');
6608 }
6609 $this->debug('Entering nusoap_parser(), length='.strlen($xml).', encoding='.$encoding);
6610 // Create an XML parser - why not xml_parser_create_ns?
6611 $this->parser = xml_parser_create($this->xml_encoding);
6612 // Set the options for parsing the XML data.
6613 //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
6614 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
6615 xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding);
6616 // Set the object for the parser.
6617 xml_set_object($this->parser, $this);
6618 // Set the element handlers for the parser.
6619 xml_set_element_handler($this->parser, 'start_element','end_element');
6620 xml_set_character_data_handler($this->parser,'character_data');
6621
6622 // Parse the XML file.
6623 if(!xml_parse($this->parser,$xml,true)){
6624 // Display an error message.
6625 $err = sprintf('XML error parsing SOAP payload on line %d: %s',
6626 xml_get_current_line_number($this->parser),
6627 xml_error_string(xml_get_error_code($this->parser)));
6628 $this->debug($err);
6629 $this->debug("XML payload:\n" . $xml);
6630 $this->setError($err);
6631 } else {
6632 $this->debug('in nusoap_parser ctor, message:');
6633 $this->appendDebug($this->varDump($this->message));
6634 $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name);
6635 // get final value
6636 $this->soapresponse = $this->message[$this->root_struct]['result'];
6637 // get header value
6638 if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){
6639 $this->soapheader = $this->message[$this->root_header]['result'];
6640 }
6641 // resolve hrefs/ids
6642 if(sizeof($this->multirefs) > 0){
6643 foreach($this->multirefs as $id => $hrefs){
6644 $this->debug('resolving multirefs for id: '.$id);
6645 $idVal = $this->buildVal($this->ids[$id]);
6646 if (is_array($idVal) && isset($idVal['!id'])) {
6647 unset($idVal['!id']);
6648 }
6649 foreach($hrefs as $refPos => $ref){
6650 $this->debug('resolving href at pos '.$refPos);
6651 $this->multirefs[$id][$refPos] = $idVal;
6652 }
6653 }
6654 }
6655 }
6656 xml_parser_free($this->parser);
6657 } else {
6658 $this->debug('xml was empty, didn\'t parse!');
6659 $this->setError('xml was empty, didn\'t parse!');
6660 }
6661 }
6662
6663 /**
6664 * start-element handler
6665 *
6666 * @param resource $parser XML parser object
6667 * @param string $name element name
6668 * @param array $attrs associative array of attributes
6669 * @access private
6670 */
6671 function start_element($parser, $name, $attrs) {
6672 // position in a total number of elements, starting from 0
6673 // update class level pos
6674 $pos = $this->position++;
6675 // and set mine
6676 $this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>'');
6677 // depth = how many levels removed from root?
6678 // set mine as current global depth and increment global depth value
6679 $this->message[$pos]['depth'] = $this->depth++;
6680
6681 // else add self as child to whoever the current parent is
6682 if($pos != 0){
6683 $this->message[$this->parent]['children'] .= '|'.$pos;
6684 }
6685 // set my parent
6686 $this->message[$pos]['parent'] = $this->parent;
6687 // set self as current parent
6688 $this->parent = $pos;
6689 // set self as current value for this depth
6690 $this->depth_array[$this->depth] = $pos;
6691 // get element prefix
6692 if(strpos($name,':')){
6693 // get ns prefix
6694 $prefix = substr($name,0,strpos($name,':'));
6695 // get unqualified name
6696 $name = substr(strstr($name,':'),1);
6697 }
6698 // set status
6699 if ($name == 'Envelope' && $this->status == '') {
6700 $this->status = 'envelope';
6701 } elseif ($name == 'Header' && $this->status == 'envelope') {
6702 $this->root_header = $pos;
6703 $this->status = 'header';
6704 } elseif ($name == 'Body' && $this->status == 'envelope'){
6705 $this->status = 'body';
6706 $this->body_position = $pos;
6707 // set method
6708 } elseif($this->status == 'body' && $pos == ($this->body_position+1)) {
6709 $this->status = 'method';
6710 $this->root_struct_name = $name;
6711 $this->root_struct = $pos;
6712 $this->message[$pos]['type'] = 'struct';
6713 $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
6714 }
6715 // set my status
6716 $this->message[$pos]['status'] = $this->status;
6717 // set name
6718 $this->message[$pos]['name'] = htmlspecialchars($name);
6719 // set attrs
6720 $this->message[$pos]['attrs'] = $attrs;
6721
6722 // loop through atts, logging ns and type declarations
6723 $attstr = '';
6724 foreach($attrs as $key => $value){
6725 $key_prefix = $this->getPrefix($key);
6726 $key_localpart = $this->getLocalPart($key);
6727 // if ns declarations, add to class level array of valid namespaces
6728 if($key_prefix == 'xmlns'){
6729 if(preg_match('/^http:\/\/www.w3.org\/[0-9]{4}\/XMLSchema$/',$value)){
6730 $this->XMLSchemaVersion = $value;
6731 $this->namespaces['xsd'] = $this->XMLSchemaVersion;
6732 $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance';
6733 }
6734 $this->namespaces[$key_localpart] = $value;
6735 // set method namespace
6736 if($name == $this->root_struct_name){
6737 $this->methodNamespace = $value;
6738 }
6739 // if it's a type declaration, set type
6740 } elseif($key_localpart == 'type'){
6741 if (isset($this->message[$pos]['type']) && $this->message[$pos]['type'] == 'array') {
6742 // do nothing: already processed arrayType
6743 } else {
6744 $value_prefix = $this->getPrefix($value);
6745 $value_localpart = $this->getLocalPart($value);
6746 $this->message[$pos]['type'] = $value_localpart;
6747 $this->message[$pos]['typePrefix'] = $value_prefix;
6748 if(isset($this->namespaces[$value_prefix])){
6749 $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
6750 } else if(isset($attrs['xmlns:'.$value_prefix])) {
6751 $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix];
6752 }
6753 // should do something here with the namespace of specified type?
6754 }
6755 } elseif($key_localpart == 'arrayType'){
6756 $this->message[$pos]['type'] = 'array';
6757 /* do arrayType ereg here
6758 [1] arrayTypeValue ::= atype asize
6759 [2] atype ::= QName rank*
6760 [3] rank ::= '[' (',')* ']'
6761 [4] asize ::= '[' length~ ']'
6762 [5] length ::= nextDimension* Digit+
6763 [6] nextDimension ::= Digit+ ','
6764 */
6765 $expr = '/([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]/';
6766 if(preg_match($expr,$value,$regs)){
6767 $this->message[$pos]['typePrefix'] = $regs[1];
6768 $this->message[$pos]['arrayTypePrefix'] = $regs[1];
6769 if (isset($this->namespaces[$regs[1]])) {
6770 $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]];
6771 } else if (isset($attrs['xmlns:'.$regs[1]])) {
6772 $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:'.$regs[1]];
6773 }
6774 $this->message[$pos]['arrayType'] = $regs[2];
6775 $this->message[$pos]['arraySize'] = $regs[3];
6776 $this->message[$pos]['arrayCols'] = $regs[4];
6777 }
6778 // specifies nil value (or not)
6779 } elseif ($key_localpart == 'nil'){
6780 $this->message[$pos]['nil'] = ($value == 'true' || $value == '1');
6781 // some other attribute
6782 } elseif ($key != 'href' && $key != 'xmlns' && $key_localpart != 'encodingStyle' && $key_localpart != 'root') {
6783 $this->message[$pos]['xattrs']['!' . $key] = $value;
6784 }
6785
6786 if ($key == 'xmlns') {
6787 $this->default_namespace = $value;
6788 }
6789 // log id
6790 if($key == 'id'){
6791 $this->ids[$value] = $pos;
6792 }
6793 // root
6794 if($key_localpart == 'root' && $value == 1){
6795 $this->status = 'method';
6796 $this->root_struct_name = $name;
6797 $this->root_struct = $pos;
6798 $this->debug("found root struct $this->root_struct_name, pos $pos");
6799 }
6800 // for doclit
6801 $attstr .= " $key=\"$value\"";
6802 }
6803 // get namespace - must be done after namespace atts are processed
6804 if(isset($prefix)){
6805 $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
6806 $this->default_namespace = $this->namespaces[$prefix];
6807 } else {
6808 $this->message[$pos]['namespace'] = $this->default_namespace;
6809 }
6810 if($this->status == 'header'){
6811 if ($this->root_header != $pos) {
6812 $this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
6813 }
6814 } elseif($this->root_struct_name != ''){
6815 $this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>";
6816 }
6817 }
6818
6819 /**
6820 * end-element handler
6821 *
6822 * @param resource $parser XML parser object
6823 * @param string $name element name
6824 * @access private
6825 */
6826 function end_element($parser, $name) {
6827 // position of current element is equal to the last value left in depth_array for my depth
6828 $pos = $this->depth_array[$this->depth--];
6829
6830 // get element prefix
6831 if(strpos($name,':')){
6832 // get ns prefix
6833 $prefix = substr($name,0,strpos($name,':'));
6834 // get unqualified name
6835 $name = substr(strstr($name,':'),1);
6836 }
6837
6838 // build to native type
6839 if(isset($this->body_position) && $pos > $this->body_position){
6840 // deal w/ multirefs
6841 if(isset($this->message[$pos]['attrs']['href'])){
6842 // get id
6843 $id = substr($this->message[$pos]['attrs']['href'],1);
6844 // add placeholder to href array
6845 $this->multirefs[$id][$pos] = 'placeholder';
6846 // add set a reference to it as the result value
6847 $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
6848 // build complexType values
6849 } elseif($this->message[$pos]['children'] != ''){
6850 // if result has already been generated (struct/array)
6851 if(!isset($this->message[$pos]['result'])){
6852 $this->message[$pos]['result'] = $this->buildVal($pos);
6853 }
6854 // build complexType values of attributes and possibly simpleContent
6855 } elseif (isset($this->message[$pos]['xattrs'])) {
6856 if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
6857 $this->message[$pos]['xattrs']['!'] = null;
6858 } elseif (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
6859 if (isset($this->message[$pos]['type'])) {
6860 $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
6861 } else {
6862 $parent = $this->message[$pos]['parent'];
6863 if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
6864 $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6865 } else {
6866 $this->message[$pos]['xattrs']['!'] = $this->message[$pos]['cdata'];
6867 }
6868 }
6869 }
6870 $this->message[$pos]['result'] = $this->message[$pos]['xattrs'];
6871 // set value of simpleType (or nil complexType)
6872 } else {
6873 //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
6874 if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) {
6875 $this->message[$pos]['xattrs']['!'] = null;
6876 } elseif (isset($this->message[$pos]['type'])) {
6877 $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'] : '');
6878 } else {
6879 $parent = $this->message[$pos]['parent'];
6880 if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
6881 $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
6882 } else {
6883 $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
6884 }
6885 }
6886
6887 /* add value to parent's result, if parent is struct/array
6888 $parent = $this->message[$pos]['parent'];
6889 if($this->message[$parent]['type'] != 'map'){
6890 if(strtolower($this->message[$parent]['type']) == 'array'){
6891 $this->message[$parent]['result'][] = $this->message[$pos]['result'];
6892 } else {
6893 $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result'];
6894 }
6895 }
6896 */
6897 }
6898 }
6899
6900 // for doclit
6901 if($this->status == 'header'){
6902 if ($this->root_header != $pos) {
6903 $this->responseHeaders .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
6904 }
6905 } elseif($pos >= $this->root_struct){
6906 $this->document .= "</" . (isset($prefix) ? $prefix . ':' : '') . "$name>";
6907 }
6908 // switch status
6909 if ($pos == $this->root_struct){
6910 $this->status = 'body';
6911 $this->root_struct_namespace = $this->message[$pos]['namespace'];
6912 } elseif ($pos == $this->root_header) {
6913 $this->status = 'envelope';
6914 } elseif ($name == 'Body' && $this->status == 'body') {
6915 $this->status = 'envelope';
6916 } elseif ($name == 'Header' && $this->status == 'header') { // will never happen
6917 $this->status = 'envelope';
6918 } elseif ($name == 'Envelope' && $this->status == 'envelope') {
6919 $this->status = '';
6920 }
6921 // set parent back to my parent
6922 $this->parent = $this->message[$pos]['parent'];
6923 }
6924
6925 /**
6926 * element content handler
6927 *
6928 * @param resource $parser XML parser object
6929 * @param string $data element content
6930 * @access private
6931 */
6932 function character_data($parser, $data){
6933 $pos = $this->depth_array[$this->depth];
6934 if ($this->xml_encoding=='UTF-8'){
6935 // TODO: add an option to disable this for folks who want
6936 // raw UTF-8 that, e.g., might not map to iso-8859-1
6937 // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
6938 if($this->decode_utf8){
6939 $data = utf8_decode($data);
6940 }
6941 }
6942 $this->message[$pos]['cdata'] .= $data;
6943 // for doclit
6944 if($this->status == 'header'){
6945 $this->responseHeaders .= $data;
6946 } else {
6947 $this->document .= $data;
6948 }
6949 }
6950
6951 /**
6952 * get the parsed message (SOAP Body)
6953 *
6954 * @return mixed
6955 * @access public
6956 * @deprecated use get_soapbody instead
6957 */
6958 function get_response(){
6959 return $this->soapresponse;
6960 }
6961
6962 /**
6963 * get the parsed SOAP Body (NULL if there was none)
6964 *
6965 * @return mixed
6966 * @access public
6967 */
6968 function get_soapbody(){
6969 return $this->soapresponse;
6970 }
6971
6972 /**
6973 * get the parsed SOAP Header (NULL if there was none)
6974 *
6975 * @return mixed
6976 * @access public
6977 */
6978 function get_soapheader(){
6979 return $this->soapheader;
6980 }
6981
6982 /**
6983 * get the unparsed SOAP Header
6984 *
6985 * @return string XML or empty if no Header
6986 * @access public
6987 */
6988 function getHeaders(){
6989 return $this->responseHeaders;
6990 }
6991
6992 /**
6993 * decodes simple types into PHP variables
6994 *
6995 * @param string $value value to decode
6996 * @param string $type XML type to decode
6997 * @param string $typens XML type namespace to decode
6998 * @return mixed PHP value
6999 * @access private
7000 */
7001 function decodeSimple($value, $type, $typens) {
7002 // TODO: use the namespace!
7003 if ((!isset($type)) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') {
7004 return (string) $value;
7005 }
7006 if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') {
7007 return (int) $value;
7008 }
7009 if ($type == 'float' || $type == 'double' || $type == 'decimal') {
7010 return (double) $value;
7011 }
7012 if ($type == 'boolean') {
7013 if (strtolower($value) == 'false' || strtolower($value) == 'f') {
7014 return false;
7015 }
7016 return (boolean) $value;
7017 }
7018 if ($type == 'base64' || $type == 'base64Binary') {
7019 $this->debug('Decode base64 value');
7020 return base64_decode($value);
7021 }
7022 // obscure numeric types
7023 if ($type == 'nonPositiveInteger' || $type == 'negativeInteger'
7024 || $type == 'nonNegativeInteger' || $type == 'positiveInteger'
7025 || $type == 'unsignedInt'
7026 || $type == 'unsignedShort' || $type == 'unsignedByte') {
7027 return (int) $value;
7028 }
7029 // bogus: parser treats array with no elements as a simple type
7030 if ($type == 'array') {
7031 return array();
7032 }
7033 // everything else
7034 return (string) $value;
7035 }
7036
7037 /**
7038 * builds response structures for compound values (arrays/structs)
7039 * and scalars
7040 *
7041 * @param integer $pos position in node tree
7042 * @return mixed PHP value
7043 * @access private
7044 */
7045 function buildVal($pos){
7046 if(!isset($this->message[$pos]['type'])){
7047 $this->message[$pos]['type'] = '';
7048 }
7049 $this->debug('in buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']);
7050 // if there are children...
7051 if($this->message[$pos]['children'] != ''){
7052 $this->debug('in buildVal, there are children');
7053 $children = explode('|',$this->message[$pos]['children']);
7054 array_shift($children); // knock off empty
7055 // md array
7056 if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != ''){
7057 $r=0; // rowcount
7058 $c=0; // colcount
7059 foreach($children as $child_pos){
7060 $this->debug("in buildVal, got an MD array element: $r, $c");
7061 $params[$r][] = $this->message[$child_pos]['result'];
7062 $c++;
7063 if($c == $this->message[$pos]['arrayCols']){
7064 $c = 0;
7065 $r++;
7066 }
7067 }
7068 // array
7069 } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array'){
7070 $this->debug('in buildVal, adding array '.$this->message[$pos]['name']);
7071 foreach($children as $child_pos){
7072 $params[] = &$this->message[$child_pos]['result'];
7073 }
7074 // apache Map type: java hashtable
7075 } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){
7076 $this->debug('in buildVal, Java Map '.$this->message[$pos]['name']);
7077 foreach($children as $child_pos){
7078 $kv = explode("|",$this->message[$child_pos]['children']);
7079 $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
7080 }
7081 // generic compound type
7082 //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
7083 } else {
7084 // Apache Vector type: treat as an array
7085 $this->debug('in buildVal, adding Java Vector or generic compound type '.$this->message[$pos]['name']);
7086 if ($this->message[$pos]['type'] == 'Vector' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') {
7087 $notstruct = 1;
7088 } else {
7089 $notstruct = 0;
7090 }
7091 //
7092 foreach($children as $child_pos){
7093 if($notstruct){
7094 $params[] = &$this->message[$child_pos]['result'];
7095 } else {
7096 if (isset($params[$this->message[$child_pos]['name']])) {
7097 // de-serialize repeated element name into an array
7098 if ((!is_array($params[$this->message[$child_pos]['name']])) || (!isset($params[$this->message[$child_pos]['name']][0]))) {
7099 $params[$this->message[$child_pos]['name']] = array($params[$this->message[$child_pos]['name']]);
7100 }
7101 $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result'];
7102 } else {
7103 $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
7104 }
7105 }
7106 }
7107 }
7108 if (isset($this->message[$pos]['xattrs'])) {
7109 $this->debug('in buildVal, handling attributes');
7110 foreach ($this->message[$pos]['xattrs'] as $n => $v) {
7111 $params[$n] = $v;
7112 }
7113 }
7114 // handle simpleContent
7115 if (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') {
7116 $this->debug('in buildVal, handling simpleContent');
7117 if (isset($this->message[$pos]['type'])) {
7118 $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
7119 } else {
7120 $parent = $this->message[$pos]['parent'];
7121 if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
7122 $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7123 } else {
7124 $params['!'] = $this->message[$pos]['cdata'];
7125 }
7126 }
7127 }
7128 $ret = is_array($params) ? $params : array();
7129 $this->debug('in buildVal, return:');
7130 $this->appendDebug($this->varDump($ret));
7131 return $ret;
7132 } else {
7133 $this->debug('in buildVal, no children, building scalar');
7134 $cdata = isset($this->message[$pos]['cdata']) ? $this->message[$pos]['cdata'] : '';
7135 if (isset($this->message[$pos]['type'])) {
7136 $ret = $this->decodeSimple($cdata, $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : '');
7137 $this->debug("in buildVal, return: $ret");
7138 return $ret;
7139 }
7140 $parent = $this->message[$pos]['parent'];
7141 if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) {
7142 $ret = $this->decodeSimple($cdata, $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : '');
7143 $this->debug("in buildVal, return: $ret");
7144 return $ret;
7145 }
7146 $ret = $this->message[$pos]['cdata'];
7147 $this->debug("in buildVal, return: $ret");
7148 return $ret;
7149 }
7150 }
7151}
7152
7153/**
7154 * Backward compatibility
7155 */
7156class soap_parser extends nusoap_parser {
7157}
7158
7159?><?php
7160
7161
7162
7163/**
7164*
7165* [nu]soapclient higher level class for easy usage.
7166*
7167* usage:
7168*
7169* // instantiate client with server info
7170* $soapclient = new nusoap_client( string path [ ,mixed wsdl] );
7171*
7172* // call method, get results
7173* echo $soapclient->call( string methodname [ ,array parameters] );
7174*
7175* // bye bye client
7176* unset($soapclient);
7177*
7178* @author Dietrich Ayala <dietrich@ganx4.com>
7179* @author Scott Nichol <snichol@users.sourceforge.net>
7180* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
7181* @access public
7182*/
7183class nusoap_client extends nusoap_base {
7184
7185 var $username = ''; // Username for HTTP authentication
7186 var $password = ''; // Password for HTTP authentication
7187 var $authtype = ''; // Type of HTTP authentication
7188 var $certRequest = array(); // Certificate for HTTP SSL authentication
7189 var $requestHeaders = false; // SOAP headers in request (text)
7190 var $responseHeaders = ''; // SOAP headers from response (incomplete namespace resolution) (text)
7191 var $responseHeader = NULL; // SOAP Header from response (parsed)
7192 var $document = ''; // SOAP body response portion (incomplete namespace resolution) (text)
7193 var $endpoint;
7194 var $forceEndpoint = ''; // overrides WSDL endpoint
7195 var $proxyhost = '';
7196 var $proxyport = '';
7197 var $proxyusername = '';
7198 var $proxypassword = '';
7199 var $portName = ''; // port name to use in WSDL
7200 var $xml_encoding = ''; // character set encoding of incoming (response) messages
7201 var $http_encoding = false;
7202 var $timeout = 0; // HTTP connection timeout
7203 var $response_timeout = 30; // HTTP response timeout
7204 var $endpointType = ''; // soap|wsdl, empty for WSDL initialization error
7205 var $persistentConnection = false;
7206 var $defaultRpcParams = false; // This is no longer used
7207 var $request = ''; // HTTP request
7208 var $response = ''; // HTTP response
7209 var $responseData = ''; // SOAP payload of response
7210 var $cookies = array(); // Cookies from response or for request
7211 var $decode_utf8 = true; // toggles whether the parser decodes element content w/ utf8_decode()
7212 var $operations = array(); // WSDL operations, empty for WSDL initialization error
7213 var $curl_options = array(); // User-specified cURL options
7214 var $bindingType = ''; // WSDL operation binding type
7215 var $use_curl = false; // whether to always try to use cURL
7216
7217 /*
7218 * fault related variables
7219 */
7220 /**
7221 * @var fault
7222 * @access public
7223 */
7224 var $fault;
7225 /**
7226 * @var faultcode
7227 * @access public
7228 */
7229 var $faultcode;
7230 /**
7231 * @var faultstring
7232 * @access public
7233 */
7234 var $faultstring;
7235 /**
7236 * @var faultdetail
7237 * @access public
7238 */
7239 var $faultdetail;
7240
7241 /**
7242 * constructor
7243 *
7244 * @param mixed $endpoint SOAP server or WSDL URL (string), or wsdl instance (object)
7245 * @param mixed $wsdl optional, set to 'wsdl' or true if using WSDL
7246 * @param string $proxyhost optional
7247 * @param string $proxyport optional
7248 * @param string $proxyusername optional
7249 * @param string $proxypassword optional
7250 * @param integer $timeout set the connection timeout
7251 * @param integer $response_timeout set the response timeout
7252 * @param string $portName optional portName in WSDL document
7253 * @access public
7254 */
7255 function nusoap_client($endpoint,$wsdl = false,$proxyhost = false,$proxyport = false,$proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30, $portName = ''){
7256 parent::nusoap_base();
7257 $this->endpoint = $endpoint;
7258 $this->proxyhost = $proxyhost;
7259 $this->proxyport = $proxyport;
7260 $this->proxyusername = $proxyusername;
7261 $this->proxypassword = $proxypassword;
7262 $this->timeout = $timeout;
7263 $this->response_timeout = $response_timeout;
7264 $this->portName = $portName;
7265
7266 $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout");
7267 $this->appendDebug('endpoint=' . $this->varDump($endpoint));
7268
7269 // make values
7270 if($wsdl){
7271 if (is_object($endpoint) && (get_class($endpoint) == 'wsdl')) {
7272 $this->wsdl = $endpoint;
7273 $this->endpoint = $this->wsdl->wsdl;
7274 $this->wsdlFile = $this->endpoint;
7275 $this->debug('existing wsdl instance created from ' . $this->endpoint);
7276 $this->checkWSDL();
7277 } else {
7278 $this->wsdlFile = $this->endpoint;
7279 $this->wsdl = null;
7280 $this->debug('will use lazy evaluation of wsdl from ' . $this->endpoint);
7281 }
7282 $this->endpointType = 'wsdl';
7283 } else {
7284 $this->debug("instantiate SOAP with endpoint at $endpoint");
7285 $this->endpointType = 'soap';
7286 }
7287 }
7288
7289 /**
7290 * calls method, returns PHP native type
7291 *
7292 * @param string $operation SOAP server URL or path
7293 * @param mixed $params An array, associative or simple, of the parameters
7294 * for the method call, or a string that is the XML
7295 * for the call. For rpc style, this call will
7296 * wrap the XML in a tag named after the method, as
7297 * well as the SOAP Envelope and Body. For document
7298 * style, this will only wrap with the Envelope and Body.
7299 * IMPORTANT: when using an array with document style,
7300 * in which case there
7301 * is really one parameter, the root of the fragment
7302 * used in the call, which encloses what programmers
7303 * normally think of parameters. A parameter array
7304 * *must* include the wrapper.
7305 * @param string $namespace optional method namespace (WSDL can override)
7306 * @param string $soapAction optional SOAPAction value (WSDL can override)
7307 * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
7308 * @param boolean $rpcParams optional (no longer used)
7309 * @param string $style optional (rpc|document) the style to use when serializing parameters (WSDL can override)
7310 * @param string $use optional (encoded|literal) the use when serializing parameters (WSDL can override)
7311 * @return mixed response from SOAP call, normally an associative array mirroring the structure of the XML response, false for certain fatal errors
7312 * @access public
7313 */
7314 function call($operation,$params=array(),$namespace='http://tempuri.org',$soapAction='',$headers=false,$rpcParams=null,$style='rpc',$use='encoded'){
7315 $this->operation = $operation;
7316 $this->fault = false;
7317 $this->setError('');
7318 $this->request = '';
7319 $this->response = '';
7320 $this->responseData = '';
7321 $this->faultstring = '';
7322 $this->faultcode = '';
7323 $this->opData = array();
7324
7325 $this->debug("call: operation=$operation, namespace=$namespace, soapAction=$soapAction, rpcParams=$rpcParams, style=$style, use=$use, endpointType=$this->endpointType");
7326 $this->appendDebug('params=' . $this->varDump($params));
7327 $this->appendDebug('headers=' . $this->varDump($headers));
7328 if ($headers) {
7329 $this->requestHeaders = $headers;
7330 }
7331 if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
7332 $this->loadWSDL();
7333 if ($this->getError())
7334 return false;
7335 }
7336 // serialize parameters
7337 if($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)){
7338 // use WSDL for operation
7339 $this->opData = $opData;
7340 $this->debug("found operation");
7341 $this->appendDebug('opData=' . $this->varDump($opData));
7342 if (isset($opData['soapAction'])) {
7343 $soapAction = $opData['soapAction'];
7344 }
7345 if (! $this->forceEndpoint) {
7346 $this->endpoint = $opData['endpoint'];
7347 } else {
7348 $this->endpoint = $this->forceEndpoint;
7349 }
7350 $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : $namespace;
7351 $style = $opData['style'];
7352 $use = $opData['input']['use'];
7353 // add ns to ns array
7354 if($namespace != '' && !isset($this->wsdl->namespaces[$namespace])){
7355 $nsPrefix = 'ns' . rand(1000, 9999);
7356 $this->wsdl->namespaces[$nsPrefix] = $namespace;
7357 }
7358 $nsPrefix = $this->wsdl->getPrefixFromNamespace($namespace);
7359 // serialize payload
7360 if (is_string($params)) {
7361 $this->debug("serializing param string for WSDL operation $operation");
7362 $payload = $params;
7363 } elseif (is_array($params)) {
7364 $this->debug("serializing param array for WSDL operation $operation");
7365 $payload = $this->wsdl->serializeRPCParameters($operation,'input',$params,$this->bindingType);
7366 } else {
7367 $this->debug('params must be array or string');
7368 $this->setError('params must be array or string');
7369 return false;
7370 }
7371 $usedNamespaces = $this->wsdl->usedNamespaces;
7372 if (isset($opData['input']['encodingStyle'])) {
7373 $encodingStyle = $opData['input']['encodingStyle'];
7374 } else {
7375 $encodingStyle = '';
7376 }
7377 $this->appendDebug($this->wsdl->getDebug());
7378 $this->wsdl->clearDebug();
7379 if ($errstr = $this->wsdl->getError()) {
7380 $this->debug('got wsdl error: '.$errstr);
7381 $this->setError('wsdl error: '.$errstr);
7382 return false;
7383 }
7384 } elseif($this->endpointType == 'wsdl') {
7385 // operation not in WSDL
7386 $this->appendDebug($this->wsdl->getDebug());
7387 $this->wsdl->clearDebug();
7388 $this->setError('operation '.$operation.' not present in WSDL.');
7389 $this->debug("operation '$operation' not present in WSDL.");
7390 return false;
7391 } else {
7392 // no WSDL
7393 //$this->namespaces['ns1'] = $namespace;
7394 $nsPrefix = 'ns' . rand(1000, 9999);
7395 // serialize
7396 $payload = '';
7397 if (is_string($params)) {
7398 $this->debug("serializing param string for operation $operation");
7399 $payload = $params;
7400 } elseif (is_array($params)) {
7401 $this->debug("serializing param array for operation $operation");
7402 foreach($params as $k => $v){
7403 $payload .= $this->serialize_val($v,$k,false,false,false,false,$use);
7404 }
7405 } else {
7406 $this->debug('params must be array or string');
7407 $this->setError('params must be array or string');
7408 return false;
7409 }
7410 $usedNamespaces = array();
7411 if ($use == 'encoded') {
7412 $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
7413 } else {
7414 $encodingStyle = '';
7415 }
7416 }
7417 // wrap RPC calls with method element
7418 if ($style == 'rpc') {
7419 if ($use == 'literal') {
7420 $this->debug("wrapping RPC request with literal method element");
7421 if ($namespace) {
7422 // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace
7423 $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
7424 $payload .
7425 "</$nsPrefix:$operation>";
7426 } else {
7427 $payload = "<$operation>" . $payload . "</$operation>";
7428 }
7429 } else {
7430 $this->debug("wrapping RPC request with encoded method element");
7431 if ($namespace) {
7432 $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" .
7433 $payload .
7434 "</$nsPrefix:$operation>";
7435 } else {
7436 $payload = "<$operation>" .
7437 $payload .
7438 "</$operation>";
7439 }
7440 }
7441 }
7442 // serialize envelope
7443 $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,$usedNamespaces,$style,$use,$encodingStyle);
7444 $this->debug("endpoint=$this->endpoint, soapAction=$soapAction, namespace=$namespace, style=$style, use=$use, encodingStyle=$encodingStyle");
7445 $this->debug('SOAP message length=' . strlen($soapmsg) . ' contents (max 1000 bytes)=' . substr($soapmsg, 0, 1000));
7446 // send
7447 $return = $this->send($this->getHTTPBody($soapmsg),$soapAction,$this->timeout,$this->response_timeout);
7448 if($errstr = $this->getError()){
7449 $this->debug('Error: '.$errstr);
7450 return false;
7451 } else {
7452 $this->return = $return;
7453 $this->debug('sent message successfully and got a(n) '.gettype($return));
7454 $this->appendDebug('return=' . $this->varDump($return));
7455
7456 // fault?
7457 if(is_array($return) && isset($return['faultcode'])){
7458 $this->debug('got fault');
7459 $this->setError($return['faultcode'].': '.$return['faultstring']);
7460 $this->fault = true;
7461 foreach($return as $k => $v){
7462 $this->$k = $v;
7463 $this->debug("$k = $v<br>");
7464 }
7465 return $return;
7466 } elseif ($style == 'document') {
7467 // NOTE: if the response is defined to have multiple parts (i.e. unwrapped),
7468 // we are only going to return the first part here...sorry about that
7469 return $return;
7470 } else {
7471 // array of return values
7472 if(is_array($return)){
7473 // multiple 'out' parameters, which we return wrapped up
7474 // in the array
7475 if(sizeof($return) > 1){
7476 return $return;
7477 }
7478 // single 'out' parameter (normally the return value)
7479 $return = array_shift($return);
7480 $this->debug('return shifted value: ');
7481 $this->appendDebug($this->varDump($return));
7482 return $return;
7483 // nothing returned (ie, echoVoid)
7484 } else {
7485 return "";
7486 }
7487 }
7488 }
7489 }
7490
7491 /**
7492 * check WSDL passed as an instance or pulled from an endpoint
7493 *
7494 * @access private
7495 */
7496 function checkWSDL() {
7497 $this->appendDebug($this->wsdl->getDebug());
7498 $this->wsdl->clearDebug();
7499 $this->debug('checkWSDL');
7500 // catch errors
7501 if ($errstr = $this->wsdl->getError()) {
7502 $this->appendDebug($this->wsdl->getDebug());
7503 $this->wsdl->clearDebug();
7504 $this->debug('got wsdl error: '.$errstr);
7505 $this->setError('wsdl error: '.$errstr);
7506 } elseif ($this->operations = $this->wsdl->getOperations($this->portName, 'soap')) {
7507 $this->appendDebug($this->wsdl->getDebug());
7508 $this->wsdl->clearDebug();
7509 $this->bindingType = 'soap';
7510 $this->debug('got '.count($this->operations).' operations from wsdl '.$this->wsdlFile.' for binding type '.$this->bindingType);
7511 } elseif ($this->operations = $this->wsdl->getOperations($this->portName, 'soap12')) {
7512 $this->appendDebug($this->wsdl->getDebug());
7513 $this->wsdl->clearDebug();
7514 $this->bindingType = 'soap12';
7515 $this->debug('got '.count($this->operations).' operations from wsdl '.$this->wsdlFile.' for binding type '.$this->bindingType);
7516 $this->debug('**************** WARNING: SOAP 1.2 BINDING *****************');
7517 } else {
7518 $this->appendDebug($this->wsdl->getDebug());
7519 $this->wsdl->clearDebug();
7520 $this->debug('getOperations returned false');
7521 $this->setError('no operations defined in the WSDL document!');
7522 }
7523 }
7524
7525 /**
7526 * instantiate wsdl object and parse wsdl file
7527 *
7528 * @access public
7529 */
7530 function loadWSDL() {
7531 $this->debug('instantiating wsdl class with doc: '.$this->wsdlFile);
7532 $this->wsdl = new wsdl('',$this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword,$this->timeout,$this->response_timeout,$this->curl_options,$this->use_curl);
7533 $this->wsdl->setCredentials($this->username, $this->password, $this->authtype, $this->certRequest);
7534 $this->wsdl->fetchWSDL($this->wsdlFile);
7535 $this->checkWSDL();
7536 }
7537
7538 /**
7539 * get available data pertaining to an operation
7540 *
7541 * @param string $operation operation name
7542 * @return array array of data pertaining to the operation
7543 * @access public
7544 */
7545 function getOperationData($operation){
7546 if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
7547 $this->loadWSDL();
7548 if ($this->getError())
7549 return false;
7550 }
7551 if(isset($this->operations[$operation])){
7552 return $this->operations[$operation];
7553 }
7554 $this->debug("No data for operation: $operation");
7555 }
7556
7557 /**
7558 * send the SOAP message
7559 *
7560 * Note: if the operation has multiple return values
7561 * the return value of this method will be an array
7562 * of those values.
7563 *
7564 * @param string $msg a SOAPx4 soapmsg object
7565 * @param string $soapaction SOAPAction value
7566 * @param integer $timeout set connection timeout in seconds
7567 * @param integer $response_timeout set response timeout in seconds
7568 * @return mixed native PHP types.
7569 * @access private
7570 */
7571 function send($msg, $soapaction = '', $timeout=0, $response_timeout=30) {
7572 $this->checkCookies();
7573 // detect transport
7574 switch(true){
7575 // http(s)
7576 case preg_match('/^http/',$this->endpoint):
7577 $this->debug('transporting via HTTP');
7578 if($this->persistentConnection == true && is_object($this->persistentConnection)){
7579 $http =& $this->persistentConnection;
7580 } else {
7581 $http = new soap_transport_http($this->endpoint, $this->curl_options, $this->use_curl);
7582 if ($this->persistentConnection) {
7583 $http->usePersistentConnection();
7584 }
7585 }
7586 $http->setContentType($this->getHTTPContentType(), $this->getHTTPContentTypeCharset());
7587 $http->setSOAPAction($soapaction);
7588 if($this->proxyhost && $this->proxyport){
7589 $http->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword);
7590 }
7591 if($this->authtype != '') {
7592 $http->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest);
7593 }
7594 if($this->http_encoding != ''){
7595 $http->setEncoding($this->http_encoding);
7596 }
7597 $this->debug('sending message, length='.strlen($msg));
7598 if(preg_match('/^http:/',$this->endpoint)){
7599 //if(strpos($this->endpoint,'http:')){
7600 $this->responseData = $http->send($msg,$timeout,$response_timeout,$this->cookies);
7601 } elseif(preg_match('/^https/',$this->endpoint)){
7602 //} elseif(strpos($this->endpoint,'https:')){
7603 //if(phpversion() == '4.3.0-dev'){
7604 //$response = $http->send($msg,$timeout,$response_timeout);
7605 //$this->request = $http->outgoing_payload;
7606 //$this->response = $http->incoming_payload;
7607 //} else
7608 $this->responseData = $http->sendHTTPS($msg,$timeout,$response_timeout,$this->cookies);
7609 } else {
7610 $this->setError('no http/s in endpoint url');
7611 }
7612 $this->request = $http->outgoing_payload;
7613 $this->response = $http->incoming_payload;
7614 $this->appendDebug($http->getDebug());
7615 $this->UpdateCookies($http->incoming_cookies);
7616
7617 // save transport object if using persistent connections
7618 if ($this->persistentConnection) {
7619 $http->clearDebug();
7620 if (!is_object($this->persistentConnection)) {
7621 $this->persistentConnection = $http;
7622 }
7623 }
7624
7625 if($err = $http->getError()){
7626 $this->setError('HTTP Error: '.$err);
7627 return false;
7628 } elseif($this->getError()){
7629 return false;
7630 } else {
7631 $this->debug('got response, length='. strlen($this->responseData).' type='.$http->incoming_headers['content-type']);
7632 return $this->parseResponse($http->incoming_headers, $this->responseData);
7633 }
7634 break;
7635 default:
7636 $this->setError('no transport found, or selected transport is not yet supported!');
7637 return false;
7638 break;
7639 }
7640 }
7641
7642 /**
7643 * processes SOAP message returned from server
7644 *
7645 * @param array $headers The HTTP headers
7646 * @param string $data unprocessed response data from server
7647 * @return mixed value of the message, decoded into a PHP type
7648 * @access private
7649 */
7650 function parseResponse($headers, $data) {
7651 $this->debug('Entering parseResponse() for data of length ' . strlen($data) . ' headers:');
7652 $this->appendDebug($this->varDump($headers));
7653 if (!isset($headers['content-type'])) {
7654 $this->setError('Response not of type text/xml (no content-type header)');
7655 return false;
7656 }
7657 if (!strstr($headers['content-type'], 'text/xml')) {
7658 $this->setError('Response not of type text/xml: ' . $headers['content-type']);
7659 return false;
7660 }
7661 if (strpos($headers['content-type'], '=')) {
7662 $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
7663 $this->debug('Got response encoding: ' . $enc);
7664 if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){
7665 $this->xml_encoding = strtoupper($enc);
7666 } else {
7667 $this->xml_encoding = 'US-ASCII';
7668 }
7669 } else {
7670 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
7671 $this->xml_encoding = 'ISO-8859-1';
7672 }
7673 $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
7674 $parser = new nusoap_parser($data,$this->xml_encoding,$this->operation,$this->decode_utf8);
7675 // add parser debug data to our debug
7676 $this->appendDebug($parser->getDebug());
7677 // if parse errors
7678 if($errstr = $parser->getError()){
7679 $this->setError( $errstr);
7680 // destroy the parser object
7681 unset($parser);
7682 return false;
7683 } else {
7684 // get SOAP headers
7685 $this->responseHeaders = $parser->getHeaders();
7686 // get SOAP headers
7687 $this->responseHeader = $parser->get_soapheader();
7688 // get decoded message
7689 $return = $parser->get_soapbody();
7690 // add document for doclit support
7691 $this->document = $parser->document;
7692 // destroy the parser object
7693 unset($parser);
7694 // return decode message
7695 return $return;
7696 }
7697 }
7698
7699 /**
7700 * sets user-specified cURL options
7701 *
7702 * @param mixed $option The cURL option (always integer?)
7703 * @param mixed $value The cURL option value
7704 * @access public
7705 */
7706 function setCurlOption($option, $value) {
7707 $this->debug("setCurlOption option=$option, value=");
7708 $this->appendDebug($this->varDump($value));
7709 $this->curl_options[$option] = $value;
7710 }
7711
7712 /**
7713 * sets the SOAP endpoint, which can override WSDL
7714 *
7715 * @param string $endpoint The endpoint URL to use, or empty string or false to prevent override
7716 * @access public
7717 */
7718 function setEndpoint($endpoint) {
7719 $this->debug("setEndpoint(\"$endpoint\")");
7720 $this->forceEndpoint = $endpoint;
7721 }
7722
7723 /**
7724 * set the SOAP headers
7725 *
7726 * @param mixed $headers String of XML with SOAP header content, or array of soapval objects for SOAP headers
7727 * @access public
7728 */
7729 function setHeaders($headers){
7730 $this->debug("setHeaders headers=");
7731 $this->appendDebug($this->varDump($headers));
7732 $this->requestHeaders = $headers;
7733 }
7734
7735 /**
7736 * get the SOAP response headers (namespace resolution incomplete)
7737 *
7738 * @return string
7739 * @access public
7740 */
7741 function getHeaders(){
7742 return $this->responseHeaders;
7743 }
7744
7745 /**
7746 * get the SOAP response Header (parsed)
7747 *
7748 * @return mixed
7749 * @access public
7750 */
7751 function getHeader(){
7752 return $this->responseHeader;
7753 }
7754
7755 /**
7756 * set proxy info here
7757 *
7758 * @param string $proxyhost
7759 * @param string $proxyport
7760 * @param string $proxyusername
7761 * @param string $proxypassword
7762 * @access public
7763 */
7764 function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') {
7765 $this->proxyhost = $proxyhost;
7766 $this->proxyport = $proxyport;
7767 $this->proxyusername = $proxyusername;
7768 $this->proxypassword = $proxypassword;
7769 }
7770
7771 /**
7772 * if authenticating, set user credentials here
7773 *
7774 * @param string $username
7775 * @param string $password
7776 * @param string $authtype (basic|digest|certificate|ntlm)
7777 * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs)
7778 * @access public
7779 */
7780 function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) {
7781 $this->debug("setCredentials username=$username authtype=$authtype certRequest=");
7782 $this->appendDebug($this->varDump($certRequest));
7783 $this->username = $username;
7784 $this->password = $password;
7785 $this->authtype = $authtype;
7786 $this->certRequest = $certRequest;
7787 }
7788
7789 /**
7790 * use HTTP encoding
7791 *
7792 * @param string $enc HTTP encoding
7793 * @access public
7794 */
7795 function setHTTPEncoding($enc='gzip, deflate'){
7796 $this->debug("setHTTPEncoding(\"$enc\")");
7797 $this->http_encoding = $enc;
7798 }
7799
7800 /**
7801 * Set whether to try to use cURL connections if possible
7802 *
7803 * @param boolean $use Whether to try to use cURL
7804 * @access public
7805 */
7806 function setUseCURL($use) {
7807 $this->debug("setUseCURL($use)");
7808 $this->use_curl = $use;
7809 }
7810
7811 /**
7812 * use HTTP persistent connections if possible
7813 *
7814 * @access public
7815 */
7816 function useHTTPPersistentConnection(){
7817 $this->debug("useHTTPPersistentConnection");
7818 $this->persistentConnection = true;
7819 }
7820
7821 /**
7822 * gets the default RPC parameter setting.
7823 * If true, default is that call params are like RPC even for document style.
7824 * Each call() can override this value.
7825 *
7826 * This is no longer used.
7827 *
7828 * @return boolean
7829 * @access public
7830 * @deprecated
7831 */
7832 function getDefaultRpcParams() {
7833 return $this->defaultRpcParams;
7834 }
7835
7836 /**
7837 * sets the default RPC parameter setting.
7838 * If true, default is that call params are like RPC even for document style
7839 * Each call() can override this value.
7840 *
7841 * This is no longer used.
7842 *
7843 * @param boolean $rpcParams
7844 * @access public
7845 * @deprecated
7846 */
7847 function setDefaultRpcParams($rpcParams) {
7848 $this->defaultRpcParams = $rpcParams;
7849 }
7850
7851 /**
7852 * dynamically creates an instance of a proxy class,
7853 * allowing user to directly call methods from wsdl
7854 *
7855 * @return object soap_proxy object
7856 * @access public
7857 */
7858 function getProxy() {
7859 $r = rand();
7860 $evalStr = $this->_getProxyClassCode($r);
7861 //$this->debug("proxy class: $evalStr");
7862 if ($this->getError()) {
7863 $this->debug("Error from _getProxyClassCode, so return NULL");
7864 return null;
7865 }
7866 // eval the class
7867 eval($evalStr);
7868 // instantiate proxy object
7869 eval("\$proxy = new nusoap_proxy_$r('');");
7870 // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
7871 $proxy->endpointType = 'wsdl';
7872 $proxy->wsdlFile = $this->wsdlFile;
7873 $proxy->wsdl = $this->wsdl;
7874 $proxy->operations = $this->operations;
7875 $proxy->defaultRpcParams = $this->defaultRpcParams;
7876 // transfer other state
7877 $proxy->soap_defencoding = $this->soap_defencoding;
7878 $proxy->username = $this->username;
7879 $proxy->password = $this->password;
7880 $proxy->authtype = $this->authtype;
7881 $proxy->certRequest = $this->certRequest;
7882 $proxy->requestHeaders = $this->requestHeaders;
7883 $proxy->endpoint = $this->endpoint;
7884 $proxy->forceEndpoint = $this->forceEndpoint;
7885 $proxy->proxyhost = $this->proxyhost;
7886 $proxy->proxyport = $this->proxyport;
7887 $proxy->proxyusername = $this->proxyusername;
7888 $proxy->proxypassword = $this->proxypassword;
7889 $proxy->http_encoding = $this->http_encoding;
7890 $proxy->timeout = $this->timeout;
7891 $proxy->response_timeout = $this->response_timeout;
7892 $proxy->persistentConnection = &$this->persistentConnection;
7893 $proxy->decode_utf8 = $this->decode_utf8;
7894 $proxy->curl_options = $this->curl_options;
7895 $proxy->bindingType = $this->bindingType;
7896 $proxy->use_curl = $this->use_curl;
7897 return $proxy;
7898 }
7899
7900 /**
7901 * dynamically creates proxy class code
7902 *
7903 * @return string PHP/NuSOAP code for the proxy class
7904 * @access private
7905 */
7906 function _getProxyClassCode($r) {
7907 $this->debug("in getProxy endpointType=$this->endpointType");
7908 $this->appendDebug("wsdl=" . $this->varDump($this->wsdl));
7909 if ($this->endpointType != 'wsdl') {
7910 $evalStr = 'A proxy can only be created for a WSDL client';
7911 $this->setError($evalStr);
7912 $evalStr = "echo \"$evalStr\";";
7913 return $evalStr;
7914 }
7915 if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) {
7916 $this->loadWSDL();
7917 if ($this->getError()) {
7918 return "echo \"" . $this->getError() . "\";";
7919 }
7920 }
7921 $evalStr = '';
7922 foreach ($this->operations as $operation => $opData) {
7923 if ($operation != '') {
7924 // create param string and param comment string
7925 if (sizeof($opData['input']['parts']) > 0) {
7926 $paramStr = '';
7927 $paramArrayStr = '';
7928 $paramCommentStr = '';
7929 foreach ($opData['input']['parts'] as $name => $type) {
7930 $paramStr .= "\$$name, ";
7931 $paramArrayStr .= "'$name' => \$$name, ";
7932 $paramCommentStr .= "$type \$$name, ";
7933 }
7934 $paramStr = substr($paramStr, 0, strlen($paramStr)-2);
7935 $paramArrayStr = substr($paramArrayStr, 0, strlen($paramArrayStr)-2);
7936 $paramCommentStr = substr($paramCommentStr, 0, strlen($paramCommentStr)-2);
7937 } else {
7938 $paramStr = '';
7939 $paramArrayStr = '';
7940 $paramCommentStr = 'void';
7941 }
7942 $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
7943 $evalStr .= "// $paramCommentStr
7944 function " . str_replace('.', '__', $operation) . "($paramStr) {
7945 \$params = array($paramArrayStr);
7946 return \$this->call('$operation', \$params, '".$opData['namespace']."', '".(isset($opData['soapAction']) ? $opData['soapAction'] : '')."');
7947 }
7948 ";
7949 unset($paramStr);
7950 unset($paramCommentStr);
7951 }
7952 }
7953 $evalStr = 'class nusoap_proxy_'.$r.' extends nusoap_client {
7954 '.$evalStr.'
7955}';
7956 return $evalStr;
7957 }
7958
7959 /**
7960 * dynamically creates proxy class code
7961 *
7962 * @return string PHP/NuSOAP code for the proxy class
7963 * @access public
7964 */
7965 function getProxyClassCode() {
7966 $r = rand();
7967 return $this->_getProxyClassCode($r);
7968 }
7969
7970 /**
7971 * gets the HTTP body for the current request.
7972 *
7973 * @param string $soapmsg The SOAP payload
7974 * @return string The HTTP body, which includes the SOAP payload
7975 * @access private
7976 */
7977 function getHTTPBody($soapmsg) {
7978 return $soapmsg;
7979 }
7980
7981 /**
7982 * gets the HTTP content type for the current request.
7983 *
7984 * Note: getHTTPBody must be called before this.
7985 *
7986 * @return string the HTTP content type for the current request.
7987 * @access private
7988 */
7989 function getHTTPContentType() {
7990 return 'text/xml';
7991 }
7992
7993 /**
7994 * gets the HTTP content type charset for the current request.
7995 * returns false for non-text content types.
7996 *
7997 * Note: getHTTPBody must be called before this.
7998 *
7999 * @return string the HTTP content type charset for the current request.
8000 * @access private
8001 */
8002 function getHTTPContentTypeCharset() {
8003 return $this->soap_defencoding;
8004 }
8005
8006 /*
8007 * whether or not parser should decode utf8 element content
8008 *
8009 * @return always returns true
8010 * @access public
8011 */
8012 function decodeUTF8($bool){
8013 $this->decode_utf8 = $bool;
8014 return true;
8015 }
8016
8017 /**
8018 * adds a new Cookie into $this->cookies array
8019 *
8020 * @param string $name Cookie Name
8021 * @param string $value Cookie Value
8022 * @return boolean if cookie-set was successful returns true, else false
8023 * @access public
8024 */
8025 function setCookie($name, $value) {
8026 if (strlen($name) == 0) {
8027 return false;
8028 }
8029 $this->cookies[] = array('name' => $name, 'value' => $value);
8030 return true;
8031 }
8032
8033 /**
8034 * gets all Cookies
8035 *
8036 * @return array with all internal cookies
8037 * @access public
8038 */
8039 function getCookies() {
8040 return $this->cookies;
8041 }
8042
8043 /**
8044 * checks all Cookies and delete those which are expired
8045 *
8046 * @return boolean always return true
8047 * @access private
8048 */
8049 function checkCookies() {
8050 if (sizeof($this->cookies) == 0) {
8051 return true;
8052 }
8053 $this->debug('checkCookie: check ' . sizeof($this->cookies) . ' cookies');
8054 $curr_cookies = $this->cookies;
8055 $this->cookies = array();
8056 foreach ($curr_cookies as $cookie) {
8057 if (! is_array($cookie)) {
8058 $this->debug('Remove cookie that is not an array');
8059 continue;
8060 }
8061 if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) {
8062 if (strtotime($cookie['expires']) > time()) {
8063 $this->cookies[] = $cookie;
8064 } else {
8065 $this->debug('Remove expired cookie ' . $cookie['name']);
8066 }
8067 } else {
8068 $this->cookies[] = $cookie;
8069 }
8070 }
8071 $this->debug('checkCookie: '.sizeof($this->cookies).' cookies left in array');
8072 return true;
8073 }
8074
8075 /**
8076 * updates the current cookies with a new set
8077 *
8078 * @param array $cookies new cookies with which to update current ones
8079 * @return boolean always return true
8080 * @access private
8081 */
8082 function UpdateCookies($cookies) {
8083 if (sizeof($this->cookies) == 0) {
8084 // no existing cookies: take whatever is new
8085 if (sizeof($cookies) > 0) {
8086 $this->debug('Setting new cookie(s)');
8087 $this->cookies = $cookies;
8088 }
8089 return true;
8090 }
8091 if (sizeof($cookies) == 0) {
8092 // no new cookies: keep what we've got
8093 return true;
8094 }
8095 // merge
8096 foreach ($cookies as $newCookie) {
8097 if (!is_array($newCookie)) {
8098 continue;
8099 }
8100 if ((!isset($newCookie['name'])) || (!isset($newCookie['value']))) {
8101 continue;
8102 }
8103 $newName = $newCookie['name'];
8104
8105 $found = false;
8106 for ($i = 0; $i < count($this->cookies); $i++) {
8107 $cookie = $this->cookies[$i];
8108 if (!is_array($cookie)) {
8109 continue;
8110 }
8111 if (!isset($cookie['name'])) {
8112 continue;
8113 }
8114 if ($newName != $cookie['name']) {
8115 continue;
8116 }
8117 $newDomain = isset($newCookie['domain']) ? $newCookie['domain'] : 'NODOMAIN';
8118 $domain = isset($cookie['domain']) ? $cookie['domain'] : 'NODOMAIN';
8119 if ($newDomain != $domain) {
8120 continue;
8121 }
8122 $newPath = isset($newCookie['path']) ? $newCookie['path'] : 'NOPATH';
8123 $path = isset($cookie['path']) ? $cookie['path'] : 'NOPATH';
8124 if ($newPath != $path) {
8125 continue;
8126 }
8127 $this->cookies[$i] = $newCookie;
8128 $found = true;
8129 $this->debug('Update cookie ' . $newName . '=' . $newCookie['value']);
8130 break;
8131 }
8132 if (! $found) {
8133 $this->debug('Add cookie ' . $newName . '=' . $newCookie['value']);
8134 $this->cookies[] = $newCookie;
8135 }
8136 }
8137 return true;
8138 }
8139}
8140
8141if (!extension_loaded('soap')) {
8142 /**
8143 * For backwards compatiblity, define soapclient unless the PHP SOAP extension is loaded.
8144 */
8145 class soapclient extends nusoap_client {
8146 }
8147}
8148?>