class.nusoap_base.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993
  1. <?php
  2. /*
  3. NuSOAP - Web Services Toolkit for PHP
  4. Copyright (c) 2002 NuSphere Corporation
  5. This library is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU Lesser General Public
  7. License as published by the Free Software Foundation; either
  8. version 2.1 of the License, or (at your option) any later version.
  9. This library is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. Lesser General Public License for more details.
  13. You should have received a copy of the GNU Lesser General Public
  14. along with this program. If not, see <https://www.gnu.org/licenses/>.
  15. The NuSOAP project home is:
  16. http://sourceforge.net/projects/nusoap/
  17. The primary support for NuSOAP is the Help forum on the project home page.
  18. If you have any questions or comments, please email:
  19. Dietrich Ayala
  20. dietrich@ganx4.com
  21. http://dietrich.ganx4.com/nusoap
  22. NuSphere Corporation
  23. http://www.nusphere.com
  24. */
  25. /*
  26. * Some of the standards implmented in whole or part by NuSOAP:
  27. *
  28. * SOAP 1.1 (http://www.w3.org/TR/2000/NOTE-SOAP-20000508/)
  29. * WSDL 1.1 (http://www.w3.org/TR/2001/NOTE-wsdl-20010315)
  30. * SOAP Messages With Attachments (http://www.w3.org/TR/SOAP-attachments)
  31. * XML 1.0 (http://www.w3.org/TR/2006/REC-xml-20060816/)
  32. * Namespaces in XML 1.0 (http://www.w3.org/TR/2006/REC-xml-names-20060816/)
  33. * XML Schema 1.0 (http://www.w3.org/TR/xmlschema-0/)
  34. * RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
  35. * RFC 2068 Hypertext Transfer Protocol -- HTTP/1.1
  36. * RFC 2617 HTTP Authentication: Basic and Digest Access Authentication
  37. */
  38. /* load classes
  39. // necessary classes
  40. require_once('class.soapclient.php');
  41. require_once('class.soap_val.php');
  42. require_once('class.soap_parser.php');
  43. require_once('class.soap_fault.php');
  44. // transport classes
  45. require_once('class.soap_transport_http.php');
  46. // optional add-on classes
  47. require_once('class.xmlschema.php');
  48. require_once('class.wsdl.php');
  49. // server class
  50. require_once('class.soap_server.php');*/
  51. // class variable emulation
  52. // cf. http://www.webkreator.com/php/techniques/php-static-class-variables.html
  53. $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = 9;
  54. /**
  55. *
  56. * nusoap_base
  57. *
  58. * @author Dietrich Ayala <dietrich@ganx4.com>
  59. * @author Scott Nichol <snichol@users.sourceforge.net>
  60. * @access public
  61. */
  62. class nusoap_base {
  63. /**
  64. * Identification for HTTP headers.
  65. *
  66. * @var string
  67. * @access private
  68. */
  69. var $title = 'NuSOAP';
  70. /**
  71. * Version for HTTP headers.
  72. *
  73. * @var string
  74. * @access private
  75. */
  76. var $version = '0.9.5';
  77. /**
  78. * CVS revision for HTTP headers.
  79. *
  80. * @var string
  81. * @access private
  82. */
  83. var $revision = '1.7';
  84. /**
  85. * Current error string (manipulated by getError/setError)
  86. *
  87. * @var string
  88. * @access private
  89. */
  90. var $error_str = '';
  91. /**
  92. * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment)
  93. *
  94. * @var string
  95. * @access private
  96. */
  97. var $debug_str = '';
  98. /**
  99. * toggles automatic encoding of special characters as entities
  100. * (should always be true, I think)
  101. *
  102. * @var boolean
  103. * @access private
  104. */
  105. var $charencoding = true;
  106. /**
  107. * the debug level for this instance
  108. *
  109. * @var integer
  110. * @access private
  111. */
  112. var $debugLevel;
  113. /**
  114. * set schema version
  115. *
  116. * @var string
  117. * @access public
  118. */
  119. var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
  120. /**
  121. * charset encoding for outgoing messages
  122. *
  123. * @var string
  124. * @access public
  125. */
  126. var $soap_defencoding = 'ISO-8859-1';
  127. //var $soap_defencoding = 'UTF-8';
  128. /**
  129. * namespaces in an array of prefix => uri
  130. *
  131. * this is "seeded" by a set of constants, but it may be altered by code
  132. *
  133. * @var array
  134. * @access public
  135. */
  136. var $namespaces = array(
  137. 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/',
  138. 'xsd' => 'http://www.w3.org/2001/XMLSchema',
  139. 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
  140. 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/'
  141. );
  142. /**
  143. * namespaces used in the current context, e.g. during serialization
  144. *
  145. * @var array
  146. * @access private
  147. */
  148. var $usedNamespaces = array();
  149. /**
  150. * XML Schema types in an array of uri => (array of xml type => php type)
  151. * is this legacy yet?
  152. * no, this is used by the nusoap_xmlschema class to verify type => namespace mappings.
  153. * @var array
  154. * @access public
  155. */
  156. var $typemap = array(
  157. 'http://www.w3.org/2001/XMLSchema' => array(
  158. 'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double',
  159. 'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'',
  160. 'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string',
  161. // abstract "any" types
  162. 'anyType'=>'string','anySimpleType'=>'string',
  163. // derived datatypes
  164. 'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'',
  165. 'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer',
  166. 'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer',
  167. 'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''),
  168. 'http://www.w3.org/2000/10/XMLSchema' => array(
  169. 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
  170. 'float'=>'double','dateTime'=>'string',
  171. 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
  172. 'http://www.w3.org/1999/XMLSchema' => array(
  173. 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double',
  174. 'float'=>'double','dateTime'=>'string',
  175. 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'),
  176. 'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'),
  177. 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'),
  178. 'http://xml.apache.org/xml-soap' => array('Map')
  179. );
  180. /**
  181. * XML entities to convert
  182. *
  183. * @var array
  184. * @access public
  185. * @deprecated
  186. * @see expandEntities
  187. */
  188. var $xmlEntities = array('quot' => '"','amp' => '&',
  189. 'lt' => '<','gt' => '>','apos' => "'");
  190. /**
  191. * constructor
  192. *
  193. * @access public
  194. */
  195. function __construct() {
  196. $this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'];
  197. }
  198. /**
  199. * gets the global debug level, which applies to future instances
  200. *
  201. * @return integer Debug level 0-9, where 0 turns off
  202. * @access public
  203. */
  204. function getGlobalDebugLevel() {
  205. return $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'];
  206. }
  207. /**
  208. * sets the global debug level, which applies to future instances
  209. *
  210. * @param int $level Debug level 0-9, where 0 turns off
  211. * @access public
  212. */
  213. function setGlobalDebugLevel($level) {
  214. $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = $level;
  215. }
  216. /**
  217. * gets the debug level for this instance
  218. *
  219. * @return int Debug level 0-9, where 0 turns off
  220. * @access public
  221. */
  222. function getDebugLevel() {
  223. return $this->debugLevel;
  224. }
  225. /**
  226. * sets the debug level for this instance
  227. *
  228. * @param int $level Debug level 0-9, where 0 turns off
  229. * @access public
  230. */
  231. function setDebugLevel($level) {
  232. $this->debugLevel = $level;
  233. }
  234. /**
  235. * adds debug data to the instance debug string with formatting
  236. *
  237. * @param string $string debug data
  238. * @access private
  239. */
  240. function debug($string){
  241. if ($this->debugLevel > 0) {
  242. $this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n");
  243. }
  244. }
  245. /**
  246. * adds debug data to the instance debug string without formatting
  247. *
  248. * @param string $string debug data
  249. * @access public
  250. */
  251. function appendDebug($string){
  252. if ($this->debugLevel > 0) {
  253. // it would be nice to use a memory stream here to use
  254. // memory more efficiently
  255. $this->debug_str .= $string;
  256. }
  257. }
  258. /**
  259. * clears the current debug data for this instance
  260. *
  261. * @access public
  262. */
  263. function clearDebug() {
  264. // it would be nice to use a memory stream here to use
  265. // memory more efficiently
  266. $this->debug_str = '';
  267. }
  268. /**
  269. * gets the current debug data for this instance
  270. *
  271. * @return debug data
  272. * @access public
  273. */
  274. function &getDebug() {
  275. // it would be nice to use a memory stream here to use
  276. // memory more efficiently
  277. return $this->debug_str;
  278. }
  279. /**
  280. * gets the current debug data for this instance as an XML comment
  281. * this may change the contents of the debug data
  282. *
  283. * @return debug data as an XML comment
  284. * @access public
  285. */
  286. function &getDebugAsXMLComment() {
  287. // it would be nice to use a memory stream here to use
  288. // memory more efficiently
  289. while (strpos($this->debug_str, '--')) {
  290. $this->debug_str = str_replace('--', '- -', $this->debug_str);
  291. }
  292. $ret = "<!--\n" . $this->debug_str . "\n-->";
  293. return $ret;
  294. }
  295. /**
  296. * expands entities, e.g. changes '<' to '&lt;'.
  297. *
  298. * @param string $val The string in which to expand entities.
  299. * @access private
  300. */
  301. function expandEntities($val) {
  302. if ($this->charencoding) {
  303. $val = str_replace('&', '&amp;', $val);
  304. $val = str_replace("'", '&apos;', $val);
  305. $val = str_replace('"', '&quot;', $val);
  306. $val = str_replace('<', '&lt;', $val);
  307. $val = str_replace('>', '&gt;', $val);
  308. }
  309. return $val;
  310. }
  311. /**
  312. * returns error string if present
  313. *
  314. * @return mixed error string or false
  315. * @access public
  316. */
  317. function getError(){
  318. if($this->error_str != ''){
  319. return $this->error_str;
  320. }
  321. return false;
  322. }
  323. /**
  324. * sets error string
  325. *
  326. * @return boolean $string error string
  327. * @access private
  328. */
  329. function setError($str){
  330. $this->error_str = $str;
  331. }
  332. /**
  333. * detect if array is a simple array or a struct (associative array)
  334. *
  335. * @param mixed $val The PHP array
  336. * @return string (arraySimple|arrayStruct)
  337. * @access private
  338. */
  339. function isArraySimpleOrStruct($val) {
  340. $keyList = array_keys($val);
  341. foreach ($keyList as $keyListValue) {
  342. if (!is_int($keyListValue)) {
  343. return 'arrayStruct';
  344. }
  345. }
  346. return 'arraySimple';
  347. }
  348. /**
  349. * serializes PHP values in accordance w/ section 5. Type information is
  350. * not serialized if $use == 'literal'.
  351. *
  352. * @param mixed $val The value to serialize
  353. * @param string $name The name (local part) of the XML element
  354. * @param string $type The XML schema type (local part) for the element
  355. * @param string $name_ns The namespace for the name of the XML element
  356. * @param string $type_ns The namespace for the type of the element
  357. * @param array $attributes The attributes to serialize as name=>value pairs
  358. * @param string $use The WSDL "use" (encoded|literal)
  359. * @param boolean $soapval Whether this is called from soapval.
  360. * @return string The serialized element, possibly with child elements
  361. * @access public
  362. */
  363. function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded',$soapval=false) {
  364. $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use, soapval=$soapval");
  365. $this->appendDebug('value=' . $this->varDump($val));
  366. $this->appendDebug('attributes=' . $this->varDump($attributes));
  367. if (is_object($val) && get_class($val) == 'soapval' && (! $soapval)) {
  368. $this->debug("serialize_val: serialize soapval");
  369. $xml = $val->serialize($use);
  370. $this->appendDebug($val->getDebug());
  371. $val->clearDebug();
  372. $this->debug("serialize_val of soapval returning $xml");
  373. return $xml;
  374. }
  375. // force valid name if necessary
  376. if (is_numeric($name)) {
  377. $name = '__numeric_' . $name;
  378. } elseif (! $name) {
  379. $name = 'noname';
  380. }
  381. // if name has ns, add ns prefix to name
  382. $xmlns = '';
  383. if($name_ns){
  384. $prefix = 'nu'.rand(1000,9999);
  385. $name = $prefix.':'.$name;
  386. $xmlns .= " xmlns:$prefix=\"$name_ns\"";
  387. }
  388. // if type is prefixed, create type prefix
  389. if($type_ns != '' && $type_ns == $this->namespaces['xsd']){
  390. // need to fix this. shouldn't default to xsd if no ns specified
  391. // w/o checking against typemap
  392. $type_prefix = 'xsd';
  393. } elseif($type_ns){
  394. $type_prefix = 'ns'.rand(1000,9999);
  395. $xmlns .= " xmlns:$type_prefix=\"$type_ns\"";
  396. }
  397. // serialize attributes if present
  398. $atts = '';
  399. if($attributes){
  400. foreach($attributes as $k => $v){
  401. $atts .= " $k=\"".$this->expandEntities($v).'"';
  402. }
  403. }
  404. // serialize null value
  405. if (is_null($val)) {
  406. $this->debug("serialize_val: serialize null");
  407. if ($use == 'literal') {
  408. // TODO: depends on minOccurs
  409. $xml = "<$name$xmlns$atts/>";
  410. $this->debug("serialize_val returning $xml");
  411. return $xml;
  412. } else {
  413. if (isset($type) && isset($type_prefix)) {
  414. $type_str = " xsi:type=\"$type_prefix:$type\"";
  415. } else {
  416. $type_str = '';
  417. }
  418. $xml = "<$name$xmlns$type_str$atts xsi:nil=\"true\"/>";
  419. $this->debug("serialize_val returning $xml");
  420. return $xml;
  421. }
  422. }
  423. // serialize if an xsd built-in primitive type
  424. if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){
  425. $this->debug("serialize_val: serialize xsd built-in primitive type");
  426. if (is_bool($val)) {
  427. if ($type == 'boolean') {
  428. $val = $val ? 'true' : 'false';
  429. } elseif (! $val) {
  430. $val = 0;
  431. }
  432. } else if (is_string($val)) {
  433. $val = $this->expandEntities($val);
  434. }
  435. if ($use == 'literal') {
  436. $xml = "<$name$xmlns$atts>$val</$name>";
  437. $this->debug("serialize_val returning $xml");
  438. return $xml;
  439. } else {
  440. $xml = "<$name$xmlns xsi:type=\"xsd:$type\"$atts>$val</$name>";
  441. $this->debug("serialize_val returning $xml");
  442. return $xml;
  443. }
  444. }
  445. // detect type and serialize
  446. $xml = '';
  447. switch(true) {
  448. case (is_bool($val) || $type == 'boolean'):
  449. $this->debug("serialize_val: serialize boolean");
  450. if ($type == 'boolean') {
  451. $val = $val ? 'true' : 'false';
  452. } elseif (! $val) {
  453. $val = 0;
  454. }
  455. if ($use == 'literal') {
  456. $xml .= "<$name$xmlns$atts>$val</$name>";
  457. } else {
  458. $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val</$name>";
  459. }
  460. break;
  461. case (is_int($val) || is_long($val) || $type == 'int'):
  462. $this->debug("serialize_val: serialize int");
  463. if ($use == 'literal') {
  464. $xml .= "<$name$xmlns$atts>$val</$name>";
  465. } else {
  466. $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val</$name>";
  467. }
  468. break;
  469. case (is_float($val)|| is_double($val) || $type == 'float'):
  470. $this->debug("serialize_val: serialize float");
  471. if ($use == 'literal') {
  472. $xml .= "<$name$xmlns$atts>$val</$name>";
  473. } else {
  474. $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val</$name>";
  475. }
  476. break;
  477. case (is_string($val) || $type == 'string'):
  478. $this->debug("serialize_val: serialize string");
  479. $val = $this->expandEntities($val);
  480. if ($use == 'literal') {
  481. $xml .= "<$name$xmlns$atts>$val</$name>";
  482. } else {
  483. $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val</$name>";
  484. }
  485. break;
  486. case is_object($val):
  487. $this->debug("serialize_val: serialize object");
  488. if (get_class($val) == 'soapval') {
  489. $this->debug("serialize_val: serialize soapval object");
  490. $pXml = $val->serialize($use);
  491. $this->appendDebug($val->getDebug());
  492. $val->clearDebug();
  493. } else {
  494. if (! $name) {
  495. $name = get_class($val);
  496. $this->debug("In serialize_val, used class name $name as element name");
  497. } else {
  498. $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val));
  499. }
  500. foreach(get_object_vars($val) as $k => $v){
  501. $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use);
  502. }
  503. }
  504. if(isset($type) && isset($type_prefix)){
  505. $type_str = " xsi:type=\"$type_prefix:$type\"";
  506. } else {
  507. $type_str = '';
  508. }
  509. if ($use == 'literal') {
  510. $xml .= "<$name$xmlns$atts>$pXml</$name>";
  511. } else {
  512. $xml .= "<$name$xmlns$type_str$atts>$pXml</$name>";
  513. }
  514. break;
  515. break;
  516. case (is_array($val) || $type):
  517. // detect if struct or array
  518. $valueType = $this->isArraySimpleOrStruct($val);
  519. if($valueType=='arraySimple' || preg_match('/^ArrayOf/',$type)){
  520. $this->debug("serialize_val: serialize array");
  521. $i = 0;
  522. if(is_array($val) && count($val)> 0){
  523. foreach($val as $v){
  524. if(is_object($v) && get_class($v) == 'soapval'){
  525. $tt_ns = $v->type_ns;
  526. $tt = $v->type;
  527. } elseif (is_array($v)) {
  528. $tt = $this->isArraySimpleOrStruct($v);
  529. } else {
  530. $tt = gettype($v);
  531. }
  532. $array_types[$tt] = 1;
  533. // TODO: for literal, the name should be $name
  534. $xml .= $this->serialize_val($v,'item',false,false,false,false,$use);
  535. ++$i;
  536. }
  537. if(count($array_types) > 1){
  538. $array_typename = 'xsd:anyType';
  539. } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) {
  540. if ($tt == 'integer') {
  541. $tt = 'int';
  542. }
  543. $array_typename = 'xsd:'.$tt;
  544. } elseif(isset($tt) && $tt == 'arraySimple'){
  545. $array_typename = 'SOAP-ENC:Array';
  546. } elseif(isset($tt) && $tt == 'arrayStruct'){
  547. $array_typename = 'unnamed_struct_use_soapval';
  548. } else {
  549. // if type is prefixed, create type prefix
  550. if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){
  551. $array_typename = 'xsd:' . $tt;
  552. } elseif ($tt_ns) {
  553. $tt_prefix = 'ns' . rand(1000, 9999);
  554. $array_typename = "$tt_prefix:$tt";
  555. $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\"";
  556. } else {
  557. $array_typename = $tt;
  558. }
  559. }
  560. $array_type = $i;
  561. if ($use == 'literal') {
  562. $type_str = '';
  563. } else if (isset($type) && isset($type_prefix)) {
  564. $type_str = " xsi:type=\"$type_prefix:$type\"";
  565. } else {
  566. $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\"";
  567. }
  568. // empty array
  569. } else {
  570. if ($use == 'literal') {
  571. $type_str = '';
  572. } else if (isset($type) && isset($type_prefix)) {
  573. $type_str = " xsi:type=\"$type_prefix:$type\"";
  574. } else {
  575. $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\"";
  576. }
  577. }
  578. // TODO: for array in literal, there is no wrapper here
  579. $xml = "<$name$xmlns$type_str$atts>".$xml."</$name>";
  580. } else {
  581. // got a struct
  582. $this->debug("serialize_val: serialize struct");
  583. if(isset($type) && isset($type_prefix)){
  584. $type_str = " xsi:type=\"$type_prefix:$type\"";
  585. } else {
  586. $type_str = '';
  587. }
  588. if ($use == 'literal') {
  589. $xml .= "<$name$xmlns$atts>";
  590. } else {
  591. $xml .= "<$name$xmlns$type_str$atts>";
  592. }
  593. foreach($val as $k => $v){
  594. // Apache Map
  595. if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') {
  596. $xml .= '<item>';
  597. $xml .= $this->serialize_val($k,'key',false,false,false,false,$use);
  598. $xml .= $this->serialize_val($v,'value',false,false,false,false,$use);
  599. $xml .= '</item>';
  600. } else {
  601. $xml .= $this->serialize_val($v,$k,false,false,false,false,$use);
  602. }
  603. }
  604. $xml .= "</$name>";
  605. }
  606. break;
  607. default:
  608. $this->debug("serialize_val: serialize unknown");
  609. $xml .= 'not detected, got '.gettype($val).' for '.$val;
  610. break;
  611. }
  612. $this->debug("serialize_val returning $xml");
  613. return $xml;
  614. }
  615. /**
  616. * serializes a message
  617. *
  618. * @param string $body the XML of the SOAP body
  619. * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array
  620. * @param array $namespaces optional the namespaces used in generating the body and headers
  621. * @param string $style optional (rpc|document)
  622. * @param string $use optional (encoded|literal)
  623. * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
  624. * @return string the message
  625. * @access public
  626. */
  627. function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded',$encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'){
  628. // TODO: add an option to automatically run utf8_encode on $body and $headers
  629. // if $this->soap_defencoding is UTF-8. Not doing this automatically allows
  630. // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1
  631. $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle");
  632. $this->debug("headers:");
  633. $this->appendDebug($this->varDump($headers));
  634. $this->debug("namespaces:");
  635. $this->appendDebug($this->varDump($namespaces));
  636. // serialize namespaces
  637. $ns_string = '';
  638. foreach(array_merge($this->namespaces,$namespaces) as $k => $v){
  639. $ns_string .= " xmlns:$k=\"$v\"";
  640. }
  641. if($encodingStyle) {
  642. $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string";
  643. }
  644. // serialize headers
  645. if($headers){
  646. if (is_array($headers)) {
  647. $xml = '';
  648. foreach ($headers as $k => $v) {
  649. if (is_object($v) && get_class($v) == 'soapval') {
  650. $xml .= $this->serialize_val($v, false, false, false, false, false, $use);
  651. } else {
  652. $xml .= $this->serialize_val($v, $k, false, false, false, false, $use);
  653. }
  654. }
  655. $headers = $xml;
  656. $this->debug("In serializeEnvelope, serialized array of headers to $headers");
  657. }
  658. $headers = "<SOAP-ENV:Header>".$headers."</SOAP-ENV:Header>";
  659. }
  660. // serialize envelope
  661. return
  662. '<?xml version="1.0" encoding="'.$this->soap_defencoding .'"?'.">".
  663. '<SOAP-ENV:Envelope'.$ns_string.">".
  664. $headers.
  665. "<SOAP-ENV:Body>".
  666. $body.
  667. "</SOAP-ENV:Body>".
  668. "</SOAP-ENV:Envelope>";
  669. }
  670. /**
  671. * formats a string to be inserted into an HTML stream
  672. *
  673. * @param string $str The string to format
  674. * @return string The formatted string
  675. * @access public
  676. * @deprecated
  677. */
  678. function formatDump($str){
  679. $str = htmlspecialchars($str);
  680. return nl2br($str);
  681. }
  682. /**
  683. * contracts (changes namespace to prefix) a qualified name
  684. *
  685. * @param string $qname qname
  686. * @return string contracted qname
  687. * @access private
  688. */
  689. function contractQname($qname){
  690. // get element namespace
  691. //$this->xdebug("Contract $qname");
  692. if (strrpos($qname, ':')) {
  693. // get unqualified name
  694. $name = substr($qname, strrpos($qname, ':') + 1);
  695. // get ns
  696. $ns = substr($qname, 0, strrpos($qname, ':'));
  697. $p = $this->getPrefixFromNamespace($ns);
  698. if ($p) {
  699. return $p . ':' . $name;
  700. }
  701. return $qname;
  702. } else {
  703. return $qname;
  704. }
  705. }
  706. /**
  707. * expands (changes prefix to namespace) a qualified name
  708. *
  709. * @param string $qname qname
  710. * @return string expanded qname
  711. * @access private
  712. */
  713. function expandQname($qname){
  714. // get element prefix
  715. if(strpos($qname,':') && !preg_match('/^http:\/\//',$qname)){
  716. // get unqualified name
  717. $name = substr(strstr($qname,':'),1);
  718. // get ns prefix
  719. $prefix = substr($qname,0,strpos($qname,':'));
  720. if(isset($this->namespaces[$prefix])){
  721. return $this->namespaces[$prefix].':'.$name;
  722. } else {
  723. return $qname;
  724. }
  725. } else {
  726. return $qname;
  727. }
  728. }
  729. /**
  730. * returns the local part of a prefixed string
  731. * returns the original string, if not prefixed
  732. *
  733. * @param string $str The prefixed string
  734. * @return string The local part
  735. * @access public
  736. */
  737. function getLocalPart($str){
  738. if($sstr = strrchr($str,':')){
  739. // get unqualified name
  740. return substr( $sstr, 1 );
  741. } else {
  742. return $str;
  743. }
  744. }
  745. /**
  746. * returns the prefix part of a prefixed string
  747. * returns false, if not prefixed
  748. *
  749. * @param string $str The prefixed string
  750. * @return mixed The prefix or false if there is no prefix
  751. * @access public
  752. */
  753. function getPrefix($str){
  754. if($pos = strrpos($str,':')){
  755. // get prefix
  756. return substr($str,0,$pos);
  757. }
  758. return false;
  759. }
  760. /**
  761. * pass it a prefix, it returns a namespace
  762. *
  763. * @param string $prefix The prefix
  764. * @return mixed The namespace, false if no namespace has the specified prefix
  765. * @access public
  766. */
  767. function getNamespaceFromPrefix($prefix){
  768. if (isset($this->namespaces[$prefix])) {
  769. return $this->namespaces[$prefix];
  770. }
  771. //$this->setError("No namespace registered for prefix '$prefix'");
  772. return false;
  773. }
  774. /**
  775. * returns the prefix for a given namespace (or prefix)
  776. * or false if no prefixes registered for the given namespace
  777. *
  778. * @param string $ns The namespace
  779. * @return mixed The prefix, false if the namespace has no prefixes
  780. * @access public
  781. */
  782. function getPrefixFromNamespace($ns) {
  783. foreach ($this->namespaces as $p => $n) {
  784. if ($ns == $n || $ns == $p) {
  785. $this->usedNamespaces[$p] = $n;
  786. return $p;
  787. }
  788. }
  789. return false;
  790. }
  791. /**
  792. * returns the time in ODBC canonical form with microseconds
  793. *
  794. * @return string The time in ODBC canonical form with microseconds
  795. * @access public
  796. */
  797. function getmicrotime() {
  798. if (function_exists('gettimeofday')) {
  799. $tod = gettimeofday();
  800. $sec = $tod['sec'];
  801. $usec = $tod['usec'];
  802. } else {
  803. $sec = time();
  804. $usec = 0;
  805. }
  806. return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec);
  807. }
  808. /**
  809. * Returns a string with the output of var_dump
  810. *
  811. * @param mixed $data The variable to var_dump
  812. * @return string The output of var_dump
  813. * @access public
  814. */
  815. function varDump($data) {
  816. ob_start();
  817. var_dump($data);
  818. $ret_val = ob_get_contents();
  819. ob_end_clean();
  820. return $ret_val;
  821. }
  822. /**
  823. * represents the object as a string
  824. *
  825. * @return string
  826. * @access public
  827. */
  828. function __toString() {
  829. return $this->varDump($this);
  830. }
  831. }
  832. // XML Schema Datatype Helper Functions
  833. //xsd:dateTime helpers
  834. /**
  835. * convert unix timestamp to ISO 8601 compliant date string
  836. *
  837. * @param int $timestamp Unix time stamp
  838. * @param boolean $utc Whether the time stamp is UTC or local
  839. * @return mixed ISO 8601 date string or false
  840. * @access public
  841. */
  842. function timestamp_to_iso8601($timestamp,$utc=true){
  843. $datestr = date('Y-m-d\TH:i:sO',$timestamp);
  844. $pos = strrpos($datestr, "+");
  845. if ($pos === FALSE) {
  846. $pos = strrpos($datestr, "-");
  847. }
  848. if ($pos !== FALSE) {
  849. if (strlen($datestr) == $pos + 5) {
  850. $datestr = substr($datestr, 0, $pos + 3) . ':' . substr($datestr, -2);
  851. }
  852. }
  853. if($utc){
  854. $pattern = '/'.
  855. '([0-9]{4})-'. // centuries & years CCYY-
  856. '([0-9]{2})-'. // months MM-
  857. '([0-9]{2})'. // days DD
  858. 'T'. // separator T
  859. '([0-9]{2}):'. // hours hh:
  860. '([0-9]{2}):'. // minutes mm:
  861. '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss...
  862. '(Z|[+\-][0-9]{2}:?[0-9]{2})?'. // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
  863. '/';
  864. if(preg_match($pattern,$datestr,$regs)){
  865. return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]);
  866. }
  867. return false;
  868. } else {
  869. return $datestr;
  870. }
  871. }
  872. /**
  873. * convert ISO 8601 compliant date string to unix timestamp
  874. *
  875. * @param string $datestr ISO 8601 compliant date string
  876. * @return mixed Unix timestamp (int) or false
  877. * @access public
  878. */
  879. function iso8601_to_timestamp($datestr){
  880. $pattern = '/'.
  881. '([0-9]{4})-'. // centuries & years CCYY-
  882. '([0-9]{2})-'. // months MM-
  883. '([0-9]{2})'. // days DD
  884. 'T'. // separator T
  885. '([0-9]{2}):'. // hours hh:
  886. '([0-9]{2}):'. // minutes mm:
  887. '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss...
  888. '(Z|[+\-][0-9]{2}:?[0-9]{2})?'. // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's
  889. '/';
  890. if(preg_match($pattern,$datestr,$regs)){
  891. // not utc
  892. if($regs[8] != 'Z'){
  893. $op = substr($regs[8],0,1);
  894. $h = substr($regs[8],1,2);
  895. $m = substr($regs[8],strlen($regs[8])-2,2);
  896. if($op == '-'){
  897. $regs[4] = $regs[4] + $h;
  898. $regs[5] = $regs[5] + $m;
  899. } elseif($op == '+'){
  900. $regs[4] = $regs[4] - $h;
  901. $regs[5] = $regs[5] - $m;
  902. }
  903. }
  904. return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
  905. // return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z");
  906. } else {
  907. return false;
  908. }
  909. }
  910. /**
  911. * sleeps some number of microseconds
  912. *
  913. * @param string $usec the number of microseconds to sleep
  914. * @access public
  915. * @deprecated
  916. */
  917. function usleepWindows($usec)
  918. {
  919. $start = gettimeofday();
  920. do
  921. {
  922. $stop = gettimeofday();
  923. $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
  924. + $stop['usec'] - $start['usec'];
  925. }
  926. while ($timePassed < $usec);
  927. }
  928. ?>