PageRenderTime 74ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/gforge/plugins/wiki/www/lib/nusoap/nusoap.php

https://github.com/neymanna/fusionforge
PHP | 4085 lines | 2653 code | 201 blank | 1231 comment | 681 complexity | d3d06aa82a6c877f6c88618c587b313d MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception
  1. <?php
  2. /**
  3. * NuSOAP - Web Services Toolkit for PHP
  4. *
  5. * @author: Dietrich Ayala
  6. */
  7. /*
  8. Copyright (c) 2002 NuSphere Corporation
  9. This library is free software; you can redistribute it and/or
  10. modify it under the terms of the GNU Lesser General Public
  11. License as published by the Free Software Foundation; either
  12. version 2.1 of the License, or (at your option) any later version.
  13. This library is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. Lesser General Public License for more details.
  17. You should have received a copy of the GNU Lesser General Public
  18. License along with this library; if not, write to the Free Software
  19. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  20. If you have any questions or comments, please email:
  21. Dietrich Ayala
  22. dietrich@ganx4.com
  23. http://dietrich.ganx4.com/nusoap
  24. NuSphere Corporation
  25. http://www.nusphere.com
  26. */
  27. /* load classes
  28. // necessary classes
  29. require_once('class.nusoapclient.php');
  30. require_once('class.soap_val.php');
  31. require_once('class.soap_parser.php');
  32. require_once('class.soap_fault.php');
  33. // transport classes
  34. require_once('class.soap_transport_http.php');
  35. // optional add-on classes
  36. require_once('class.xmlschema.php');
  37. require_once('class.wsdl.php');
  38. // server class
  39. require_once('class.soap_server.php');*/
  40. /**
  41. *
  42. * nusoap_base
  43. *
  44. * @author Dietrich Ayala <dietrich@ganx4.com>
  45. * @version v 0.6.3
  46. * @access public
  47. */
  48. class nusoap_base {
  49. var $title = 'NuSOAP';
  50. var $version = '0.6.3';
  51. var $error_str = false;
  52. var $debug_str = '';
  53. // toggles automatic encoding of special characters
  54. var $charencoding = true;
  55. /**
  56. * set schema version
  57. *
  58. * @var XMLSchemaVersion
  59. * @access public
  60. */
  61. var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
  62. /**
  63. * set default encoding
  64. *
  65. * @var soap_defencoding
  66. * @access public
  67. */
  68. //var $soap_defencoding = 'UTF-8';
  69. var $soap_defencoding = 'ISO-8859-1';
  70. /**
  71. * load namespace uris into an array of uri => prefix
  72. *
  73. * @var namespaces
  74. * @access public
  75. */
  76. var $namespaces = array(
  77. 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
  78. 'xsd' => 'http://www.w3.org/2001/XMLSchema',
  79. 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
  80. 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/',
  81. 'si' => 'http://soapinterop.org/xsd');
  82. /**
  83. * load types into typemap array
  84. * is this legacy yet?
  85. * no, this is used by the xmlschema class to verify type => namespace mappings.
  86. * @var typemap
  87. * @access public
  88. */
  89. var $typemap =
  90. array(
  91. 'http://www.w3.org/2001/XMLSchema' =>
  92. array(
  93. 'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
  94. 'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
  95. 'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
  96. // derived datatypes
  97. 'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
  98. 'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
  99. 'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
  100. 'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
  101. 'http://www.w3.org/1999/XMLSchema' =>
  102. array(
  103. 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
  104. 'float'=>'double','dateTime'=>'string',
  105. 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
  106. 'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
  107. 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
  108. 'http://xml.apache.org/xml-soap' => array('Map')
  109. );
  110. /**
  111. * entities to convert
  112. *
  113. * @var xmlEntities
  114. * @access public
  115. */
  116. var $xmlEntities = array('quot' => '"','amp' => '&',
  117. 'lt' => '<','gt' => '>','apos' => "'");
  118. /**
  119. * adds debug data to the class level debug string
  120. *
  121. * @param string $string debug data
  122. * @access private
  123. */
  124. function debug($string){
  125. $this->debug_str .= get_class($this).": $string\n";
  126. }
  127. /**
  128. * returns error string if present
  129. *
  130. * @return boolean $string error string
  131. * @access public
  132. */
  133. function getError(){
  134. if($this->error_str != ''){
  135. return $this->error_str;
  136. }
  137. return false;
  138. }
  139. /**
  140. * sets error string
  141. *
  142. * @return boolean $string error string
  143. * @access private
  144. */
  145. function setError($str){
  146. $this->error_str = $str;
  147. }
  148. /**
  149. * serializes PHP values in accordance w/ section 5. Type information is
  150. * not serialized if $use == 'literal'.
  151. *
  152. * @return string
  153. * @access public
  154. */
  155. function serialize_val($val,$name=false,$type=false,$name_ns=false,
  156. $type_ns=false,$attributes=false,$use='encoded') {
  157. if(is_object($val) && strtolower(get_class($val)) == 'soapval'){
  158. return $val->serialize($use);
  159. }
  160. $this->debug( "in serialize_val: $val, $name, $type, $name_ns, $type_ns, $attributes, $use");
  161. // if no name, use item
  162. $name = (!$name|| is_numeric($name)) ? 'soapVal' : $name;
  163. // if name has ns, add ns prefix to name
  164. $xmlns = '';
  165. if($name_ns){
  166. $prefix = 'nu'.rand(1000,9999);
  167. $name = $prefix.':'.$name;
  168. $xmlns .= " xmlns:$prefix=\"$name_ns\"";
  169. }
  170. // if type is prefixed, create type prefix
  171. if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
  172. // need to fix this. shouldn't default to xsd if no ns specified
  173. // w/o checking against typemap
  174. $type_prefix = 'xsd';
  175. } elseif($type_ns){
  176. $type_prefix = 'ns'.rand(1000,9999);
  177. $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
  178. }
  179. // serialize attributes if present
  180. if($attributes){
  181. foreach($attributes as $k => $v){
  182. $atts .= " $k=\"$v\"";
  183. }
  184. }
  185. // serialize if an xsd built-in primitive type
  186. if ($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
  187. if ($use == 'literal') {
  188. return "<$name$xmlns>$val</$name>";
  189. } else {
  190. return "<$name$xmlns xsi:type=\"xsd:$type\">$val</$name>";
  191. }
  192. }
  193. // detect type and serialize
  194. $xml = '';
  195. $atts = '';
  196. switch(true) {
  197. case ($type == '' && is_null($val)):
  198. if ($use == 'literal') {
  199. // TODO: depends on nillable
  200. $xml .= "<$name$xmlns/>";
  201. } else {
  202. $xml .= "<$name$xmlns xsi:type=\"xsd:nil\"/>";
  203. }
  204. break;
  205. case (is_bool($val) || $type == 'boolean'):
  206. if(!$val){
  207. $val = 0;
  208. }
  209. if ($use == 'literal') {
  210. $xml .= "<$name$xmlns $atts>$val</$name>";
  211. } else {
  212. $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
  213. }
  214. break;
  215. case (is_int($val) || is_long($val) || $type == 'int'):
  216. if ($use == 'literal') {
  217. $xml .= "<$name$xmlns $atts>$val</$name>";
  218. } else {
  219. $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
  220. }
  221. break;
  222. case (is_float($val)|| is_double($val) || $type == 'float'):
  223. if ($use == 'literal') {
  224. $xml .= "<$name$xmlns $atts>$val</$name>";
  225. } else {
  226. $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
  227. }
  228. break;
  229. case (is_string($val) || $type == 'string'):
  230. if($this->charencoding){
  231. $val = htmlspecialchars($val, ENT_QUOTES);
  232. }
  233. if ($use == 'literal') {
  234. $xml .= "<$name$xmlns $atts>$val</$name>";
  235. } else {
  236. $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
  237. }
  238. break;
  239. case is_object($val):
  240. $name = strtolower(get_class($val));
  241. foreach(get_object_vars($val) as $k => $v){
  242. $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
  243. }
  244. $xml .= '<'.$name.'>'.$pXml.'</'.$name.'>';
  245. break;
  246. break;
  247. case (is_array($val) || $type):
  248. // detect if struct or array
  249. $keyList = array_keys($val);
  250. $valueType = 'arraySimple';
  251. foreach($keyList as $keyListValue){
  252. if(!is_int($keyListValue)){
  253. $valueType = 'arrayStruct';
  254. break;
  255. }
  256. }
  257. if($valueType=='arraySimple' || ereg('^ArrayOf',$type)){
  258. $i = 0;
  259. if(is_array($val) && count($val)> 0){
  260. foreach($val as $v){
  261. if(is_object($v) && strtolower(get_class($v)) == 'soapval'){
  262. $tt = $v->type;
  263. } else {
  264. $tt = gettype($v);
  265. }
  266. $array_types[$tt] = 1;
  267. $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
  268. if(is_array($v) && is_numeric(key($v))){
  269. $i += sizeof($v);
  270. } else {
  271. ++$i;
  272. }
  273. }
  274. if(count($array_types) > 1){
  275. $array_typename = 'xsd:ur-type';
  276. } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
  277. $array_typename = 'xsd:'.$tt;
  278. } elseif($tt == 'array' || $tt == 'Array'){
  279. $array_typename = 'SOAP-ENC:Array';
  280. } else {
  281. $array_typename = $tt;
  282. }
  283. if(isset($array_types['array'])){
  284. $array_type = $i.",".$i;
  285. } else {
  286. $array_type = $i;
  287. }
  288. if ($use == 'literal') {
  289. $xml = "<$name $atts>".$xml."</$name>";
  290. } else {
  291. $xml = "<$name xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"$atts>".$xml."</$name>";
  292. }
  293. // empty array
  294. } else {
  295. if ($use == 'literal') {
  296. $xml = "<$name $atts>".$xml."</$name>";;
  297. } else {
  298. $xml = "<$name xsi:type=\"SOAP-ENC:Array\" $atts>".$xml."</$name>";;
  299. }
  300. }
  301. } else {
  302. // got a struct
  303. if(isset($type) && isset($type_prefix)){
  304. $type_str = " xsi:type=\"$type_prefix:$type\"";
  305. } else {
  306. $type_str = '';
  307. }
  308. if ($use == 'literal') {
  309. $xml .= "<$name$xmlns $atts>";
  310. } else {
  311. $xml .= "<$name$xmlns$type_str$atts>";
  312. }
  313. foreach($val as $k => $v){
  314. $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
  315. }
  316. $xml .= "</$name>";
  317. }
  318. break;
  319. default:
  320. $xml .= 'not detected, got '.gettype($val).' for '.$val;
  321. break;
  322. }
  323. return $xml;
  324. }
  325. /**
  326. * serialize message
  327. *
  328. * @param string body
  329. * @param string headers
  330. * @param array namespaces
  331. * @param string style
  332. * @return string message
  333. * @access public
  334. */
  335. function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc'){
  336. // serialize namespaces
  337. $ns_string = '';
  338. foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
  339. $ns_string .= " xmlns:$k=\"$v\"";
  340. }
  341. if($style == 'rpc') {
  342. $ns_string = ' SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"' . $ns_string;
  343. }
  344. // serialize headers
  345. if($headers){
  346. $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
  347. }
  348. // serialize envelope
  349. return
  350. '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
  351. '<SOAP-ENV:Envelope'.$ns_string.">".
  352. $headers.
  353. "<SOAP-ENV:Body>".
  354. $body.
  355. "</SOAP-ENV:Body>".
  356. "</SOAP-ENV:Envelope>";
  357. }
  358. function formatDump($str){
  359. $str = htmlspecialchars($str);
  360. return nl2br($str);
  361. }
  362. /**
  363. * returns the local part of a prefixed string
  364. * returns the original string, if not prefixed
  365. *
  366. * @param string
  367. * @return string
  368. * @access public
  369. */
  370. function getLocalPart($str){
  371. if($sstr = strrchr($str,':')){
  372. // get unqualified name
  373. return substr( $sstr, 1 );
  374. } else {
  375. return $str;
  376. }
  377. }
  378. /**
  379. * returns the prefix part of a prefixed string
  380. * returns false, if not prefixed
  381. *
  382. * @param string
  383. * @return mixed
  384. * @access public
  385. */
  386. function getPrefix($str){
  387. if($pos = strrpos($str,':')){
  388. // get prefix
  389. return substr($str,0,$pos);
  390. }
  391. return false;
  392. }
  393. function varDump($data) {
  394. ob_start();
  395. var_dump($data);
  396. $ret_val = ob_get_contents();
  397. ob_end_clean();
  398. return $ret_val;
  399. }
  400. }
  401. // XML Schema Datatype Helper Functions
  402. //xsd:dateTime helpers
  403. /**
  404. * convert unix timestamp to ISO 8601 compliant date string
  405. *
  406. * @param string $timestamp Unix time stamp
  407. * @access public
  408. */
  409. function timestamp_to_iso8601($timestamp,$utc=true){
  410. $datestr = date('Y-m-d\TH:i:sO',$timestamp);
  411. if($utc){
  412. $eregStr =
  413. '([0-9]{4})-'. // centuries & years CCYY-
  414. '([0-9]{2})-'. // months MM-
  415. '([0-9]{2})'. // days DD
  416. 'T'. // separator T
  417. '([0-9]{2}):'. // hours hh:
  418. '([0-9]{2}):'. // minutes mm:
  419. '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
  420. '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
  421. if(ereg($eregStr,$datestr,$regs)){
  422. return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
  423. }
  424. return false;
  425. } else {
  426. return $datestr;
  427. }
  428. }
  429. /**
  430. * convert ISO 8601 compliant date string to unix timestamp
  431. *
  432. * @param string $datestr ISO 8601 compliant date string
  433. * @access public
  434. */
  435. function iso8601_to_timestamp($datestr){
  436. $eregStr =
  437. '([0-9]{4})-'. // centuries & years CCYY-
  438. '([0-9]{2})-'. // months MM-
  439. '([0-9]{2})'. // days DD
  440. 'T'. // separator T
  441. '([0-9]{2}):'. // hours hh:
  442. '([0-9]{2}):'. // minutes mm:
  443. '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
  444. '(Z|[+\-][0-9]{2}:?[0-9]{2})?'; // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
  445. if(ereg($eregStr,$datestr,$regs)){
  446. // not utc
  447. if($regs[8] != 'Z'){
  448. $op = substr($regs[8],0,1);
  449. $h = substr($regs[8],1,2);
  450. $m = substr($regs[8],strlen($regs[8])-2,2);
  451. if($op == '-'){
  452. $regs[4] = $regs[4] + $h;
  453. $regs[5] = $regs[5] + $m;
  454. } elseif($op == '+'){
  455. $regs[4] = $regs[4] - $h;
  456. $regs[5] = $regs[5] - $m;
  457. }
  458. }
  459. return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
  460. } else {
  461. return false;
  462. }
  463. }
  464. ?><?php
  465. /**
  466. * soap_fault class, allows for creation of faults
  467. * mainly used for returning faults from deployed functions
  468. * in a server instance.
  469. * @author Dietrich Ayala <dietrich@ganx4.com>
  470. * @version v 0.6.3
  471. * @access public
  472. */
  473. class soap_fault extends nusoap_base {
  474. var $faultcode;
  475. var $faultactor;
  476. var $faultstring;
  477. var $faultdetail;
  478. /**
  479. * constructor
  480. *
  481. * @param string $faultcode (client | server)
  482. * @param string $faultactor only used when msg routed between multiple actors
  483. * @param string $faultstring human readable error message
  484. * @param string $faultdetail
  485. */
  486. function soap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){
  487. $this->faultcode = $faultcode;
  488. $this->faultactor = $faultactor;
  489. $this->faultstring = $faultstring;
  490. $this->faultdetail = $faultdetail;
  491. }
  492. /**
  493. * serialize a fault
  494. *
  495. * @access public
  496. */
  497. function serialize(){
  498. $ns_string = '';
  499. foreach($this->namespaces as $k => $v){
  500. $ns_string .= "\n xmlns:$k=\"$v\"";
  501. }
  502. $return_msg =
  503. '<?xml version="1.0"?'.">\n".
  504. '<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"'.$ns_string.">\n".
  505. '<SOAP-ENV:Body>'.
  506. '<SOAP-ENV:Fault>'.
  507. '<faultcode>'.$this->faultcode.'</faultcode>'.
  508. '<faultactor>'.$this->faultactor.'</faultactor>'.
  509. '<faultstring>'.$this->faultstring.'</faultstring>'.
  510. '<detail>'.$this->serialize_val($this->faultdetail).'</detail>'.
  511. '</SOAP-ENV:Fault>'.
  512. '</SOAP-ENV:Body>'.
  513. '</SOAP-ENV:Envelope>';
  514. return $return_msg;
  515. }
  516. }
  517. ?><?php
  518. /**
  519. * parses an XML Schema, allows access to it's data, other utility methods
  520. * no validation... yet.
  521. * very experimental and limited. As is discussed on XML-DEV, I'm one of the people
  522. * that just doesn't have time to read the spec(s) thoroughly, and just have a couple of trusty
  523. * tutorials I refer to :)
  524. *
  525. * @author Dietrich Ayala <dietrich@ganx4.com>
  526. * @version v 0.6.3
  527. * @access public
  528. */
  529. class XMLSchema extends nusoap_base {
  530. // files
  531. var $schema = '';
  532. var $xml = '';
  533. // define internal arrays of bindings, ports, operations, messages, etc.
  534. var $complexTypes = array();
  535. // target namespace
  536. var $schemaTargetNamespace = '';
  537. // parser vars
  538. var $parser;
  539. var $position;
  540. var $depth = 0;
  541. var $depth_array = array();
  542. /**
  543. * constructor
  544. *
  545. * @param string $schema schema document URI
  546. * @param string $xml xml document URI
  547. * @access public
  548. */
  549. function XMLSchema($schema='',$xml=''){
  550. $this->debug('xmlschema class instantiated, inside constructor');
  551. // files
  552. $this->schema = $schema;
  553. $this->xml = $xml;
  554. // parse schema file
  555. if($schema != ''){
  556. $this->debug('initial schema file: '.$schema);
  557. $this->parseFile($schema);
  558. }
  559. // parse xml file
  560. if($xml != ''){
  561. $this->debug('initial xml file: '.$xml);
  562. $this->parseFile($xml);
  563. }
  564. }
  565. /**
  566. * parse an XML file
  567. *
  568. * @param string $xml, path/URL to XML file
  569. * @param string $type, (schema | xml)
  570. * @return boolean
  571. * @access public
  572. */
  573. function parseFile($xml,$type){
  574. // parse xml file
  575. if($xml != ""){
  576. $this->debug('parsing $xml');
  577. $xmlStr = @join("",@file($xml));
  578. if($xmlStr == ""){
  579. $this->setError('No file at the specified URL: '.$xml);
  580. return false;
  581. } else {
  582. $this->parseString($xmlStr,$type);
  583. return true;
  584. }
  585. }
  586. return false;
  587. }
  588. /**
  589. * parse an XML string
  590. *
  591. * @param string $xml path or URL
  592. * @param string $type, (schema|xml)
  593. * @access private
  594. */
  595. function parseString($xml,$type){
  596. // parse xml string
  597. if($xml != ""){
  598. // Create an XML parser.
  599. $this->parser = xml_parser_create();
  600. // Set the options for parsing the XML data.
  601. xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
  602. // Set the object for the parser.
  603. xml_set_object($this->parser, $this);
  604. // Set the element handlers for the parser.
  605. if($type == "schema"){
  606. xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
  607. xml_set_character_data_handler($this->parser,'schemaCharacterData');
  608. } elseif($type == "xml"){
  609. xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
  610. xml_set_character_data_handler($this->parser,'xmlCharacterData');
  611. }
  612. // Parse the XML file.
  613. if(!xml_parse($this->parser,$xml,true)){
  614. // Display an error message.
  615. $errstr = sprintf('XML error on line %d: %s',
  616. xml_get_current_line_number($this->parser),
  617. xml_error_string(xml_get_error_code($this->parser))
  618. );
  619. $this->debug('XML parse error: '.$errstr);
  620. $this->setError('Parser error: '.$errstr);
  621. }
  622. xml_parser_free($this->parser);
  623. } else{
  624. $this->debug('no xml passed to parseString()!!');
  625. $this->setError('no xml passed to parseString()!!');
  626. }
  627. }
  628. /**
  629. * start-element handler
  630. *
  631. * @param string $parser XML parser object
  632. * @param string $name element name
  633. * @param string $attrs associative array of attributes
  634. * @access private
  635. */
  636. function schemaStartElement($parser, $name, $attrs) {
  637. // position in the total number of elements, starting from 0
  638. $pos = $this->position++;
  639. $depth = $this->depth++;
  640. // set self as current value for this depth
  641. $this->depth_array[$depth] = $pos;
  642. // get element prefix
  643. if($prefix = $this->getPrefix($name)){
  644. // get unqualified name
  645. $name = $this->getLocalPart($name);
  646. } else {
  647. $prefix = '';
  648. }
  649. // loop thru attributes, expanding, and registering namespace declarations
  650. if(count($attrs) > 0){
  651. foreach($attrs as $k => $v){
  652. // if ns declarations, add to class level array of valid namespaces
  653. if(ereg("^xmlns",$k)){
  654. //$this->xdebug("$k: $v");
  655. //$this->xdebug('ns_prefix: '.$this->getPrefix($k));
  656. if($ns_prefix = substr(strrchr($k,':'),1)){
  657. $this->namespaces[$ns_prefix] = $v;
  658. } else {
  659. $this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
  660. }
  661. if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema'){
  662. $this->XMLSchemaVersion = $v;
  663. $this->namespaces['xsi'] = $v.'-instance';
  664. }
  665. }
  666. }
  667. foreach($attrs as $k => $v) {
  668. // expand each attribute
  669. $k = strpos($k,':') ? $this->expandQname($k) : $k;
  670. $v = strpos($v,':') ? $this->expandQname($v) : $v;
  671. $eAttrs[$k] = $v;
  672. }
  673. $attrs = $eAttrs;
  674. } else {
  675. $attrs = array();
  676. }
  677. // find status, register data
  678. switch($name) {
  679. case ('all'|'choice'|'sequence'):
  680. //$this->complexTypes[$this->currentComplexType]['compositor'] = 'all';
  681. $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
  682. if($name == 'all') {
  683. $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
  684. }
  685. break;
  686. case 'attribute':
  687. //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']);
  688. if(isset($attrs['name'])){
  689. $this->attributes[$attrs['name']] = $attrs;
  690. $aname = $attrs['name'];
  691. } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
  692. $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
  693. } elseif(isset($attrs['ref'])){
  694. $aname = $attrs['ref'];
  695. $this->attributes[$attrs['ref']] = $attrs;
  696. }
  697. if(isset($this->currentComplexType)){
  698. $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
  699. } elseif(isset($this->currentElement)){
  700. $this->elements[$this->currentElement]['attrs'][$aname] = $attrs;
  701. }
  702. // arrayType attribute
  703. if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
  704. $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
  705. $prefix = $this->getPrefix($aname);
  706. if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
  707. $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
  708. } else {
  709. $v = '';
  710. }
  711. if(strpos($v,'[,]')){
  712. $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
  713. }
  714. $v = substr($v,0,strpos($v,'[')); // clip the []
  715. if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
  716. $v = $this->XMLSchemaVersion.':'.$v;
  717. }
  718. $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
  719. }
  720. break;
  721. case 'complexType':
  722. if(isset($attrs['name'])){
  723. $this->currentElement = false;
  724. $this->currentComplexType = $attrs['name'];
  725. $this->complexTypes[$this->currentComplexType] = $attrs;
  726. $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
  727. if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
  728. $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
  729. } else {
  730. $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
  731. }
  732. $this->xdebug('processing complexType '.$attrs['name']);
  733. }
  734. break;
  735. case 'element':
  736. if(isset($attrs['type'])){
  737. $this->xdebug("processing element ".$attrs['name']);
  738. $this->currentElement = $attrs['name'];
  739. $this->elements[ $attrs['name'] ] = $attrs;
  740. $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
  741. $ename = $attrs['name'];
  742. } elseif(isset($attrs['ref'])){
  743. $ename = $attrs['ref'];
  744. } else {
  745. $this->xdebug('adding complexType '.$attrs['name']);
  746. $this->currentComplexType = $attrs['name'];
  747. $this->complexTypes[ $attrs['name'] ] = $attrs;
  748. $this->complexTypes[ $attrs['name'] ]['element'] = 1;
  749. $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
  750. }
  751. if(isset($ename) && $this->currentComplexType){
  752. $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
  753. }
  754. break;
  755. case 'restriction':
  756. $this->xdebug("in restriction for ct: $this->currentComplexType and ce: $this->currentElement");
  757. if($this->currentElement){
  758. $this->elements[$this->currentElement]['type'] = $attrs['base'];
  759. } elseif($this->currentComplexType){
  760. $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
  761. if(strstr($attrs['base'],':') == ':Array'){
  762. $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
  763. }
  764. }
  765. break;
  766. case 'schema':
  767. $this->schema = $attrs;
  768. $this->schema['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
  769. break;
  770. case 'simpleType':
  771. $this->currentElement = $attrs['name'];
  772. $this->elements[ $attrs['name'] ] = $attrs;
  773. $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
  774. break;
  775. }
  776. }
  777. /**
  778. * end-element handler
  779. *
  780. * @param string $parser XML parser object
  781. * @param string $name element name
  782. * @access private
  783. */
  784. function schemaEndElement($parser, $name) {
  785. // position of current element is equal to the last value left in depth_array for my depth
  786. if(isset($this->depth_array[$this->depth])){
  787. $pos = $this->depth_array[$this->depth];
  788. }
  789. // bring depth down a notch
  790. $this->depth--;
  791. // move on...
  792. if($name == 'complexType'){
  793. $this->currentComplexType = false;
  794. $this->currentElement = false;
  795. }
  796. if($name == 'element'){
  797. $this->currentElement = false;
  798. }
  799. }
  800. /**
  801. * element content handler
  802. *
  803. * @param string $parser XML parser object
  804. * @param string $data element content
  805. * @access private
  806. */
  807. function schemaCharacterData($parser, $data){
  808. $pos = $this->depth_array[$this->depth];
  809. $this->message[$pos]['cdata'] .= $data;
  810. }
  811. /**
  812. * serialize the schema
  813. *
  814. * @access public
  815. */
  816. function serializeSchema(){
  817. $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
  818. $xml = '';
  819. // complex types
  820. foreach($this->complexTypes as $typeName => $attrs){
  821. $contentStr = '';
  822. // serialize child elements
  823. if(count($attrs['elements']) > 0){
  824. foreach($attrs['elements'] as $element => $eParts){
  825. if(isset($eParts['ref'])){
  826. $contentStr .= "<element ref=\"$element\"/>";
  827. } else {
  828. $contentStr .= "<element name=\"$element\" type=\"$eParts[type]\"/>";
  829. }
  830. }
  831. }
  832. // attributes
  833. if(count($attrs['attrs']) >= 1){
  834. foreach($attrs['attrs'] as $attr => $aParts){
  835. $contentStr .= '<attribute ref="'.$aParts['ref'].'"';
  836. if(isset($aParts['wsdl:arrayType'])){
  837. $contentStr .= ' wsdl:arrayType="'.$aParts['wsdl:arrayType'].'"';
  838. }
  839. $contentStr .= '/>';
  840. }
  841. }
  842. // if restriction
  843. if( isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
  844. $contentStr = "<$schemaPrefix:restriction base=\"".$attrs['restrictionBase']."\">".$contentStr."</$schemaPrefix:restriction>";
  845. }
  846. // "all" compositor obviates complex/simple content
  847. if(isset($attrs['compositor']) && $attrs['compositor'] == 'all'){
  848. $contentStr = "<$schemaPrefix:$attrs[compositor]>".$contentStr."</$schemaPrefix:$attrs[compositor]>";
  849. }
  850. // complex or simple content
  851. elseif( count($attrs['elements']) > 0 || count($attrs['attrs']) > 0){
  852. $contentStr = "<$schemaPrefix:complexContent>".$contentStr."</$schemaPrefix:complexContent>";
  853. }
  854. // compositors
  855. if(isset($attrs['compositor']) && $attrs['compositor'] != '' && $attrs['compositor'] != 'all'){
  856. $contentStr = "<$schemaPrefix:$attrs[compositor]>".$contentStr."</$schemaPrefix:$attrs[compositor]>";
  857. }
  858. // finalize complex type
  859. if($contentStr != ''){
  860. $contentStr = "<$schemaPrefix:complexType name=\"$typeName\">".$contentStr."</$schemaPrefix:complexType>";
  861. } else {
  862. $contentStr = "<$schemaPrefix:complexType name=\"$typeName\"/>";
  863. }
  864. $xml .= $contentStr;
  865. }
  866. // elements
  867. if(isset($this->elements) && count($this->elements) > 0){
  868. foreach($this->elements as $element => $eParts){
  869. $xml .= "<$schemaPrefix:element name=\"$element\" type=\"".$eParts['type']."\"/>";
  870. }
  871. }
  872. // attributes
  873. if(isset($this->attributes) && count($this->attributes) > 0){
  874. foreach($this->attributes as $attr => $aParts){
  875. $xml .= "<$schemaPrefix:attribute name=\"$attr\" type=\"".$aParts['type']."\"/>";
  876. }
  877. }
  878. // finish 'er up
  879. $xml = "<$schemaPrefix:schema xmlns=\"$this->XMLSchemaVersion\" targetNamespace=\"$this->schemaTargetNamespace\">".$xml."</$schemaPrefix:schema>";
  880. return $xml;
  881. }
  882. /**
  883. * expands a qualified name
  884. *
  885. * @param string $string qname
  886. * @return string expanded qname
  887. * @access private
  888. */
  889. function expandQname($qname){
  890. // get element prefix
  891. if(strpos($qname,':') && !ereg('^http://',$qname)){
  892. // get unqualified name
  893. $name = substr(strstr($qname,':'),1);
  894. // get ns prefix
  895. $prefix = substr($qname,0,strpos($qname,':'));
  896. if(isset($this->namespaces[$prefix])){
  897. return $this->namespaces[$prefix].':'.$name;
  898. } else {
  899. return $qname;
  900. }
  901. } else {
  902. return $qname;
  903. }
  904. }
  905. /**
  906. * adds debug data to the clas level debug string
  907. *
  908. * @param string $string debug data
  909. * @access private
  910. */
  911. function xdebug($string){
  912. $this->debug(' xmlschema: '.$string);
  913. }
  914. /**
  915. * get the PHP type of a user defined type in the schema
  916. * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays
  917. * returns false if no type exists, or not w/ the given namespace
  918. * else returns a string that is either a native php type, or 'struct'
  919. *
  920. * @param string $type, name of defined type
  921. * @param string $ns, namespace of type
  922. * @return mixed
  923. * @access public
  924. */
  925. function getPHPType($type,$ns){
  926. global $typemap;
  927. if(isset($typemap[$ns][$type])){
  928. //print "found type '$type' and ns $ns in typemap<br>";
  929. return $typemap[$ns][$type];
  930. } elseif(isset($this->complexTypes[$type])){
  931. //print "getting type '$type' and ns $ns from complexTypes array<br>";
  932. return $this->complexTypes[$type]['phpType'];
  933. }
  934. return false;
  935. }
  936. /**
  937. * returns the local part of a prefixed string
  938. * returns the original string, if not prefixed
  939. *
  940. * @param string
  941. * @return string
  942. * @access public
  943. */
  944. function getLocalPart($str){
  945. if($sstr = strrchr($str,':')){
  946. // get unqualified name
  947. return substr( $sstr, 1 );
  948. } else {
  949. return $str;
  950. }
  951. }
  952. /**
  953. * returns the prefix part of a prefixed string
  954. * returns false, if not prefixed
  955. *
  956. * @param string
  957. * @return mixed
  958. * @access public
  959. */
  960. function getPrefix($str){
  961. if($pos = strrpos($str,':')){
  962. // get prefix
  963. return substr($str,0,$pos);
  964. }
  965. return false;
  966. }
  967. /**
  968. * pass it a prefix, it returns a namespace
  969. * returns false if no namespace registered with the given prefix
  970. *
  971. * @param string
  972. * @return mixed
  973. * @access public
  974. */
  975. function getNamespaceFromPrefix($prefix){
  976. if(isset($this->namespaces[$prefix])){
  977. return $this->namespaces[$prefix];
  978. }
  979. //$this->setError("No namespace registered for prefix '$prefix'");
  980. return false;
  981. }
  982. /**
  983. * returns the prefix for a given namespace (or prefix)
  984. * or false if no prefixes registered for the given namespace
  985. *
  986. * @param string
  987. * @return mixed
  988. * @access public
  989. */
  990. function getPrefixFromNamespace($ns){
  991. foreach($this->namespaces as $p => $n){
  992. if($ns == $n || $ns == $p){
  993. $this->usedNamespaces[$p] = $n;
  994. return $p;
  995. }
  996. }
  997. return false;
  998. }
  999. /**
  1000. * returns an array of information about a given type
  1001. * returns false if no type exists by the given name
  1002. *
  1003. * typeDef = array(
  1004. * 'elements' => array(), // refs to elements array
  1005. * 'restrictionBase' => '',
  1006. * 'phpType' => '',
  1007. * 'order' => '(sequence|all)',
  1008. * 'attrs' => array() // refs to attributes array
  1009. * )
  1010. *
  1011. * @param string
  1012. * @return mixed
  1013. * @access public
  1014. */
  1015. function getTypeDef($type){
  1016. if(isset($this->complexTypes[$type])){
  1017. return $this->complexTypes[$type];
  1018. } elseif(isset($this->elements[$type])){
  1019. return $this->elements[$type];
  1020. } elseif(isset($this->attributes[$type])){
  1021. return $this->attributes[$type];
  1022. }
  1023. return false;
  1024. }
  1025. /**
  1026. * returns a sample serialization of a given type, or false if no type by the given name
  1027. *
  1028. * @param string $type, name of type
  1029. * @return mixed
  1030. * @access public
  1031. */
  1032. function serializeTypeDef($type){
  1033. //print "in sTD() for type $type<br>";
  1034. if($typeDef = $this->getTypeDef($type)){
  1035. $str .= '<'.$type;
  1036. if(is_array($typeDef['attrs'])){
  1037. foreach($attrs as $attName => $data){
  1038. $str .= " $attName=\"{type = ".$data['type']."}\"";
  1039. }
  1040. }
  1041. $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
  1042. if(count($typeDef['elements']) > 0){
  1043. $str .= ">";
  1044. foreach($typeDef['elements'] as $element => $eData){
  1045. $str .= $this->serializeTypeDef($element);
  1046. }
  1047. $str .= "</$type>";
  1048. } elseif($typeDef['typeClass'] == 'element') {
  1049. $str .= "></$type>";
  1050. } else {
  1051. $str .= "/>";
  1052. }
  1053. return $str;
  1054. }
  1055. return false;
  1056. }
  1057. /**
  1058. * returns HTML form elements that allow a user
  1059. * to enter values for creating an instance of the given type.
  1060. *
  1061. * @param string $name, name for type instance
  1062. * @param string $type, name of type
  1063. * @return string
  1064. * @access public
  1065. */
  1066. function typeToForm($name,$type){
  1067. // get typedef
  1068. if($typeDef = $this->getTypeDef($type)){
  1069. // if struct
  1070. if($typeDef['phpType'] == 'struct'){
  1071. $buffer .= '<table>';
  1072. foreach($typeDef['elements'] as $child => $childDef){
  1073. $buffer .= "<tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>".
  1074. "<td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
  1075. }
  1076. $buffer .= '</table>';
  1077. // if array
  1078. } elseif($typeDef['phpType'] == 'array'){
  1079. $buffer .= '<table>';
  1080. for($i=0;$i < 3; $i++){
  1081. $buffer .= "<tr><td align='right'>array item (type: $typeDef[arrayType]):</td>".
  1082. "<td><input type='text' name='parameters[".$name."][]'></td></tr>";
  1083. }
  1084. $buffer .= '</table>';
  1085. // if scalar
  1086. } else {
  1087. $buffer .= "<input type='text' name='parameters[$name]'>";
  1088. }
  1089. } else {
  1090. $buffer .= "<input type='text' name='parameters[$name]'>";
  1091. }
  1092. return $buffer;
  1093. }
  1094. /**
  1095. * adds an XML Schema complex type to the WSDL types
  1096. *
  1097. * example: array
  1098. *
  1099. * addType(
  1100. * 'ArrayOfstring',
  1101. * 'complexType',
  1102. * 'array',
  1103. * '',
  1104. * 'SOAP-ENC:Array',
  1105. * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'),
  1106. * 'xsd:string'
  1107. * );
  1108. *
  1109. * example: PHP associative array ( SOAP Struct )
  1110. *
  1111. * addType(
  1112. * 'SOAPStruct',
  1113. * 'complexType',
  1114. * 'struct',
  1115. * 'all',
  1116. * array('myVar'=> array('name'=>'myVar','type'=>'string')
  1117. * );
  1118. *
  1119. * @param name
  1120. * @param typeClass (complexType|simpleType|attribute)
  1121. * @param phpType: currently supported are array and struct (php assoc array)
  1122. * @param compositor (all|sequence|choice)
  1123. * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array)
  1124. * @param elements = array ( name = array(name=>'',type=>'') )
  1125. * @param attrs = array(
  1126. * array(
  1127. * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType",
  1128. * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]"
  1129. * )
  1130. * )
  1131. * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string)
  1132. *
  1133. */
  1134. function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
  1135. $this->complexTypes[$name] = array(
  1136. 'name' => $name,
  1137. 'typeClass' => $typeClass,
  1138. 'phpType' => $phpType,
  1139. 'compositor'=> $compositor,
  1140. 'restrictionBase' => $restrictionBase,
  1141. 'elements' => $elements,
  1142. 'attrs' => $attrs,
  1143. 'arrayType' => $arrayType
  1144. );
  1145. }
  1146. }
  1147. ?><?php
  1148. /**
  1149. * for creating serializable abstractions of native PHP types
  1150. * NOTE: this is only really used when WSDL is not available.
  1151. *
  1152. * @author Dietrich Ayala <dietrich@ganx4.com>
  1153. * @version v 0.6.3
  1154. * @access public
  1155. */
  1156. class soapval extends nusoap_base {
  1157. /**
  1158. * constructor
  1159. *
  1160. * @param string $name optional name
  1161. * @param string $type optional type name
  1162. * @param mixed $value optional value
  1163. * @param string $namespace optional namespace of value
  1164. * @param string $type_namespace optional namespace of type
  1165. * @param array $attributes associative array of attributes to add to element serialization
  1166. * @access public
  1167. */
  1168. function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) {
  1169. $this->name = $name;
  1170. $this->value = $value;
  1171. $this->type = $type;
  1172. $this->element_ns = $element_ns;
  1173. $this->type_ns = $type_ns;
  1174. $this->attributes = $attributes;
  1175. }
  1176. /**
  1177. * return serialized value
  1178. *
  1179. * @return string XML data
  1180. * @access private
  1181. */
  1182. function serialize($use='encoded') {
  1183. return $this->serialize_val($this->value,$this->name,$this->type,$this->element_ns,$this->type_ns,$this->attributes,$use);
  1184. }
  1185. /**
  1186. * decodes a soapval object into a PHP native type
  1187. *
  1188. * @param object $soapval optional SOAPx4 soapval object, else uses self
  1189. * @return mixed
  1190. * @access public
  1191. */
  1192. function decode(){
  1193. return $this->value;
  1194. }
  1195. }
  1196. ?><?php
  1197. /**
  1198. * transport class for sending/receiving data via HTTP and HTTPS
  1199. * NOTE: PHP must be compiled with the CURL extension for HTTPS support
  1200. *
  1201. * @author Dietrich Ayala <dietrich@ganx4.com>
  1202. * @version v 0.6.3
  1203. * @access public
  1204. */
  1205. class soap_transport_http extends nusoap_base {
  1206. var $username = '';
  1207. var $password = '';
  1208. var $url = '';
  1209. var $proxyhost = '';
  1210. var $proxyport = '';
  1211. var $scheme = '';
  1212. var $request_method = 'POST';
  1213. var $protocol_version = '1.0';
  1214. var $encoding = '';
  1215. var $outgoing_headers = array();
  1216. var $incoming_headers = array();
  1217. var $outgoing_payload = '';
  1218. var $incoming_payload = '';
  1219. var $useSOAPAction = true;
  1220. /**
  1221. * constructor
  1222. */
  1223. function soap_transport_http($url){
  1224. $this->url = $url;
  1225. $u = parse_url($url);
  1226. foreach($u as $k => $v){
  1227. $this->debug("$k = $v");
  1228. $this->$k = $v;
  1229. }
  1230. if(isset($u['query']) && $u['query'] != ''){
  1231. $this->path .= '?' . $u['query'];
  1232. }
  1233. if(!isset($u['port']) && $u['scheme'] == 'http'){
  1234. $this->port = 80;
  1235. }
  1236. }
  1237. function connect($timeout){
  1238. // proxy
  1239. if($this->proxyhost != '' && $this->proxyport != ''){
  1240. $host = $this->proxyhost;
  1241. $port = $this->proxyport;
  1242. $this->debug("using http proxy: $host, $port");
  1243. } else {
  1244. $host = $this->host;
  1245. $port = $this->port;
  1246. }
  1247. // ssl
  1248. if($this->scheme == 'https'){
  1249. $host = 'ssl://'.$host;
  1250. $port = 443;
  1251. }
  1252. $this->debug("connection params: $host, $port");
  1253. // timeout
  1254. if($timeout > 0){
  1255. $fp = fsockopen($host, $port, $this->errno, $this->error_str, $timeout);
  1256. } else {
  1257. $fp = fsockopen($host, $port, $this->errno, $this->error_str);
  1258. }
  1259. // test pointer
  1260. if(!$fp) {
  1261. $this->debug('Couldn\'t open socket connection to server '.$this->url.', Error: '.$this->error_str);
  1262. $this->setError('Couldn\'t open socket connection to server: '.$this->url.', Error: '.$this->error_str);
  1263. return false;
  1264. }
  1265. return $fp;
  1266. }
  1267. /**
  1268. * send the SOAP message via HTTP
  1269. *
  1270. * @param string $data message data
  1271. * @param integer $timeout set timeout in seconds
  1272. * @return string data
  1273. * @access public
  1274. */
  1275. function send($data, $timeout=0) {
  1276. $this->debug('entered send() with data of length: '.strlen($data));
  1277. // get connnection
  1278. if(!$fp = $this->connect($timeout)){
  1279. return false;
  1280. }
  1281. $this->debug('socket connected');
  1282. // start building outgoing payload:
  1283. // swap url for path if going through a proxy
  1284. if($this->proxyhost != '' && $this->proxyport != ''){
  1285. $this->outgoing_payload = "$this->request_method $this->url ".strtoupper($this->scheme)."/$this->protocol_version\r\n";
  1286. } else {
  1287. $this->outgoing_payload = "$this->request_method $this->path ".strtoupper($this->scheme)."/$this->protocol_version\r\n";
  1288. }
  1289. // make payload
  1290. $this->outgoing_payload .=
  1291. "User-Agent: $this->title/$this->version\r\n".
  1292. "Host: ".$this->host."\r\n";
  1293. // http auth
  1294. $credentials = '';
  1295. if($this->username != '') {
  1296. $this->debug('setting http auth credentials');
  1297. $this->outgoing_payload .= 'Authorization: Basic '.base64_encode("$this->username:$this->password")."\r\n";
  1298. }
  1299. // set content type
  1300. $this->outgoing_payload .= 'Content-Type: text/xml; charset='.$this->soap_defencoding."\r\nContent-Length: ".strlen($data)."\r\n";
  1301. // http encoding
  1302. if($this->encoding != '' && function_exists('gzdeflate')){
  1303. $this->outgoing_payload .= "Accept-Encoding: $this->encoding\r\n".
  1304. "Connection: close\r\n";
  1305. set_magic_quotes_runtime(0);
  1306. }
  1307. // set soapaction
  1308. if($this->useSOAPAction){
  1309. $this->outgoing_payload .= "SOAPAction: \"$this->soapaction\""."\r\n";
  1310. }
  1311. $this->outgoing_payload .= "\r\n";
  1312. // add data
  1313. $this->outgoing_payload .= $data;
  1314. // send payload
  1315. if(!fputs($fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
  1316. $this->setError('couldn\'t write message data to socket');
  1317. $this->debug('Write error');
  1318. }
  1319. $this->debug('wrote data to socket');
  1320. // get response
  1321. $this->incoming_payload = '';
  1322. //$strlen = 0;
  1323. while( $data = fread($fp, 32768) ){
  1324. $this->incoming_payload .= $data;
  1325. //$strlen += strlen($data);
  1326. }
  1327. $this->debug('received '.strlen($this->incoming_payload).' bytes of data from server');
  1328. // close filepointer
  1329. fclose($fp);
  1330. $this->debug('closed socket');
  1331. // connection was closed unexpectedly
  1332. if($this->incoming_payload == ''){
  1333. $this->setError('no response from server');
  1334. return false;
  1335. }
  1336. $this->debug('received incoming payload: '.strlen($this->incoming_payload));
  1337. $data = $this->incoming_payload."\r\n\r\n\r\n\r\n";
  1338. // remove 100 header
  1339. if(ereg('^HTTP/1.1 100',$data)){
  1340. if($pos = strpos($data,"\r\n\r\n") ){
  1341. $data = ltrim(substr($data,$pos));
  1342. } elseif($pos = strpos($data,"\n\n") ){
  1343. $data = ltrim(substr($data,$pos));
  1344. }
  1345. }//
  1346. // separate content from HTTP headers
  1347. if( $pos = strpos($data,"\r\n\r\n") ){
  1348. $lb = "\r\n";
  1349. } elseif( $pos = strpos($data,"\n\n") ){
  1350. $lb = "\n";
  1351. } else {
  1352. $this->setError('no proper separation of headers and document');
  1353. return false;
  1354. }
  1355. $header_data = trim(substr($data,0,$pos));
  1356. $header_array = explode($lb,$header_data);
  1357. $data = ltrim(substr($data,$pos));
  1358. $this->debug('found proper separation of headers and document');
  1359. $this->debug('cleaned data, stringlen: '.strlen($data));
  1360. // clean headers
  1361. foreach($header_array as $header_line){
  1362. $arr = explode(':',$header_line);
  1363. if(count($arr) >= 2){
  1364. $headers[trim($arr[0])] = trim($arr[1]);
  1365. }
  1366. }
  1367. //print "headers: <pre>$header_data</pre><br>";
  1368. //print "data: <pre>$data</pre><br>";
  1369. // decode transfer-encoding
  1370. if(isset($headers['Transfer-Encoding']) && $headers['Transfer-Encoding'] == 'chunked'){
  1371. //$timer->setMarker('starting to decode chunked content');
  1372. if(!$data = $this->decodeChunked($data)){
  1373. $this->setError('Decoding of chunked data failed');
  1374. return false;
  1375. }
  1376. //$timer->setMarker('finished decoding of chunked content');
  1377. //print "<pre>\nde-chunked:\n---------------\n$data\n\n---------------\n</pre>";
  1378. }
  1379. // decode content-encoding
  1380. if(isset($headers['Content-Encoding']) && $headers['Content-Encoding'] != ''){
  1381. if($headers['Content-Encoding'] == 'deflate' || $headers['Content-Encoding'] == 'gzip'){
  1382. // if decoding works, use it. else assume data wasn't gzencoded
  1383. if(function_exists('gzinflate')){
  1384. //$timer->setMarker('starting decoding of gzip/deflated content');
  1385. if($headers['Content-Encoding'] == 'deflate' && $degzdata = @gzinflate($data)){
  1386. $data = $degzdata;
  1387. } elseif($headers['Content-Encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))){
  1388. $data = $degzdata;
  1389. } else {
  1390. $this->setError('Errors occurred when trying to decode the data');
  1391. }
  1392. //$timer->setMarker('finished decoding of gzip/deflated content');
  1393. //print "<xmp>\nde-inflated:\n---------------\n$data\n-------------\n</xmp>";
  1394. } else {
  1395. $this->setError('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
  1396. }
  1397. }
  1398. }
  1399. if(strlen($data) == 0){
  1400. $this->debug('no data after headers!');
  1401. $this->setError('no data present after HTTP headers');
  1402. return false;
  1403. }
  1404. $this->debug('end of send()');
  1405. return $data;
  1406. }
  1407. /**
  1408. * send the SOAP message via HTTPS 1.0 using CURL
  1409. *
  1410. * @param string $msg message data
  1411. * @param integer $timeout set timeout in seconds
  1412. * @return string data
  1413. * @access public
  1414. */
  1415. function sendHTTPS($data, $timeout=0) {
  1416. //global $t;
  1417. //$t->setMarker('inside sendHTTPS()');
  1418. $this->debug('entered sendHTTPS() with data of length: '.strlen($data));
  1419. // init CURL
  1420. $ch = curl_init();
  1421. //$t->setMarker('got curl handle');
  1422. // set proxy
  1423. if($this->proxyhost && $this->proxyport){
  1424. $host = $this->proxyhost;
  1425. $port = $this->proxyport;
  1426. } else {
  1427. $host = $this->host;
  1428. $port = $this->port;
  1429. }
  1430. // set url
  1431. $hostURL = ($port != '') ? "https://$host:$port" : "https://$host";
  1432. // add path
  1433. $hostURL .= $this->path;
  1434. curl_setopt($ch, CURLOPT_URL, $hostURL);
  1435. // set other options
  1436. curl_setopt($ch, CURLOPT_HEADER, 1);
  1437. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  1438. // encode
  1439. if(function_exists('gzinflate')){
  1440. curl_setopt($ch, CURLOPT_ENCODING, 'deflate');
  1441. }
  1442. // persistent connection
  1443. //curl_setopt($ch, CURL_HTTP_VERSION_1_1, true);
  1444. // set timeout
  1445. if($timeout != 0){
  1446. curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
  1447. }
  1448. $credentials = '';
  1449. if($this->username != '') {
  1450. $credentials = 'Authorization: Basic '.base64_encode("$this->username:$this->password").'\r\n';
  1451. }
  1452. if($this->encoding != ''){
  1453. if(function_exists('gzdeflate')){
  1454. $encoding_headers = "Accept-Encoding: $this->encoding\r\n".
  1455. "Connection: close\r\n";
  1456. set_magic_quotes_runtime(0);
  1457. }
  1458. }
  1459. if($this->proxyhost && $this->proxyport){
  1460. $this->outgoing_payload = "POST $this->url HTTP/$this->protocol_version\r\n";
  1461. } else {
  1462. $this->outgoing_payload = "POST $this->path HTTP/$this->protocol_version\r\n";
  1463. }
  1464. $this->outgoing_payload .=
  1465. "User-Agent: $this->title v$this->version\r\n".
  1466. "Host: ".$this->host."\r\n".
  1467. $encoding_headers.
  1468. $credentials.
  1469. "Content-Type: text/xml; charset=\"$this->soap_defencoding\"\r\n".
  1470. "Content-Length: ".strlen($data)."\r\n".
  1471. "SOAPAction: \"$this->soapaction\""."\r\n\r\n".
  1472. $data;
  1473. // set payload
  1474. curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->outgoing_payload);
  1475. //$t->setMarker('set curl options, executing...');
  1476. // send and receive
  1477. $this->incoming_payload = curl_exec($ch);
  1478. //$t->setMarker('executed transfer');
  1479. $data = $this->incoming_payload;
  1480. $cErr = curl_error($ch);
  1481. if($cErr != ''){
  1482. $err = 'cURL ERROR: '.curl_errno($ch).': '.$cErr.'<br>';
  1483. foreach(curl_getinfo($ch) as $k => $v){
  1484. $err .= "$k: $v<br>";
  1485. }
  1486. $this->setError($err);
  1487. curl_close($ch);
  1488. return false;
  1489. } else {
  1490. //echo '<pre>';
  1491. //var_dump(curl_getinfo($ch));
  1492. //echo '</pre>';
  1493. }
  1494. // close curl
  1495. curl_close($ch);
  1496. //$t->setMarker('closed curl');
  1497. // remove 100 header
  1498. if(ereg('^HTTP/1.1 100',$data)){
  1499. if($pos = strpos($data,"\r\n\r\n") ){
  1500. $data = ltrim(substr($data,$pos));
  1501. } elseif($pos = strpos($data,"\n\n") ){
  1502. $data = ltrim(substr($data,$pos));
  1503. }
  1504. }//
  1505. // separate content from HTTP headers
  1506. if( $pos = strpos($data,"\r\n\r\n") ){
  1507. $lb = "\r\n";
  1508. } elseif( $pos = strpos($data,"\n\n") ){
  1509. $lb = "\n";
  1510. } else {
  1511. $this->setError('no proper separation of headers and document');
  1512. return false;
  1513. }
  1514. $header_data = trim(substr($data,0,$pos));
  1515. $header_array = explode($lb,$header_data);
  1516. $data = ltrim(substr($data,$pos));
  1517. $this->debug('found proper separation of headers and document');
  1518. $this->debug('cleaned data, stringlen: '.strlen($data));
  1519. // clean headers
  1520. foreach($header_array as $header_line){
  1521. $arr = explode(':',$header_line);
  1522. $headers[trim($arr[0])] = trim($arr[1]);
  1523. }
  1524. if(strlen($data) == 0){
  1525. $this->debug('no data after headers!');
  1526. $this->setError('no data present after HTTP headers.');
  1527. return false;
  1528. }
  1529. // decode transfer-encoding
  1530. if($headers['Transfer-Encoding'] == 'chunked'){
  1531. if(!$data = $this->decodeChunked($data)){
  1532. $this->setError('Decoding of chunked data failed');
  1533. return false;
  1534. }
  1535. }
  1536. // decode content-encoding
  1537. if($headers['Content-Encoding'] != ''){
  1538. if($headers['Content-Encoding'] == 'deflate' || $headers['Content-Encoding'] == 'gzip'){
  1539. // if decoding works, use it. else assume data wasn't gzencoded
  1540. if(function_exists('gzinflate')){
  1541. if($headers['Content-Encoding'] == 'deflate' && $degzdata = @gzinflate($data)){
  1542. $data = $degzdata;
  1543. } elseif($headers['Content-Encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))){
  1544. $data = $degzdata;
  1545. } else {
  1546. $this->setError('Errors occurred when trying to decode the data');
  1547. }
  1548. } else {
  1549. $this->setError('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
  1550. }
  1551. }
  1552. }
  1553. // set decoded payload
  1554. $this->incoming_payload = $header_data."\r\n\r\n".$data;
  1555. return $data;
  1556. }
  1557. /**
  1558. * if authenticating, set user credentials here
  1559. *
  1560. * @param string $user
  1561. * @param string $pass
  1562. * @access public
  1563. */
  1564. function setCredentials($username, $password) {
  1565. $this->username = $username;
  1566. $this->password = $password;
  1567. }
  1568. /**
  1569. * set the soapaction value
  1570. *
  1571. * @param string $soapaction
  1572. * @access public
  1573. */
  1574. function setSOAPAction($soapaction) {
  1575. $this->soapaction = $soapaction;
  1576. }
  1577. /**
  1578. * use http encoding
  1579. *
  1580. * @param string $enc encoding style. supported values: gzip, deflate, or both
  1581. * @access public
  1582. */
  1583. function setEncoding($enc='gzip, deflate'){
  1584. $this->encoding = $enc;
  1585. $this->protocol_version = '1.1';
  1586. }
  1587. /**
  1588. * set proxy info here
  1589. *
  1590. * @param string $proxyhost
  1591. * @param string $proxyport
  1592. * @access public
  1593. */
  1594. function setProxy($proxyhost, $proxyport) {
  1595. $this->proxyhost = $proxyhost;
  1596. $this->proxyport = $proxyport;
  1597. }
  1598. /**
  1599. * decode a string that is encoded w/ "chunked' transfer encoding
  1600. * as defined in RFC2068 19.4.6
  1601. *
  1602. * @param string $buffer
  1603. * @returns string
  1604. * @access public
  1605. */
  1606. function decodeChunked($buffer){
  1607. // length := 0
  1608. $length = 0;
  1609. $new = '';
  1610. // read chunk-size, chunk-extension (if any) and CRLF
  1611. // get the position of the linebreak
  1612. $chunkend = strpos($buffer,"\r\n") + 2;
  1613. $temp = substr($buffer,0,$chunkend);
  1614. $chunk_size = hexdec( trim($temp) );
  1615. $chunkstart = $chunkend;
  1616. // while (chunk-size > 0) {
  1617. while ($chunk_size > 0) {
  1618. $chunkend = strpos( $buffer, "\r\n", $chunkstart + $chunk_size);
  1619. // Just in case we got a broken connection
  1620. if ($chunkend == FALSE) {
  1621. $chunk = substr($buffer,$chunkstart);
  1622. // append chunk-data to entity-body
  1623. $new .= $chunk;
  1624. $length += strlen($chunk);
  1625. break;
  1626. }
  1627. // read chunk-data and CRLF
  1628. $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
  1629. // append chunk-data to entity-body
  1630. $new .= $chunk;
  1631. // length := length + chunk-size
  1632. $length += strlen($chunk);
  1633. // read chunk-size and CRLF
  1634. $chunkstart = $chunkend + 2;
  1635. $chunkend = strpos($buffer,"\r\n",$chunkstart)+2;
  1636. if ($chunkend == FALSE) {
  1637. break; //Just in case we got a broken connection
  1638. }
  1639. $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
  1640. $chunk_size = hexdec( trim($temp) );
  1641. $chunkstart = $chunkend;
  1642. }
  1643. // Update headers
  1644. //$this->Header['content-length'] = $length;
  1645. //unset($this->Header['transfer-encoding']);
  1646. return $new;
  1647. }
  1648. }
  1649. ?><?php
  1650. /**
  1651. *
  1652. * soap_server allows the user to create a SOAP server
  1653. * that is capable of receiving messages and returning responses
  1654. *
  1655. * NOTE: WSDL functionality is experimental
  1656. *
  1657. * @author Dietrich Ayala <dietrich@ganx4.com>
  1658. * @version v 0.6.3
  1659. * @access public
  1660. */
  1661. class soap_server extends nusoap_base {
  1662. var $service = ''; // service name
  1663. var $operations = array(); // assoc array of operations => opData
  1664. var $responseHeaders = false;
  1665. var $headers = '';
  1666. var $request = '';
  1667. var $charset_encoding = 'UTF-8';
  1668. var $fault = false;
  1669. var $result = 'successful';
  1670. var $wsdl = false;
  1671. var $externalWSDLURL = false;
  1672. var $debug_flag = true;
  1673. /**
  1674. * constructor
  1675. * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
  1676. *
  1677. * @param string $wsdl path or URL to a WSDL file
  1678. * @access public
  1679. */
  1680. function soap_server($wsdl=false){
  1681. // turn on debugging?
  1682. global $debug;
  1683. if(isset($debug)){
  1684. $this->debug_flag = 1;
  1685. }
  1686. // wsdl
  1687. if($wsdl){
  1688. $this->wsdl = new wsdl($wsdl);
  1689. $this->externalWSDLURL = $wsdl;
  1690. if($err = $this->wsdl->getError()){
  1691. die('WSDL ERROR: '.$err);
  1692. }
  1693. }
  1694. }
  1695. /**
  1696. * processes request and returns response
  1697. *
  1698. * @param string $data usually is the value of $HTTP_RAW_POST_DATA
  1699. * @access public
  1700. */
  1701. function service($data){
  1702. // print wsdl
  1703. global $QUERY_STRING, $HTTP_SERVER_VARS;
  1704. if(isset($HTTP_SERVER_VARS['QUERY_STRING'])){
  1705. $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
  1706. } elseif(isset($GLOBALS['QUERY_STRING'])){
  1707. $qs = $GLOBALS['QUERY_STRING'];
  1708. } elseif(isset($QUERY_STRING) && $QUERY_STRING != ''){
  1709. $qs = $QUERY_STRING;
  1710. }
  1711. // gen wsdl
  1712. if(isset($qs) && ereg('wsdl', $qs) ){
  1713. if($this->externalWSDLURL){
  1714. header('Location: '.$this->externalWSDLURL);
  1715. exit();
  1716. } else {
  1717. header("Content-Type: text/xml\r\n");
  1718. print $this->wsdl->serialize();
  1719. exit();
  1720. }
  1721. }
  1722. // print web interface
  1723. if($data == '' && $this->wsdl){
  1724. print $this->webDescription();
  1725. } else {
  1726. // $response is the serialized response message
  1727. $response = $this->parse_request($data);
  1728. $this->debug('server sending...');
  1729. $payload = $response;
  1730. // add debug data if in debug mode
  1731. if(isset($this->debug_flag) && $this->debug_flag == 1){
  1732. $payload .= "<!--\n".str_replace('--','- -',$this->debug_str)."\n-->";
  1733. }
  1734. // print headers
  1735. if($this->fault){
  1736. $header[] = "HTTP/1.0 500 Internal Server Error\r\n";
  1737. $header[] = "Status: 500 Internal Server Error\r\n";
  1738. } else {
  1739. $header[] = "Status: 200 OK\r\n";
  1740. }
  1741. $header[] = "Server: $this->title Server v$this->version\r\n";
  1742. $header[] = "Connection: Close\r\n";
  1743. $header[] = "Content-Type: text/xml; charset=$this->charset_encoding\r\n";
  1744. $header[] = "Content-Length: ".strlen($payload)."\r\n\r\n";
  1745. reset($header);
  1746. foreach($header as $hdr){
  1747. header($hdr);
  1748. }
  1749. $this->response = join("\r\n",$header).$payload;
  1750. print $payload;
  1751. }
  1752. }
  1753. /**
  1754. * parses request and posts response
  1755. *
  1756. * @param string $data XML string
  1757. * @return string XML response msg
  1758. * @access private
  1759. */
  1760. function parse_request($data='') {
  1761. if (!isset($_SERVER))
  1762. $_SERVER =& $GLOBALS['HTTP_SERVER_VARS'];
  1763. $this->debug('entering parseRequest() on '.date('H:i Y-m-d'));
  1764. $dump = '';
  1765. // get headers
  1766. if(function_exists('getallheaders')){
  1767. $this->headers = getallheaders();
  1768. foreach($this->headers as $k=>$v){
  1769. $dump .= "$k: $v\r\n";
  1770. $this->debug("$k: $v");
  1771. }
  1772. // get SOAPAction header
  1773. if(isset($this->headers['SOAPAction'])){
  1774. $this->SOAPAction = str_replace('"','',$this->headers['SOAPAction']);
  1775. }
  1776. // get the character encoding of the incoming request
  1777. if(strpos($this->headers['Content-Type'],'=')){
  1778. $enc = str_replace('"','',substr(strstr($this->headers["Content-Type"],'='),1));
  1779. if(eregi('^(ISO-8859-1|US-ASCII|UTF-8)$',$enc)){
  1780. $this->xml_encoding = $enc;
  1781. } else {
  1782. $this->xml_encoding = 'us-ascii';
  1783. }
  1784. }
  1785. $this->debug('got encoding: '.$this->charset_encoding);
  1786. } elseif(is_array($_SERVER)){
  1787. $this->headers['User-Agent'] = $_SERVER['HTTP_USER_AGENT'];
  1788. $this->SOAPAction = isset($_SERVER['SOAPAction']) ? $_SERVER['SOAPAction'] : '';
  1789. }
  1790. $this->request = $dump."\r\n\r\n".$data;
  1791. // parse response, get soap parser obj
  1792. $parser = new soap_parser($data,$this->charset_encoding);
  1793. // if fault occurred during message parsing
  1794. if($err = $parser->getError()){
  1795. // parser debug
  1796. $this->debug("parser debug: \n".$parser->debug_str);
  1797. $this->result = 'fault: error in msg parsing: '.$err;
  1798. $this->fault('Server',"error in msg parsing:\n".$err);
  1799. // return soapresp
  1800. return $this->fault->serialize();
  1801. // else successfully parsed request into soapval object
  1802. } else {
  1803. // get/set methodname
  1804. $this->methodname = $parser->root_struct_name;
  1805. $this->debug('method name: '.$this->methodname);
  1806. // does method exist?
  1807. if(!function_exists($this->methodname)){
  1808. // "method not found" fault here
  1809. $this->debug("method '$this->methodname' not found!");
  1810. $this->debug("parser debug: \n".$parser->debug_str);
  1811. $this->result = 'fault: method not found';
  1812. $this->fault('Server',"method '$this->methodname' not defined in service '$this->service'");
  1813. return $this->fault->serialize();
  1814. }
  1815. if($this->wsdl){
  1816. if(!$this->opData = $this->wsdl->getOperationData($this->methodname)){
  1817. //if(
  1818. $this->fault('Server',"Operation '$this->methodname' is not defined in the WSDL for this service");
  1819. return $this->fault->serialize();
  1820. }
  1821. }
  1822. $this->debug("method '$this->methodname' exists");
  1823. // evaluate message, getting back parameters
  1824. $this->debug('calling parser->get_response()');
  1825. $request_data = $parser->get_response();
  1826. // parser debug
  1827. $this->debug("parser debug: \n".$parser->debug_str);
  1828. // verify that request parameters match the method's signature
  1829. if($this->verify_method($this->methodname,$request_data)){
  1830. // if there are parameters to pass
  1831. $this->debug('params var dump '.$this->varDump($request_data));
  1832. if($request_data){
  1833. $this->debug("calling '$this->methodname' with params");
  1834. if (! function_exists('call_user_func_array')) {
  1835. $this->debug('calling method using eval()');
  1836. $funcCall = $this->methodname.'(';
  1837. foreach($request_data as $param) {
  1838. $funcCall .= "\"$param\",";
  1839. }
  1840. $funcCall = substr($funcCall, 0, -1).')';
  1841. $this->debug('function call:<br>'.$funcCall);
  1842. @eval("\$method_response = $funcCall;");
  1843. } else {
  1844. $this->debug('calling method using call_user_func_array()');
  1845. $method_response = call_user_func_array("$this->methodname",$request_data);
  1846. }
  1847. $this->debug('response var dump'.$this->varDump($method_response));
  1848. } else {
  1849. // call method w/ no parameters
  1850. $this->debug("calling $this->methodname w/ no params");
  1851. $m = $this->methodname;
  1852. $method_response = @$m();
  1853. }
  1854. $this->debug("done calling method: $this->methodname, received $method_response of type".gettype($method_response));
  1855. // if we got nothing back. this might be ok (echoVoid)
  1856. if(isset($method_response) && $method_response != '' || is_bool($method_response)) {
  1857. // if fault
  1858. if(strtolower(get_class($method_response)) == 'soap_fault'){
  1859. $this->debug('got a fault object from method');
  1860. $this->fault = $method_response;
  1861. return $method_response->serialize();
  1862. // if return val is soapval object
  1863. } elseif(strtolower(get_class($method_response)) == 'soapval'){
  1864. $this->debug('got a soapval object from method');
  1865. $return_val = $method_response->serialize();
  1866. // returned other
  1867. } else {
  1868. $this->debug('got a(n) '.gettype($method_response).' from method');
  1869. $this->debug('serializing return value');
  1870. if($this->wsdl){
  1871. // weak attempt at supporting multiple output params
  1872. if(sizeof($this->opData['output']['parts']) > 1){
  1873. $opParams = $method_response;
  1874. } else {
  1875. $opParams = array($method_response);
  1876. }
  1877. $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams);
  1878. } else {
  1879. $return_val = $this->serialize_val($method_response);
  1880. }
  1881. }
  1882. $this->debug('return val:'.$this->varDump($return_val));
  1883. } else {
  1884. $return_val = '';
  1885. $this->debug('got no response from method');
  1886. }
  1887. $this->debug('serializing response');
  1888. $payload = '<'.$this->methodname."Response>".$return_val.'</'.$this->methodname."Response>";
  1889. $this->result = 'successful';
  1890. if($this->wsdl){
  1891. //if($this->debug_flag){
  1892. $this->debug("WSDL debug data:\n".$this->wsdl->debug_str);
  1893. // }
  1894. // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
  1895. return $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style']);
  1896. } else {
  1897. return $this->serializeEnvelope($payload,$this->responseHeaders);
  1898. }
  1899. } else {
  1900. // debug
  1901. $this->debug('ERROR: request not verified against method signature');
  1902. $this->result = 'fault: request failed validation against method signature';
  1903. // return fault
  1904. $this->fault('Server',"Operation '$this->methodname' not defined in service.");
  1905. return $this->fault->serialize();
  1906. }
  1907. }
  1908. }
  1909. /**
  1910. * takes the value that was created by parsing the request
  1911. * and compares to the method's signature, if available.
  1912. *
  1913. * @param mixed
  1914. * @return boolean
  1915. * @access private
  1916. */
  1917. function verify_method($operation,$request){
  1918. if(isset($this->wsdl) && is_object($this->wsdl)){
  1919. if($this->wsdl->getOperationData($operation)){
  1920. return true;
  1921. }
  1922. } elseif(isset($this->operations[$operation])){
  1923. return true;
  1924. }
  1925. return false;
  1926. }
  1927. /**
  1928. * add a method to the dispatch map
  1929. *
  1930. * @param string $methodname
  1931. * @param string $in array of input values
  1932. * @param string $out array of output values
  1933. * @access public
  1934. */
  1935. function add_to_map($methodname,$in,$out){
  1936. $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out);
  1937. }
  1938. /**
  1939. * register a service with the server
  1940. *
  1941. * @param string $methodname
  1942. * @param string $in assoc array of input values: key = param name, value = param type
  1943. * @param string $out assoc array of output values: key = param name, value = param type
  1944. * @param string $namespace
  1945. * @param string $soapaction
  1946. * @param string $style (rpc|literal)
  1947. * @access public
  1948. */
  1949. function register($name,$in=false,$out=false,$namespace=false,$soapaction=false,$style=false,$use=false){
  1950. if($this->externalWSDLURL){
  1951. die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
  1952. }
  1953. if(false == $in) {
  1954. }
  1955. if(false == $out) {
  1956. }
  1957. if(false == $namespace) {
  1958. }
  1959. if(false == $soapaction) {
  1960. global $SERVER_NAME, $SCRIPT_NAME;
  1961. $soapaction = "http://$SERVER_NAME$SCRIPT_NAME";
  1962. }
  1963. if(false == $style) {
  1964. $style = "rpc";
  1965. }
  1966. if(false == $use) {
  1967. $use = "encoded";
  1968. }
  1969. $this->operations[] = array($name => array());
  1970. $this->operations[$name] = array(
  1971. 'name' => $name,
  1972. 'in' => $in,
  1973. 'out' => $out,
  1974. 'namespace' => $namespace,
  1975. 'soapaction' => $soapaction,
  1976. 'style' => $style);
  1977. if($this->wsdl){
  1978. $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use);
  1979. }
  1980. return true;
  1981. }
  1982. /**
  1983. * create a fault. this also acts as a flag to the server that a fault has occured.
  1984. *
  1985. * @param string faultcode
  1986. * @param string faultactor
  1987. * @param string faultstring
  1988. * @param string faultdetail
  1989. * @access public
  1990. */
  1991. function fault($faultcode,$faultactor,$faultstring='',$faultdetail=''){
  1992. $this->fault = new soap_fault($faultcode,$faultactor,$faultstring,$faultdetail);
  1993. }
  1994. /**
  1995. * prints html description of services
  1996. *
  1997. * @access private
  1998. */
  1999. function webDescription(){
  2000. $b = '
  2001. <html><head><title>NuSOAP: '.$this->wsdl->serviceName.'</title>
  2002. <style type="text/css">
  2003. body { font-family: arial; color: #000000; background-color: #ffffff; margin: 0px 0px 0px 0px; }
  2004. p { font-family: arial; color: #000000; margin-top: 0px; margin-bottom: 12px; }
  2005. pre { background-color: silver; padding: 5px; font-family: Courier New; font-size: x-small; color: #000000;}
  2006. ul { margin-top: 10px; margin-left: 20px; }
  2007. li { list-style-type: none; margin-top: 10px; color: #000000; }
  2008. .content{
  2009. margin-left: 0px; padding-bottom: 2em; }
  2010. .nav {
  2011. padding-top: 10px; padding-bottom: 10px; padding-left: 15px; font-size: .70em;
  2012. margin-top: 10px; margin-left: 0px; color: #000000;
  2013. background-color: #ccccff; width: 20%; margin-left: 20px; margin-top: 20px; }
  2014. .title {
  2015. font-family: arial; font-size: 26px; color: #ffffff;
  2016. background-color: #999999; width: 105%; margin-left: 0px;
  2017. padding-top: 10px; padding-bottom: 10px; padding-left: 15px;}
  2018. .hidden {
  2019. position: absolute; visibility: hidden; z-index: 200; left: 250px; top: 100px;
  2020. font-family: arial; overflow: hidden; width: 600;
  2021. padding: 20px; font-size: 10px; background-color: #999999;
  2022. layer-background-color:#FFFFFF; }
  2023. a,a:active { color: charcoal; font-weight: bold; }
  2024. a:visited { color: #666666; font-weight: bold; }
  2025. a:hover { color: cc3300; font-weight: bold; }
  2026. </style>
  2027. <script language="JavaScript" type="text/javascript">
  2028. <!--
  2029. // POP-UP CAPTIONS...
  2030. function lib_bwcheck(){ //Browsercheck (needed)
  2031. this.ver=navigator.appVersion
  2032. this.agent=navigator.userAgent
  2033. this.dom=document.getElementById?1:0
  2034. this.opera5=this.agent.indexOf("Opera 5")>-1
  2035. this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom && !this.opera5)?1:0;
  2036. this.ie6=(this.ver.indexOf("MSIE 6")>-1 && this.dom && !this.opera5)?1:0;
  2037. this.ie4=(document.all && !this.dom && !this.opera5)?1:0;
  2038. this.ie=this.ie4||this.ie5||this.ie6
  2039. this.mac=this.agent.indexOf("Mac")>-1
  2040. this.ns6=(this.dom && parseInt(this.ver) >= 5) ?1:0;
  2041. this.ns4=(document.layers && !this.dom)?1:0;
  2042. this.bw=(this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera5)
  2043. return this
  2044. }
  2045. var bw = new lib_bwcheck()
  2046. //Makes crossbrowser object.
  2047. function makeObj(obj){
  2048. this.evnt=bw.dom? document.getElementById(obj):bw.ie4?document.all[obj]:bw.ns4?document.layers[obj]:0;
  2049. if(!this.evnt) return false
  2050. this.css=bw.dom||bw.ie4?this.evnt.style:bw.ns4?this.evnt:0;
  2051. this.wref=bw.dom||bw.ie4?this.evnt:bw.ns4?this.css.document:0;
  2052. this.writeIt=b_writeIt;
  2053. return this
  2054. }
  2055. // A unit of measure that will be added when setting the position of a layer.
  2056. //var px = bw.ns4||window.opera?"":"px";
  2057. function b_writeIt(text){
  2058. if (bw.ns4){this.wref.write(text);this.wref.close()}
  2059. else this.wref.innerHTML = text
  2060. }
  2061. //Shows the messages
  2062. var oDesc;
  2063. function popup(divid){
  2064. if(oDesc = new makeObj(divid)){
  2065. oDesc.css.visibility = "visible"
  2066. }
  2067. }
  2068. function popout(){ // Hides message
  2069. if(oDesc) oDesc.css.visibility = "hidden"
  2070. }
  2071. //-->
  2072. </script>
  2073. </head>
  2074. <body>
  2075. <div class=content>
  2076. <br><br>
  2077. <div class=title>'.$this->wsdl->serviceName.'</div>
  2078. <div class=nav>
  2079. <p>View the <a href="'.$GLOBALS['PHP_SELF'].'?wsdl">WSDL</a> for the service.
  2080. Click on an operation name to view it&apos;s details.</p>
  2081. <ul>';
  2082. foreach($this->wsdl->getOperations() as $op => $data){
  2083. $b .= "<li><a href='#' onclick=\"popup('$op')\">$op</a></li>";
  2084. // create hidden div
  2085. $b .= "<div id='$op' class='hidden'>
  2086. <a href='#' onclick='popout()'><font color='#ffffff'>Close</font></a><br><br>";
  2087. foreach($data as $donnie => $marie){ // loop through opdata
  2088. if($donnie == 'input' || $donnie == 'output'){ // show input/output data
  2089. $b .= "<font color='white'>".ucfirst($donnie).':</font><br>';
  2090. foreach($marie as $captain => $tenille){ // loop through data
  2091. if($captain == 'parts'){ // loop thru parts
  2092. $b .= "&nbsp;&nbsp;$captain:<br>";
  2093. //if(is_array($tenille)){
  2094. foreach($tenille as $joanie => $chachi){
  2095. $b .= "&nbsp;&nbsp;&nbsp;&nbsp;$joanie: $chachi<br>";
  2096. }
  2097. //}
  2098. } else {
  2099. $b .= "&nbsp;&nbsp;$captain: $tenille<br>";
  2100. }
  2101. }
  2102. } else {
  2103. $b .= "<font color='white'>".ucfirst($donnie).":</font> $marie<br>";
  2104. }
  2105. }
  2106. $b .= '</div>';
  2107. }
  2108. $b .= '
  2109. <ul>
  2110. </div>
  2111. </div></body></html>';
  2112. return $b;
  2113. }
  2114. /**
  2115. * sets up wsdl object
  2116. * this acts as a flag to enable internal WSDL generation
  2117. * NOTE: NOT FUNCTIONAL
  2118. *
  2119. * @param string $serviceName, name of the service
  2120. * @param string $namespace, tns namespace
  2121. */
  2122. function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http') {
  2123. if (!isset($_SERVER))
  2124. $_SERVER =& $GLOBALS['HTTP_SERVER_VARS'];
  2125. $SERVER_NAME = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : $GLOBALS['SERVER_NAME'];
  2126. $SCRIPT_NAME = isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : $GLOBALS['SCRIPT_NAME'];
  2127. if(false == $namespace) {
  2128. $namespace = "http://$SERVER_NAME/soap/$serviceName";
  2129. }
  2130. if(false == $endpoint) {
  2131. $endpoint = "http://$SERVER_NAME$SCRIPT_NAME";
  2132. }
  2133. $this->wsdl = new wsdl;
  2134. $this->wsdl->serviceName = $serviceName;
  2135. $this->wsdl->endpoint = $endpoint;
  2136. $this->wsdl->namespaces['tns'] = $namespace;
  2137. $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
  2138. $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
  2139. $this->wsdl->bindings[$serviceName.'Binding'] = array(
  2140. 'name'=>$serviceName.'Binding',
  2141. 'style'=>$style,
  2142. 'transport'=>$transport,
  2143. 'portType'=>$serviceName.'PortType');
  2144. $this->wsdl->ports[$serviceName.'Port'] = array(
  2145. 'binding'=>$serviceName.'Binding',
  2146. 'location'=>$endpoint,
  2147. 'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/');
  2148. }
  2149. }
  2150. ?><?php
  2151. /**
  2152. * parses a WSDL file, allows access to it's data, other utility methods
  2153. *
  2154. * @author Dietrich Ayala <dietrich@ganx4.com>
  2155. * @version v 0.6.3
  2156. * @access public
  2157. */
  2158. class wsdl extends XMLSchema {
  2159. var $wsdl;
  2160. // define internal arrays of bindings, ports, operations, messages, etc.
  2161. var $message = array();
  2162. var $complexTypes = array();
  2163. var $messages = array();
  2164. var $currentMessage;
  2165. var $currentOperation;
  2166. var $portTypes = array();
  2167. var $currentPortType;
  2168. var $bindings = array();
  2169. var $currentBinding;
  2170. var $ports = array();
  2171. var $currentPort;
  2172. var $opData = array();
  2173. var $status = '';
  2174. var $documentation = false;
  2175. var $endpoint = '';
  2176. // array of wsdl docs to import
  2177. var $import = array();
  2178. // parser vars
  2179. var $parser;
  2180. var $position = 0;
  2181. var $depth = 0;
  2182. var $depth_array = array();
  2183. var $usedNamespaces = array();
  2184. // for getting wsdl
  2185. var $proxyhost = '';
  2186. var $proxyport = '';
  2187. /**
  2188. * constructor
  2189. *
  2190. * @param string $wsdl WSDL document URL
  2191. * @access public
  2192. */
  2193. function wsdl($wsdl = '',$proxyhost=false,$proxyport=false){
  2194. $this->wsdl = $wsdl;
  2195. $this->proxyhost = $proxyhost;
  2196. $this->proxyport = $proxyport;
  2197. // parse wsdl file
  2198. if ($wsdl != "") {
  2199. $this->debug('initial wsdl file: ' . $wsdl);
  2200. $this->parseWSDL($wsdl);
  2201. }
  2202. // imports
  2203. if (sizeof($this->import) > 0) {
  2204. foreach($this->import as $ns => $url) {
  2205. $this->debug('importing wsdl from ' . $url);
  2206. $this->parseWSDL($url);
  2207. }
  2208. }
  2209. }
  2210. /**
  2211. * parses the wsdl document
  2212. *
  2213. * @param string $wsdl path or URL
  2214. * @access private
  2215. */
  2216. function parseWSDL($wsdl = '')
  2217. {
  2218. if ($wsdl == '') {
  2219. $this->debug('no wsdl passed to parseWSDL()!!');
  2220. $this->setError('no wsdl passed to parseWSDL()!!');
  2221. return false;
  2222. }
  2223. $this->debug('getting ' . $wsdl);
  2224. // parse $wsdl for url format
  2225. $wsdl_props = parse_url($wsdl);
  2226. if (isset($wsdl_props['host'])) {
  2227. // get wsdl
  2228. $tr = new soap_transport_http($wsdl);
  2229. $tr->request_method = 'GET';
  2230. $tr->useSOAPAction = false;
  2231. if($this->proxyhost && $this->proxyport){
  2232. $tr->setProxy($this->proxyhost,$this->proxyport);
  2233. }
  2234. if (isset($wsdl_props['user'])) {
  2235. $tr->setCredentials($wsdl_props['user'],$wsdl_props['pass']);
  2236. }
  2237. $wsdl_string = $tr->send('');
  2238. // catch errors
  2239. if($err = $tr->getError() ){
  2240. $this->debug('HTTP ERROR: '.$err);
  2241. $this->setError('HTTP ERROR: '.$err);
  2242. return false;
  2243. }
  2244. unset($tr);
  2245. /* $wsdl seems to be a valid url, not a file path, do an fsockopen/HTTP GET
  2246. $fsockopen_timeout = 30;
  2247. // check if a port value is supplied in url
  2248. if (isset($wsdl_props['port'])) {
  2249. // yes
  2250. $wsdl_url_port = $wsdl_props['port'];
  2251. } else {
  2252. // no, assign port number, based on url protocol (scheme)
  2253. switch ($wsdl_props['scheme']) {
  2254. case ('https') :
  2255. case ('ssl') :
  2256. case ('tls') :
  2257. $wsdl_url_port = 443;
  2258. break;
  2259. case ('http') :
  2260. default :
  2261. $wsdl_url_port = 80;
  2262. }
  2263. }
  2264. // FIXME: should implement SSL/TLS support here if CURL is available
  2265. if ($fp = fsockopen($wsdl_props['host'], $wsdl_url_port, $fsockopen_errnum, $fsockopen_errstr, $fsockopen_timeout)) {
  2266. // perform HTTP GET for WSDL file
  2267. // 10.9.02 - added poulter fix for doing this properly
  2268. $sHeader = "GET " . $wsdl_props['path'];
  2269. if (isset($wsdl_props['query'])) {
  2270. $sHeader .= "?" . $wsdl_props['query'];
  2271. }
  2272. $sHeader .= " HTTP/1.0\r\n";
  2273. if (isset($wsdl_props['user'])) {
  2274. $base64auth = base64_encode($wsdl_props['user'] . ":" . $wsdl_props['pass']);
  2275. $sHeader .= "Authorization: Basic $base64auth\r\n";
  2276. }
  2277. $sHeader .= "Host: " . $wsdl_props['host'] . ( isset($wsdl_props['port']) ? ":".$wsdl_props['port'] : "" ) . "\r\n\r\n";
  2278. fputs($fp, $sHeader);
  2279. while (fgets($fp, 1024) != "\r\n") {
  2280. // do nothing, just read/skip past HTTP headers
  2281. // FIXME: should actually detect HTTP response code, and act accordingly if error
  2282. // HTTP headers end with extra CRLF before content body
  2283. }
  2284. // read in WSDL just like regular fopen()
  2285. $wsdl_string = '';
  2286. while ($data = fread($fp, 32768)) {
  2287. $wsdl_string .= $data;
  2288. }
  2289. fclose($fp);
  2290. } else {
  2291. $this->setError('bad path to WSDL file.');
  2292. return false;
  2293. }
  2294. */
  2295. } else {
  2296. // $wsdl seems to be a non-url file path, do the regular fopen
  2297. if ($fp = @fopen($wsdl, 'r')) {
  2298. $wsdl_string = '';
  2299. while ($data = fread($fp, 32768)) {
  2300. $wsdl_string .= $data;
  2301. }
  2302. fclose($fp);
  2303. } else {
  2304. $this->setError('bad path to WSDL file.');
  2305. return false;
  2306. }
  2307. }
  2308. // end new code added
  2309. // Create an XML parser.
  2310. $this->parser = xml_parser_create();
  2311. // Set the options for parsing the XML data.
  2312. // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
  2313. xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
  2314. // Set the object for the parser.
  2315. xml_set_object($this->parser, $this);
  2316. // Set the element handlers for the parser.
  2317. xml_set_element_handler($this->parser, 'start_element', 'end_element');
  2318. xml_set_character_data_handler($this->parser, 'character_data');
  2319. // Parse the XML file.
  2320. if (!xml_parse($this->parser, $wsdl_string, true)) {
  2321. // Display an error message.
  2322. $errstr = sprintf(
  2323. 'XML error on line %d: %s',
  2324. xml_get_current_line_number($this->parser),
  2325. xml_error_string(xml_get_error_code($this->parser))
  2326. );
  2327. $this->debug('XML parse error: ' . $errstr);
  2328. $this->setError('Parser error: ' . $errstr);
  2329. return false;
  2330. }
  2331. // free the parser
  2332. xml_parser_free($this->parser);
  2333. // catch wsdl parse errors
  2334. if($this->getError()){
  2335. return false;
  2336. }
  2337. // add new data to operation data
  2338. foreach($this->bindings as $binding => $bindingData) {
  2339. if (isset($bindingData['operations']) && is_array($bindingData['operations'])) {
  2340. foreach($bindingData['operations'] as $operation => $data) {
  2341. $this->debug('post-parse data gathering for ' . $operation);
  2342. $this->bindings[$binding]['operations'][$operation]['input'] =
  2343. isset($this->bindings[$binding]['operations'][$operation]['input']) ?
  2344. array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) :
  2345. $this->portTypes[ $bindingData['portType'] ][$operation]['input'];
  2346. $this->bindings[$binding]['operations'][$operation]['output'] =
  2347. isset($this->bindings[$binding]['operations'][$operation]['output']) ?
  2348. array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) :
  2349. $this->portTypes[ $bindingData['portType'] ][$operation]['output'];
  2350. if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){
  2351. $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ];
  2352. }
  2353. if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){
  2354. $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ];
  2355. }
  2356. if (isset($bindingData['style'])) {
  2357. $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style'];
  2358. }
  2359. $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : '';
  2360. $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : '';
  2361. $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : '';
  2362. }
  2363. }
  2364. }
  2365. return true;
  2366. }
  2367. /**
  2368. * start-element handler
  2369. *
  2370. * @param string $parser XML parser object
  2371. * @param string $name element name
  2372. * @param string $attrs associative array of attributes
  2373. * @access private
  2374. */
  2375. function start_element($parser, $name, $attrs)
  2376. {
  2377. if ($this->status == 'schema' || ereg('schema$', $name)) {
  2378. // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")");
  2379. $this->status = 'schema';
  2380. $this->schemaStartElement($parser, $name, $attrs);
  2381. } else {
  2382. // position in the total number of elements, starting from 0
  2383. $pos = $this->position++;
  2384. $depth = $this->depth++;
  2385. // set self as current value for this depth
  2386. $this->depth_array[$depth] = $pos;
  2387. $this->message[$pos] = array('cdata' => '');
  2388. // get element prefix
  2389. if (ereg(':', $name)) {
  2390. // get ns prefix
  2391. $prefix = substr($name, 0, strpos($name, ':'));
  2392. // get ns
  2393. $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : '';
  2394. // get unqualified name
  2395. $name = substr(strstr($name, ':'), 1);
  2396. }
  2397. if (count($attrs) > 0) {
  2398. foreach($attrs as $k => $v) {
  2399. // if ns declarations, add to class level array of valid namespaces
  2400. if (ereg("^xmlns", $k)) {
  2401. if ($ns_prefix = substr(strrchr($k, ':'), 1)) {
  2402. $this->namespaces[$ns_prefix] = $v;
  2403. } else {
  2404. $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v;
  2405. }
  2406. if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema') {
  2407. $this->XMLSchemaVersion = $v;
  2408. $this->namespaces['xsi'] = $v . '-instance';
  2409. }
  2410. } //
  2411. // expand each attribute
  2412. $k = strpos($k, ':') ? $this->expandQname($k) : $k;
  2413. if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') {
  2414. $v = strpos($v, ':') ? $this->expandQname($v) : $v;
  2415. }
  2416. $eAttrs[$k] = $v;
  2417. }
  2418. $attrs = $eAttrs;
  2419. } else {
  2420. $attrs = array();
  2421. }
  2422. // find status, register data
  2423. switch ($this->status) {
  2424. case 'message':
  2425. if ($name == 'part') {
  2426. if (isset($attrs['type'])) {
  2427. $this->debug("msg " . $this->currentMessage . ": found part $attrs[name]: " . implode(',', $attrs));
  2428. $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type'];
  2429. }
  2430. if (isset($attrs['element'])) {
  2431. $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'];
  2432. }
  2433. }
  2434. break;
  2435. case 'portType':
  2436. switch ($name) {
  2437. case 'operation':
  2438. $this->currentPortOperation = $attrs['name'];
  2439. $this->debug("portType $this->currentPortType operation: $this->currentPortOperation");
  2440. if (isset($attrs['parameterOrder'])) {
  2441. $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder'];
  2442. }
  2443. break;
  2444. case 'documentation':
  2445. $this->documentation = true;
  2446. break;
  2447. // merge input/output data
  2448. default:
  2449. $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : '';
  2450. $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m;
  2451. break;
  2452. }
  2453. break;
  2454. case 'binding':
  2455. switch ($name) {
  2456. case 'binding':
  2457. // get ns prefix
  2458. if (isset($attrs['style'])) {
  2459. $this->bindings[$this->currentBinding]['prefix'] = $prefix;
  2460. }
  2461. $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs);
  2462. break;
  2463. case 'header':
  2464. $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs;
  2465. break;
  2466. case 'operation':
  2467. if (isset($attrs['soapAction'])) {
  2468. $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction'];
  2469. }
  2470. if (isset($attrs['style'])) {
  2471. $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style'];
  2472. }
  2473. if (isset($attrs['name'])) {
  2474. $this->currentOperation = $attrs['name'];
  2475. $this->debug("current binding operation: $this->currentOperation");
  2476. $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name'];
  2477. $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding;
  2478. $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : '';
  2479. }
  2480. break;
  2481. case 'input':
  2482. $this->opStatus = 'input';
  2483. break;
  2484. case 'output':
  2485. $this->opStatus = 'output';
  2486. break;
  2487. case 'body':
  2488. if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) {
  2489. $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs);
  2490. } else {
  2491. $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs;
  2492. }
  2493. break;
  2494. }
  2495. break;
  2496. case 'service':
  2497. switch ($name) {
  2498. case 'port':
  2499. $this->currentPort = $attrs['name'];
  2500. $this->debug('current port: ' . $this->currentPort);
  2501. $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']);
  2502. break;
  2503. case 'address':
  2504. $this->ports[$this->currentPort]['location'] = $attrs['location'];
  2505. $this->ports[$this->currentPort]['bindingType'] = $namespace;
  2506. $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace;
  2507. $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location'];
  2508. break;
  2509. }
  2510. break;
  2511. }
  2512. // set status
  2513. switch ($name) {
  2514. case "import":
  2515. if (isset($attrs['location'])) {
  2516. $this->import[$attrs['namespace']] = $attrs['location'];
  2517. }
  2518. break;
  2519. case 'types':
  2520. $this->status = 'schema';
  2521. break;
  2522. case 'message':
  2523. $this->status = 'message';
  2524. $this->messages[$attrs['name']] = array();
  2525. $this->currentMessage = $attrs['name'];
  2526. break;
  2527. case 'portType':
  2528. $this->status = 'portType';
  2529. $this->portTypes[$attrs['name']] = array();
  2530. $this->currentPortType = $attrs['name'];
  2531. break;
  2532. case "binding":
  2533. if (isset($attrs['name'])) {
  2534. // get binding name
  2535. if (strpos($attrs['name'], ':')) {
  2536. $this->currentBinding = $this->getLocalPart($attrs['name']);
  2537. } else {
  2538. $this->currentBinding = $attrs['name'];
  2539. }
  2540. $this->status = 'binding';
  2541. $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']);
  2542. $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']);
  2543. }
  2544. break;
  2545. case 'service':
  2546. $this->serviceName = $attrs['name'];
  2547. $this->status = 'service';
  2548. $this->debug('current service: ' . $this->serviceName);
  2549. break;
  2550. case 'definitions':
  2551. foreach ($attrs as $name => $value) {
  2552. $this->wsdl_info[$name] = $value;
  2553. }
  2554. break;
  2555. }
  2556. }
  2557. }
  2558. /**
  2559. * end-element handler
  2560. *
  2561. * @param string $parser XML parser object
  2562. * @param string $name element name
  2563. * @access private
  2564. */
  2565. function end_element($parser, $name){
  2566. // unset schema status
  2567. if (ereg('types$', $name) || ereg('schema$', $name)) {
  2568. $this->status = "";
  2569. }
  2570. if ($this->status == 'schema') {
  2571. $this->schemaEndElement($parser, $name);
  2572. } else {
  2573. // bring depth down a notch
  2574. $this->depth--;
  2575. }
  2576. // end documentation
  2577. if ($this->documentation) {
  2578. $this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation;
  2579. $this->documentation = false;
  2580. }
  2581. }
  2582. /**
  2583. * element content handler
  2584. *
  2585. * @param string $parser XML parser object
  2586. * @param string $data element content
  2587. * @access private
  2588. */
  2589. function character_data($parser, $data)
  2590. {
  2591. $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0;
  2592. if (isset($this->message[$pos]['cdata'])) {
  2593. $this->message[$pos]['cdata'] .= $data;
  2594. }
  2595. if ($this->documentation) {
  2596. $this->documentation .= $data;
  2597. }
  2598. }
  2599. function getBindingData($binding)
  2600. {
  2601. if (is_array($this->bindings[$binding])) {
  2602. return $this->bindings[$binding];
  2603. }
  2604. }
  2605. /**
  2606. * returns an assoc array of operation names => operation data
  2607. * NOTE: currently only supports multiple services of differing binding types
  2608. * This method needs some work
  2609. *
  2610. * @param string $bindingType eg: soap, smtp, dime (only soap is currently supported)
  2611. * @return array
  2612. * @access public
  2613. */
  2614. function getOperations($bindingType = 'soap')
  2615. {
  2616. if ($bindingType == 'soap') {
  2617. $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
  2618. }
  2619. // loop thru ports
  2620. foreach($this->ports as $port => $portData) {
  2621. // binding type of port matches parameter
  2622. if ($portData['bindingType'] == $bindingType) {
  2623. // get binding
  2624. return $this->bindings[ $portData['binding'] ]['operations'];
  2625. }
  2626. }
  2627. return array();
  2628. }
  2629. /**
  2630. * returns an associative array of data necessary for calling an operation
  2631. *
  2632. * @param string $operation , name of operation
  2633. * @param string $bindingType , type of binding eg: soap
  2634. * @return array
  2635. * @access public
  2636. */
  2637. function getOperationData($operation, $bindingType = 'soap')
  2638. {
  2639. if ($bindingType == 'soap') {
  2640. $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/';
  2641. }
  2642. // loop thru ports
  2643. foreach($this->ports as $port => $portData) {
  2644. // binding type of port matches parameter
  2645. if ($portData['bindingType'] == $bindingType) {
  2646. // get binding
  2647. //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) {
  2648. foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) {
  2649. if ($operation == $bOperation) {
  2650. $opData = $this->bindings[ $portData['binding'] ]['operations'][$operation];
  2651. return $opData;
  2652. }
  2653. }
  2654. }
  2655. }
  2656. }
  2657. /**
  2658. * serialize the parsed wsdl
  2659. *
  2660. * @return string , serialization of WSDL
  2661. * @access public
  2662. */
  2663. function serialize()
  2664. {
  2665. $xml = '<?xml version="1.0"?><definitions';
  2666. foreach($this->namespaces as $k => $v) {
  2667. $xml .= " xmlns:$k=\"$v\"";
  2668. }
  2669. // 10.9.02 - add poulter fix for wsdl and tns declarations
  2670. if (isset($this->namespaces['wsdl'])) {
  2671. $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\"";
  2672. }
  2673. if (isset($this->namespaces['tns'])) {
  2674. $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\"";
  2675. }
  2676. $xml .= '>';
  2677. // imports
  2678. if (sizeof($this->import) > 0) {
  2679. foreach($this->import as $ns => $url) {
  2680. $xml .= '<import location="' . $url . '" namespace="' . $ns . '" />';
  2681. }
  2682. }
  2683. // types
  2684. if (count($this->complexTypes)>=1) {
  2685. $xml .= '<types>';
  2686. $xml .= $this->serializeSchema();
  2687. $xml .= '</types>';
  2688. }
  2689. // messages
  2690. if (count($this->messages) >= 1) {
  2691. foreach($this->messages as $msgName => $msgParts) {
  2692. $xml .= '<message name="' . $msgName . '">';
  2693. foreach($msgParts as $partName => $partType) {
  2694. // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'<br>';
  2695. if (strpos($partType, ':')) {
  2696. $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType));
  2697. } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) {
  2698. // print 'checking typemap: '.$this->XMLSchemaVersion.'<br>';
  2699. $typePrefix = 'xsd';
  2700. } else {
  2701. foreach($this->typemap as $ns => $types) {
  2702. if (isset($types[$partType])) {
  2703. $typePrefix = $this->getPrefixFromNamespace($ns);
  2704. }
  2705. }
  2706. if (!isset($typePrefix)) {
  2707. die("$partType has no namespace!");
  2708. }
  2709. }
  2710. $xml .= '<part name="' . $partName . '" type="' . $typePrefix . ':' . $this->getLocalPart($partType) . '" />';
  2711. }
  2712. $xml .= '</message>';
  2713. }
  2714. }
  2715. // bindings & porttypes
  2716. if (count($this->bindings) >= 1) {
  2717. $binding_xml = '';
  2718. $portType_xml = '';
  2719. foreach($this->bindings as $bindingName => $attrs) {
  2720. $binding_xml .= '<binding name="' . $bindingName . '" type="tns:' . $attrs['portType'] . '">';
  2721. $binding_xml .= '<soap:binding style="' . $attrs['style'] . '" transport="' . $attrs['transport'] . '"/>';
  2722. $portType_xml .= '<portType name="' . $attrs['portType'] . '">';
  2723. foreach($attrs['operations'] as $opName => $opParts) {
  2724. $binding_xml .= '<operation name="' . $opName . '">';
  2725. $binding_xml .= '<soap:operation soapAction="' . $opParts['soapAction'] . '" style="'. $attrs['style'] . '"/>';
  2726. $binding_xml .= '<input><soap:body use="' . $opParts['input']['use'] . '" namespace="' . $opParts['input']['namespace'] . '" encodingStyle="' . $opParts['input']['encodingStyle'] . '"/></input>';
  2727. $binding_xml .= '<output><soap:body use="' . $opParts['output']['use'] . '" namespace="' . $opParts['output']['namespace'] . '" encodingStyle="' . $opParts['output']['encodingStyle'] . '"/></output>';
  2728. $binding_xml .= '</operation>';
  2729. $portType_xml .= '<operation name="' . $opParts['name'] . '"';
  2730. if (isset($opParts['parameterOrder'])) {
  2731. $portType_xml .= ' parameterOrder="' . $opParts['parameterOrder'] . '"';
  2732. }
  2733. $portType_xml .= '>';
  2734. $portType_xml .= '<input message="tns:' . $opParts['input']['message'] . '"/>';
  2735. $portType_xml .= '<output message="tns:' . $opParts['output']['message'] . '"/>';
  2736. $portType_xml .= '</operation>';
  2737. }
  2738. $portType_xml .= '</portType>';
  2739. $binding_xml .= '</binding>';
  2740. }
  2741. $xml .= $portType_xml . $binding_xml;
  2742. }
  2743. // services
  2744. $xml .= '<service name="' . $this->serviceName . '">';
  2745. if (count($this->ports) >= 1) {
  2746. foreach($this->ports as $pName => $attrs) {
  2747. $xml .= '<port name="' . $pName . '" binding="tns:' . $attrs['binding'] . '">';
  2748. $xml .= '<soap:address location="' . $attrs['location'] . '"/>';
  2749. $xml .= '</port>';
  2750. }
  2751. }
  2752. $xml .= '</service>';
  2753. return $xml . '</definitions>';
  2754. }
  2755. /**
  2756. * serialize a PHP value according to a WSDL message definition
  2757. *
  2758. * TODO
  2759. * - multi-ref serialization
  2760. * - validate PHP values against type definitions, return errors if invalid
  2761. *
  2762. * @param string $ type name
  2763. * @param mixed $ param value
  2764. * @return mixed new param or false if initial value didn't validate
  2765. */
  2766. function serializeRPCParameters($operation, $direction, $parameters)
  2767. {
  2768. $this->debug('in serializeRPCParameters with operation '.$operation.', direction '.$direction.' and '.count($parameters).' param(s), and xml schema version ' . $this->XMLSchemaVersion);
  2769. if ($direction != 'input' && $direction != 'output') {
  2770. $this->debug('The value of the \$direction argument needs to be either "input" or "output"');
  2771. $this->setError('The value of the \$direction argument needs to be either "input" or "output"');
  2772. return false;
  2773. }
  2774. if (!$opData = $this->getOperationData($operation)) {
  2775. $this->debug('Unable to retrieve WSDL data for operation: ' . $operation);
  2776. $this->setError('Unable to retrieve WSDL data for operation: ' . $operation);
  2777. return false;
  2778. }
  2779. $this->debug($this->varDump($opData));
  2780. // set input params
  2781. $xml = '';
  2782. if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) {
  2783. $use = $opData[$direction]['use'];
  2784. $this->debug("use=$use");
  2785. $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)');
  2786. foreach($opData[$direction]['parts'] as $name => $type) {
  2787. $this->debug('serializing part "'.$name.'" of type "'.$type.'"');
  2788. // NOTE: add error handling here
  2789. // if serializeType returns false, then catch global error and fault
  2790. if (isset($parameters[$name])) {
  2791. $this->debug('calling serializeType w/ named param');
  2792. $xml .= $this->serializeType($name, $type, $parameters[$name], $use);
  2793. } elseif(is_array($parameters)) {
  2794. $this->debug('calling serializeType w/ unnamed param');
  2795. $xml .= $this->serializeType($name, $type, array_shift($parameters), $use);
  2796. } else {
  2797. $this->debug('no parameters passed.');
  2798. }
  2799. }
  2800. }
  2801. return $xml;
  2802. }
  2803. /**
  2804. * serializes a PHP value according a given type definition
  2805. *
  2806. * @param string $name , name of type (part)
  2807. * @param string $type , type of type, heh (type or element)
  2808. * @param mixed $value , a native PHP value (parameter value)
  2809. * @param string $use , use for part (encoded|literal)
  2810. * @return string serialization
  2811. * @access public
  2812. */
  2813. function serializeType($name, $type, $value, $use='encoded')
  2814. {
  2815. $this->debug("in serializeType: $name, $type, $value, $use");
  2816. $xml = '';
  2817. if (strpos($type, ':')) {
  2818. $uqType = substr($type, strrpos($type, ':') + 1);
  2819. $ns = substr($type, 0, strrpos($type, ':'));
  2820. $this->debug("got a prefixed type: $uqType, $ns");
  2821. if($ns == $this->XMLSchemaVersion ||
  2822. ($this->getNamespaceFromPrefix($ns)) == $this->XMLSchemaVersion){
  2823. if ($uqType == 'boolean' && !$value) {
  2824. $value = 0;
  2825. } elseif ($uqType == 'boolean') {
  2826. $value = 1;
  2827. }
  2828. if ($this->charencoding && $uqType == 'string' && gettype($value) == 'string') {
  2829. $value = htmlspecialchars($value);
  2830. }
  2831. // it's a scalar
  2832. if ($use == 'literal') {
  2833. return "<$name>$value</$name>";
  2834. } else {
  2835. return "<$name xsi:type=\"" . $this->getPrefixFromNamespace($this->XMLSchemaVersion) . ":$uqType\">$value</$name>";
  2836. }
  2837. }
  2838. } else {
  2839. $uqType = $type;
  2840. }
  2841. if(!$typeDef = $this->getTypeDef($uqType)){
  2842. $this->setError("$uqType is not a supported type.");
  2843. return false;
  2844. } else {
  2845. //foreach($typeDef as $k => $v) {
  2846. //$this->debug("typedef, $k: $v");
  2847. //}
  2848. }
  2849. $phpType = $typeDef['phpType'];
  2850. $this->debug("serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') );
  2851. // if php type == struct, map value to the <all> element names
  2852. if ($phpType == 'struct') {
  2853. if (isset($typeDef['element']) && $typeDef['element']) {
  2854. $elementName = $uqType;
  2855. // TODO: use elementFormDefault="qualified|unqualified" to determine
  2856. // how to scope the namespace
  2857. $elementNS = " xmlns=\"$ns\"";
  2858. } else {
  2859. $elementName = $name;
  2860. $elementNS = '';
  2861. }
  2862. if ($use == 'literal') {
  2863. $xml = "<$elementName$elementNS>";
  2864. } else {
  2865. $xml = "<$elementName$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">";
  2866. }
  2867. if (isset($this->complexTypes[$uqType]['elements']) && is_array($this->complexTypes[$uqType]['elements'])) {
  2868. //if (is_array($this->complexTypes[$uqType]['elements'])) {
  2869. // toggle whether all elements are present - ideally should validate against schema
  2870. if(count($this->complexTypes[$uqType]['elements']) != count($value)){
  2871. $optionals = true;
  2872. }
  2873. foreach($this->complexTypes[$uqType]['elements'] as $eName => $attrs) {
  2874. // if user took advantage of a minOccurs=0, then only serialize named parameters
  2875. if(isset($optionals) && !isset($value[$eName])){
  2876. // do nothing
  2877. } else {
  2878. // get value
  2879. if (isset($value[$eName])) {
  2880. $v = $value[$eName];
  2881. } elseif (is_array($value)) {
  2882. $v = array_shift($value);
  2883. }
  2884. // serialize schema-defined type
  2885. if (!isset($attrs['type'])) {
  2886. $xml .= $this->serializeType($eName, $attrs['name'], $v, $use);
  2887. // serialize generic type
  2888. } else {
  2889. $this->debug("calling serialize_val() for $eName, $v, " . $this->getLocalPart($attrs['type']), false, $use);
  2890. $xml .= $this->serialize_val($v, $eName, $this->getLocalPart($attrs['type']), null, $this->getNamespaceFromPrefix($this->getPrefix($attrs['type'])), false, $use);
  2891. }
  2892. }
  2893. }
  2894. }
  2895. $xml .= "</$elementName>";
  2896. } elseif ($phpType == 'array') {
  2897. $rows = sizeof($value);
  2898. if (isset($typeDef['multidimensional'])) {
  2899. $nv = array();
  2900. foreach($value as $v) {
  2901. $cols = ',' . sizeof($v);
  2902. $nv = array_merge($nv, $v);
  2903. }
  2904. $value = $nv;
  2905. } else {
  2906. $cols = '';
  2907. }
  2908. if (is_array($value) && sizeof($value) >= 1) {
  2909. $contents = '';
  2910. foreach($value as $k => $v) {
  2911. $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]");
  2912. //if (strpos($typeDef['arrayType'], ':') ) {
  2913. if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) {
  2914. $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use);
  2915. } else {
  2916. $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use);
  2917. }
  2918. }
  2919. $this->debug('contents: '.$this->varDump($contents));
  2920. } else {
  2921. $contents = null;
  2922. }
  2923. if ($use == 'literal') {
  2924. $xml = "<$name>"
  2925. .$contents
  2926. ."</$name>";
  2927. } else {
  2928. $xml = "<$name xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '.
  2929. $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/')
  2930. .':arrayType="'
  2931. .$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType']))
  2932. .":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">"
  2933. .$contents
  2934. ."</$name>";
  2935. }
  2936. }
  2937. $this->debug('returning: '.$this->varDump($xml));
  2938. return $xml;
  2939. }
  2940. /**
  2941. * register a service with the server
  2942. *
  2943. * @param string $methodname
  2944. * @param string $in assoc array of input values: key = param name, value = param type
  2945. * @param string $out assoc array of output values: key = param name, value = param type
  2946. * @param string $namespace
  2947. * @param string $soapaction
  2948. * @param string $style (rpc|literal)
  2949. * @access public
  2950. */
  2951. function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '')
  2952. {
  2953. if ($style == 'rpc' && $use == 'encoded') {
  2954. $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
  2955. } else {
  2956. $encodingStyle = '';
  2957. }
  2958. // get binding
  2959. $this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] =
  2960. array(
  2961. 'name' => $name,
  2962. 'binding' => $this->serviceName . 'Binding',
  2963. 'endpoint' => $this->endpoint,
  2964. 'soapAction' => $soapaction,
  2965. 'style' => $style,
  2966. 'input' => array(
  2967. 'use' => $use,
  2968. 'namespace' => $namespace,
  2969. 'encodingStyle' => $encodingStyle,
  2970. 'message' => $name . 'Request',
  2971. 'parts' => $in),
  2972. 'output' => array(
  2973. 'use' => $use,
  2974. 'namespace' => $namespace,
  2975. 'encodingStyle' => $encodingStyle,
  2976. 'message' => $name . 'Response',
  2977. 'parts' => $out),
  2978. 'namespace' => $namespace,
  2979. 'transport' => 'http://schemas.xmlsoap.org/soap/http',
  2980. 'documentation' => $documentation);
  2981. // add portTypes
  2982. // add messages
  2983. if($in)
  2984. {
  2985. foreach($in as $pName => $pType)
  2986. {
  2987. if(strpos($pType,':')) {
  2988. $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
  2989. }
  2990. $this->messages[$name.'Request'][$pName] = $pType;
  2991. }
  2992. }
  2993. if($out)
  2994. {
  2995. foreach($out as $pName => $pType)
  2996. {
  2997. if(strpos($pType,':')) {
  2998. $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType);
  2999. }
  3000. $this->messages[$name.'Response'][$pName] = $pType;
  3001. }
  3002. }
  3003. return true;
  3004. }
  3005. }
  3006. ?><?php
  3007. /**
  3008. *
  3009. * soap_parser class parses SOAP XML messages into native PHP values
  3010. *
  3011. * @author Dietrich Ayala <dietrich@ganx4.com>
  3012. * @version v 0.6.3
  3013. * @access public
  3014. */
  3015. class soap_parser extends nusoap_base {
  3016. var $xml = '';
  3017. var $xml_encoding = '';
  3018. var $method = '';
  3019. var $root_struct = '';
  3020. var $root_struct_name = '';
  3021. var $root_header = '';
  3022. var $document = '';
  3023. // determines where in the message we are (envelope,header,body,method)
  3024. var $status = '';
  3025. var $position = 0;
  3026. var $depth = 0;
  3027. var $default_namespace = '';
  3028. var $namespaces = array();
  3029. var $message = array();
  3030. var $parent = '';
  3031. var $fault = false;
  3032. var $fault_code = '';
  3033. var $fault_str = '';
  3034. var $fault_detail = '';
  3035. var $depth_array = array();
  3036. var $debug_flag = true;
  3037. var $soapresponse = NULL;
  3038. var $responseHeaders = '';
  3039. var $body_position = 0;
  3040. // for multiref parsing:
  3041. // array of id => pos
  3042. var $ids = array();
  3043. // array of id => hrefs => pos
  3044. var $multirefs = array();
  3045. /**
  3046. * constructor
  3047. *
  3048. * @param string $xml SOAP message
  3049. * @param string $encoding character encoding scheme of message
  3050. * @access public
  3051. */
  3052. function soap_parser($xml,$encoding='UTF-8',$method=''){
  3053. $this->xml = $xml;
  3054. $this->xml_encoding = $encoding;
  3055. $this->method = $method;
  3056. // Check whether content has been read.
  3057. if(!empty($xml)){
  3058. $this->debug('Entering soap_parser()');
  3059. // Create an XML parser.
  3060. $this->parser = xml_parser_create($this->xml_encoding);
  3061. // Set the options for parsing the XML data.
  3062. //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
  3063. xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
  3064. // Set the object for the parser.
  3065. xml_set_object($this->parser, $this);
  3066. // Set the element handlers for the parser.
  3067. xml_set_element_handler($this->parser, 'start_element','end_element');
  3068. xml_set_character_data_handler($this->parser,'character_data');
  3069. // Parse the XML file.
  3070. if(!xml_parse($this->parser,$xml,true)){
  3071. // Display an error message.
  3072. $err = sprintf('XML error on line %d: %s',
  3073. xml_get_current_line_number($this->parser),
  3074. xml_error_string(xml_get_error_code($this->parser)));
  3075. $this->debug('parse error: '.$err);
  3076. $this->errstr = $err;
  3077. } else {
  3078. $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name);
  3079. // get final value
  3080. $this->soapresponse = $this->message[$this->root_struct]['result'];
  3081. // get header value
  3082. if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){
  3083. $this->responseHeaders = $this->message[$this->root_header]['result'];
  3084. }
  3085. // resolve hrefs/ids
  3086. if(sizeof($this->multirefs) > 0){
  3087. foreach($this->multirefs as $id => $hrefs){
  3088. $this->debug('resolving multirefs for id: '.$id);
  3089. $idVal = $this->buildVal($this->ids[$id]);
  3090. foreach($hrefs as $refPos => $ref){
  3091. $this->debug('resolving href at pos '.$refPos);
  3092. $this->multirefs[$id][$refPos] = $idVal;
  3093. }
  3094. }
  3095. }
  3096. }
  3097. xml_parser_free($this->parser);
  3098. } else {
  3099. $this->debug('xml was empty, didn\'t parse!');
  3100. $this->errstr = 'xml was empty, didn\'t parse!';
  3101. }
  3102. }
  3103. /**
  3104. * start-element handler
  3105. *
  3106. * @param string $parser XML parser object
  3107. * @param string $name element name
  3108. * @param string $attrs associative array of attributes
  3109. * @access private
  3110. */
  3111. function start_element($parser, $name, $attrs) {
  3112. // position in a total number of elements, starting from 0
  3113. // update class level pos
  3114. $pos = $this->position++;
  3115. // and set mine
  3116. $this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>'');
  3117. // depth = how many levels removed from root?
  3118. // set mine as current global depth and increment global depth value
  3119. $this->message[$pos]['depth'] = $this->depth++;
  3120. // else add self as child to whoever the current parent is
  3121. if($pos != 0){
  3122. $this->message[$this->parent]['children'] .= '|'.$pos;
  3123. }
  3124. // set my parent
  3125. $this->message[$pos]['parent'] = $this->parent;
  3126. // set self as current parent
  3127. $this->parent = $pos;
  3128. // set self as current value for this depth
  3129. $this->depth_array[$this->depth] = $pos;
  3130. // get element prefix
  3131. if(strpos($name,':')){
  3132. // get ns prefix
  3133. $prefix = substr($name,0,strpos($name,':'));
  3134. // get unqualified name
  3135. $name = substr(strstr($name,':'),1);
  3136. }
  3137. // set status
  3138. if($name == 'Envelope'){
  3139. $this->status = 'envelope';
  3140. } elseif($name == 'Header'){
  3141. $this->root_header = $pos;
  3142. $this->status = 'header';
  3143. } elseif($name == 'Body'){
  3144. $this->status = 'body';
  3145. $this->body_position = $pos;
  3146. // set method
  3147. } elseif($this->status == 'body' && $pos == ($this->body_position+1)){
  3148. $this->status = 'method';
  3149. $this->root_struct_name = $name;
  3150. $this->root_struct = $pos;
  3151. $this->message[$pos]['type'] = 'struct';
  3152. $this->debug("found root struct $this->root_struct_name, pos $this->root_struct");
  3153. }
  3154. // set my status
  3155. $this->message[$pos]['status'] = $this->status;
  3156. // set name
  3157. $this->message[$pos]['name'] = htmlspecialchars($name);
  3158. // set attrs
  3159. $this->message[$pos]['attrs'] = $attrs;
  3160. // loop through atts, logging ns and type declarations
  3161. $attstr = '';
  3162. foreach($attrs as $key => $value){
  3163. $key_prefix = $this->getPrefix($key);
  3164. $key_localpart = $this->getLocalPart($key);
  3165. // if ns declarations, add to class level array of valid namespaces
  3166. if($key_prefix == 'xmlns'){
  3167. if(ereg('^http://www.w3.org/[0-9]{4}/XMLSchema$',$value)){
  3168. $this->XMLSchemaVersion = $value;
  3169. $this->namespaces['xsd'] = $this->XMLSchemaVersion;
  3170. $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance';
  3171. }
  3172. $this->namespaces[$key_localpart] = $value;
  3173. // set method namespace
  3174. if($name == $this->root_struct_name){
  3175. $this->methodNamespace = $value;
  3176. }
  3177. // if it's a type declaration, set type
  3178. } elseif($key_localpart == 'type'){
  3179. $value_prefix = $this->getPrefix($value);
  3180. $value_localpart = $this->getLocalPart($value);
  3181. $this->message[$pos]['type'] = $value_localpart;
  3182. $this->message[$pos]['typePrefix'] = $value_prefix;
  3183. if(isset($this->namespaces[$value_prefix])){
  3184. $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix];
  3185. } else if(isset($attrs['xmlns:'.$value_prefix])) {
  3186. $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix];
  3187. }
  3188. // should do something here with the namespace of specified type?
  3189. } elseif($key_localpart == 'arrayType'){
  3190. $this->message[$pos]['type'] = 'array';
  3191. /* do arrayType ereg here
  3192. [1] arrayTypeValue ::= atype asize
  3193. [2] atype ::= QName rank*
  3194. [3] rank ::= '[' (',')* ']'
  3195. [4] asize ::= '[' length~ ']'
  3196. [5] length ::= nextDimension* Digit+
  3197. [6] nextDimension ::= Digit+ ','
  3198. */
  3199. $expr = '([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]';
  3200. if(ereg($expr,$value,$regs)){
  3201. $this->message[$pos]['typePrefix'] = $regs[1];
  3202. $this->message[$pos]['arraySize'] = $regs[3];
  3203. $this->message[$pos]['arrayCols'] = $regs[4];
  3204. }
  3205. }
  3206. // log id
  3207. if($key == 'id'){
  3208. $this->ids[$value] = $pos;
  3209. }
  3210. // root
  3211. if($key_localpart == 'root' && $value == 1){
  3212. $this->status = 'method';
  3213. $this->root_struct_name = $name;
  3214. $this->root_struct = $pos;
  3215. $this->debug("found root struct $this->root_struct_name, pos $pos");
  3216. }
  3217. // for doclit
  3218. $attstr .= " $key=\"$value\"";
  3219. }
  3220. // get namespace - must be done after namespace atts are processed
  3221. if(isset($prefix)){
  3222. $this->message[$pos]['namespace'] = $this->namespaces[$prefix];
  3223. $this->default_namespace = $this->namespaces[$prefix];
  3224. } else {
  3225. $this->message[$pos]['namespace'] = $this->default_namespace;
  3226. }
  3227. if($this->status == 'header'){
  3228. $this->responseHeaders .= "<$name$attstr>";
  3229. } elseif($this->root_struct_name != ''){
  3230. $this->document .= "<$name$attstr>";
  3231. }
  3232. }
  3233. /**
  3234. * end-element handler
  3235. *
  3236. * @param string $parser XML parser object
  3237. * @param string $name element name
  3238. * @access private
  3239. */
  3240. function end_element($parser, $name) {
  3241. // position of current element is equal to the last value left in depth_array for my depth
  3242. $pos = $this->depth_array[$this->depth--];
  3243. // get element prefix
  3244. if(strpos($name,':')){
  3245. // get ns prefix
  3246. $prefix = substr($name,0,strpos($name,':'));
  3247. // get unqualified name
  3248. $name = substr(strstr($name,':'),1);
  3249. }
  3250. // build to native type
  3251. if(isset($this->body_position) && $pos > $this->body_position){
  3252. // deal w/ multirefs
  3253. if(isset($this->message[$pos]['attrs']['href'])){
  3254. // get id
  3255. $id = substr($this->message[$pos]['attrs']['href'],1);
  3256. // add placeholder to href array
  3257. $this->multirefs[$id][$pos] = "placeholder";
  3258. // add set a reference to it as the result value
  3259. $this->message[$pos]['result'] =& $this->multirefs[$id][$pos];
  3260. // build complex values
  3261. } elseif($this->message[$pos]['children'] != ""){
  3262. $this->message[$pos]['result'] = $this->buildVal($pos);
  3263. } else {
  3264. $this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']);
  3265. if(is_numeric($this->message[$pos]['cdata']) ){
  3266. if( strpos($this->message[$pos]['cdata'],'.') ){
  3267. $this->message[$pos]['result'] = doubleval($this->message[$pos]['cdata']);
  3268. } else {
  3269. $this->message[$pos]['result'] = intval($this->message[$pos]['cdata']);
  3270. }
  3271. } else {
  3272. $this->message[$pos]['result'] = $this->message[$pos]['cdata'];
  3273. }
  3274. }
  3275. }
  3276. // switch status
  3277. if($pos == $this->root_struct){
  3278. $this->status = 'body';
  3279. } elseif($name == 'Body'){
  3280. $this->status = 'header';
  3281. } elseif($name == 'Header'){
  3282. $this->status = 'envelope';
  3283. } elseif($name == 'Envelope'){
  3284. //
  3285. }
  3286. // set parent back to my parent
  3287. $this->parent = $this->message[$pos]['parent'];
  3288. // for doclit
  3289. if($this->status == 'header'){
  3290. $this->responseHeaders .= "</$name>";
  3291. } elseif($pos >= $this->root_struct){
  3292. $this->document .= "</$name>";
  3293. }
  3294. }
  3295. /**
  3296. * element content handler
  3297. *
  3298. * @param string $parser XML parser object
  3299. * @param string $data element content
  3300. * @access private
  3301. */
  3302. function character_data($parser, $data){
  3303. $pos = $this->depth_array[$this->depth];
  3304. if ($this->xml_encoding=='UTF-8'){
  3305. $data = utf8_decode($data);
  3306. }
  3307. $this->message[$pos]['cdata'] .= $data;
  3308. // for doclit
  3309. if($this->status == 'header'){
  3310. $this->responseHeaders .= $data;
  3311. } else {
  3312. $this->document .= $data;
  3313. }
  3314. }
  3315. /**
  3316. * get the parsed message
  3317. *
  3318. * @return mixed
  3319. * @access public
  3320. */
  3321. function get_response(){
  3322. return $this->soapresponse;
  3323. }
  3324. /**
  3325. * get the parsed headers
  3326. *
  3327. * @return string XML or empty if no headers
  3328. * @access public
  3329. */
  3330. function getHeaders(){
  3331. return $this->responseHeaders;
  3332. }
  3333. /**
  3334. * decodes entities
  3335. *
  3336. * @param string $text string to translate
  3337. * @access private
  3338. */
  3339. function decode_entities($text){
  3340. foreach($this->entities as $entity => $encoded){
  3341. $text = str_replace($encoded,$entity,$text);
  3342. }
  3343. return $text;
  3344. }
  3345. /**
  3346. * builds response structures for compound values (arrays/structs)
  3347. *
  3348. * @param string $pos position in node tree
  3349. * @access private
  3350. */
  3351. function buildVal($pos){
  3352. if(!isset($this->message[$pos]['type'])){
  3353. $this->message[$pos]['type'] = '';
  3354. }
  3355. $this->debug('inside buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']);
  3356. // if there are children...
  3357. if($this->message[$pos]['children'] != ''){
  3358. $children = explode('|',$this->message[$pos]['children']);
  3359. array_shift($children); // knock off empty
  3360. // md array
  3361. if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != ''){
  3362. $r=0; // rowcount
  3363. $c=0; // colcount
  3364. foreach($children as $child_pos){
  3365. $this->debug("got an MD array element: $r, $c");
  3366. $params[$r][] = $this->message[$child_pos]['result'];
  3367. $c++;
  3368. if($c == $this->message[$pos]['arrayCols']){
  3369. $c = 0;
  3370. $r++;
  3371. }
  3372. }
  3373. // array
  3374. } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array'){
  3375. $this->debug('adding array '.$this->message[$pos]['name']);
  3376. foreach($children as $child_pos){
  3377. $params[] = &$this->message[$child_pos]['result'];
  3378. }
  3379. // apache Map type: java hashtable
  3380. } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){
  3381. foreach($children as $child_pos){
  3382. $kv = explode("|",$this->message[$child_pos]['children']);
  3383. $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result'];
  3384. }
  3385. // generic compound type
  3386. //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') {
  3387. } else {
  3388. // is array or struct? better way to do this probably
  3389. foreach($children as $child_pos){
  3390. if(isset($keys) && isset($keys[$this->message[$child_pos]['name']])){
  3391. $struct = 1;
  3392. break;
  3393. }
  3394. $keys[$this->message[$child_pos]['name']] = 1;
  3395. }
  3396. //
  3397. foreach($children as $child_pos){
  3398. if(isset($struct)){
  3399. $params[] = &$this->message[$child_pos]['result'];
  3400. } else {
  3401. $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result'];
  3402. }
  3403. }
  3404. }
  3405. return is_array($params) ? $params : array();
  3406. } else {
  3407. $this->debug('no children');
  3408. if(strpos($this->message[$pos]['cdata'],'&')){
  3409. return strtr($this->message[$pos]['cdata'],array_flip($this->entities));
  3410. } else {
  3411. return $this->message[$pos]['cdata'];
  3412. }
  3413. }
  3414. }
  3415. }
  3416. ?><?php
  3417. /**
  3418. *
  3419. * nusoapclient higher level class for easy usage.
  3420. *
  3421. * usage:
  3422. *
  3423. * // instantiate client with server info
  3424. * $nusoapclient = new nusoapclient( string path [ ,boolean wsdl] );
  3425. *
  3426. * // call method, get results
  3427. * echo $nusoapclient->call( string methodname [ ,array parameters] );
  3428. *
  3429. * // bye bye client
  3430. * unset($nusoapclient);
  3431. *
  3432. * @author Dietrich Ayala <dietrich@ganx4.com>
  3433. * @version v 0.6.3
  3434. * @access public
  3435. */
  3436. class nusoapclient extends nusoap_base {
  3437. var $username = '';
  3438. var $password = '';
  3439. var $requestHeaders = false;
  3440. var $responseHeaders;
  3441. var $endpoint;
  3442. var $error_str = false;
  3443. var $proxyhost = '';
  3444. var $proxyport = '';
  3445. var $xml_encoding = '';
  3446. var $http_encoding = false;
  3447. var $timeout = 0;
  3448. var $endpointType = '';
  3449. var $persistentConnection = false;
  3450. var $defaultRpcParams = false;
  3451. /**
  3452. * fault related variables
  3453. *
  3454. * @var fault
  3455. * @var faultcode
  3456. * @var faultstring
  3457. * @var faultdetail
  3458. * @access public
  3459. */
  3460. var $fault, $faultcode, $faultstring, $faultdetail;
  3461. /**
  3462. * constructor
  3463. *
  3464. * @param string $endpoint SOAP server or WSDL URL
  3465. * @param bool $wsdl optional, set to true if using WSDL
  3466. * @param int $portName optional portName in WSDL document
  3467. * @access public
  3468. */
  3469. function nusoapclient($endpoint,$wsdl = false){
  3470. $this->endpoint = $endpoint;
  3471. // make values
  3472. if ($wsdl){
  3473. $this->endpointType = 'wsdl';
  3474. $this->wsdlFile = $this->endpoint;
  3475. // instantiate wsdl object and parse wsdl file
  3476. $this->debug('instantiating wsdl class with doc: '.$endpoint);
  3477. $this->wsdl =& new wsdl($this->wsdlFile,$this->proxyhost,$this->proxyport);
  3478. $this->debug("wsdl debug: \n".$this->wsdl->debug_str);
  3479. $this->wsdl->debug_str = '';
  3480. // catch errors
  3481. if($errstr = $this->wsdl->getError()){
  3482. $this->debug('got wsdl error: '.$errstr);
  3483. $this->setError('wsdl error: '.$errstr);
  3484. } elseif($this->operations = $this->wsdl->getOperations()){
  3485. $this->debug( 'got '.count($this->operations).' operations from wsdl '.$this->wsdlFile);
  3486. } else {
  3487. $this->debug( 'getOperations returned false');
  3488. $this->setError('no operations defined in the WSDL document!');
  3489. }
  3490. }
  3491. }
  3492. /**
  3493. * calls method, returns PHP native type
  3494. *
  3495. * @param string $method SOAP server URL or path
  3496. * @param array $params array of parameters, can be associative or not
  3497. * @param string $namespace optional method namespace
  3498. * @param string $soapAction optional SOAPAction value
  3499. * @param boolean $headers optional array of soapval objects for headers
  3500. * @param boolean $rpcParams optional treat params as RPC for use="literal"
  3501. * This can be used on a per-call basis to overrider defaultRpcParams.
  3502. * @return mixed
  3503. * @access public
  3504. */
  3505. function call($operation,$params=array(),$namespace='',$soapAction='',$headers=false,$rpcParams=null){
  3506. $this->operation = $operation;
  3507. $this->fault = false;
  3508. $this->error_str = '';
  3509. $this->request = '';
  3510. $this->response = '';
  3511. $this->faultstring = '';
  3512. $this->faultcode = '';
  3513. $this->opData = array();
  3514. $this->debug("call: $operation, $params, $namespace, $soapAction, $headers, $rpcParams");
  3515. $this->debug("endpointType: $this->endpointType");
  3516. // if wsdl, get operation data and process parameters
  3517. if($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)){
  3518. $this->opData = $opData;
  3519. foreach($opData as $key => $value){
  3520. $this->debug("$key -> $value");
  3521. }
  3522. $soapAction = $opData['soapAction'];
  3523. $this->endpoint = $opData['endpoint'];
  3524. $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : 'http://testuri.org';
  3525. $style = $opData['style'];
  3526. // add ns to ns array
  3527. if($namespace != '' && !isset($this->wsdl->namespaces[$namespace])){
  3528. $this->wsdl->namespaces['nu'] = $namespace;
  3529. }
  3530. // serialize payload
  3531. if($opData['input']['use'] == 'literal') {
  3532. if (is_null($rpcParams)) {
  3533. $rpcParams = $this->defaultRpcParams;
  3534. }
  3535. if ($rpcParams) {
  3536. $this->debug("serializing literal params for operation $operation");
  3537. $payload = $this->wsdl->serializeRPCParameters($operation,'input',$params);
  3538. $defaultNamespace = $this->wsdl->wsdl_info['targetNamespace'];
  3539. } else {
  3540. $this->debug("serializing literal document for operation $operation");
  3541. $payload = is_array($params) ? array_shift($params) : $params;
  3542. }
  3543. } else {
  3544. $this->debug("serializing encoded params for operation $operation");
  3545. $payload = "<".$this->wsdl->getPrefixFromNamespace($namespace).":$operation>".
  3546. $this->wsdl->serializeRPCParameters($operation,'input',$params).
  3547. '</'.$this->wsdl->getPrefixFromNamespace($namespace).":$operation>";
  3548. }
  3549. $this->debug('payload size: '.strlen($payload));
  3550. // serialize envelope
  3551. $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,$this->wsdl->usedNamespaces,$style);
  3552. $this->debug("wsdl debug: \n".$this->wsdl->debug_str);
  3553. $this->wsdl->debug_str = '';
  3554. } elseif($this->endpointType == 'wsdl') {
  3555. $this->setError( 'operation '.$operation.' not present.');
  3556. $this->debug("operation '$operation' not present.");
  3557. $this->debug("wsdl debug: \n".$this->wsdl->debug_str);
  3558. return false;
  3559. // no wsdl
  3560. } else {
  3561. // make message
  3562. if(!isset($style)){
  3563. $style = 'rpc';
  3564. }
  3565. if($namespace == ''){
  3566. $namespace = 'http://testuri.org';
  3567. $this->wsdl->namespaces['ns1'] = $namespace;
  3568. }
  3569. // serialize envelope
  3570. $payload = '';
  3571. foreach($params as $k => $v){
  3572. $payload .= $this->serialize_val($v,$k);
  3573. }
  3574. $payload = "<ns1:$operation xmlns:ns1=\"$namespace\">\n".$payload."</ns1:$operation>\n";
  3575. $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders);
  3576. }
  3577. $this->debug("endpoint: $this->endpoint, soapAction: $soapAction, namespace: $namespace");
  3578. // send
  3579. $this->debug('sending msg (len: '.strlen($soapmsg).") w/ soapaction '$soapAction'...");
  3580. $return = $this->send($soapmsg,$soapAction,$this->timeout);
  3581. if($errstr = $this->getError()){
  3582. $this->debug('Error: '.$errstr);
  3583. return false;
  3584. } else {
  3585. $this->return = $return;
  3586. $this->debug('sent message successfully and got a(n) '.gettype($return).' back');
  3587. // fault?
  3588. if(is_array($return) && isset($return['faultcode'])){
  3589. $this->debug('got fault');
  3590. $this->setError($return['faultcode'].': '.$return['faultstring']);
  3591. $this->fault = true;
  3592. foreach($return as $k => $v){
  3593. $this->$k = $v;
  3594. $this->debug("$k = $v<br>");
  3595. }
  3596. return $return;
  3597. } else {
  3598. // array of return values
  3599. if(is_array($return)){
  3600. // multiple 'out' parameters
  3601. if(sizeof($return) > 1){
  3602. return $return;
  3603. }
  3604. // single 'out' parameter
  3605. return array_shift($return);
  3606. // nothing returned (ie, echoVoid)
  3607. } else {
  3608. return "";
  3609. }
  3610. }
  3611. }
  3612. }
  3613. /**
  3614. * get available data pertaining to an operation
  3615. *
  3616. * @param string $operation operation name
  3617. * @return array array of data pertaining to the operation
  3618. * @access public
  3619. */
  3620. function getOperationData($operation){
  3621. if(isset($this->operations[$operation])){
  3622. return $this->operations[$operation];
  3623. }
  3624. $this->debug("No data for operation: $operation");
  3625. }
  3626. /**
  3627. * send the SOAP message
  3628. *
  3629. * Note: if the operation has multiple return values
  3630. * the return value of this method will be an array
  3631. * of those values.
  3632. *
  3633. * @param string $msg a SOAPx4 soapmsg object
  3634. * @param string $soapaction SOAPAction value
  3635. * @param integer $timeout set timeout in seconds
  3636. * @return mixed native PHP types.
  3637. * @access private
  3638. */
  3639. function send($msg, $soapaction = '', $timeout=0) {
  3640. // detect transport
  3641. switch(true){
  3642. // http(s)
  3643. case ereg('^http',$this->endpoint):
  3644. $this->debug('transporting via HTTP');
  3645. if($this->persistentConnection && is_object($this->persistentConnection)){
  3646. $http =& $this->persistentConnection;
  3647. } else {
  3648. $http = new soap_transport_http($this->endpoint);
  3649. // pass encoding into transport layer, so appropriate http headers are sent
  3650. $http->soap_defencoding = $this->soap_defencoding;
  3651. }
  3652. $http->setSOAPAction($soapaction);
  3653. if($this->proxyhost && $this->proxyport){
  3654. $http->setProxy($this->proxyhost,$this->proxyport);
  3655. }
  3656. if($this->username != '' && $this->password != '') {
  3657. $http->setCredentials($this->username,$this->password);
  3658. }
  3659. if($this->http_encoding != ''){
  3660. $http->setEncoding($this->http_encoding);
  3661. }
  3662. $this->debug('sending message, length: '.strlen($msg));
  3663. if(ereg('^http:',$this->endpoint)){
  3664. //if(strpos($this->endpoint,'http:')){
  3665. $response = $http->send($msg,$timeout);
  3666. } elseif(ereg('^https',$this->endpoint)){
  3667. //} elseif(strpos($this->endpoint,'https:')){
  3668. //if(phpversion() == '4.3.0-dev'){
  3669. //$response = $http->send($msg,$timeout);
  3670. //$this->request = $http->outgoing_payload;
  3671. //$this->response = $http->incoming_payload;
  3672. //} else
  3673. if (extension_loaded('curl')) {
  3674. $response = $http->sendHTTPS($msg,$timeout);
  3675. } else {
  3676. $this->setError('CURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS');
  3677. }
  3678. } else {
  3679. $this->setError('no http/s in endpoint url');
  3680. }
  3681. $this->request = $http->outgoing_payload;
  3682. $this->response = $http->incoming_payload;
  3683. $this->debug("transport debug data...\n".$http->debug_str);
  3684. // save transport object if using persistent connections
  3685. if($this->persistentConnection && !is_object($this->persistentConnection)){
  3686. $this->persistentConnection = $http;
  3687. }
  3688. if($err = $http->getError()){
  3689. $this->setError('HTTP Error: '.$err);
  3690. return false;
  3691. } elseif($this->getError()){
  3692. return false;
  3693. } else {
  3694. $this->debug('got response, length: '.strlen($response));
  3695. return $this->parseResponse($response);
  3696. }
  3697. break;
  3698. default:
  3699. $this->setError('no transport found, or selected transport is not yet supported!');
  3700. return false;
  3701. break;
  3702. }
  3703. }
  3704. /**
  3705. * processes SOAP message returned from server
  3706. *
  3707. * @param string unprocessed response data from server
  3708. * @return mixed value of the message, decoded into a PHP type
  3709. * @access private
  3710. */
  3711. function parseResponse($data) {
  3712. $this->debug('Entering parseResponse(), about to create soap_parser instance');
  3713. $parser = new soap_parser($data,$this->xml_encoding,$this->operation);
  3714. // if parse errors
  3715. if($errstr = $parser->getError()){
  3716. $this->setError( $errstr);
  3717. // destroy the parser object
  3718. unset($parser);
  3719. return false;
  3720. } else {
  3721. // get SOAP headers
  3722. $this->responseHeaders = $parser->getHeaders();
  3723. // get decoded message
  3724. $return = $parser->get_response();
  3725. // add parser debug data to our debug
  3726. $this->debug($parser->debug_str);
  3727. // add document for doclit support
  3728. $this->document = $parser->document;
  3729. // destroy the parser object
  3730. unset($parser);
  3731. // return decode message
  3732. return $return;
  3733. }
  3734. }
  3735. /**
  3736. * set the SOAP headers
  3737. *
  3738. * @param $headers string XML
  3739. * @access public
  3740. */
  3741. function setHeaders($headers){
  3742. $this->requestHeaders = $headers;
  3743. }
  3744. /**
  3745. * get the response headers
  3746. *
  3747. * @return mixed object SOAPx4 soapval object or empty if no headers
  3748. * @access public
  3749. */
  3750. function getHeaders(){
  3751. if($this->responseHeaders != '') {
  3752. return $this->responseHeaders;
  3753. }
  3754. }
  3755. /**
  3756. * set proxy info here
  3757. *
  3758. * @param string $proxyhost
  3759. * @param string $proxyport
  3760. * @access public
  3761. */
  3762. function setHTTPProxy($proxyhost, $proxyport) {
  3763. $this->proxyhost = $proxyhost;
  3764. $this->proxyport = $proxyport;
  3765. }
  3766. /**
  3767. * if authenticating, set user credentials here
  3768. *
  3769. * @param string $username
  3770. * @param string $password
  3771. * @access public
  3772. */
  3773. function setCredentials($username, $password) {
  3774. $this->username = $username;
  3775. $this->password = $password;
  3776. }
  3777. /**
  3778. * use HTTP encoding
  3779. *
  3780. * @param string $enc
  3781. * @access public
  3782. */
  3783. function setHTTPEncoding($enc='gzip, deflate'){
  3784. $this->http_encoding = $enc;
  3785. }
  3786. /**
  3787. * use HTTP persistent connections if possible
  3788. *
  3789. * @access public
  3790. */
  3791. function useHTTPPersistentConnection(){
  3792. $this->persistentConnection = true;
  3793. }
  3794. /**
  3795. * gets the default RPC parameter setting.
  3796. * If true, default is that call params are like RPC even for document style.
  3797. * Each call() can override this value.
  3798. *
  3799. * @access public
  3800. */
  3801. function getDefaultRpcParams() {
  3802. return $this->defaultRpcParams;
  3803. }
  3804. /**
  3805. * sets the default RPC parameter setting.
  3806. * If true, default is that call params are like RPC even for document style
  3807. * Each call() can override this value.
  3808. *
  3809. * @param boolean $rpcParams
  3810. * @access public
  3811. */
  3812. function setDefaultRpcParams($rpcParams) {
  3813. $this->defaultRpcParams = $rpcParams;
  3814. }
  3815. /**
  3816. * dynamically creates proxy class, allowing user to directly call methods from wsdl
  3817. *
  3818. * @return object soap_proxy object
  3819. * @access public
  3820. */
  3821. function getProxy(){
  3822. $evalStr = '';
  3823. foreach($this->operations as $operation => $opData){
  3824. if($operation != ''){
  3825. // create param string
  3826. $paramStr = '';
  3827. if(sizeof($opData['input']['parts']) > 0){
  3828. foreach($opData['input']['parts'] as $name => $type){
  3829. $paramStr .= "\$$name,";
  3830. }
  3831. $paramStr = substr($paramStr,0,strlen($paramStr)-1);
  3832. }
  3833. $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace'];
  3834. $evalStr .= "function $operation ($paramStr){
  3835. // load params into array
  3836. \$params = array($paramStr);
  3837. return \$this->call('$operation',\$params,'".$opData['namespace']."','".$opData['soapAction']."');
  3838. }";
  3839. unset($paramStr);
  3840. }
  3841. }
  3842. $r = rand();
  3843. $evalStr = 'class soap_proxy_'.$r.' extends nusoapclient {
  3844. '.$evalStr.'
  3845. }';
  3846. //print "proxy class:<pre>$evalStr</pre>";
  3847. // eval the class
  3848. eval($evalStr);
  3849. // instantiate proxy object
  3850. eval("\$proxy = new soap_proxy_$r('');");
  3851. // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice
  3852. $proxy->endpointType = 'wsdl';
  3853. $proxy->wsdlFile = $this->wsdlFile;
  3854. $proxy->wsdl = $this->wsdl;
  3855. $proxy->operations = $this->operations;
  3856. $proxy->defaultRpcParams = $this->defaultRpcParams;
  3857. return $proxy;
  3858. }
  3859. }
  3860. // Local Variables:
  3861. // mode: php
  3862. // tab-width: 8
  3863. // c-basic-offset: 4
  3864. // c-hanging-comment-ender-p: nil
  3865. // indent-tabs-mode: nil
  3866. // End:
  3867. ?>