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