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