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