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