class.nusoap_base.php 30 KB

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