Source for file nusoap.php

Documentation is available at nusoap.php

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

Documentation generated on Tue, 06 Nov 2007 10:35:46 -0500 by phpDocumentor 1.3.0RC3