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