PageRenderTime 45ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/classes/nusoap.php

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