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