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