| [ PHPXref.com ] | [ Generated: Sun Jul 20 21:02:40 2008 ] | [ Web Form Factory 0.1.2 ] |
| [ Index ] [ Variables ] [ Functions ] [ Classes ] [ Constants ] [ Statistics ] | ||
[Summary view] [Print] [Text view]
1 <?php 2 3 /* 4 $Id: nusoap.php,v 1.95 2006/02/02 15:52:34 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 If you have any questions or comments, please email: 25 26 Dietrich Ayala 27 dietrich@ganx4.com 28 http://dietrich.ganx4.com/nusoap 29 30 NuSphere Corporation 31 http://www.nusphere.com 32 33 */ 34 35 /* load classes 36 37 // necessary classes 38 require_once('class.soapclient.php'); 39 require_once('class.soap_val.php'); 40 require_once('class.soap_parser.php'); 41 require_once('class.soap_fault.php'); 42 43 // transport classes 44 require_once('class.soap_transport_http.php'); 45 46 // optional add-on classes 47 require_once('class.xmlschema.php'); 48 require_once('class.wsdl.php'); 49 50 // server class 51 require_once('class.soap_server.php');*/ 52 53 // class variable emulation 54 // cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html 55 $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = 9; 56 57 /** 58 * 59 * nusoap_base 60 * 61 * @author Dietrich Ayala <dietrich@ganx4.com> 62 * @version $Id: nusoap.php,v 1.95 2006/02/02 15:52:34 snichol Exp $ 63 * @access public 64 */ 65 class nusoap_base { 66 /** 67 * Identification for HTTP headers. 68 * 69 * @var string 70 * @access private 71 */ 72 var $title = 'NuSOAP'; 73 /** 74 * Version for HTTP headers. 75 * 76 * @var string 77 * @access private 78 */ 79 var $version = '0.7.2'; 80 /** 81 * CVS revision for HTTP headers. 82 * 83 * @var string 84 * @access private 85 */ 86 var $revision = '$Revision: 1.95 $'; 87 /** 88 * Current error string (manipulated by getError/setError) 89 * 90 * @var string 91 * @access private 92 */ 93 var $error_str = ''; 94 /** 95 * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment) 96 * 97 * @var string 98 * @access private 99 */ 100 var $debug_str = ''; 101 /** 102 * toggles automatic encoding of special characters as entities 103 * (should always be true, I think) 104 * 105 * @var boolean 106 * @access private 107 */ 108 var $charencoding = true; 109 /** 110 * the debug level for this instance 111 * 112 * @var integer 113 * @access private 114 */ 115 var $debugLevel; 116 117 /** 118 * set schema version 119 * 120 * @var string 121 * @access public 122 */ 123 var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema'; 124 125 /** 126 * charset encoding for outgoing messages 127 * 128 * @var string 129 * @access public 130 */ 131 var $soap_defencoding = 'ISO-8859-1'; 132 //var $soap_defencoding = 'UTF-8'; 133 134 /** 135 * namespaces in an array of prefix => uri 136 * 137 * this is "seeded" by a set of constants, but it may be altered by code 138 * 139 * @var array 140 * @access public 141 */ 142 var $namespaces = array( 143 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/', 144 'xsd' => 'http://www.w3.org/2001/XMLSchema', 145 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 146 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/' 147 ); 148 149 /** 150 * namespaces used in the current context, e.g. during serialization 151 * 152 * @var array 153 * @access private 154 */ 155 var $usedNamespaces = array(); 156 157 /** 158 * XML Schema types in an array of uri => (array of xml type => php type) 159 * is this legacy yet? 160 * no, this is used by the xmlschema class to verify type => namespace mappings. 161 * @var array 162 * @access public 163 */ 164 var $typemap = array( 165 'http://www.w3.org/2001/XMLSchema' => array( 166 'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double', 167 'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'', 168 'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string', 169 // abstract "any" types 170 'anyType'=>'string','anySimpleType'=>'string', 171 // derived datatypes 172 'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'', 173 'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer', 174 'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer', 175 'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''), 176 'http://www.w3.org/2000/10/XMLSchema' => array( 177 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double', 178 'float'=>'double','dateTime'=>'string', 179 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'), 180 'http://www.w3.org/1999/XMLSchema' => array( 181 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double', 182 'float'=>'double','dateTime'=>'string', 183 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'), 184 'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'), 185 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'), 186 'http://xml.apache.org/xml-soap' => array('Map') 187 ); 188 189 /** 190 * XML entities to convert 191 * 192 * @var array 193 * @access public 194 * @deprecated 195 * @see expandEntities 196 */ 197 var $xmlEntities = array('quot' => '"','amp' => '&', 198 'lt' => '<','gt' => '>','apos' => "'"); 199 200 /** 201 * constructor 202 * 203 * @access public 204 */ 205 function nusoap_base() { 206 $this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel; 207 } 208 209 /** 210 * gets the global debug level, which applies to future instances 211 * 212 * @return integer Debug level 0-9, where 0 turns off 213 * @access public 214 */ 215 function getGlobalDebugLevel() { 216 return $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel; 217 } 218 219 /** 220 * sets the global debug level, which applies to future instances 221 * 222 * @param int $level Debug level 0-9, where 0 turns off 223 * @access public 224 */ 225 function setGlobalDebugLevel($level) { 226 $GLOBALS['_transient']['static']['nusoap_base']->globalDebugLevel = $level; 227 } 228 229 /** 230 * gets the debug level for this instance 231 * 232 * @return int Debug level 0-9, where 0 turns off 233 * @access public 234 */ 235 function getDebugLevel() { 236 return $this->debugLevel; 237 } 238 239 /** 240 * sets the debug level for this instance 241 * 242 * @param int $level Debug level 0-9, where 0 turns off 243 * @access public 244 */ 245 function setDebugLevel($level) { 246 $this->debugLevel = $level; 247 } 248 249 /** 250 * adds debug data to the instance debug string with formatting 251 * 252 * @param string $string debug data 253 * @access private 254 */ 255 function debug($string){ 256 if ($this->debugLevel > 0) { 257 $this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n"); 258 } 259 } 260 261 /** 262 * adds debug data to the instance debug string without formatting 263 * 264 * @param string $string debug data 265 * @access public 266 */ 267 function appendDebug($string){ 268 if ($this->debugLevel > 0) { 269 // it would be nice to use a memory stream here to use 270 // memory more efficiently 271 $this->debug_str .= $string; 272 } 273 } 274 275 /** 276 * clears the current debug data for this instance 277 * 278 * @access public 279 */ 280 function clearDebug() { 281 // it would be nice to use a memory stream here to use 282 // memory more efficiently 283 $this->debug_str = ''; 284 } 285 286 /** 287 * gets the current debug data for this instance 288 * 289 * @return debug data 290 * @access public 291 */ 292 function &getDebug() { 293 // it would be nice to use a memory stream here to use 294 // memory more efficiently 295 return $this->debug_str; 296 } 297 298 /** 299 * gets the current debug data for this instance as an XML comment 300 * this may change the contents of the debug data 301 * 302 * @return debug data as an XML comment 303 * @access public 304 */ 305 function &getDebugAsXMLComment() { 306 // it would be nice to use a memory stream here to use 307 // memory more efficiently 308 while (strpos($this->debug_str, '--')) { 309 $this->debug_str = str_replace('--', '- -', $this->debug_str); 310 } 311 return "<!--\n" . $this->debug_str . "\n-->"; 312 } 313 314 /** 315 * expands entities, e.g. changes '<' to '<'. 316 * 317 * @param string $val The string in which to expand entities. 318 * @access private 319 */ 320 function expandEntities($val) { 321 if ($this->charencoding) { 322 $val = str_replace('&', '&', $val); 323 $val = str_replace("'", ''', $val); 324 $val = str_replace('"', '"', $val); 325 $val = str_replace('<', '<', $val); 326 $val = str_replace('>', '>', $val); 327 } 328 return $val; 329 } 330 331 /** 332 * returns error string if present 333 * 334 * @return mixed error string or false 335 * @access public 336 */ 337 function getError(){ 338 if($this->error_str != ''){ 339 return $this->error_str; 340 } 341 return false; 342 } 343 344 /** 345 * sets error string 346 * 347 * @return boolean $string error string 348 * @access private 349 */ 350 function setError($str){ 351 $this->error_str = $str; 352 } 353 354 /** 355 * detect if array is a simple array or a struct (associative array) 356 * 357 * @param mixed $val The PHP array 358 * @return string (arraySimple|arrayStruct) 359 * @access private 360 */ 361 function isArraySimpleOrStruct($val) { 362 $keyList = array_keys($val); 363 foreach ($keyList as $keyListValue) { 364 if (!is_int($keyListValue)) { 365 return 'arrayStruct'; 366 } 367 } 368 return 'arraySimple'; 369 } 370 371 /** 372 * serializes PHP values in accordance w/ section 5. Type information is 373 * not serialized if $use == 'literal'. 374 * 375 * @param mixed $val The value to serialize 376 * @param string $name The name (local part) of the XML element 377 * @param string $type The XML schema type (local part) for the element 378 * @param string $name_ns The namespace for the name of the XML element 379 * @param string $type_ns The namespace for the type of the element 380 * @param array $attributes The attributes to serialize as name=>value pairs 381 * @param string $use The WSDL "use" (encoded|literal) 382 * @return string The serialized element, possibly with child elements 383 * @access public 384 */ 385 function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded'){ 386 $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use"); 387 $this->appendDebug('value=' . $this->varDump($val)); 388 $this->appendDebug('attributes=' . $this->varDump($attributes)); 389 390 if(is_object($val) && get_class($val) == 'soapval'){ 391 return $val->serialize($use); 392 } 393 // force valid name if necessary 394 if (is_numeric($name)) { 395 $name = '__numeric_' . $name; 396 } elseif (! $name) { 397 $name = 'noname'; 398 } 399 // if name has ns, add ns prefix to name 400 $xmlns = ''; 401 if($name_ns){ 402 $prefix = 'nu'.rand(1000,9999); 403 $name = $prefix.':'.$name; 404 $xmlns .= " xmlns:$prefix=\"$name_ns\""; 405 } 406 // if type is prefixed, create type prefix 407 if($type_ns != '' && $type_ns == $this->namespaces['xsd']){ 408 // need to fix this. shouldn't default to xsd if no ns specified 409 // w/o checking against typemap 410 $type_prefix = 'xsd'; 411 } elseif($type_ns){ 412 $type_prefix = 'ns'.rand(1000,9999); 413 $xmlns .= " xmlns:$type_prefix=\"$type_ns\""; 414 } 415 // serialize attributes if present 416 $atts = ''; 417 if($attributes){ 418 foreach($attributes as $k => $v){ 419 $atts .= " $k=\"".$this->expandEntities($v).'"'; 420 } 421 } 422 // serialize null value 423 if (is_null($val)) { 424 if ($use == 'literal') { 425 // TODO: depends on minOccurs 426 return "<$name$xmlns $atts/>"; 427 } else { 428 if (isset($type) && isset($type_prefix)) { 429 $type_str = " xsi:type=\"$type_prefix:$type\""; 430 } else { 431 $type_str = ''; 432 } 433 return "<$name$xmlns$type_str $atts xsi:nil=\"true\"/>"; 434 } 435 } 436 // serialize if an xsd built-in primitive type 437 if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){ 438 if (is_bool($val)) { 439 if ($type == 'boolean') { 440 $val = $val ? 'true' : 'false'; 441 } elseif (! $val) { 442 $val = 0; 443 } 444 } else if (is_string($val)) { 445 $val = $this->expandEntities($val); 446 } 447 if ($use == 'literal') { 448 return "<$name$xmlns $atts>$val</$name>"; 449 } else { 450 return "<$name$xmlns $atts xsi:type=\"xsd:$type\">$val</$name>"; 451 } 452 } 453 // detect type and serialize 454 $xml = ''; 455 switch(true) { 456 case (is_bool($val) || $type == 'boolean'): 457 if ($type == 'boolean') { 458 $val = $val ? 'true' : 'false'; 459 } elseif (! $val) { 460 $val = 0; 461 } 462 if ($use == 'literal') { 463 $xml .= "<$name$xmlns $atts>$val</$name>"; 464 } else { 465 $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>"; 466 } 467 break; 468 case (is_int($val) || is_long($val) || $type == 'int'): 469 if ($use == 'literal') { 470 $xml .= "<$name$xmlns $atts>$val</$name>"; 471 } else { 472 $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>"; 473 } 474 break; 475 case (is_float($val)|| is_double($val) || $type == 'float'): 476 if ($use == 'literal') { 477 $xml .= "<$name$xmlns $atts>$val</$name>"; 478 } else { 479 $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>"; 480 } 481 break; 482 case (is_string($val) || $type == 'string'): 483 $val = $this->expandEntities($val); 484 if ($use == 'literal') { 485 $xml .= "<$name$xmlns $atts>$val</$name>"; 486 } else { 487 $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>"; 488 } 489 break; 490 case is_object($val): 491 if (! $name) { 492 $name = get_class($val); 493 $this->debug("In serialize_val, used class name $name as element name"); 494 } else { 495 $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val)); 496 } 497 foreach(get_object_vars($val) as $k => $v){ 498 $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use); 499 } 500 $xml .= '<'.$name.'>'.$pXml.'</'.$name.'>'; 501 break; 502 break; 503 case (is_array($val) || $type): 504 // detect if struct or array 505 $valueType = $this->isArraySimpleOrStruct($val); 506 if($valueType=='arraySimple' || ereg('^ArrayOf',$type)){ 507 $i = 0; 508 if(is_array($val) && count($val)> 0){ 509 foreach($val as $v){ 510 if(is_object($v) && get_class($v) == 'soapval'){ 511 $tt_ns = $v->type_ns; 512 $tt = $v->type; 513 } elseif (is_array($v)) { 514 $tt = $this->isArraySimpleOrStruct($v); 515 } else { 516 $tt = gettype($v); 517 } 518 $array_types[$tt] = 1; 519 // TODO: for literal, the name should be $name 520 $xml .= $this->serialize_val($v,'item',false,false,false,false,$use); 521 ++$i; 522 } 523 if(count($array_types) > 1){ 524 $array_typename = 'xsd:anyType'; 525 } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) { 526 if ($tt == 'integer') { 527 $tt = 'int'; 528 } 529 $array_typename = 'xsd:'.$tt; 530 } elseif(isset($tt) && $tt == 'arraySimple'){ 531 $array_typename = 'SOAP-ENC:Array'; 532 } elseif(isset($tt) && $tt == 'arrayStruct'){ 533 $array_typename = 'unnamed_struct_use_soapval'; 534 } else { 535 // if type is prefixed, create type prefix 536 if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){ 537 $array_typename = 'xsd:' . $tt; 538 } elseif ($tt_ns) { 539 $tt_prefix = 'ns' . rand(1000, 9999); 540 $array_typename = "$tt_prefix:$tt"; 541 $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\""; 542 } else { 543 $array_typename = $tt; 544 } 545 } 546 $array_type = $i; 547 if ($use == 'literal') { 548 $type_str = ''; 549 } else if (isset($type) && isset($type_prefix)) { 550 $type_str = " xsi:type=\"$type_prefix:$type\""; 551 } else { 552 $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\""; 553 } 554 // empty array 555 } else { 556 if ($use == 'literal') { 557 $type_str = ''; 558 } else if (isset($type) && isset($type_prefix)) { 559 $type_str = " xsi:type=\"$type_prefix:$type\""; 560 } else { 561 $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\""; 562 } 563 } 564 // TODO: for array in literal, there is no wrapper here 565 $xml = "<$name$xmlns$type_str$atts>".$xml."</$name>"; 566 } else { 567 // got a struct 568 if(isset($type) && isset($type_prefix)){ 569 $type_str = " xsi:type=\"$type_prefix:$type\""; 570 } else { 571 $type_str = ''; 572 } 573 if ($use == 'literal') { 574 $xml .= "<$name$xmlns $atts>"; 575 } else { 576 $xml .= "<$name$xmlns$type_str$atts>"; 577 } 578 foreach($val as $k => $v){ 579 // Apache Map 580 if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') { 581 $xml .= '<item>'; 582 $xml .= $this->serialize_val($k,'key',false,false,false,false,$use); 583 $xml .= $this->serialize_val($v,'value',false,false,false,false,$use); 584 $xml .= '</item>'; 585 } else { 586 $xml .= $this->serialize_val($v,$k,false,false,false,false,$use); 587 } 588 } 589 $xml .= "</$name>"; 590 } 591 break; 592 default: 593 $xml .= 'not detected, got '.gettype($val).' for '.$val; 594 break; 595 } 596 return $xml; 597 } 598 599 /** 600 * serializes a message 601 * 602 * @param string $body the XML of the SOAP body 603 * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers 604 * @param array $namespaces optional the namespaces used in generating the body and headers 605 * @param string $style optional (rpc|document) 606 * @param string $use optional (encoded|literal) 607 * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded) 608 * @return string the message 609 * @access public 610 */ 611 function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded',$encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'){ 612 // TODO: add an option to automatically run utf8_encode on $body and $headers 613 // if $this->soap_defencoding is UTF-8. Not doing this automatically allows 614 // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1 615 616 $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle"); 617 $this->debug("headers:"); 618 $this->appendDebug($this->varDump($headers)); 619 $this->debug("namespaces:"); 620 $this->appendDebug($this->varDump($namespaces)); 621 622 // serialize namespaces 623 $ns_string = ''; 624 foreach(array_merge($this->namespaces,$namespaces) as $k => $v){ 625 $ns_string .= " xmlns:$k=\"$v\""; 626 } 627 if($encodingStyle) { 628 $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string"; 629 } 630 631 // serialize headers 632 if($headers){ 633 if (is_array($headers)) { 634 $xml = ''; 635 foreach ($headers as $header) { 636 $xml .= $this->serialize_val($header, false, false, false, false, false, $use); 637 } 638 $headers = $xml; 639 $this->debug("In serializeEnvelope, serialzied array of headers to $headers"); 640 } 641 $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>"; 642 } 643 // serialize envelope 644 return 645 '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">". 646 '<SOAP-ENV:Envelope'.$ns_string.">". 647 $headers. 648 "<SOAP-ENV:Body>". 649 $body. 650 "</SOAP-ENV:Body>". 651 "</SOAP-ENV:Envelope>"; 652 } 653 654 /** 655 * formats a string to be inserted into an HTML stream 656 * 657 * @param string $str The string to format 658 * @return string The formatted string 659 * @access public 660 * @deprecated 661 */ 662 function formatDump($str){ 663 $str = htmlspecialchars($str); 664 return nl2br($str); 665 } 666 667 /** 668 * contracts (changes namespace to prefix) a qualified name 669 * 670 * @param string $qname qname 671 * @return string contracted qname 672 * @access private 673 */ 674 function contractQname($qname){ 675 // get element namespace 676 //$this->xdebug("Contract $qname"); 677 if (strrpos($qname, ':')) { 678 // get unqualified name 679 $name = substr($qname, strrpos($qname, ':') + 1); 680 // get ns 681 $ns = substr($qname, 0, strrpos($qname, ':')); 682 $p = $this->getPrefixFromNamespace($ns); 683 if ($p) { 684 return $p . ':' . $name; 685 } 686 return $qname; 687 } else { 688 return $qname; 689 } 690 } 691 692 /** 693 * expands (changes prefix to namespace) a qualified name 694 * 695 * @param string $string qname 696 * @return string expanded qname 697 * @access private 698 */ 699 function expandQname($qname){ 700 // get element prefix 701 if(strpos($qname,':') && !ereg('^http://',$qname)){ 702 // get unqualified name 703 $name = substr(strstr($qname,':'),1); 704 // get ns prefix 705 $prefix = substr($qname,0,strpos($qname,':')); 706 if(isset($this->namespaces[$prefix])){ 707 return $this->namespaces[$prefix].':'.$name; 708 } else { 709 return $qname; 710 } 711 } else { 712 return $qname; 713 } 714 } 715 716 /** 717 * returns the local part of a prefixed string 718 * returns the original string, if not prefixed 719 * 720 * @param string $str The prefixed string 721 * @return string The local part 722 * @access public 723 */ 724 function getLocalPart($str){ 725 if($sstr = strrchr($str,':')){ 726 // get unqualified name 727 return substr( $sstr, 1 ); 728 } else { 729 return $str; 730 } 731 } 732 733 /** 734 * returns the prefix part of a prefixed string 735 * returns false, if not prefixed 736 * 737 * @param string $str The prefixed string 738 * @return mixed The prefix or false if there is no prefix 739 * @access public 740 */ 741 function getPrefix($str){ 742 if($pos = strrpos($str,':')){ 743 // get prefix 744 return substr($str,0,$pos); 745 } 746 return false; 747 } 748 749 /** 750 * pass it a prefix, it returns a namespace 751 * 752 * @param string $prefix The prefix 753 * @return mixed The namespace, false if no namespace has the specified prefix 754 * @access public 755 */ 756 function getNamespaceFromPrefix($prefix){ 757 if (isset($this->namespaces[$prefix])) { 758 return $this->namespaces[$prefix]; 759 } 760 //$this->setError("No namespace registered for prefix '$prefix'"); 761 return false; 762 } 763 764 /** 765 * returns the prefix for a given namespace (or prefix) 766 * or false if no prefixes registered for the given namespace 767 * 768 * @param string $ns The namespace 769 * @return mixed The prefix, false if the namespace has no prefixes 770 * @access public 771 */ 772 function getPrefixFromNamespace($ns) { 773 foreach ($this->namespaces as $p => $n) { 774 if ($ns == $n || $ns == $p) { 775 $this->usedNamespaces[$p] = $n; 776 return $p; 777 } 778 } 779 return false; 780 } 781 782 /** 783 * returns the time in ODBC canonical form with microseconds 784 * 785 * @return string The time in ODBC canonical form with microseconds 786 * @access public 787 */ 788 function getmicrotime() { 789 if (function_exists('gettimeofday')) { 790 $tod = gettimeofday(); 791 $sec = $tod['sec']; 792 $usec = $tod['usec']; 793 } else { 794 $sec = time(); 795 $usec = 0; 796 } 797 return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec); 798 } 799 800 /** 801 * Returns a string with the output of var_dump 802 * 803 * @param mixed $data The variable to var_dump 804 * @return string The output of var_dump 805 * @access public 806 */ 807 function varDump($data) { 808 ob_start(); 809 var_dump($data); 810 $ret_val = ob_get_contents(); 811 ob_end_clean(); 812 return $ret_val; 813 } 814 } 815 816 // XML Schema Datatype Helper Functions 817 818 //xsd:dateTime helpers 819 820 /** 821 * convert unix timestamp to ISO 8601 compliant date string 822 * 823 * @param string $timestamp Unix time stamp 824 * @access public 825 */ 826 function timestamp_to_iso8601($timestamp,$utc=true){ 827 $datestr = date('Y-m-d\TH:i:sO',$timestamp); 828 if($utc){ 829 $eregStr = 830 '([0-9]{4})-'. // centuries & years CCYY- 831 '([0-9]{2})-'. // months MM- 832 '([0-9]{2})'. // days DD 833 'T'. // separator T 834 '([0-9]{2}):'. // hours hh: 835 '([0-9]{2}):'. // minutes mm: 836 '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss... 837 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's 838 839 if(ereg($eregStr,$datestr,$regs)){ 840 return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]); 841 } 842 return false; 843 } else { 844 return $datestr; 845 } 846 } 847 848 /** 849 * convert ISO 8601 compliant date string to unix timestamp 850 * 851 * @param string $datestr ISO 8601 compliant date string 852 * @access public 853 */ 854 function iso8601_to_timestamp($datestr){ 855 $eregStr = 856 '([0-9]{4})-'. // centuries & years CCYY- 857 '([0-9]{2})-'. // months MM- 858 '([0-9]{2})'. // days DD 859 'T'. // separator T 860 '([0-9]{2}):'. // hours hh: 861 '([0-9]{2}):'. // minutes mm: 862 '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss... 863 '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's 864 if(ereg($eregStr,$datestr,$regs)){ 865 // not utc 866 if($regs[8] != 'Z'){ 867 $op = substr($regs[8],0,1); 868 $h = substr($regs[8],1,2); 869 $m = substr($regs[8],strlen($regs[8])-2,2); 870 if($op == '-'){ 871 $regs[4] = $regs[4] + $h; 872 $regs[5] = $regs[5] + $m; 873 } elseif($op == '+'){ 874 $regs[4] = $regs[4] - $h; 875 $regs[5] = $regs[5] - $m; 876 } 877 } 878 return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z"); 879 } else { 880 return false; 881 } 882 } 883 884 /** 885 * sleeps some number of microseconds 886 * 887 * @param string $usec the number of microseconds to sleep 888 * @access public 889 * @deprecated 890 */ 891 function usleepWindows($usec) 892 { 893 $start = gettimeofday(); 894 895 do 896 { 897 $stop = gettimeofday(); 898 $timePassed = 1000000 * ($stop['sec'] - $start['sec']) 899 + $stop['usec'] - $start['usec']; 900 } 901 while ($timePassed < $usec); 902 } 903 904 ?><?php 905 906 907 908 /** 909 * Contains information for a SOAP fault. 910 * Mainly used for returning faults from deployed functions 911 * in a server instance. 912 * @author Dietrich Ayala <dietrich@ganx4.com> 913 * @version $Id: nusoap.php,v 1.95 2006/02/02 15:52:34 snichol Exp $ 914 * @access public 915 */ 916 class soap_fault extends nusoap_base { 917 /** 918 * The fault code (client|server) 919 * @var string 920 * @access private 921 */ 922 var $faultcode; 923 /** 924 * The fault actor 925 * @var string 926 * @access private 927 */ 928 var $faultactor; 929 /** 930 * The fault string, a description of the fault 931 * @var string 932 * @access private 933 */ 934 var $faultstring; 935 /** 936 * The fault detail, typically a string or array of string 937 * @var mixed 938 * @access private 939 */ 940 var $faultdetail; 941 942 /** 943 * constructor 944 * 945 * @param string $faultcode (SOAP-ENV:Client | SOAP-ENV:Server) 946 * @param string $faultactor only used when msg routed between multiple actors 947 * @param string $faultstring human readable error message 948 * @param mixed $faultdetail detail, typically a string or array of string 949 */ 950 function soap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){ 951 parent::nusoap_base(); 952 $this->faultcode = $faultcode; 953 $this->faultactor = $faultactor; 954 $this->faultstring = $faultstring; 955 $this->faultdetail = $faultdetail; 956 } 957 958 /** 959 * serialize a fault 960 * 961 * @return string The serialization of the fault instance. 962 * @access public 963 */ 964 function serialize(){ 965 $ns_string = ''; 966 foreach($this->namespaces as $k => $v){ 967 $ns_string .= "\n xmlns:$k=\"$v\""; 968 } 969 $return_msg = 970 '<?xml version="1.0" encoding="'.$this->soap_defencoding.'"?>'. 971 '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n". 972 '<SOAP-ENV:Body>'. 973 '<SOAP-ENV:Fault>'. 974 $this->serialize_val($this->faultcode, 'faultcode'). 975 $this->serialize_val($this->faultactor, 'faultactor'). 976 $this->serialize_val($this->faultstring, 'faultstring'). 977 $this->serialize_val($this->faultdetail, 'detail'). 978 '</SOAP-ENV:Fault>'. 979 '</SOAP-ENV:Body>'. 980 '</SOAP-ENV:Envelope>'; 981 return $return_msg; 982 } 983 } 984 985 986 987 ?><?php 988 989 990 991 /** 992 * parses an XML Schema, allows access to it's data, other utility methods 993 * no validation... yet. 994 * very experimental and limited. As is discussed on XML-DEV, I'm one of the people 995 * that just doesn't have time to read the spec(s) thoroughly, and just have a couple of trusty 996 * tutorials I refer to :) 997 * 998 * @author Dietrich Ayala <dietrich@ganx4.com> 999 * @version $Id: nusoap.php,v 1.95 2006/02/02 15:52:34 snichol Exp $ 1000 * @access public 1001 */ 1002 class XMLSchema extends nusoap_base { 1003 1004 // files 1005 var $schema = ''; 1006 var $xml = ''; 1007 // namespaces 1008 var $enclosingNamespaces; 1009 // schema info 1010 var $schemaInfo = array(); 1011 var $schemaTargetNamespace = ''; 1012 // types, elements, attributes defined by the schema 1013 var $attributes = array(); 1014 var $complexTypes = array(); 1015 var $complexTypeStack = array(); 1016 var $currentComplexType = null; 1017 var $elements = array(); 1018 var $elementStack = array(); 1019 var $currentElement = null; 1020 var $simpleTypes = array(); 1021 var $simpleTypeStack = array(); 1022 var $currentSimpleType = null; 1023 // imports 1024 var $imports = array(); 1025 // parser vars 1026 var $parser; 1027 var $position = 0; 1028 var $depth = 0; 1029 var $depth_array = array(); 1030 var $message = array(); 1031 var $defaultNamespace = array(); 1032 1033 /** 1034 * constructor 1035 * 1036 * @param string $schema schema document URI 1037 * @param string $xml xml document URI 1038 * @param string $namespaces namespaces defined in enclosing XML 1039 * @access public 1040 */ 1041 function XMLSchema($schema='',$xml='',$namespaces=array()){ 1042 parent::nusoap_base(); 1043 $this->debug('xmlschema class instantiated, inside constructor'); 1044 // files 1045 $this->schema = $schema; 1046 $this->xml = $xml; 1047 1048 // namespaces 1049 $this->enclosingNamespaces = $namespaces; 1050 $this->namespaces = array_merge($this->namespaces, $namespaces); 1051 1052 // parse schema file 1053 if($schema != ''){ 1054 $this->debug('initial schema file: '.$schema); 1055 $this->parseFile($schema, 'schema'); 1056 } 1057 1058 // parse xml file 1059 if($xml != ''){ 1060 $this->debug('initial xml file: '.$xml); 1061 $this->parseFile($xml, 'xml'); 1062 } 1063 1064 } 1065 1066 /** 1067 * parse an XML file 1068 * 1069 * @param string $xml, path/URL to XML file 1070 * @param string $type, (schema | xml) 1071 * @return boolean 1072 * @access public 1073 */ 1074 function parseFile($xml,$type){ 1075 // parse xml file 1076 if($xml != ""){ 1077 $xmlStr = @join("",@file($xml)); 1078 if($xmlStr == ""){ 1079 $msg = 'Error reading XML from '.$xml; 1080 $this->setError($msg); 1081 $this->debug($msg); 1082 return false; 1083 } else { 1084 $this->debug("parsing $xml"); 1085 $this->parseString($xmlStr,$type); 1086 $this->debug("done parsing $xml"); 1087 return true; 1088 } 1089 } 1090 return false; 1091 } 1092 1093 /** 1094 * parse an XML string 1095 * 1096 * @param string $xml path or URL 1097 * @param string $type, (schema|xml) 1098 * @access private 1099 */ 1100 function parseString($xml,$type){ 1101 // parse xml string 1102 if($xml != ""){ 1103 1104 // Create an XML parser. 1105 $this->parser = xml_parser_create(); 1106 // Set the options for parsing the XML data. 1107 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); 1108 1109 // Set the object for the parser. 1110 xml_set_object($this->parser, $this); 1111 1112 // Set the element handlers for the parser. 1113 if($type == "schema"){ 1114 xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement'); 1115 xml_set_character_data_handler($this->parser,'schemaCharacterData'); 1116 } elseif($type == "xml"){ 1117 xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement'); 1118 xml_set_character_data_handler($this->parser,'xmlCharacterData'); 1119 } 1120 1121 // Parse the XML file. 1122 if(!xml_parse($this->parser,$xml,true)){ 1123 // Display an error message. 1124 $errstr = sprintf('XML error parsing XML schema on line %d: %s', 1125 xml_get_current_line_number($this->parser), 1126 xml_error_string(xml_get_error_code($this->parser)) 1127 ); 1128 $this->debug($errstr); 1129 $this->debug("XML payload:\n" . $xml); 1130 $this->setError($errstr); 1131 } 1132 1133 xml_parser_free($this->parser); 1134 } else{ 1135 $this->debug('no xml passed to parseString()!!'); 1136 $this->setError('no xml passed to parseString()!!'); 1137 } 1138 } 1139 1140 /** 1141 * start-element handler 1142 * 1143 * @param string $parser XML parser object 1144 * @param string $name element name 1145 * @param string $attrs associative array of attributes 1146 * @access private 1147 */ 1148 function schemaStartElement($parser, $name, $attrs) { 1149 1150 // position in the total number of elements, starting from 0 1151 $pos = $this->position++; 1152 $depth = $this->depth++; 1153 // set self as current value for this depth 1154 $this->depth_array[$depth] = $pos; 1155 $this->message[$pos] = array('cdata' => ''); 1156 if ($depth > 0) { 1157 $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]]; 1158 } else { 1159 $this->defaultNamespace[$pos] = false; 1160 } 1161 1162 // get element prefix 1163 if($prefix = $this->getPrefix($name)){ 1164 // get unqualified name 1165 $name = $this->getLocalPart($name); 1166 } else { 1167 $prefix = ''; 1168 } 1169 1170 // loop thru attributes, expanding, and registering namespace declarations 1171 if(count($attrs) > 0){ 1172 foreach($attrs as $k => $v){ 1173 // if ns declarations, add to class level array of valid namespaces 1174 if(ereg("^xmlns",$k)){ 1175 //$this->xdebug("$k: $v"); 1176 //$this->xdebug('ns_prefix: '.$this->getPrefix($k)); 1177 if($ns_prefix = substr(strrchr($k,':'),1)){ 1178 //$this->xdebug("Add namespace[$ns_prefix] = $v"); 1179 $this->namespaces[$ns_prefix] = $v; 1180 } else { 1181 $this->defaultNamespace[$pos] = $v; 1182 if (! $this->getPrefixFromNamespace($v)) { 1183 $this->namespaces['ns'.(count($this->namespaces)+1)] = $v; 1184 } 1185 } 1186 if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema'){ 1187 $this->XMLSchemaVersion = $v; 1188 $this->namespaces['xsi'] = $v.'-instance'; 1189 } 1190 } 1191 } 1192 foreach($attrs as $k => $v){ 1193 // expand each attribute 1194 $k = strpos($k,':') ? $this->expandQname($k) : $k; 1195 $v = strpos($v,':') ? $this->expandQname($v) : $v; 1196 $eAttrs[$k] = $v; 1197 } 1198 $attrs = $eAttrs; 1199 } else { 1200 $attrs = array(); 1201 } 1202 // find status, register data 1203 switch($name){ 1204 case 'all': // (optional) compositor content for a complexType 1205 case 'choice': 1206 case 'group': 1207 case 'sequence': 1208 //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement"); 1209 $this->complexTypes[$this->currentComplexType]['compositor'] = $name; 1210 //if($name == 'all' || $name == 'sequence'){ 1211 // $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct'; 1212 //} 1213 break; 1214 case 'attribute': // complexType attribute 1215 //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']); 1216 $this->xdebug("parsing attribute:"); 1217 $this->appendDebug($this->varDump($attrs)); 1218 if (!isset($attrs['form'])) { 1219 $attrs['form'] = $this->schemaInfo['attributeFormDefault']; 1220 } 1221 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) { 1222 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; 1223 if (!strpos($v, ':')) { 1224 // no namespace in arrayType attribute value... 1225 if ($this->defaultNamespace[$pos]) { 1226 // ...so use the default 1227 $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; 1228 } 1229 } 1230 } 1231 if(isset($attrs['name'])){ 1232 $this->attributes[$attrs['name']] = $attrs; 1233 $aname = $attrs['name']; 1234 } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){ 1235 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) { 1236 $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; 1237 } else { 1238 $aname = ''; 1239 } 1240 } elseif(isset($attrs['ref'])){ 1241 $aname = $attrs['ref']; 1242 $this->attributes[$attrs['ref']] = $attrs; 1243 } 1244 1245 if($this->currentComplexType){ // This should *always* be 1246 $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs; 1247 } 1248 // arrayType attribute 1249 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){ 1250 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; 1251 $prefix = $this->getPrefix($aname); 1252 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){ 1253 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; 1254 } else { 1255 $v = ''; 1256 } 1257 if(strpos($v,'[,]')){ 1258 $this->complexTypes[$this->currentComplexType]['multidimensional'] = true; 1259 } 1260 $v = substr($v,0,strpos($v,'[')); // clip the [] 1261 if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){ 1262 $v = $this->XMLSchemaVersion.':'.$v; 1263 } 1264 $this->complexTypes[$this->currentComplexType]['arrayType'] = $v; 1265 } 1266 break; 1267 case 'complexContent': // (optional) content for a complexType 1268 break; 1269 case 'complexType': 1270 array_push($this->complexTypeStack, $this->currentComplexType); 1271 if(isset($attrs['name'])){ 1272 $this->xdebug('processing named complexType '.$attrs['name']); 1273 //$this->currentElement = false; 1274 $this->currentComplexType = $attrs['name']; 1275 $this->complexTypes[$this->currentComplexType] = $attrs; 1276 $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType'; 1277 // This is for constructs like 1278 // <complexType name="ListOfString" base="soap:Array"> 1279 // <sequence> 1280 // <element name="string" type="xsd:string" 1281 // minOccurs="0" maxOccurs="unbounded" /> 1282 // </sequence> 1283 // </complexType> 1284 if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){ 1285 $this->xdebug('complexType is unusual array'); 1286 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; 1287 } else { 1288 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct'; 1289 } 1290 }else{ 1291 $this->xdebug('processing unnamed complexType for element '.$this->currentElement); 1292 $this->currentComplexType = $this->currentElement . '_ContainedType'; 1293 //$this->currentElement = false; 1294 $this->complexTypes[$this->currentComplexType] = $attrs; 1295 $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType'; 1296 // This is for constructs like 1297 // <complexType name="ListOfString" base="soap:Array"> 1298 // <sequence> 1299 // <element name="string" type="xsd:string" 1300 // minOccurs="0" maxOccurs="unbounded" /> 1301 // </sequence> 1302 // </complexType> 1303 if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){ 1304 $this->xdebug('complexType is unusual array'); 1305 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; 1306 } else { 1307 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct'; 1308 } 1309 } 1310 break; 1311 case 'element': 1312 array_push($this->elementStack, $this->currentElement); 1313 // elements defined as part of a complex type should 1314 // not really be added to $this->elements, but for some 1315 // reason, they are 1316 if (!isset($attrs['form'])) { 1317 $attrs['form'] = $this->schemaInfo['elementFormDefault']; 1318 } 1319 if(isset($attrs['type'])){ 1320 $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']); 1321 if (! $this->getPrefix($attrs['type'])) { 1322 if ($this->defaultNamespace[$pos]) { 1323 $attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type']; 1324 $this->xdebug('used default namespace to make type ' . $attrs['type']); 1325 } 1326 } 1327 // This is for constructs like 1328 // <complexType name="ListOfString" base="soap:Array"> 1329 // <sequence> 1330 // <element name="string" type="xsd:string" 1331 // minOccurs="0" maxOccurs="unbounded" /> 1332 // </sequence> 1333 // </complexType> 1334 if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') { 1335 $this->xdebug('arrayType for unusual array is ' . $attrs['type']); 1336 $this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type']; 1337 } 1338 $this->currentElement = $attrs['name']; 1339 $this->elements[ $attrs['name'] ] = $attrs; 1340 $this->elements[ $attrs['name'] ]['typeClass'] = 'element'; 1341 $ename = $attrs['name']; 1342 } elseif(isset($attrs['ref'])){ 1343 $this->xdebug("processing element as ref to ".$attrs['ref']); 1344 $this->currentElement = "ref to ".$attrs['ref']; 1345 $ename = $this->getLocalPart($attrs['ref']); 1346 } else { 1347 $this->xdebug("processing untyped element ".$attrs['name']); 1348 $this->currentElement = $attrs['name']; 1349 $this->elements[ $attrs['name'] ] = $attrs; 1350 $this->elements[ $attrs['name'] ]['typeClass'] = 'element'; 1351 $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['name'] . '_ContainedType'; 1352 $this->elements[ $attrs['name'] ]['type'] = $attrs['type']; 1353 $ename = $attrs['name']; 1354 } 1355 if(isset($ename) && $this->currentComplexType){ 1356 $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs; 1357 } 1358 break; 1359 case 'enumeration': // restriction value list member 1360 $this->xdebug('enumeration ' . $attrs['value']); 1361 if ($this->currentSimpleType) { 1362 $this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value']; 1363 } elseif ($this->currentComplexType) { 1364 $this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value']; 1365 } 1366 break; 1367 case 'extension': // simpleContent or complexContent type extension 1368 $this->xdebug('extension ' . $attrs['base']); 1369 if ($this->currentComplexType) { 1370 $this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base']; 1371 } 1372 break; 1373 case 'import': 1374 if (isset($attrs['schemaLocation'])) { 1375 //$this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']); 1376 $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false); 1377 } else { 1378 //$this->xdebug('import namespace ' . $attrs['namespace']); 1379 $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true); 1380 if (! $this->getPrefixFromNamespace($attrs['namespace'])) { 1381 $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace']; 1382 } 1383 } 1384 break; 1385 case 'list': // simpleType value list 1386 break; 1387 case 'restriction': // simpleType, simpleContent or complexContent value restriction 1388 $this->xdebug('restriction ' . $attrs['base']); 1389 if($this->currentSimpleType){ 1390 $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base']; 1391 } elseif($this->currentComplexType){ 1392 $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base']; 1393 if(strstr($attrs['base'],':') == ':Array'){ 1394 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; 1395 } 1396 } 1397 break; 1398 case 'schema': 1399 $this->schemaInfo = $attrs; 1400 $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix); 1401 if (isset($attrs['targetNamespace'])) { 1402 $this->schemaTargetNamespace = $attrs['targetNamespace']; 1403 } 1404 if (!isset($attrs['elementFormDefault'])) { 1405 $this->schemaInfo['elementFormDefault'] = 'unqualified'; 1406 } 1407 if (!isset($attrs['attributeFormDefault'])) { 1408 $this->schemaInfo['attributeFormDefault'] = 'unqualified'; 1409 } 1410 break; 1411 case 'simpleContent': // (optional) content for a complexType 1412 break; 1413 case 'simpleType': 1414 array_push($this->simpleTypeStack, $this->currentSimpleType); 1415 if(isset($attrs['name'])){ 1416 $this->xdebug("processing simpleType for name " . $attrs['name']); 1417 $this->currentSimpleType = $attrs['name']; 1418 $this->simpleTypes[ $attrs['name'] ] = $attrs; 1419 $this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType'; 1420 $this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar'; 1421 } else { 1422 $this->xdebug('processing unnamed simpleType for element '.$this->currentElement); 1423 $this->currentSimpleType = $this->currentElement . '_ContainedType'; 1424 //$this->currentElement = false; 1425 $this->simpleTypes[$this->currentSimpleType] = $attrs; 1426 $this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar'; 1427 } 1428 break; 1429 case 'union': // simpleType type list 1430 break; 1431 default: 1432 //$this->xdebug("do not have anything to do for element $name"); 1433 } 1434 } 1435 1436 /** 1437 * end-element handler 1438 * 1439 * @param string $parser XML parser object 1440 * @param string $name element name 1441 * @access private 1442 */ 1443 function schemaEndElement($parser, $name) { 1444 // bring depth down a notch 1445 $this->depth--; 1446 // position of current element is equal to the last value left in depth_array for my depth 1447 if(isset($this->depth_array[$this->depth])){ 1448 $pos = $this->depth_array[$this->depth]; 1449 } 1450 // get element prefix 1451 if ($prefix = $this->getPrefix($name)){ 1452 // get unqualified name 1453 $name = $this->getLocalPart($name); 1454 } else { 1455 $prefix = ''; 1456 } 1457 // move on... 1458 if($name == 'complexType'){ 1459 $this->xdebug('done processing complexType ' . ($this->currentComplexType ? $this->currentComplexType : '(unknown)')); 1460 $this->currentComplexType = array_pop($this->complexTypeStack); 1461 //$this->currentElement = false; 1462 } 1463 if($name == 'element'){ 1464 $this->xdebug('done processing element ' . ($this->currentElement ? $this->currentElement : '(unknown)')); 1465 $this->currentElement = array_pop($this->elementStack); 1466 } 1467 if($name == 'simpleType'){ 1468 $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ? $this->currentSimpleType : '(unknown)')); 1469 $this->currentSimpleType = array_pop($this->simpleTypeStack); 1470 } 1471 } 1472 1473 /** 1474 * element content handler 1475 * 1476 * @param string $parser XML parser object 1477 * @param string $data element content 1478 * @access private 1479 */ 1480 function schemaCharacterData($parser, $data){ 1481 $pos = $this->depth_array[$this->depth - 1]; 1482 $this->message[$pos]['cdata'] .= $data; 1483 } 1484 1485 /** 1486 * serialize the schema 1487 * 1488 * @access public 1489 */ 1490 function serializeSchema(){ 1491 1492 $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion); 1493 $xml = ''; 1494 // imports 1495 if (sizeof($this->imports) > 0) { 1496 foreach($this->imports as $ns => $list) { 1497 foreach ($list as $ii) { 1498 if ($ii['location'] != '') { 1499 $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n"; 1500 } else { 1501 $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n"; 1502 } 1503 } 1504 } 1505 } 1506 // complex types 1507 foreach($this->complexTypes as $typeName => $attrs){ 1508 $contentStr = ''; 1509 // serialize child elements 1510 if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){ 1511 foreach($attrs['elements'] as $element => $eParts){ 1512 if(isset($eParts['ref'])){ 1513 $contentStr .= " <$schemaPrefix:element ref=\"$element\"/>\n"; 1514 } else { 1515 $contentStr .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\""; 1516 foreach ($eParts as $aName => $aValue) { 1517 // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable 1518 if ($aName != 'name' && $aName != 'type') { 1519 $contentStr .= " $aName=\"$aValue\""; 1520 } 1521 } 1522 $contentStr .= "/>\n"; 1523 } 1524 } 1525 // compositor wraps elements 1526 if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) { 1527 $contentStr = " <$schemaPrefix:$attrs[compositor]>\n".$contentStr." </$schemaPrefix:$attrs[compositor]>\n"; 1528 } 1529 } 1530 // attributes 1531 if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){ 1532 foreach($attrs['attrs'] as $attr => $aParts){ 1533 $contentStr .= " <$schemaPrefix:attribute"; 1534 foreach ($aParts as $a => $v) { 1535 if ($a == 'ref' || $a == 'type') { 1536 $contentStr .= " $a=\"".$this->contractQName($v).'"'; 1537 } elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') { 1538 $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl']; 1539 $contentStr .= ' wsdl:arrayType="'.$this->contractQName($v).'"'; 1540 } else { 1541 $contentStr .= " $a=\"$v\""; 1542 } 1543 } 1544 $contentStr .= "/>\n"; 1545 } 1546 } 1547 // if restriction 1548 if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){ 1549 $contentStr = " <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr." </$schemaPrefix:restriction>\n"; 1550 // complex or simple content 1551 if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){ 1552 $contentStr = " <$schemaPrefix:complexContent>\n".$contentStr." </$schemaPrefix:complexContent>\n"; 1553 } 1554 } 1555 // finalize complex type 1556 if($contentStr != ''){ 1557 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n"; 1558 } else { 1559 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n"; 1560 } 1561 $xml .= $contentStr; 1562 } 1563 // simple types 1564 if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){ 1565 foreach($this->simpleTypes as $typeName => $eParts){ 1566 $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n <$schemaPrefix:restriction base=\"".$this->contractQName($eParts['type'])."\"/>\n"; 1567 if (isset($eParts['enumeration'])) { 1568 foreach ($eParts['enumeration'] as $e) { 1569 $xml .= " <$schemaPrefix:enumeration value=\"$e\"/>\n"; 1570 } 1571 } 1572 $xml .= " </$schemaPrefix:simpleType>"; 1573 } 1574 } 1575 // elements 1576 if(isset($this->elements) && count($this->elements) > 0){ 1577 foreach($this->elements as $element => $eParts){ 1578 $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n"; 1579 } 1580 } 1581 // attributes 1582 if(isset($this->attributes) && count($this->attributes) > 0){ 1583 foreach($this->attributes as $attr => $aParts){ 1584 $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>"; 1585 } 1586 } 1587 // finish 'er up 1588 $el = "<$schemaPrefix:schema targetNamespace=\"$this->schemaTargetNamespace\"\n"; 1589 foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) { 1590 $el .= " xmlns:$nsp=\"$ns\""; 1591 } 1592 $xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n"; 1593 return $xml; 1594 } 1595 1596 /** 1597 * adds debug data to the clas level debug string 1598 * 1599 * @param string $string debug data 1600 * @access private 1601 */ 1602 function xdebug($string){ 1603 $this->debug('<' . $this->schemaTargetNamespace . '> '.$string); 1604 } 1605 1606 /** 1607 * get the PHP type of a user defined type in the schema 1608 * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays 1609 * returns false if no type exists, or not w/ the given namespace 1610 * else returns a string that is either a native php type, or 'struct' 1611 * 1612 * @param string $type, name of defined type 1613 * @param string $ns, namespace of type 1614 * @return mixed 1615 * @access public 1616 * @deprecated 1617 */ 1618 function getPHPType($type,$ns){ 1619 if(isset($this->typemap[$ns][$type])){ 1620 //print "found type '$type' and ns $ns in typemap<br>"; 1621 return $this->typemap[$ns][$type]; 1622 } elseif(isset($this->complexTypes[$type])){ 1623 //print "getting type '$type' and ns $ns from complexTypes array<br>"; 1624 return $this->complexTypes[$type]['phpType']; 1625 } 1626 return false; 1627 } 1628 1629 /** 1630 * returns an associative array of information about a given type 1631 * returns false if no type exists by the given name 1632 * 1633 * For a complexType typeDef = array( 1634 * 'restrictionBase' => '', 1635 * 'phpType' => '', 1636 * 'compositor' => '(sequence|all)', 1637 * 'elements' => array(), // refs to elements array 1638 * 'attrs' => array() // refs to attributes array 1639 * ... and so on (see addComplexType) 1640 * ) 1641 * 1642 * For simpleType or element, the array has different keys. 1643 * 1644 * @param string 1645 * @return mixed 1646 * @access public 1647 * @see addComplexType 1648 * @see addSimpleType 1649 * @see addElement 1650 */ 1651 function getTypeDef($type){ 1652 //$this->debug("in getTypeDef for type $type"); 1653 if(isset($this->complexTypes[$type])){ 1654 $this->xdebug("in getTypeDef, found complexType $type"); 1655 return $this->complexTypes[$type]; 1656 } elseif(isset($this->simpleTypes[$type])){ 1657 $this->xdebug("in getTypeDef, found simpleType $type"); 1658 if (!isset($this->simpleTypes[$type]['phpType'])) { 1659 // get info for type to tack onto the simple type 1660 // TODO: can this ever really apply (i.e. what is a simpleType really?) 1661 $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1); 1662 $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':')); 1663 $etype = $this->getTypeDef($uqType); 1664 if ($etype) { 1665 $this->xdebug("in getTypeDef, found type for simpleType $type:"); 1666 $this->xdebug($this->varDump($etype)); 1667 if (isset($etype['phpType'])) { 1668 $this->simpleTypes[$type]['phpType'] = $etype['phpType']; 1669 } 1670 if (isset($etype['elements'])) { 1671 $this->simpleTypes[$type]['elements'] = $etype['elements']; 1672 } 1673 } 1674 } 1675 return $this->simpleTypes[$type]; 1676 } elseif(isset($this->elements[$type])){ 1677 $this->xdebug("in getTypeDef, found element $type"); 1678 if (!isset($this->elements[$type]['phpType'])) { 1679 // get info for type to tack onto the element 1680 $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1); 1681 $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':')); 1682 $etype = $this->getTypeDef($uqType); 1683 if ($etype) { 1684 $this->xdebug("in getTypeDef, found type for element $type:"); 1685 $this->xdebug($this->varDump($etype)); 1686 if (isset($etype['phpType'])) { 1687 $this->elements[$type]['phpType'] = $etype['phpType']; 1688 } 1689 if (isset($etype['elements'])) { 1690 $this->elements[$type]['elements'] = $etype['elements']; 1691 } 1692 } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') { 1693 $this->xdebug("in getTypeDef, element $type is an XSD type"); 1694 $this->elements[$type]['phpType'] = 'scalar'; 1695 } 1696 } 1697 return $this->elements[$type]; 1698 } elseif(isset($this->attributes[$type])){ 1699 $this->xdebug("in getTypeDef, found attribute $type"); 1700 return $this->attributes[$type]; 1701 } elseif (ereg('_ContainedType$', $type)) { 1702 $this->xdebug("in getTypeDef, have an untyped element $type"); 1703 $typeDef['typeClass'] = 'simpleType'; 1704 $typeDef['phpType'] = 'scalar'; 1705 $typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string'; 1706 return $typeDef; 1707 } 1708 $this->xdebug("in getTypeDef, did not find $type"); 1709 return false; 1710 } 1711 1712 /** 1713 * returns a sample serialization of a given type, or false if no type by the given name 1714 * 1715 * @param string $type, name of type 1716 * @return mixed 1717 * @access public 1718 * @deprecated 1719 */ 1720 function serializeTypeDef($type){ 1721 //print "in sTD() for type $type<br>"; 1722 if($typeDef = $this->getTypeDef($type)){ 1723 $str .= '<'.$type; 1724 if(is_array($typeDef['attrs'])){ 1725 foreach($attrs as $attName => $data){ 1726 $str .= " $attName=\"{type = ".$data['type']."}\""; 1727 } 1728 } 1729 $str .= " xmlns=\"".$this->schema['targetNamespace']."\""; 1730 if(count($typeDef['elements']) > 0){ 1731 $str .= ">"; 1732 foreach($typeDef['elements'] as $element => $eData){ 1733 $str .= $this->serializeTypeDef($element); 1734 } 1735 $str .= "</$type>"; 1736 } elseif($typeDef['typeClass'] == 'element') { 1737 $str .= "></$type>"; 1738 } else { 1739 $str .= "/>"; 1740 } 1741 return $str; 1742 } 1743 return false; 1744 } 1745 1746 /** 1747 * returns HTML form elements that allow a user 1748 * to enter values for creating an instance of the given type. 1749 * 1750 * @param string $name, name for type instance 1751 * @param string $type, name of type 1752 * @return string 1753 * @access public 1754 * @deprecated 1755 */ 1756 function typeToForm($name,$type){ 1757 // get typedef 1758 if($typeDef = $this->getTypeDef($type)){ 1759 // if struct 1760 if($typeDef['phpType'] == 'struct'){ 1761 $buffer .= '<table>'; 1762 foreach($typeDef['elements'] as $child => $childDef){ 1763 $buffer .= " 1764 <tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td> 1765 <td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>"; 1766 } 1767 $buffer .= '</table>'; 1768 // if array 1769 } elseif($typeDef['phpType'] == 'array'){ 1770 $buffer .= '<table>'; 1771 for($i=0;$i < 3; $i++){ 1772 $buffer .= " 1773 <tr><td align='right'>array item (type: $typeDef[arrayType]):</td> 1774 <td><input type='text' name='parameters[".$name."][]'></td></tr>"; 1775 } 1776 $buffer .= '</table>'; 1777 // if scalar 1778 } else { 1779 $buffer .= "<input type='text' name='parameters[$name]'>"; 1780 } 1781 } else { 1782 $buffer .= "<input type='text' name='parameters[$name]'>"; 1783 } 1784 return $buffer; 1785 } 1786 1787 /** 1788 * adds a complex type to the schema 1789 * 1790 * example: array 1791 * 1792 * addType( 1793 * 'ArrayOfstring', 1794 * 'complexType', 1795 * 'array', 1796 * '', 1797 * 'SOAP-ENC:Array', 1798 * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'), 1799 * 'xsd:string' 1800 * ); 1801 * 1802 * example: PHP associative array ( SOAP Struct ) 1803 * 1804 * addType( 1805 * 'SOAPStruct', 1806 * 'complexType', 1807 * 'struct', 1808 * 'all', 1809 * array('myVar'=> array('name'=>'myVar','type'=>'string') 1810 * ); 1811 * 1812 * @param name 1813 * @param typeClass (complexType|simpleType|attribute) 1814 * @param phpType: currently supported are array and struct (php assoc array) 1815 * @param compositor (all|sequence|choice) 1816 * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array) 1817 * @param elements = array ( name = array(name=>'',type=>'') ) 1818 * @param attrs = array( 1819 * array( 1820 * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType", 1821 * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]" 1822 * ) 1823 * ) 1824 * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string) 1825 * @access public 1826 * @see getTypeDef 1827 */ 1828 function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){ 1829 $this->complexTypes[$name] = array( 1830 'name' => $name, 1831 'typeClass' => $typeClass, 1832 'phpType' => $phpType, 1833 'compositor'=> $compositor, 1834 'restrictionBase' => $restrictionBase, 1835 'elements' => $elements, 1836 'attrs' => $attrs, 1837 'arrayType' => $arrayType 1838 ); 1839 1840 $this->xdebug("addComplexType $name:"); 1841 $this->appendDebug($this->varDump($this->complexTypes[$name])); 1842 } 1843 1844 /** 1845 * adds a simple type to the schema 1846 * 1847 * @param string $name 1848 * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array) 1849 * @param string $typeClass (should always be simpleType) 1850 * @param string $phpType (should always be scalar) 1851 * @param array $enumeration array of values 1852 * @access public 1853 * @see xmlschema 1854 * @see getTypeDef 1855 */ 1856 function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) { 1857 $this->simpleTypes[$name] = array( 1858 'name' => $name, 1859 'typeClass' => $typeClass, 1860 'phpType' => $phpType, 1861 'type' => $restrictionBase, 1862 'enumeration' => $enumeration 1863 ); 1864 1865 $this->xdebug("addSimpleType $name:"); 1866 $this->appendDebug($this->varDump($this->simpleTypes[$name])); 1867 } 1868 1869 /** 1870 * adds an element to the schema 1871 * 1872 * @param array $attrs attributes that must include name and type 1873 * @see xmlschema 1874 * @access public 1875 */ 1876 function addElement($attrs) { 1877 if (! $this->getPrefix($attrs['type'])) { 1878 $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type']; 1879 } 1880 $this->elements[ $attrs['name'] ] = $attrs; 1881 $this->elements[ $attrs['name'] ]['typeClass'] = 'element'; 1882 1883 $this->xdebug("addElement " . $attrs['name']); 1884 $this->appendDebug($this->varDump($this->elements[ $attrs['name'] ])); 1885 } 1886 } 1887 1888 1889 1890 ?><?php 1891 1892 1893 1894 /** 1895 * For creating serializable abstractions of native PHP types. This class 1896 * allows element name/namespace, XSD type, and XML attributes to be 1897 * associated with a value. This is extremely useful when WSDL is not 1898 * used, but is also useful when WSDL is used with polymorphic types, including 1899 * xsd:anyType and user-defined types. 1900 * 1901 * @author Dietrich Ayala <dietrich@ganx4.com> 1902 * @version $Id: nusoap.php,v 1.95 2006/02/02 15:52:34 snichol Exp $ 1903 * @access public 1904 */ 1905 class soapval extends nusoap_base { 1906 /** 1907 * The XML element name 1908 * 1909 * @var string 1910 * @access private 1911 */ 1912 var $name; 1913 /** 1914 * The XML type name (string or false) 1915 * 1916 * @var mixed 1917 * @access private 1918 */ 1919 var $type; 1920 /** 1921 * The PHP value 1922 * 1923 * @var mixed 1924 * @access private 1925 */ 1926 var $value; 1927 /** 1928 * The XML element namespace (string or false) 1929 * 1930 * @var mixed 1931 * @access private 1932 */ 1933 var $element_ns; 1934 /** 1935 * The XML type namespace (string or false) 1936 * 1937 * @var mixed 1938 * @access private 1939 */ 1940 var $type_ns; 1941 /** 1942 * The XML element attributes (array or false) 1943 * 1944 * @var mixed 1945 * @access private 1946 */ 1947 var $attributes; 1948 1949 /** 1950 * constructor 1951 * 1952 * @param string $name optional name 1953 * @param mixed $type optional type name 1954 * @param mixed $value optional value 1955 * @param mixed $element_ns optional namespace of value 1956 * @param mixed $type_ns optional namespace of type 1957 * @param mixed $attributes associative array of attributes to add to element serialization 1958 * @access public 1959 */ 1960 function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) { 1961 parent::nusoap_base(); 1962 $this->name = $name; 1963 $this->type = $type; 1964 $this->value = $value; 1965 $this->element_ns = $element_ns; 1966 $this->type_ns = $type_ns; 1967 $this->attributes = $attributes; 1968 } 1969 1970 /** 1971 * return serialized value 1972 * 1973 * @param string $use The WSDL use value (encoded|literal) 1974 * @return string XML data 1975 * @access public 1976 */ 1977 function serialize($use='encoded') { 1978 return $this->serialize_val($this->value,$this->name,$this->type,$this->element_ns,$this->type_ns,$this->attributes,$use); 1979 } 1980 1981 /** 1982 * decodes a soapval object into a PHP native type 1983 * 1984 * @return mixed 1985 * @access public 1986 */ 1987 function decode(){ 1988 return $this->value; 1989 } 1990 } 1991 1992 1993 1994 ?><?php 1995 1996 1997 1998 /** 1999 * transport class for sending/receiving data via HTTP and HTTPS 2000 * NOTE: PHP must be compiled with the CURL extension for HTTPS support 2001 * 2002 * @author Dietrich Ayala <dietrich@ganx4.com> 2003 * @version $Id: nusoap.php,v 1.95 2006/02/02 15:52:34 snichol Exp $ 2004 * @access public 2005 */ 2006 class soap_transport_http extends nusoap_base { 2007 2008 var $url = ''; 2009 var $uri = ''; 2010 var $digest_uri = ''; 2011 var $scheme = ''; 2012 var $host = ''; 2013 var $port = ''; 2014 var $path = ''; 2015 var $request_method = 'POST'; 2016 var $protocol_version = '1.0'; 2017 var $encoding = ''; 2018 var $outgoing_headers = array(); 2019 var $incoming_headers = array(); 2020 var $incoming_cookies = array(); 2021 var $outgoing_payload = ''; 2022 var $incoming_payload = ''; 2023 var $useSOAPAction = true; 2024 var $persistentConnection = false; 2025 var $ch = false; // cURL handle 2026 var $username = ''; 2027 var $password = ''; 2028 var $authtype = ''; 2029 var $digestRequest = array(); 2030 var $certRequest = array(); // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional) 2031 // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem' 2032 // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem' 2033 // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem' 2034 // passphrase: SSL key password/passphrase 2035 // verifypeer: default is 1 2036 // verifyhost: default is 1 2037 2038 /** 2039 * constructor 2040 */ 2041 function soap_transport_http($url){ 2042 parent::nusoap_base(); 2043 $this->setURL($url); 2044 ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev); 2045 $this->outgoing_headers['User-Agent'] = $this->title.'/'.$this->version.' ('.$rev[1].')'; 2046 $this->debug('set User-Agent: ' . $this->outgoing_headers['User-Agent']); 2047 } 2048 2049 function setURL($url) { 2050 $this->url = $url; 2051 2052 $u = parse_url($url); 2053 foreach($u as $k => $v){ 2054 $this->debug("$k = $v"); 2055 $this->$k = $v; 2056 } 2057 2058 // add any GET params to path 2059 if(isset($u['query']) && $u['query'] != ''){ 2060 $this->path .= '?' . $u['query']; 2061 } 2062 2063 // set default port 2064 if(!isset($u['port'])){ 2065 if($u['scheme'] == 'https'){ 2066 $this->port = 443; 2067 } else { 2068 $this->port = 80; 2069 } 2070 } 2071 2072 $this->uri = $this->path; 2073 $this->digest_uri = $this->uri; 2074 2075 // build headers 2076 if (!isset($u['port'])) { 2077 $this->outgoing_headers['Host'] = $this->host; 2078 } else { 2079 $this->outgoing_headers['Host'] = $this->host.':'.$this->port; 2080 } 2081 $this->debug('set Host: ' . $this->outgoing_headers['Host']); 2082 2083 if (isset($u['user']) && $u['user'] != '') { 2084 $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : ''); 2085 } 2086 } 2087 2088 function connect($connection_timeout=0,$response_timeout=30){ 2089 // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like 2090 // "regular" socket. 2091 // TODO: disabled for now because OpenSSL must be *compiled* in (not just 2092 // loaded), and until PHP5 stream_get_wrappers is not available. 2093 // if ($this->scheme == 'https') { 2094 // if (version_compare(phpversion(), '4.3.0') >= 0) { 2095 // if (extension_loaded('openssl')) { 2096 // $this->scheme = 'ssl'; 2097 // $this->debug('Using SSL over OpenSSL'); 2098 // } 2099 // } 2100 // } 2101 $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port"); 2102 if ($this->scheme == 'http' || $this->scheme == 'ssl') { 2103 // use persistent connection 2104 if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){ 2105 if (!feof($this->fp)) { 2106 $this->debug('Re-use persistent connection'); 2107 return true; 2108 } 2109 fclose($this->fp); 2110 $this->debug('Closed persistent connection at EOF'); 2111 } 2112 2113 // munge host if using OpenSSL 2114 if ($this->scheme == 'ssl') { 2115 $host = 'ssl://' . $this->host; 2116 } else { 2117 $host = $this->host; 2118 } 2119 $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout); 2120 2121 // open socket 2122 if($connection_timeout > 0){ 2123 $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout); 2124 } else { 2125 $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str); 2126 } 2127 2128 // test pointer 2129 if(!$this->fp) { 2130 $msg = 'Couldn\'t open socket connection to server ' . $this->url; 2131 if ($this->errno) { 2132 $msg .= ', Error ('.$this->errno.'): '.$this->error_str; 2133 } else { 2134 $msg .= ' prior to connect(). This is often a problem looking up the host name.'; 2135 } 2136 $this->debug($msg); 2137 $this->setError($msg); 2138 return false; 2139 } 2140 2141 // set response timeout 2142 $this->debug('set response timeout to ' . $response_timeout); 2143 socket_set_timeout( $this->fp, $response_timeout); 2144 2145 $this->debug('socket connected'); 2146 return true; 2147 } else if ($this->scheme == 'https') { 2148 if (!extension_loaded('curl')) { 2149 $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS'); 2150 return false; 2151 } 2152 $this->debug('connect using https'); 2153 // init CURL 2154 $this->ch = curl_init(); 2155 // set url 2156 $hostURL = ($this->port != '') ? "https://$this->host:$this->port" : "https://$this->host"; 2157 // add path 2158 $hostURL .= $this->path; 2159 curl_setopt($this->ch, CURLOPT_URL, $hostURL); 2160 // follow location headers (re-directs) 2161 curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, 1); 2162 // ask for headers in the response output 2163 curl_setopt($this->ch, CURLOPT_HEADER, 1); 2164 // ask for the response output as the return value 2165 curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1); 2166 // encode 2167 // We manage this ourselves through headers and encoding 2168 // if(function_exists('gzuncompress')){ 2169 // curl_setopt($this->ch, CURLOPT_ENCODING, 'deflate'); 2170 // } 2171 // persistent connection 2172 if ($this->persistentConnection) { 2173 // The way we send data, we cannot use persistent connections, since 2174 // there will be some "junk" at the end of our request. 2175 //curl_setopt($this->ch, CURL_HTTP_VERSION_1_1, true); 2176 $this->persistentConnection = false; 2177 $this->outgoing_headers['Connection'] = 'close'; 2178 $this->debug('set Connection: ' . $this->outgoing_headers['Connection']); 2179 } 2180 // set timeout 2181 if ($connection_timeout != 0) { 2182 curl_setopt($this->ch, CURLOPT_TIMEOUT, $connection_timeout); 2183 } 2184 // TODO: cURL has added a connection timeout separate from the response timeout 2185 //if ($connection_timeout != 0) { 2186 // curl_setopt($this->ch, CURLOPT_CONNECTIONTIMEOUT, $connection_timeout); 2187 //} 2188 //if ($response_timeout != 0) { 2189 // curl_setopt($this->ch, CURLOPT_TIMEOUT, $response_timeout); 2190 //} 2191 2192 // recent versions of cURL turn on peer/host checking by default, 2193 // while PHP binaries are not compiled with a default location for the 2194 // CA cert bundle, so disable peer/host checking. 2195 //curl_setopt($this->ch, CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt'); 2196 curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 0); 2197 curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 0); 2198 2199 // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo) 2200 if ($this->authtype == 'certificate') { 2201 if (isset($this->certRequest['cainfofile'])) { 2202 curl_setopt($this->ch, CURLOPT_CAINFO, $this->certRequest['cainfofile']); 2203 } 2204 if (isset($this->certRequest['verifypeer'])) { 2205 curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']); 2206 } else { 2207 curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 1); 2208 } 2209 if (isset($this->certRequest['verifyhost'])) { 2210 curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']); 2211 } else { 2212 curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 1); 2213 } 2214 if (isset($this->certRequest['sslcertfile'])) { 2215 curl_setopt($this->ch, CURLOPT_SSLCERT, $this->certRequest['sslcertfile']); 2216 } 2217 if (isset($this->certRequest['sslkeyfile'])) { 2218 curl_setopt($this->ch, CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']); 2219 } 2220 if (isset($this->certRequest['passphrase'])) { 2221 curl_setopt($this->ch, CURLOPT_SSLKEYPASSWD , $this->certRequest['passphrase']); 2222 } 2223 } 2224 $this->debug('cURL connection set up'); 2225 return true; 2226 } else { 2227 $this->setError('Unknown scheme ' . $this->scheme); 2228 $this->debug('Unknown scheme ' . $this->scheme); 2229 return false; 2230 } 2231 } 2232 2233 /** 2234 * send the SOAP message via HTTP 2235 * 2236 * @param string $data message data 2237 * @param integer $timeout set connection timeout in seconds 2238 * @param integer $response_timeout set response timeout in seconds 2239 * @param array $cookies cookies to send 2240 * @return string data 2241 * @access public 2242 */ 2243 function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) { 2244 2245 $this->debug('entered send() with data of length: '.strlen($data)); 2246 2247 $this->tryagain = true; 2248 $tries = 0; 2249 while ($this->tryagain) { 2250 $this->tryagain = false; 2251 if ($tries++ < 2) { 2252 // make connnection 2253 if (!$this->connect($timeout, $response_timeout)){ 2254 return false; 2255 } 2256 2257 // send request 2258 if (!$this->sendRequest($data, $cookies)){ 2259 return false; 2260 } 2261 2262 // get response 2263 $respdata = $this->getResponse(); 2264 } else { 2265 $this->setError('Too many tries to get an OK response'); 2266 } 2267 } 2268 $this->debug('end of send()'); 2269 return $respdata; 2270 } 2271 2272 2273 /** 2274 * send the SOAP message via HTTPS 1.0 using CURL 2275 * 2276 * @param string $msg message data 2277 * @param integer $timeout set connection timeout in seconds 2278 * @param integer $response_timeout set response timeout in seconds 2279 * @param array $cookies cookies to send 2280 * @return string data 2281 * @access public 2282 */ 2283 function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) { 2284 return $this->send($data, $timeout, $response_timeout, $cookies); 2285 } 2286 2287 /** 2288 * if authenticating, set user credentials here 2289 * 2290 * @param string $username 2291 * @param string $password 2292 * @param string $authtype (basic, digest, certificate) 2293 * @param array $digestRequest (keys must be nonce, nc, realm, qop) 2294 * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs) 2295 * @access public 2296 */ 2297 function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) { 2298 $this->debug("Set credentials for authtype $authtype"); 2299 // cf. RFC 2617 2300 if ($authtype == 'basic') { 2301 $this->outgoing_headers['Authorization'] = 'Basic '.base64_encode(str_replace(':','',$username).':'.$password); 2302 } elseif ($authtype == 'digest') { 2303 if (isset($digestRequest['nonce'])) { 2304 $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1; 2305 2306 // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html) 2307 2308 // A1 = unq(username-value) ":" unq(realm-value) ":" passwd 2309 $A1 = $username. ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password; 2310 2311 // H(A1) = MD5(A1) 2312 $HA1 = md5($A1); 2313 2314 // A2 = Method ":" digest-uri-value 2315 $A2 = 'POST:' . $this->digest_uri; 2316 2317 // H(A2) 2318 $HA2 = md5($A2); 2319 2320 // KD(secret, data) = H(concat(secret, ":", data)) 2321 // if qop == auth: 2322 // request-digest = <"> < KD ( H(A1), unq(nonce-value) 2323 // ":" nc-value 2324 // ":" unq(cnonce-value) 2325 // ":" unq(qop-value) 2326 // ":" H(A2) 2327 // ) <"> 2328 // if qop is missing, 2329 // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <"> 2330 2331 $unhashedDigest = ''; 2332 $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : ''; 2333 $cnonce = $nonce; 2334 if ($digestRequest['qop'] != '') { 2335 $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2; 2336 } else { 2337 $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2; 2338 } 2339 2340 $hashedDigest = md5($unhashedDigest); 2341 2342 $this->outgoing_headers['Authorization'] = 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"'; 2343 } 2344 } elseif ($authtype == 'certificate') { 2345 $this->certRequest = $certRequest; 2346 } 2347 $this->username = $username; 2348 $this->password = $password; 2349 $this->authtype = $authtype; 2350 $this->digestRequest = $digestRequest; 2351 2352 if (isset($this->outgoing_headers['Authorization'])) { 2353 $this->debug('set Authorization: ' . substr($this->outgoing_headers['Authorization'], 0, 12) . '...'); 2354 } else { 2355 $this->debug('Authorization header not set'); 2356 } 2357 } 2358 2359 /** 2360 * set the soapaction value 2361 * 2362 * @param string $soapaction 2363 * @access public 2364 */ 2365 function setSOAPAction($soapaction) { 2366 $this->outgoing_headers['SOAPAction'] = '"' . $soapaction . '"'; 2367 $this->debug('set SOAPAction: ' . $this->outgoing_headers['SOAPAction']); 2368 } 2369 2370 /** 2371 * use http encoding 2372 * 2373 * @param string $enc encoding style. supported values: gzip, deflate, or both 2374 * @access public 2375 */ 2376 function setEncoding($enc='gzip, deflate') { 2377 if (function_exists('gzdeflate')) { 2378 $this->protocol_version = '1.1'; 2379 $this->outgoing_headers['Accept-Encoding'] = $enc; 2380 $this->debug('set Accept-Encoding: ' . $this->outgoing_headers['Accept-Encoding']); 2381 if (!isset($this->outgoing_headers['Connection'])) { 2382 $this->outgoing_headers['Connection'] = 'close'; 2383 $this->persistentConnection = false; 2384 $this->debug('set Connection: ' . $this->outgoing_headers['Connection']); 2385 } 2386 set_magic_quotes_runtime(0); 2387 // deprecated 2388 $this->encoding = $enc; 2389 } 2390 } 2391 2392 /** 2393 * set proxy info here 2394 * 2395 * @param string $proxyhost 2396 * @param string $proxyport 2397 * @param string $proxyusername 2398 * @param string $proxypassword 2399 * @access public 2400 */ 2401 function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') { 2402 $this->uri = $this->url; 2403 $this->host = $proxyhost; 2404 $this->port = $proxyport; 2405 if ($proxyusername != '' && $proxypassword != '') { 2406 $this->outgoing_headers['Proxy-Authorization'] = ' Basic '.base64_encode($proxyusername.':'.$proxypassword); 2407 $this->debug('set Proxy-Authorization: ' . $this->outgoing_headers['Proxy-Authorization']); 2408 } 2409 } 2410 2411 /** 2412 * decode a string that is encoded w/ "chunked' transfer encoding 2413 * as defined in RFC2068 19.4.6 2414 * 2415 * @param string $buffer 2416 * @param string $lb 2417 * @returns string 2418 * @access public 2419 * @deprecated 2420 */ 2421 function decodeChunked($buffer, $lb){ 2422 // length := 0 2423 $length = 0; 2424 $new = ''; 2425 2426 // read chunk-size, chunk-extension (if any) and CRLF 2427 // get the position of the linebreak 2428 $chunkend = strpos($buffer, $lb); 2429 if ($chunkend == FALSE) { 2430 $this->debug('no linebreak found in decodeChunked'); 2431 return $new; 2432 } 2433 $temp = substr($buffer,0,$chunkend); 2434 $chunk_size = hexdec( trim($temp) ); 2435 $chunkstart = $chunkend + strlen($lb); 2436 // while (chunk-size > 0) { 2437 while ($chunk_size > 0) { 2438 $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size"); 2439 $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size); 2440 2441 // Just in case we got a broken connection 2442 if ($chunkend == FALSE) { 2443 $chunk = substr($buffer,$chunkstart); 2444 // append chunk-data to entity-body 2445 $new .= $chunk; 2446 $length += strlen($chunk); 2447 break; 2448 } 2449 2450 // read chunk-data and CRLF 2451 $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart); 2452 // append chunk-data to entity-body 2453 $new .= $chunk; 2454 // length := length + chunk-size 2455 $length += strlen($chunk); 2456 // read chunk-size and CRLF 2457 $chunkstart = $chunkend + strlen($lb); 2458 2459 $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb); 2460 if ($chunkend == FALSE) { 2461 break; //Just in case we got a broken connection 2462 } 2463 $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart); 2464 $chunk_size = hexdec( trim($temp) ); 2465 $chunkstart = $chunkend; 2466 } 2467 return $new; 2468 } 2469 2470 /* 2471 * Writes payload, including HTTP headers, to $this->outgoing_payload. 2472 */ 2473 function buildPayload($data, $cookie_str = '') { 2474 // add content-length header 2475 $this->outgoing_headers['Content-Length'] = strlen($data); 2476 $this->debug('set Content-Length: ' . $this->outgoing_headers['Content-Length']); 2477 2478 // start building outgoing payload: 2479 $req = "$this->request_method $this->uri HTTP/$this->protocol_version"; 2480 $this->debug("HTTP request: $req"); 2481 $this->outgoing_payload = "$req\r\n"; 2482 2483 // loop thru headers, serializing 2484 foreach($this->outgoing_headers as $k => $v){ 2485 $hdr = $k.': '.$v; 2486 $this->debug("HTTP header: $hdr"); 2487 $this->outgoing_payload .= "$hdr\r\n"; 2488 } 2489 2490 // add any cookies 2491 if ($cookie_str != '') { 2492 $hdr = 'Cookie: '.$cookie_str; 2493 $this->debug("HTTP header: $hdr"); 2494 $this->outgoing_payload .= "$hdr\r\n"; 2495 } 2496 2497 // header/body separator 2498 $this->outgoing_payload .= "\r\n"; 2499 2500 // add data 2501 $this->outgoing_payload .= $data; 2502 } 2503 2504 function sendRequest($data, $cookies = NULL) { 2505 // build cookie string 2506 $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https'))); 2507 2508 // build payload 2509 $this->buildPayload($data, $cookie_str); 2510 2511 if ($this->scheme == 'http' || $this->scheme == 'ssl') { 2512 // send payload 2513 if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) { 2514 $this->setError('couldn\'t write message data to socket'); 2515 $this->debug('couldn\'t write message data to socket'); 2516 return false; 2517 } 2518 $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload)); 2519 return true; 2520 } else if ($this->scheme == 'https') { 2521 // set payload 2522 // TODO: cURL does say this should only be the verb, and in fact it 2523 // turns out that the URI and HTTP version are appended to this, which 2524 // some servers refuse to work with 2525 //curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, $this->outgoing_payload); 2526 foreach($this->outgoing_headers as $k => $v){ 2527 $curl_headers[] = "$k: $v"; 2528 } 2529 if ($cookie_str != '') { 2530 $curl_headers[] = 'Cookie: ' . $cookie_str; 2531 } 2532 curl_setopt($this->ch, CURLOPT_HTTPHEADER, $curl_headers); 2533 if ($this->request_method == "POST") { 2534 curl_setopt($this->ch, CURLOPT_POST, 1); 2535 curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data); 2536 } else { 2537 } 2538 $this->debug('set cURL payload'); 2539 return true; 2540 } 2541 } 2542 2543 function getResponse(){ 2544 $this->incoming_payload = ''; 2545 2546 if ($this->scheme == 'http' || $this->scheme == 'ssl') { 2547 // loop until headers have been retrieved 2548 $data = ''; 2549 while (!isset($lb)){ 2550 2551 // We might EOF during header read. 2552 if(feof($this->fp)) { 2553 $this->incoming_payload = $data; 2554 $this->debug('found no headers before EOF after length ' . strlen($data)); 2555 $this->debug("received before EOF:\n" . $data); 2556 $this->setError('server failed to send headers'); 2557 return false; 2558 } 2559 2560 $tmp = fgets($this->fp, 256); 2561 $tmplen = strlen($tmp); 2562 $this->debug("read line of $tmplen bytes: " . trim($tmp)); 2563 2564 if ($tmplen == 0) { 2565 $this->incoming_payload = $data; 2566 $this->debug('socket read of headers timed out after length ' . strlen($data)); 2567 $this->debug("read before timeout: " . $data); 2568 $this->setError('socket read of headers timed out'); 2569 return false; 2570 } 2571 2572 $data .= $tmp; 2573 $pos = strpos($data,"\r\n\r\n"); 2574 if($pos > 1){ 2575 $lb = "\r\n"; 2576 } else { 2577 $pos = strpos($data,"\n\n"); 2578 if($pos > 1){ 2579 $lb = "\n"; 2580 } 2581 } 2582 // remove 100 header 2583 if(isset($lb) && ereg('^HTTP/1.1 100',$data)){ 2584 unset($lb); 2585 $data = ''; 2586 }// 2587 } 2588 // store header data 2589 $this->incoming_payload .= $data; 2590 $this->debug('found end of headers after length ' . strlen($data)); 2591 // process headers 2592 $header_data = trim(substr($data,0,$pos)); 2593 $header_array = explode($lb,$header_data); 2594 $this->incoming_headers = array(); 2595 $this->incoming_cookies = array(); 2596 foreach($header_array as $header_line){ 2597 $arr = explode(':',$header_line, 2); 2598 if(count($arr) > 1){ 2599 $header_name = strtolower(trim($arr[0])); 2600 $this->incoming_headers[$header_name] = trim($arr[1]); 2601 if ($header_name == 'set-cookie') { 2602 // TODO: allow multiple cookies from parseCookie 2603 $cookie = $this->parseCookie(trim($arr[1])); 2604 if ($cookie) { 2605 $this->incoming_cookies[] = $cookie; 2606 $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']); 2607 } else { 2608 $this->debug('did not find cookie in ' . trim($arr[1])); 2609 } 2610 } 2611 } else if (isset($header_name)) { 2612 // append continuation line to previous header 2613 $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line; 2614 } 2615 } 2616 2617 // loop until msg has been received 2618 if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') { 2619 $content_length = 2147483647; // ignore any content-length header 2620 $chunked = true; 2621 $this->debug("want to read chunked content"); 2622 } elseif (isset($this->incoming_headers['content-length'])) { 2623 $content_length = $this->incoming_headers['content-length']; 2624 $chunked = false; 2625 $this->debug("want to read content of length $content_length"); 2626 } else { 2627 $content_length = 2147483647; 2628 $chunked = false; 2629 $this->debug("want to read content to EOF"); 2630 } 2631 $data = ''; 2632 do { 2633 if ($chunked) { 2634 $tmp = fgets($this->fp, 256); 2635 $tmplen = strlen($tmp); 2636 $this->debug("read chunk line of $tmplen bytes"); 2637 if ($tmplen == 0) { 2638 $this->incoming_payload = $data; 2639 $this->debug('socket read of chunk length timed out after length ' . strlen($data)); 2640 $this->debug("read before timeout:\n" . $data); 2641 $this->setError('socket read of chunk length timed out'); 2642 return false; 2643 } 2644 $content_length = hexdec(trim($tmp)); 2645 $this->debug("chunk length $content_length"); 2646 } 2647 $strlen = 0; 2648 while (($strlen < $content_length) && (!feof($this->fp))) { 2649 $readlen = min(8192, $content_length - $strlen); 2650 $tmp = fread($this->fp, $readlen); 2651 $tmplen = strlen($tmp); 2652 $this->debug("read buffer of $tmplen bytes"); 2653 if (($tmplen == 0) && (!feof($this->fp))) { 2654 $this->incoming_payload = $data; 2655 $this->debug('socket read of body timed out after length ' . strlen($data)); 2656 $this->debug("read before timeout:\n" . $data); 2657 $this->setError('socket read of body timed out'); 2658 return false; 2659 } 2660 $strlen += $tmplen; 2661 $data .= $tmp; 2662 } 2663 if ($chunked && ($content_length > 0)) { 2664 $tmp = fgets($this->fp, 256); 2665 $tmplen = strlen($tmp); 2666 $this->debug("read chunk terminator of $tmplen bytes"); 2667 if ($tmplen == 0) { 2668 $this->incoming_payload = $data; 2669 $this->debug('socket read of chunk terminator timed out after length ' . strlen($data)); 2670 $this->debug("read before timeout:\n" . $data); 2671 $this->setError('socket read of chunk terminator timed out'); 2672 return false; 2673 } 2674 } 2675 } while ($chunked && ($content_length > 0) && (!feof($this->fp))); 2676 if (feof($this->fp)) { 2677 $this->debug('read to EOF'); 2678 } 2679 $this->debug('read body of length ' . strlen($data)); 2680 $this->incoming_payload .= $data; 2681 $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server'); 2682 2683 // close filepointer 2684 if( 2685 (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') || 2686 (! $this->persistentConnection) || feof($this->fp)){ 2687 fclose($this->fp); 2688 $this->fp = false; 2689 $this->debug('closed socket'); 2690 } 2691 2692 // connection was closed unexpectedly 2693 if($this->incoming_payload == ''){ 2694 $this->setError('no response from server'); 2695 return false; 2696 } 2697 2698 // decode transfer-encoding 2699 // if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){ 2700 // if(!$data = $this->decodeChunked($data, $lb)){ 2701 // $this->setError('Decoding of chunked data failed'); 2702 // return false; 2703 // } 2704 //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>"; 2705 // set decoded payload 2706 // $this->incoming_payload = $header_data.$lb.$lb.$data; 2707 // } 2708 2709 } else if ($this->scheme == 'https') { 2710 // send and receive 2711 $this->debug('send and receive with cURL'); 2712 $this->incoming_payload = curl_exec($this->ch); 2713 $data = $this->incoming_payload; 2714 2715 $cErr = curl_error($this->ch); 2716 if ($cErr != '') { 2717 $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'<br>'; 2718 // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE 2719 foreach(curl_getinfo($this->ch) as $k => $v){ 2720 $err .= "$k: $v<br>"; 2721 } 2722 $this->debug($err); 2723 $this->setError($err); 2724 curl_close($this->ch); 2725 return false; 2726 } else { 2727 //echo '<pre>'; 2728 //var_dump(curl_getinfo($this->ch)); 2729 //echo '</pre>'; 2730 } 2731 // close curl 2732 $this->debug('No cURL error, closing cURL'); 2733 curl_close($this->ch); 2734 2735 // remove 100 header(s) 2736 while (ereg('^HTTP/1.1 100',$data)) { 2737 if ($pos = strpos($data,"\r\n\r\n")) { 2738 $data = ltrim(substr($data,$pos)); 2739 } elseif($pos = strpos($data,"\n\n") ) { 2740 $data = ltrim(substr($data,$pos)); 2741 } 2742 } 2743 2744 // separate content from HTTP headers 2745 if ($pos = strpos($data,"\r\n\r\n")) { 2746 $lb = "\r\n"; 2747 } elseif( $pos = strpos($data,"\n\n")) { 2748 $lb = "\n"; 2749 } else { 2750 $this->debug('no proper separation of headers and document'); 2751 $this->setError('no proper separation of headers and document'); 2752 return false; 2753 } 2754 $header_data = trim(substr($data,0,$pos)); 2755 $header_array = explode($lb,$header_data); 2756 $data = ltrim(substr($data,$pos)); 2757 $this->debug('found proper separation of headers and document'); 2758 $this->debug('cleaned data, stringlen: '.strlen($data)); 2759 // clean headers 2760 foreach ($header_array as $header_line) { 2761 $arr = explode(':',$header_line,2); 2762 if(count($arr) > 1){ 2763 $header_name = strtolower(trim($arr[0])); 2764 $this->incoming_headers[$header_name] = trim($arr[1]); 2765 if ($header_name == 'set-cookie') { 2766 // TODO: allow multiple cookies from parseCookie 2767 $cookie = $this->parseCookie(trim($arr[1])); 2768 if ($cookie) { 2769 $this->incoming_cookies[] = $cookie; 2770 $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']); 2771 } else { 2772 $this->debug('did not find cookie in ' . trim($arr[1])); 2773 } 2774 } 2775 } else if (isset($header_name)) { 2776 // append continuation line to previous header 2777 $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line; 2778 } 2779 } 2780 } 2781 2782 $arr = explode(' ', $header_array[0], 3); 2783 $http_version = $arr[0]; 2784 $http_status = intval($arr[1]); 2785 $http_reason = count($arr) > 2 ? $arr[2] : ''; 2786 2787 // see if we need to resend the request with http digest authentication 2788 if (isset($this->incoming_headers['location']) && $http_status == 301) { 2789 $this->debug("Got 301 $http_reason with Location: " . $this->incoming_headers['location']); 2790 $this->setURL($this->incoming_headers['location']); 2791 $this->tryagain = true; 2792 return false; 2793 } 2794 2795 // see if we need to resend the request with http digest authentication 2796 if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) { 2797 $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']); 2798 if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) { 2799 $this->debug('Server wants digest authentication'); 2800 // remove "Digest " from our elements 2801 $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']); 2802 2803 // parse elements into array 2804 $digestElements = explode(',', $digestString); 2805 foreach ($digestElements as $val) { 2806 $tempElement = explode('=', trim($val), 2); 2807 $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]); 2808 } 2809 2810 // should have (at least) qop, realm, nonce 2811 if (isset($digestRequest['nonce'])) { 2812 $this->setCredentials($this->username, $this->password, 'digest', $digestRequest); 2813 $this->tryagain = true; 2814 return false; 2815 } 2816 } 2817 $this->debug('HTTP authentication failed'); 2818 $this->setError('HTTP authentication failed'); 2819 return false; 2820 } 2821 2822 if ( 2823 ($http_status >= 300 && $http_status <= 307) || 2824 ($http_status >= 400 && $http_status <= 417) || 2825 ($http_status >= 501 && $http_status <= 505) 2826 ) { 2827 $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)"); 2828 return false; 2829 } 2830 2831 // decode content-encoding 2832 if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){ 2833 if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){ 2834 // if decoding works, use it. else assume data wasn't gzencoded 2835 if(function_exists('gzinflate')){ 2836 //$timer->setMarker('starting decoding of gzip/deflated content'); 2837 // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress) 2838 // this means there are no Zlib headers, although there should be 2839 $this->debug('The gzinflate function exists'); 2840 $datalen = strlen($data); 2841 if ($this->incoming_headers['content-encoding'] == 'deflate') { 2842 if ($degzdata = @gzinflate($data)) { 2843 $data = $degzdata; 2844 $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes'); 2845 if (strlen($data) < $datalen) { 2846 // test for the case that the payload has been compressed twice 2847 $this->debug('The inflated payload is smaller than the gzipped one; try again'); 2848 if ($degzdata = @gzinflate($data)) { 2849 $data = $degzdata; 2850 $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes'); 2851 } 2852 } 2853 } else { 2854 $this->debug('Error using gzinflate to inflate the payload'); 2855 $this->setError('Error using gzinflate to inflate the payload'); 2856 } 2857 } elseif ($this->incoming_headers['content-encoding'] == 'gzip') { 2858 if ($degzdata = @gzinflate(substr($data, 10))) { // do our best 2859 $data = $degzdata; 2860 $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes'); 2861 if (strlen($data) < $datalen) { 2862 // test for the case that the payload has been compressed twice 2863 $this->debug('The un-gzipped payload is smaller than the gzipped one; try again'); 2864 if ($degzdata = @gzinflate(substr($data, 10))) { 2865 $data = $degzdata; 2866 $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes'); 2867 } 2868 } 2869 } else { 2870 $this->debug('Error using gzinflate to un-gzip the payload'); 2871 $this->setError('Error using gzinflate to un-gzip the payload'); 2872 } 2873 } 2874 //$timer->setMarker('finished decoding of gzip/deflated content'); 2875 //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>"; 2876 // set decoded payload 2877 $this->incoming_payload = $header_data.$lb.$lb.$data; 2878 } else { 2879 $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.'); 2880 $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.'); 2881 } 2882 } else { 2883 $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']); 2884 $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']); 2885 } 2886 } else { 2887 $this->debug('No Content-Encoding header'); 2888 } 2889 2890 if(strlen($data) == 0){ 2891 $this->debug('no data after headers!'); 2892 $this->setError('no data present after HTTP headers'); 2893 return false; 2894 } 2895 2896 return $data; 2897 } 2898 2899 function setContentType($type, $charset = false) { 2900 $this->outgoing_headers['Content-Type'] = $type . ($charset ? '; charset=' . $charset : ''); 2901 $this->debug('set Content-Type: ' . $this->outgoing_headers['Content-Type']); 2902 } 2903 2904 function usePersistentConnection(){ 2905 if (isset($this->outgoing_headers['Accept-Encoding'])) { 2906 return false; 2907 } 2908 $this->protocol_version = '1.1'; 2909 $this->persistentConnection = true; 2910 $this->outgoing_headers['Connection'] = 'Keep-Alive'; 2911 $this->debug('set Connection: ' . $this->outgoing_headers['Connection']); 2912 return true; 2913 } 2914 2915 /** 2916 * parse an incoming Cookie into it's parts 2917 * 2918 * @param string $cookie_str content of cookie 2919 * @return array with data of that cookie 2920 * @access private 2921 */ 2922 /* 2923 * TODO: allow a Set-Cookie string to be parsed into multiple cookies 2924 */ 2925 function parseCookie($cookie_str) { 2926 $cookie_str = str_replace('; ', ';', $cookie_str) . ';'; 2927 $data = split(';', $cookie_str); 2928 $value_str = $data[0]; 2929 2930 $cookie_param = 'domain='; 2931 $start = strpos($cookie_str, $cookie_param); 2932 if ($start > 0) { 2933 $domain = substr($cookie_str, $start + strlen($cookie_param)); 2934 $domain = substr($domain, 0, strpos($domain, ';')); 2935 } else { 2936 $domain = ''; 2937 } 2938 2939 $cookie_param = 'expires='; 2940 $start = strpos($cookie_str, $cookie_param); 2941 if ($start > 0) { 2942 $expires = substr($cookie_str, $start + strlen($cookie_param)); 2943 $expires = substr($expires, 0, strpos($expires, ';')); 2944 } else { 2945 $expires = ''; 2946 } 2947 2948 $cookie_param = 'path='; 2949 $start = strpos($cookie_str, $cookie_param); 2950 if ( $start > 0 ) { 2951 $path = substr($cookie_str, $start + strlen($cookie_param)); 2952 $path = substr($path, 0, strpos($path, ';')); 2953 } else { 2954 $path = '/'; 2955 } 2956 2957 $cookie_param = ';secure;'; 2958 if (strpos($cookie_str, $cookie_param) !== FALSE) { 2959 $secure = true; 2960 } else { 2961 $secure = false; 2962 } 2963 2964 $sep_pos = strpos($value_str, '='); 2965 2966 if ($sep_pos) { 2967 $name = substr($value_str, 0, $sep_pos); 2968 $value = substr($value_str, $sep_pos + 1); 2969 $cookie= array( 'name' => $name, 2970 'value' => $value, 2971 'domain' => $domain, 2972 'path' => $path, 2973 'expires' => $expires, 2974 'secure' => $secure 2975 ); 2976 return $cookie; 2977 } 2978 return false; 2979 } 2980 2981 /** 2982 * sort out cookies for the current request 2983 * 2984 * @param array $cookies array with all cookies 2985 * @param boolean $secure is the send-content secure or not? 2986 * @return string for Cookie-HTTP-Header 2987 * @access private 2988 */ 2989 function getCookiesForRequest($cookies, $secure=false) { 2990 $cookie_str = ''; 2991 if ((! is_null($cookies)) && (is_array($cookies))) { 2992 foreach ($cookies as $cookie) { 2993 if (! is_array($cookie)) { 2994 continue; 2995 } 2996 $this->debug("check cookie for validity: ".$cookie['name'].'='.$cookie['value']); 2997 if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) { 2998 if (strtotime($cookie['expires']) <= time()) { 2999 $this->debug('cookie has expired'); 3000 continue; 3001 } 3002 } 3003 if ((isset($cookie['domain'])) && (! empty($cookie['domain']))) { 3004 $domain = preg_quote($cookie['domain']); 3005 if (! preg_match("'.*$domain$'i", $this->host)) { 3006 $this->debug('cookie has different domain'); 3007 continue; 3008 } 3009 } 3010 if ((isset($cookie['path'])) && (! empty($cookie['path']))) { 3011 $path = preg_quote($cookie['path']); 3012 if (! preg_match("'^$path.*'i", $this->path)) { 3013 $this->debug('cookie is for a different path'); 3014 continue; 3015 } 3016 } 3017 if ((! $secure) && (isset($cookie['secure'])) && ($cookie['secure'])) { 3018 $this->debug('cookie is secure, transport is not'); 3019 continue; 3020 } 3021 $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; '; 3022 $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']); 3023 } 3024 } 3025 return $cookie_str; 3026 } 3027 } 3028 3029 ?><?php 3030 3031 3032 3033 /** 3034 * 3035 * soap_server allows the user to create a SOAP server 3036 * that is capable of receiving messages and returning responses 3037 * 3038 * NOTE: WSDL functionality is experimental 3039 * 3040 * @author Dietrich Ayala <dietrich@ganx4.com> 3041 * @version $Id: nusoap.php,v 1.95 2006/02/02 15:52:34 snichol Exp $ 3042 * @access public 3043 */ 3044 class soap_server extends nusoap_base { 3045 /** 3046 * HTTP headers of request 3047 * @var array 3048 * @access private 3049 */ 3050 var $headers = array(); 3051 /** 3052 * HTTP request 3053 * @var string 3054 * @access private 3055 */ 3056 var $request = ''; 3057 /** 3058 * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text) 3059 * @var string 3060 * @access public 3061 */ 3062 var $requestHeaders = ''; 3063 /** 3064 * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text) 3065 * @var string 3066 * @access public 3067 */ 3068 var $document = ''; 3069 /** 3070 * SOAP payload for request (text) 3071 * @var string 3072 * @access public 3073 */ 3074 var $requestSOAP = ''; 3075 /** 3076 * requested method namespace URI 3077 * @var string 3078 * @access private 3079 */ 3080 var $methodURI = ''; 3081 /** 3082 * name of method requested 3083 * @var string 3084 * @access private 3085 */ 3086 var $methodname = ''; 3087 /** 3088 * method parameters from request 3089 * @var array 3090 * @access private 3091 */ 3092 var $methodparams = array(); 3093 /** 3094 * SOAP Action from request 3095 * @var string 3096 * @access private 3097 */ 3098 var $SOAPAction = ''; 3099 /** 3100 * character set encoding of incoming (request) messages 3101 * @var string 3102 * @access public 3103 */ 3104 var $xml_encoding = ''; 3105 /** 3106 * toggles whether the parser decodes element content w/ utf8_decode() 3107 * @var boolean 3108 * @access public 3109 */ 3110 var $decode_utf8 = true; 3111 3112 /** 3113 * HTTP headers of response 3114 * @var array 3115 * @access public 3116 */ 3117 var $outgoing_headers = array(); 3118 /** 3119 * HTTP response 3120 * @var string 3121 * @access private 3122 */ 3123 var $response = ''; 3124 /** 3125 * SOAP headers for response (text) 3126 * @var string 3127 * @access public 3128 */ 3129 var $responseHeaders = ''; 3130 /** 3131 * SOAP payload for response (text) 3132 * @var string 3133 * @access private 3134 */ 3135 var $responseSOAP = ''; 3136 /** 3137 * method return value to place in response 3138 * @var mixed 3139 * @access private 3140 */ 3141 var $methodreturn = false; 3142 /** 3143 * whether $methodreturn is a string of literal XML 3144 * @var boolean 3145 * @access public 3146 */ 3147 var $methodreturnisliteralxml = false; 3148 /** 3149 * SOAP fault for response (or false) 3150 * @var mixed 3151 * @access private 3152 */ 3153 var $fault = false; 3154 /** 3155 * text indication of result (for debugging) 3156 * @var string 3157 * @access private 3158 */ 3159 var $result = 'successful'; 3160 3161 /** 3162 * assoc array of operations => opData; operations are added by the register() 3163 * method or by parsing an external WSDL definition 3164 * @var array 3165 * @access private 3166 */ 3167 var $operations = array(); 3168 /** 3169 * wsdl instance (if one) 3170 * @var mixed 3171 * @access private 3172 */ 3173 var $wsdl = false; 3174 /** 3175 * URL for WSDL (if one) 3176 * @var mixed 3177 * @access private 3178 */ 3179 var $externalWSDLURL = false; 3180 /** 3181 * whether to append debug to response as XML comment 3182 * @var boolean 3183 * @access public 3184 */ 3185 var $debug_flag = false; 3186 3187 3188 /** 3189 * constructor 3190 * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to. 3191 * 3192 * @param mixed $wsdl file path or URL (string), or wsdl instance (object) 3193 * @access public 3194 */ 3195 function soap_server($wsdl=false){ 3196 parent::nusoap_base(); 3197 // turn on debugging? 3198 global $debug; 3199 global $HTTP_SERVER_VARS; 3200 3201 if (isset($_SERVER)) { 3202 $this->debug("_SERVER is defined:"); 3203 $this->appendDebug($this->varDump($_SERVER)); 3204 } elseif (isset($HTTP_SERVER_VARS)) { 3205 $this->debug("HTTP_SERVER_VARS is defined:"); 3206 $this->appendDebug($this->varDump($HTTP_SERVER_VARS)); 3207 } else { 3208 $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined."); 3209 } 3210 3211 if (isset($debug)) { 3212 $this->debug("In soap_server, set debug_flag=$debug based on global flag"); 3213 $this->debug_flag = $debug; 3214 } elseif (isset($_SERVER['QUERY_STRING'])) { 3215 $qs = explode('&', $_SERVER['QUERY_STRING']); 3216 foreach ($qs as $v) { 3217 if (substr($v, 0, 6) == 'debug=') { 3218 $this->debug("In soap_server, set debug_flag=" . substr($v, 6) . " based on query string #1"); 3219 $this->debug_flag = substr($v, 6); 3220 } 3221 } 3222 } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) { 3223 $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']); 3224 foreach ($qs as $v) { 3225 if (substr($v, 0, 6) == 'debug=') { 3226 $this->debug("In soap_server, set debug_flag=" . substr($v, 6) . " based on query string #2"); 3227 $this->debug_flag = substr($v, 6); 3228 } 3229 } 3230 } 3231 3232 // wsdl 3233 if($wsdl){ 3234 $this->debug("In soap_server, WSDL is specified"); 3235 if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) { 3236 $this->wsdl = $wsdl; 3237 $this->externalWSDLURL = $this->wsdl->wsdl; 3238 $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL); 3239 } else { 3240 $this->debug('Create wsdl from ' . $wsdl); 3241 $this->wsdl = new wsdl($wsdl); 3242 $this->externalWSDLURL = $wsdl; 3243 } 3244 $this->appendDebug($this->wsdl->getDebug()); 3245 $this->wsdl->clearDebug(); 3246 if($err = $this->wsdl->getError()){ 3247 die('WSDL ERROR: '.$err); 3248 } 3249 } 3250 } 3251 3252 /** 3253 * processes request and returns response 3254 * 3255 * @param string $data usually is the value of $HTTP_RAW_POST_DATA 3256 * @access public 3257 */ 3258 function service($data){ 3259 global $HTTP_SERVER_VARS; 3260 3261 if (isset($_SERVER['QUERY_STRING'])) { 3262 $qs = $_SERVER['QUERY_STRING']; 3263 } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) { 3264 $qs = $HTTP_SERVER_VARS['QUERY_STRING']; 3265 } else { 3266 $qs = ''; 3267 } 3268 $this->debug("In service, query string=$qs"); 3269 3270 if (ereg('wsdl', $qs) ){ 3271 $this->debug("In service, this is a request for WSDL"); 3272 if($this->externalWSDLURL){ 3273 if (strpos($this->externalWSDLURL,"://")!==false) { // assume URL 3274 header('Location: '.$this->externalWSDLURL); 3275 } else { // assume file 3276 header("Content-Type: text/xml\r\n"); 3277 $fp = fopen($this->externalWSDLURL, 'r'); 3278 fpassthru($fp); 3279 } 3280 } elseif ($this->wsdl) { 3281 header("Content-Type: text/xml; charset=ISO-8859-1\r\n"); 3282 print $this->wsdl->serialize($this->debug_flag); 3283 if ($this->debug_flag) { 3284 $this->debug('wsdl:'); 3285 $this->appendDebug($this->varDump($this->wsdl)); 3286 print $this->getDebugAsXMLComment(); 3287 } 3288 } else { 3289 header("Content-Type: text/html; charset=ISO-8859-1\r\n"); 3290 print "This service does not provide WSDL"; 3291 } 3292 } elseif ($data == '' && $this->wsdl) { 3293 $this->debug("In service, there is no data, so return Web description"); 3294 print $this->wsdl->webDescription(); 3295 } else { 3296 $this->debug("In service, invoke the request"); 3297 $this->parse_request($data); 3298 if (! $this->fault) { 3299 $this->invoke_method(); 3300 } 3301 if (! $this->fault) { 3302 $this->serialize_return(); 3303 } 3304 $this->send_response(); 3305 } 3306 } 3307 3308 /** 3309 * parses HTTP request headers. 3310 * 3311 * The following fields are set by this function (when successful) 3312 * 3313 * headers 3314 * request 3315 * xml_encoding 3316 * SOAPAction 3317 * 3318 * @access private 3319 */ 3320 function parse_http_headers() { 3321 global $HTTP_SERVER_VARS; 3322 3323 $this->request = ''; 3324 $this->SOAPAction = ''; 3325 if(function_exists('getallheaders')){ 3326 $this->debug("In parse_http_headers, use getallheaders"); 3327 $headers = getallheaders(); 3328 foreach($headers as $k=>$v){ 3329 $k = strtolower($k); 3330 $this->headers[$k] = $v; 3331 $this->request .= "$k: $v\r\n"; 3332 $this->debug("$k: $v"); 3333 } 3334 // get SOAPAction header 3335 if(isset($this->headers['soapaction'])){ 3336 $this->SOAPAction = str_replace('"','',$this->headers['soapaction']); 3337 } 3338 // get the character encoding of the incoming request 3339 if(isset($this->headers['content-type']) && strpos($this->headers['content-type'],'=')){ 3340 $enc = str_replace('"','',substr(strstr($this->headers["content-type"],'='),1)); 3341 if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){ 3342 $this->xml_encoding = strtoupper($enc); 3343 } else { 3344 $this->xml_encoding = 'US-ASCII'; 3345 } 3346 } else { 3347 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 3348 $this->xml_encoding = 'ISO-8859-1'; 3349 } 3350 } elseif(isset($_SERVER) && is_array($_SERVER)){ 3351 $this->debug("In parse_http_headers, use _SERVER"); 3352 foreach ($_SERVER as $k => $v) { 3353 if (substr($k, 0, 5) == 'HTTP_') { 3354 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); $k = strtolower(substr($k, 5)); 3355 } else { 3356 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); $k = strtolower($k); 3357 } 3358 if ($k == 'soapaction') { 3359 // get SOAPAction header 3360 $k = 'SOAPAction'; 3361 $v = str_replace('"', '', $v); 3362 $v = str_replace('\\', '', $v); 3363 $this->SOAPAction = $v; 3364 } else if ($k == 'content-type') { 3365 // get the character encoding of the incoming request 3366 if (strpos($v, '=')) { 3367 $enc = substr(strstr($v, '='), 1); 3368 $enc = str_replace('"', '', $enc); 3369 $enc = str_replace('\\', '', $enc); 3370 if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) { 3371 $this->xml_encoding = strtoupper($enc); 3372 } else { 3373 $this->xml_encoding = 'US-ASCII'; 3374 } 3375 } else { 3376 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 3377 $this->xml_encoding = 'ISO-8859-1'; 3378 } 3379 } 3380 $this->headers[$k] = $v; 3381 $this->request .= "$k: $v\r\n"; 3382 $this->debug("$k: $v"); 3383 } 3384 } elseif (is_array($HTTP_SERVER_VARS)) { 3385 $this->debug("In parse_http_headers, use HTTP_SERVER_VARS"); 3386 foreach ($HTTP_SERVER_VARS as $k => $v) { 3387 if (substr($k, 0, 5) == 'HTTP_') { 3388 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); $k = strtolower(substr($k, 5)); 3389 } else { 3390 $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); $k = strtolower($k); 3391 } 3392 if ($k == 'soapaction') { 3393 // get SOAPAction header 3394 $k = 'SOAPAction'; 3395 $v = str_replace('"', '', $v); 3396 $v = str_replace('\\', '', $v); 3397 $this->SOAPAction = $v; 3398 } else if ($k == 'content-type') { 3399 // get the character encoding of the incoming request 3400 if (strpos($v, '=')) { 3401 $enc = substr(strstr($v, '='), 1); 3402 $enc = str_replace('"', '', $enc); 3403 $enc = str_replace('\\', '', $enc); 3404 if (eregi('^(ISO-8859-1|US-ASCII|UTF-8)$', $enc)) { 3405 $this->xml_encoding = strtoupper($enc); 3406 } else { 3407 $this->xml_encoding = 'US-ASCII'; 3408 } 3409 } else { 3410 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 3411 $this->xml_encoding = 'ISO-8859-1'; 3412 } 3413 } 3414 $this->headers[$k] = $v; 3415 $this->request .= "$k: $v\r\n"; 3416 $this->debug("$k: $v"); 3417 } 3418 } else { 3419 $this->debug("In parse_http_headers, HTTP headers not accessible"); 3420 $this->setError("HTTP headers not accessible"); 3421 } 3422 } 3423 3424 /** 3425 * parses a request 3426 * 3427 * The following fields are set by this function (when successful) 3428 * 3429 * headers 3430 * request 3431 * xml_encoding 3432 * SOAPAction 3433 * request 3434 * requestSOAP 3435 * methodURI 3436 * methodname 3437 * methodparams 3438 * requestHeaders 3439 * document 3440 * 3441 * This sets the fault field on error 3442 * 3443 * @param string $data XML string 3444 * @access private 3445 */ 3446 function parse_request($data='') { 3447 $this->debug('entering parse_request()'); 3448 $this->parse_http_headers(); 3449 $this->debug('got character encoding: '.$this->xml_encoding); 3450 // uncompress if necessary 3451 if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') { 3452 $this->debug('got content encoding: ' . $this->headers['content-encoding']); 3453 if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') { 3454 // if decoding works, use it. else assume data wasn't gzencoded 3455 if (function_exists('gzuncompress')) { 3456 if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) { 3457 $data = $degzdata; 3458 } elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) { 3459 $data = $degzdata; 3460 } else { 3461 $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data'); 3462 return; 3463 } 3464 } else { 3465 $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data'); 3466 return; 3467 } 3468 } 3469 } 3470 $this->request .= "\r\n".$data; 3471 $data = $this->parseRequest($this->headers, $data); 3472 $this->requestSOAP = $data; 3473 $this->debug('leaving parse_request'); 3474 } 3475 3476 /** 3477 * invokes a PHP function for the requested SOAP method 3478 * 3479 * The following fields are set by this function (when successful) 3480 * 3481 * methodreturn 3482 * 3483 * Note that the PHP function that is called may also set the following 3484 * fields to affect the response sent to the client 3485 * 3486 * responseHeaders 3487 * outgoing_headers 3488 * 3489 * This sets the fault field on error 3490 * 3491 * @access private 3492 */ 3493 function invoke_method() { 3494 $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction); 3495 3496 if ($this->wsdl) { 3497 if ($this->opData = $this->wsdl->getOperationData($this->methodname)) { 3498 $this->debug('in invoke_method, found WSDL operation=' . $this->methodname); 3499 $this->appendDebug('opData=' . $this->varDump($this->opData)); 3500 } elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) { 3501 // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element 3502 $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']); 3503 $this->appendDebug('opData=' . $this->varDump($this->opData)); 3504 $this->methodname = $this->opData['name']; 3505 } else { 3506 $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname); 3507 $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service"); 3508 return; 3509 } 3510 } else { 3511 $this->debug('in invoke_method, no WSDL to validate method'); 3512 } 3513 3514 // if a . is present in $this->methodname, we see if there is a class in scope, 3515 // which could be referred to. We will also distinguish between two deliminators, 3516 // to allow methods to be called a the class or an instance 3517 $class = ''; 3518 $method = ''; 3519 if (strpos($this->methodname, '..') > 0) { 3520 $delim = '..'; 3521 } else if (strpos($this->methodname, '.') > 0) { 3522 $delim = '.'; 3523 } else { 3524 $delim = ''; 3525 } 3526 3527 if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1 && 3528 class_exists(substr($this->methodname, 0, strpos($this->methodname, $delim)))) { 3529 // get the class and method name 3530 $class = substr($this->methodname, 0, strpos($this->methodname, $delim)); 3531 $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim)); 3532 $this->debug("in invoke_method, class=$class method=$method delim=$delim"); 3533 } 3534 3535 // does method exist? 3536 if ($class == '') { 3537 if (!function_exists($this->methodname)) { 3538 $this->debug("in invoke_method, function '$this->methodname' not found!"); 3539 $this->result = 'fault: method not found'; 3540 $this->fault('SOAP-ENV:Client',"method '$this->methodname' not defined in service"); 3541 return; 3542 } 3543 } else { 3544 $method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method; 3545 if (!in_array($method_to_compare, get_class_methods($class))) { 3546 $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!"); 3547 $this->result = 'fault: method not found'; 3548 $this->fault('SOAP-ENV:Client',"method '$this->methodname' not defined in service"); 3549 return; 3550 } 3551 } 3552 3553 // evaluate message, getting back parameters 3554 // verify that request parameters match the method's signature 3555 if(! $this->verify_method($this->methodname,$this->methodparams)){ 3556 // debug 3557 $this->debug('ERROR: request not verified against method signature'); 3558 $this->result = 'fault: request failed validation against method signature'; 3559 // return fault 3560 $this->fault('SOAP-ENV:Client',"Operation '$this->methodname' not defined in service."); 3561 return; 3562 } 3563 3564 // if there are parameters to pass 3565 $this->debug('in invoke_method, params:'); 3566 $this->appendDebug($this->varDump($this->methodparams)); 3567 $this->debug("in invoke_method, calling '$this->methodname'"); 3568 if (!function_exists('call_user_func_array')) { 3569 if ($class == '') { 3570 $this->debug('in invoke_method, calling function using eval()'); 3571 $funcCall = "\$this->methodreturn = $this->methodname("; 3572 } else { 3573 if ($delim == '..') { 3574 $this->debug('in invoke_method, calling class method using eval()'); 3575 $funcCall = "\$this->methodreturn = ".$class."::".$method."("; 3576 } else { 3577 $this->debug('in invoke_method, calling instance method using eval()'); 3578 // generate unique instance name 3579 $instname = "\$inst_".time(); 3580 $funcCall = $instname." = new ".$class."(); "; 3581 $funcCall .= "\$this->methodreturn = ".$instname."->".$method."("; 3582 } 3583 } 3584 if ($this->methodparams) { 3585 foreach ($this->methodparams as $param) { 3586 if (is_array($param)) { 3587 $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available'); 3588 return; 3589 } 3590 $funcCall .= "\"$param\","; 3591 } 3592 $funcCall = substr($funcCall, 0, -1); 3593 } 3594 $funcCall .= ');'; 3595 $this->debug('in invoke_method, function call: '.$funcCall); 3596 @eval($funcCall); 3597 } else { 3598 if ($class == '') { 3599 $this->debug('in invoke_method, calling function using call_user_func_array()'); 3600 $call_arg = "$this->methodname"; // straight assignment changes $this->methodname to lower case after call_user_func_array() 3601 } elseif ($delim == '..') { 3602 $this->debug('in invoke_method, calling class method using call_user_func_array()'); 3603 $call_arg = array ($class, $method); 3604 } else { 3605 $this->debug('in invoke_method, calling instance method using call_user_func_array()'); 3606 $instance = new $class (); 3607 $call_arg = array(&$instance, $method); 3608 } 3609 $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams)); 3610 } 3611 $this->debug('in invoke_method, methodreturn:'); 3612 $this->appendDebug($this->varDump($this->methodreturn)); 3613 $this->debug("in invoke_method, called method $this->methodname, received $this->methodreturn of type ".gettype($this->methodreturn)); 3614 } 3615 3616 /** 3617 * serializes the return value from a PHP function into a full SOAP Envelope 3618 * 3619 * The following fields are set by this function (when successful) 3620 * 3621 * responseSOAP 3622 * 3623 * This sets the fault field on error 3624 * 3625 * @access private 3626 */ 3627 function serialize_return() { 3628 $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI); 3629 // if fault 3630 if (isset($this->methodreturn) && (get_class($this->methodreturn) == 'soap_fault')) { 3631 $this->debug('got a fault object from method'); 3632 $this->fault = $this->methodreturn; 3633 return; 3634 } elseif ($this->methodreturnisliteralxml) { 3635 $return_val = $this->methodreturn; 3636 // returned value(s) 3637 } else { 3638 $this->debug('got a(n) '.gettype($this->methodreturn).' from method'); 3639 $this->debug('serializing return value'); 3640 if($this->wsdl){ 3641 // weak attempt at supporting multiple output params 3642 if(sizeof($this->opData['output']['parts']) > 1){ 3643 $opParams = $this->methodreturn; 3644 } else { 3645 // TODO: is this really necessary? 3646 $opParams = array($this->methodreturn); 3647 } 3648 $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams); 3649 $this->appendDebug($this->wsdl->getDebug()); 3650 $this->wsdl->clearDebug(); 3651 if($errstr = $this->wsdl->getError()){ 3652 $this->debug('got wsdl error: '.$errstr); 3653 $this->fault('SOAP-ENV:Server', 'unable to serialize result'); 3654 return; 3655 } 3656 } else { 3657 if (isset($this->methodreturn)) { 3658 $return_val = $this->serialize_val($this->methodreturn, 'return'); 3659 } else { 3660 $return_val = ''; 3661 $this->debug('in absence of WSDL, assume void return for backward compatibility'); 3662 } 3663 } 3664 } 3665 $this->debug('return value:'); 3666 $this->appendDebug($this->varDump($return_val)); 3667 3668 $this->debug('serializing response'); 3669 if ($this->wsdl) { 3670 $this->debug('have WSDL for serialization: style is ' . $this->opData['style']); 3671 if ($this->opData['style'] == 'rpc') { 3672 $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']); 3673 if ($this->opData['output']['use'] == 'literal') { 3674 $payload = '<'.$this->methodname.'Response xmlns="'.$this->methodURI.'">'.$return_val.'</'.$this->methodname."Response>"; 3675 } else { 3676 $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>"; 3677 } 3678 } else { 3679 $this->debug('style is not rpc for serialization: assume document'); 3680 $payload = $return_val; 3681 } 3682 } else { 3683 $this->debug('do not have WSDL for serialization: assume rpc/encoded'); 3684 $payload = '<ns1:'.$this->methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'</ns1:'.$this->methodname."Response>"; 3685 } 3686 $this->result = 'successful'; 3687 if($this->wsdl){ 3688 //if($this->debug_flag){ 3689 $this->appendDebug($this->wsdl->getDebug()); 3690 // } 3691 if (isset($opData['output']['encodingStyle'])) { 3692 $encodingStyle = $opData['output']['encodingStyle']; 3693 } else { 3694 $encodingStyle = ''; 3695 } 3696 // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces. 3697 $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style'],$encodingStyle); 3698 } else { 3699 $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders); 3700 } 3701 $this->debug("Leaving serialize_return"); 3702 } 3703 3704 /** 3705 * sends an HTTP response 3706 * 3707 * The following fields are set by this function (when successful) 3708 * 3709 * outgoing_headers 3710 * response 3711 * 3712 * @access private 3713 */ 3714 function send_response() { 3715 $this->debug('Enter send_response'); 3716 if ($this->fault) { 3717 $payload = $this->fault->serialize(); 3718 $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error"; 3719 $this->outgoing_headers[] = "Status: 500 Internal Server Error"; 3720 } else { 3721 $payload = $this->responseSOAP; 3722 // Some combinations of PHP+Web server allow the Status 3723 // to come through as a header. Since OK is the default 3724 // just do nothing. 3725 // $this->outgoing_headers[] = "HTTP/1.0 200 OK"; 3726 // $this->outgoing_headers[] = "Status: 200 OK"; 3727 } 3728 // add debug data if in debug mode 3729 if(isset($this->debug_flag) && $this->debug_flag){ 3730 $payload .= $this->getDebugAsXMLComment(); 3731 } 3732 $this->outgoing_headers[] = "Server: $this->title Server v$this->version"; 3733 ereg('\$Revisio' . 'n: ([^ ]+)', $this->revision, $rev); 3734 $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")"; 3735 // Let the Web server decide about this 3736 //$this->outgoing_headers[] = "Connection: Close\r\n"; 3737 $payload = $this->getHTTPBody($payload); 3738 $type = $this->getHTTPContentType(); 3739 $charset = $this->getHTTPContentTypeCharset(); 3740 $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : ''); 3741 //begin code to compress payload - by John 3742 // NOTE: there is no way to know whether the Web server will also compress 3743 // this data. 3744 if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) { 3745 if (strstr($this->headers['accept-encoding'], 'gzip')) { 3746 if (function_exists('gzencode')) { 3747 if (isset($this->debug_flag) && $this->debug_flag) { 3748 $payload .= "<!-- Content being gzipped -->"; 3749 } 3750 $this->outgoing_headers[] = "Content-Encoding: gzip"; 3751 $payload = gzencode($payload); 3752 } else { 3753 if (isset($this->debug_flag) && $this->debug_flag) { 3754 $payload .= "<!-- Content will not be gzipped: no gzencode -->"; 3755 } 3756 } 3757 } elseif (strstr($this->headers['accept-encoding'], 'deflate')) { 3758 // Note: MSIE requires gzdeflate output (no Zlib header and checksum), 3759 // instead of gzcompress output, 3760 // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5) 3761 if (function_exists('gzdeflate')) { 3762 if (isset($this->debug_flag) && $this->debug_flag) { 3763 $payload .= "<!-- Content being deflated -->"; 3764 } 3765 $this->outgoing_headers[] = "Content-Encoding: deflate"; 3766 $payload = gzdeflate($payload); 3767 } else { 3768 if (isset($this->debug_flag) && $this->debug_flag) { 3769 $payload .= "<!-- Content will not be deflated: no gzcompress -->"; 3770 } 3771 } 3772 } 3773 } 3774 //end code 3775 $this->outgoing_headers[] = "Content-Length: ".strlen($payload); 3776 reset($this->outgoing_headers); 3777 foreach($this->outgoing_headers as $hdr){ 3778 header($hdr, false); 3779 } 3780 print $payload; 3781 $this->response = join("\r\n",$this->outgoing_headers)."\r\n\r\n".$payload; 3782 } 3783 3784 /** 3785 * takes the value that was created by parsing the request 3786 * and compares to the method's signature, if available. 3787 * 3788 * @param string $operation The operation to be invoked 3789 * @param array $request The array of parameter values 3790 * @return boolean Whether the operation was found 3791 * @access private 3792 */ 3793 function verify_method($operation,$request){ 3794 if(isset($this->wsdl) && is_object($this->wsdl)){ 3795 if($this->wsdl->getOperationData($operation)){ 3796 return true; 3797 } 3798 } elseif(isset($this->operations[$operation])){ 3799 return true; 3800 } 3801 return false; 3802 } 3803 3804 /** 3805 * processes SOAP message received from client 3806 * 3807 * @param array $headers The HTTP headers 3808 * @param string $data unprocessed request data from client 3809 * @return mixed value of the message, decoded into a PHP type 3810 * @access private 3811 */ 3812 function parseRequest($headers, $data) { 3813 $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' and type ' . $headers['content-type']); 3814 if (!strstr($headers['content-type'], 'text/xml')) { 3815 $this->setError('Request not of type text/xml'); 3816 return false; 3817 } 3818 if (strpos($headers['content-type'], '=')) { 3819 $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1)); 3820 $this->debug('Got response encoding: ' . $enc); 3821 if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){ 3822 $this->xml_encoding = strtoupper($enc); 3823 } else { 3824 $this->xml_encoding = 'US-ASCII'; 3825 } 3826 } else { 3827 // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 3828 $this->xml_encoding = 'ISO-8859-1'; 3829 } 3830 $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating soap_parser'); 3831 // parse response, get soap parser obj 3832 $parser = new soap_parser($data,$this->xml_encoding,'',$this->decode_utf8); 3833 // parser debug 3834 $this->debug("parser debug: \n".$parser->getDebug()); 3835 // if fault occurred during message parsing 3836 if($err = $parser->getError()){ 3837 $this->result = 'fault: error in msg parsing: '.$err; 3838 $this->fault('SOAP-ENV:Client',"error in msg parsing:\n".$err); 3839 // else successfully parsed request into soapval object 3840 } else { 3841 // get/set methodname 3842 $this->methodURI = $parser->root_struct_namespace; 3843 $this->methodname = $parser->root_struct_name; 3844 $this->debug('methodname: '.$this->methodname.' methodURI: '.$this->methodURI); 3845