Source for file nusoap.php

Documentation is available at nusoap.php

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

Documentation generated on Wed, 3 Aug 2005 21:29:33 -0400 by phpDocumentor 1.3.0RC3