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