Base.php 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078
  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available at through the world-wide-web at |
  11. // | http://www.php.net/license/2_02.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | license@php.net so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more |
  17. // | Authors: Dietrich Ayala <dietrich@ganx4.com> Original Author |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id: Base.php,v 1.40.2.4 2004/01/17 14:22:58 arnaud Exp $
  21. //
  22. /*
  23. SOAP_OBJECT_STRUCT makes pear::soap use objects for soap structures
  24. rather than arrays. This has been done to provide a closer match to php-soap.
  25. If the old behaviour is needed, set to false. The old behaviour is depricated.
  26. */
  27. $GLOBALS['SOAP_OBJECT_STRUCT'] = TRUE;
  28. /*
  29. SOAP_RAW_CONVERT makes pear::soap attempt to determine what SOAP type
  30. a php string COULD be. This may result in slightly better interoperability when
  31. you are not using WSDL, and are being lazy and not using SOAP_Value to define
  32. types for your values.
  33. */
  34. $GLOBALS['SOAP_RAW_CONVERT'] = FALSE;
  35. require_once 'PEAR.php';
  36. #require_once 'SOAP/Fault.php';
  37. require_once 'SOAP/Type/dateTime.php';
  38. require_once 'SOAP/Type/hexBinary.php';
  39. // optional features
  40. $GLOBALS['SOAP_options'] = array();
  41. @include_once 'Mail/mimePart.php';
  42. @include_once 'Mail/mimeDecode.php';
  43. if (class_exists('Mail_mimePart')) {
  44. $GLOBALS['SOAP_options']['Mime'] = 1;
  45. define('MAIL_MIMEPART_CRLF',"\r\n");
  46. }
  47. @include_once 'Net/DIME.php';
  48. if (class_exists('Net_DIME_Message')) {
  49. $GLOBALS['SOAP_options']['DIME'] = 1;
  50. }
  51. /**
  52. * Enable debugging informations?
  53. *
  54. * @const SOAP_DEBUG
  55. */
  56. $GLOBALS['SOAP_DEBUG']=false;
  57. if (!function_exists('version_compare') ||
  58. version_compare(phpversion(), '4.1', '<')) {
  59. die("requires PHP 4.1 or higher\n");
  60. }
  61. if (version_compare(phpversion(), '4.1', '>=') &&
  62. version_compare(phpversion(), '4.2', '<')) {
  63. define('FLOAT', 'double');
  64. } else {
  65. define('FLOAT', 'float');
  66. }
  67. # for float support
  68. # is there a way to calculate INF for the platform?
  69. define('INF', 1.8e307);
  70. define('NAN', 0.0);
  71. define('SOAP_LIBRARY_VERSION', '0.8.0RC2');
  72. define('SOAP_LIBRARY_NAME', 'PEAR-SOAP 0.8.0RC2-devel');
  73. // set schema version
  74. define('SOAP_XML_SCHEMA_VERSION', 'http://www.w3.org/2001/XMLSchema');
  75. define('SOAP_XML_SCHEMA_INSTANCE', 'http://www.w3.org/2001/XMLSchema-instance');
  76. define('SOAP_XML_SCHEMA_1999', 'http://www.w3.org/1999/XMLSchema');
  77. define('SOAP_SCHEMA', 'http://schemas.xmlsoap.org/wsdl/soap/');
  78. define('SOAP_SCHEMA_ENCODING', 'http://schemas.xmlsoap.org/soap/encoding/');
  79. define('SOAP_ENVELOP', 'http://schemas.xmlsoap.org/soap/envelope/');
  80. define('SCHEMA_DISCO', 'http://schemas.xmlsoap.org/disco/');
  81. define('SCHEMA_DISCO_SCL', 'http://schemas.xmlsoap.org/disco/scl/');
  82. define('SCHEMA_SOAP', 'http://schemas.xmlsoap.org/wsdl/soap/');
  83. define('SCHEMA_SOAP_HTTP', 'http://schemas.xmlsoap.org/soap/http');
  84. define('SCHEMA_WSDL_HTTP', 'http://schemas.xmlsoap.org/wsdl/http/');
  85. define('SCHEMA_MIME', 'http://schemas.xmlsoap.org/wsdl/mime/');
  86. define('SCHEMA_WSDL', 'http://schemas.xmlsoap.org/wsdl/');
  87. define('SCHEMA_DIME', 'http://schemas.xmlsoap.org/ws/2002/04/dime/wsdl/');
  88. define('SCHEMA_CONTENT', 'http://schemas.xmlsoap.org/ws/2002/04/content-type/');
  89. define('SCHEMA_REF', 'http://schemas.xmlsoap.org/ws/2002/04/reference/');
  90. define('SOAP_DEFAULT_ENCODING', 'UTF-8');
  91. if (!function_exists('is_a'))
  92. {
  93. function is_a(&$object, $class_name)
  94. {
  95. if (strtolower(get_class($object)) == $class_name) return TRUE;
  96. else return is_subclass_of($object, $class_name);
  97. }
  98. }
  99. if (!class_exists('stdClass')) {
  100. /* PHP5 doesn't define this? */
  101. class stdClass {
  102. function __constructor() {}
  103. };
  104. }
  105. class SOAP_Base_Object extends PEAR
  106. {
  107. /**
  108. * Store debugging information in $debug_data?
  109. *
  110. * @var boolean if true debugging informations will be store in $debug_data
  111. * @see $debug_data, SOAP_Base
  112. */
  113. var $_debug_flag = false;
  114. /**
  115. * String containing debugging informations if $debug_flag is set to true
  116. *
  117. * @var string debugging informations - mostyl error messages
  118. * @see $debug_flag, SOAP_Base
  119. * @access public
  120. */
  121. var $_debug_data = '';
  122. # supported encodings, limited by XML extension
  123. var $_encodings = array('ISO-8859-1','US-ASCII','UTF-8');
  124. /**
  125. * Fault code
  126. *
  127. * @var string
  128. */
  129. var $_myfaultcode = '';
  130. /**
  131. * Recent PEAR error object
  132. *
  133. * @var object PEAR Error
  134. */
  135. var $fault = NULL;
  136. /**
  137. * Constructor
  138. *
  139. * @param string error code
  140. * @see $debug_data, _debug()
  141. */
  142. function SOAP_Base_Object($faultcode = 'Client')
  143. {
  144. $this->_myfaultcode = $faultcode;
  145. $this->_debug_flag = $GLOBALS['SOAP_DEBUG'];
  146. parent::PEAR('SOAP_Fault');
  147. }
  148. /**
  149. * Raise a soap error
  150. *
  151. * Please referr to the SOAP definition for an impression of what a certain parameter
  152. * stands for.
  153. *
  154. * Use $debug_flag to store errors to the member variable $debug_data
  155. *
  156. * @param string error message
  157. * @param string detailed error message.
  158. * @param string actor
  159. * @param mixed
  160. * @param mixed
  161. * @param mixed
  162. * @param boolean
  163. * @see $debug_flag, $debug_data
  164. */
  165. function &_raiseSoapFault($str, $detail = '', $actorURI = '', $code = null, $mode = null, $options = null, $skipmsg = false)
  166. {
  167. # pass through previous faults
  168. $is_instance = isset($this);
  169. if (is_object($str)) {
  170. $fault =& $str;
  171. } else {
  172. if (!$code) $code = $is_instance?$this->_myfaultcode:'Client';
  173. $fault =& new SOAP_Fault($str,
  174. $code,
  175. $actorURI,
  176. $detail,
  177. $mode,
  178. $options);
  179. }
  180. if ($is_instance) $this->fault =& $fault;
  181. return $fault;
  182. }
  183. function __isfault()
  184. {
  185. return $this->fault != NULL;
  186. }
  187. function &__getfault()
  188. {
  189. return $this->fault;
  190. }
  191. /**
  192. * maintains a string of debug data
  193. *
  194. * @param debugging message - sometimes an error message
  195. */
  196. function _debug($string)
  197. {
  198. if ($this->_debug_flag) {
  199. $this->_debug_data .= get_class($this) . ': ' . preg_replace("/>/", ">\r\n", $string) . "\n";
  200. }
  201. }
  202. }
  203. /**
  204. * SOAP_Base
  205. * Common base class of all Soap lclasses
  206. *
  207. * @access public
  208. * @version $Id: Base.php,v 1.40.2.4 2004/01/17 14:22:58 arnaud Exp $
  209. * @package SOAP::Client
  210. * @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
  211. */
  212. class SOAP_Base extends SOAP_Base_Object
  213. {
  214. var $_XMLSchema = array('http://www.w3.org/2001/XMLSchema', 'http://www.w3.org/1999/XMLSchema');
  215. var $_XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
  216. // load types into typemap array
  217. var $_typemap = array(
  218. 'http://www.w3.org/2001/XMLSchema' => array(
  219. 'string' => 'string',
  220. 'boolean' => 'boolean',
  221. 'float' => FLOAT,
  222. 'double' => FLOAT,
  223. 'decimal' => FLOAT,
  224. 'duration' => 'integer',
  225. 'dateTime' => 'string',
  226. 'time' => 'string',
  227. 'date' => 'string',
  228. 'gYearMonth' => 'integer',
  229. 'gYear' => 'integer',
  230. 'gMonthDay' => 'integer',
  231. 'gDay' => 'integer',
  232. 'gMonth' => 'integer',
  233. 'hexBinary' => 'string',
  234. 'base64Binary' => 'string',
  235. // derived datatypes
  236. 'normalizedString' => 'string',
  237. 'token' => 'string',
  238. 'language' => 'string',
  239. 'NMTOKEN' => 'string',
  240. 'NMTOKENS' => 'string',
  241. 'Name' => 'string',
  242. 'NCName' => 'string',
  243. 'ID' => 'string',
  244. 'IDREF' => 'string',
  245. 'IDREFS' => 'string',
  246. 'ENTITY' => 'string',
  247. 'ENTITIES' => 'string',
  248. 'integer' => 'integer',
  249. 'nonPositiveInteger' => 'integer',
  250. 'negativeInteger' => 'integer',
  251. 'long' => 'integer',
  252. 'int' => 'integer',
  253. 'short' => 'integer',
  254. 'byte' => 'string',
  255. 'nonNegativeInteger' => 'integer',
  256. 'unsignedLong' => 'integer',
  257. 'unsignedInt' => 'integer',
  258. 'unsignedShort' => 'integer',
  259. 'unsignedByte' => 'integer',
  260. 'positiveInteger' => 'integer',
  261. 'anyType' => 'string',
  262. 'anyURI' => 'string',
  263. 'QName' => 'string'
  264. ),
  265. 'http://www.w3.org/1999/XMLSchema' => array(
  266. 'i4' => 'integer',
  267. 'int' => 'integer',
  268. 'boolean' => 'boolean',
  269. 'string' => 'string',
  270. 'double' => FLOAT,
  271. 'float' => FLOAT,
  272. 'dateTime' => 'string',
  273. 'timeInstant' => 'string',
  274. 'base64Binary' => 'string',
  275. 'base64' => 'string',
  276. 'ur-type' => 'string'
  277. ),
  278. 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64' => 'string','array' => 'array','Array' => 'array', 'Struct'=>'array')
  279. );
  280. // load namespace uris into an array of uri => prefix
  281. var $_namespaces;
  282. var $_ns_count = 0;
  283. var $_xmlEntities = array ( '&' => '&amp;', '<' => '&lt;', '>' => '&gt;', "'" => '&apos;', '"' => '&quot;' );
  284. var $_doconversion = FALSE;
  285. var $__attachments = array();
  286. var $_wsdl = NULL;
  287. /**
  288. * section5
  289. *
  290. * @var boolean defines if we use section 5 encoding, or false if this is literal
  291. */
  292. var $_section5 = TRUE;
  293. // handle type to class mapping
  294. var $_auto_translation = false;
  295. var $_type_translation = array();
  296. /**
  297. * Constructor
  298. *
  299. * @param string error code
  300. * @see $debug_data, _debug()
  301. */
  302. function SOAP_Base($faultcode = 'Client')
  303. {
  304. parent::SOAP_Base_Object($faultcode);
  305. $this->_resetNamespaces();
  306. }
  307. function _resetNamespaces()
  308. {
  309. $this->_namespaces = array(
  310. 'http://schemas.xmlsoap.org/soap/envelope/' => 'SOAP-ENV',
  311. 'http://www.w3.org/2001/XMLSchema' => 'xsd',
  312. 'http://www.w3.org/2001/XMLSchema-instance' => 'xsi',
  313. 'http://schemas.xmlsoap.org/soap/encoding/' => 'SOAP-ENC');
  314. }
  315. /**
  316. * _setSchemaVersion
  317. *
  318. * sets the schema version used in the soap message
  319. *
  320. * @param string (see globals.php)
  321. *
  322. * @access private
  323. */
  324. function _setSchemaVersion($schemaVersion)
  325. {
  326. if (!in_array($schemaVersion, $this->_XMLSchema)) {
  327. return $this->_raiseSoapFault("unsuported XMLSchema $schemaVersion");
  328. }
  329. $this->_XMLSchemaVersion = $schemaVersion;
  330. $tmpNS = array_flip($this->_namespaces);
  331. $tmpNS['xsd'] = $this->_XMLSchemaVersion;
  332. $tmpNS['xsi'] = $this->_XMLSchemaVersion.'-instance';
  333. $this->_namespaces = array_flip($tmpNS);
  334. }
  335. function _getNamespacePrefix($ns)
  336. {
  337. if (array_key_exists($ns,$this->_namespaces)) {
  338. return $this->_namespaces[$ns];
  339. }
  340. $prefix = 'ns'.count($this->_namespaces);
  341. $this->_namespaces[$ns] = $prefix;
  342. return $prefix;
  343. return NULL;
  344. }
  345. function _getNamespaceForPrefix($prefix)
  346. {
  347. $flipped = array_flip($this->_namespaces);
  348. if (array_key_exists($prefix,$flipped)) {
  349. return $flipped[$prefix];
  350. }
  351. return NULL;
  352. }
  353. function _isSoapValue(&$value)
  354. {
  355. return is_object($value) && is_a($value,'soap_value');
  356. }
  357. function _serializeValue(&$value, $name = '', $type = false, $elNamespace = NULL, $typeNamespace=NULL, $options=array(), $attributes = array(), $artype='')
  358. {
  359. $namespaces = array();
  360. $arrayType = $array_depth = $xmlout_value = null;
  361. $typePrefix = $elPrefix = $xmlout_offset = $xmlout_arrayType = $xmlout_type = $xmlns = '';
  362. $ptype = $array_type_ns = '';
  363. if (!$name || is_numeric($name)) {
  364. $name = 'item';
  365. }
  366. if ($this->_wsdl)
  367. list($ptype, $arrayType, $array_type_ns, $array_depth)
  368. = $this->_wsdl->getSchemaType($type, $name, $typeNamespace);
  369. if (!$arrayType) $arrayType = $artype;
  370. if (!$ptype) $ptype = $this->_getType($value);
  371. if (!$type) $type = $ptype;
  372. if (strcasecmp($ptype,'Struct') == 0 || strcasecmp($type,'Struct') == 0) {
  373. // struct
  374. $vars = NULL;
  375. if (is_object($value)) {
  376. $vars = get_object_vars($value);
  377. } else {
  378. $vars = &$value;
  379. }
  380. if (is_array($vars)) {
  381. foreach (array_keys($vars) as $k) {
  382. if ($k[0]=='_') continue; // hide private vars
  383. if (is_object($vars[$k])) {
  384. if (is_a($vars[$k],'soap_value')) {
  385. $xmlout_value .= $vars[$k]->serialize($this);
  386. } else {
  387. // XXX get the members and serialize them instead
  388. // converting to an array is more overhead than we
  389. // should realy do, but php-soap is on it's way.
  390. $xmlout_value .= $this->_serializeValue(get_object_vars($vars[$k]), $k, false, $this->_section5?NULL:$elNamespace);
  391. }
  392. } else {
  393. $xmlout_value .= $this->_serializeValue($vars[$k],$k, false, $this->_section5?NULL:$elNamespace);
  394. }
  395. }
  396. }
  397. } else if (strcasecmp($ptype,'Array')==0 || strcasecmp($type,'Array')==0) {
  398. // array
  399. $typeNamespace = SOAP_SCHEMA_ENCODING;
  400. $orig_type = $type;
  401. $type = 'Array';
  402. $numtypes = 0;
  403. // XXX this will be slow on larger array's. Basicly, it flattens array's to allow us
  404. // to serialize multi-dimensional array's. We only do this if arrayType is set,
  405. // which will typicaly only happen if we are using WSDL
  406. if (isset($options['flatten']) || ($arrayType && (strchr($arrayType,',') || strstr($arrayType,'][')))) {
  407. $numtypes = $this->_multiArrayType($value, $arrayType, $ar_size, $xmlout_value);
  408. }
  409. $array_type = $array_type_prefix = '';
  410. if ($numtypes != 1) {
  411. $arrayTypeQName =& new QName($arrayType);
  412. $arrayType = $arrayTypeQName->name;
  413. $array_types = array();
  414. $array_val = NULL;
  415. // serialize each array element
  416. $ar_size = count($value);
  417. for ($i=0; $i < $ar_size; $i++) {
  418. $array_val =& $value[$i];
  419. if ($this->_isSoapValue($array_val)) {
  420. $array_type = $array_val->type;
  421. $array_types[$array_type] = 1;
  422. $array_type_ns = $array_val->type_namespace;
  423. $xmlout_value .= $array_val->serialize($this);
  424. } else {
  425. $array_type = $this->_getType($array_val);
  426. $array_types[$array_type] = 1;
  427. $xmlout_value .= $this->_serializeValue($array_val,'item', $array_type, $this->_section5?NULL:$elNamespace);
  428. }
  429. }
  430. $xmlout_offset = " SOAP-ENC:offset=\"[0]\"";
  431. if (!$arrayType) {
  432. $numtypes = count($array_types);
  433. if ($numtypes == 1) $arrayType = $array_type;
  434. // using anyType is more interoperable
  435. if ($array_type == 'Struct') {
  436. $array_type = '';
  437. } else if ($array_type == 'Array') {
  438. $arrayType = 'anyType';
  439. $array_type_prefix = 'xsd';
  440. } else
  441. if (!$arrayType) $arrayType = $array_type;
  442. }
  443. }
  444. if (!$arrayType || $numtypes > 1) {
  445. $arrayType = 'xsd:anyType'; // should reference what schema we're using
  446. } else {
  447. if ($array_type_ns) {
  448. $array_type_prefix = $this->_getNamespacePrefix($array_type_ns);
  449. } else if (array_key_exists($arrayType, $this->_typemap[$this->_XMLSchemaVersion])) {
  450. $array_type_prefix = $this->_namespaces[$this->_XMLSchemaVersion];
  451. }
  452. if ($array_type_prefix)
  453. $arrayType = $array_type_prefix.':'.$arrayType;
  454. }
  455. $xmlout_arrayType = " SOAP-ENC:arrayType=\"" . $arrayType;
  456. if ($array_depth != null) {
  457. for ($i = 0; $i < $array_depth; $i++) {
  458. $xmlout_arrayType .= '[]';
  459. }
  460. }
  461. $xmlout_arrayType .= "[$ar_size]\"";
  462. } else if ($this->_isSoapValue($value)) {
  463. $xmlout_value =& $value->serialize($this);
  464. } else if ($type == 'string') {
  465. $xmlout_value = htmlspecialchars($value);
  466. } else if ($type == 'rawstring') {
  467. $xmlout_value =& $value;
  468. } else if ($type == 'boolean') {
  469. $xmlout_value = $value?'true':'false';
  470. } else {
  471. $xmlout_value =& $value;
  472. }
  473. // add namespaces
  474. if ($elNamespace) {
  475. $elPrefix = $this->_getNamespacePrefix($elNamespace);
  476. $xmlout_name = "$elPrefix:$name";
  477. } else {
  478. $xmlout_name = $name;
  479. }
  480. if ($typeNamespace) {
  481. $typePrefix = $this->_getNamespacePrefix($typeNamespace);
  482. $xmlout_type = "$typePrefix:$type";
  483. } else if ($type && array_key_exists($type, $this->_typemap[$this->_XMLSchemaVersion])) {
  484. $typePrefix = $this->_namespaces[$this->_XMLSchemaVersion];
  485. $xmlout_type = "$typePrefix:$type";
  486. }
  487. // handle additional attributes
  488. $xml_attr = '';
  489. if (count($attributes) > 0) {
  490. foreach ($attributes as $k => $v) {
  491. $kqn =& new QName($k);
  492. $vqn =& new QName($v);
  493. $xml_attr .= ' '.$kqn->fqn().'="'.$vqn->fqn().'"';
  494. }
  495. }
  496. // store the attachement for mime encoding
  497. if (isset($options['attachment']))
  498. $this->__attachments[] = $options['attachment'];
  499. if ($this->_section5) {
  500. if ($xmlout_type) $xmlout_type = " xsi:type=\"$xmlout_type\"";
  501. if (is_null($xmlout_value)) {
  502. $xml = "\r\n<$xmlout_name$xmlout_type$xmlns$xmlout_arrayType$xml_attr/>";
  503. } else {
  504. $xml = "\r\n<$xmlout_name$xmlout_type$xmlns$xmlout_arrayType$xmlout_offset$xml_attr>".
  505. $xmlout_value."</$xmlout_name>";
  506. }
  507. } else {
  508. if (is_null($xmlout_value)) {
  509. $xml = "\r\n<$xmlout_name$xmlns$xml_attr/>";
  510. } else {
  511. $xml = "\r\n<$xmlout_name$xmlns$xml_attr>".
  512. $xmlout_value."</$xmlout_name>";
  513. }
  514. }
  515. return $xml;
  516. }
  517. /**
  518. * SOAP::Value::_getType
  519. *
  520. * convert php type to soap type
  521. * @param string value
  522. *
  523. * @return string type - soap type
  524. * @access private
  525. */
  526. function _getType(&$value) {
  527. global $SOAP_OBJECT_STRUCT,$SOAP_RAW_CONVERT;
  528. $type = gettype($value);
  529. switch ($type) {
  530. case 'object':
  531. if (is_a($value,'soap_value')) {
  532. $type = $value->type;
  533. } else {
  534. $type = 'Struct';
  535. }
  536. break;
  537. case 'array':
  538. // XXX hashes always get done as structs by pear::soap
  539. if ($this->_isHash($value)) {
  540. $type = 'Struct';
  541. } else {
  542. $ar_size = count($value);
  543. if ($ar_size > 0 && isset($value[0]) && is_a($value[0],'soap_value')) {
  544. // fixme for non-wsdl structs that are all teh same type
  545. if ($ar_size > 1 &&
  546. $this->_isSoapValue($value[0]) &&
  547. $this->_isSoapValue($value[1]) &&
  548. $value[0]->name != $value[1]->name) {
  549. // this is a struct, not an array
  550. $type = 'Struct';
  551. } else {
  552. $type = 'Array';
  553. }
  554. } else {
  555. $type = 'Array';
  556. }
  557. }
  558. break;
  559. case 'integer':
  560. case 'long':
  561. $type = 'int';
  562. break;
  563. case 'boolean':
  564. #$value = $value?'true':'false';
  565. break;
  566. case 'double':
  567. $type = 'float'; // double is deprecated in 4.2 and later
  568. break;
  569. case 'NULL':
  570. $type = '';
  571. break;
  572. case 'string':
  573. if ($SOAP_RAW_CONVERT) {
  574. if (is_numeric($value)) {
  575. if (strstr($value,'.')) $type = 'float';
  576. else $type = 'int';
  577. } else
  578. if (SOAP_Type_hexBinary::is_hexbin($value)) {
  579. $type = 'hexBinary';
  580. } else
  581. if ($this->_isBase64($value)) {
  582. $type = 'base64Binary';
  583. } else {
  584. $dt =& new SOAP_Type_dateTime($value);
  585. if ($dt->toUnixtime() != -1) {
  586. $type = 'dateTime';
  587. #$value = $dt->toSOAP();
  588. }
  589. }
  590. }
  591. default:
  592. break;
  593. }
  594. return $type;
  595. }
  596. function _multiArrayType(&$value, &$type, &$size, &$xml)
  597. {
  598. $sz = count($value);
  599. if (is_array($value)) {
  600. // seems we have a multi dimensional array, figure it out if we do
  601. $c = count($value);
  602. for ($i=0; $i<$c; $i++) {
  603. $this->_multiArrayType($value[$i], $type, $size, $xml);
  604. }
  605. if ($size) {
  606. $size = $sz.','.$size;
  607. } else {
  608. $size = $sz;
  609. }
  610. return 1;
  611. } else {
  612. if (is_object($value)) {
  613. $type = $value->type;
  614. $xml .= $value->serialize($this);
  615. } else {
  616. $type = $this->_getType($value);
  617. $xml .= $this->_serializeValue($value,'item',$type);
  618. }
  619. }
  620. $size = NULL;
  621. return 1;
  622. }
  623. // support functions
  624. /**
  625. *
  626. * @param string
  627. * @return string
  628. */
  629. function _isBase64(&$value)
  630. {
  631. $l = strlen($value);
  632. if ($l > 0)
  633. return $value[$l-1] == '=' && preg_match("/[A-Za-z=\/\+]+/",$value);
  634. return FALSE;
  635. }
  636. /**
  637. *
  638. * @param mixed
  639. * @return boolean
  640. */
  641. function _isHash(&$a) {
  642. # XXX I realy dislike having to loop through this in php code,
  643. # realy large arrays will be slow. We need a C function to do this.
  644. $names = array();
  645. $it = 0;
  646. foreach ($a as $k => $v) {
  647. # checking the type is faster than regexp.
  648. $t = gettype($k);
  649. if ($t != 'integer') {
  650. return TRUE;
  651. } else if ($this->_isSoapValue($v)) {
  652. $names[$v->name] = 1;
  653. }
  654. // if someone has a large hash they should realy be defining the type
  655. if ($it++ > 10) return FALSE;
  656. }
  657. return count($names)>1;
  658. }
  659. function &_un_htmlentities($string)
  660. {
  661. $trans_tbl = get_html_translation_table (HTML_ENTITIES);
  662. $trans_tbl = array_flip($trans_tbl);
  663. return strtr($string, $trans_tbl);
  664. }
  665. /**
  666. *
  667. * @param mixed
  668. */
  669. function &_decode(&$soapval)
  670. {
  671. global $SOAP_OBJECT_STRUCT;
  672. if (!$this->_isSoapValue($soapval)) {
  673. return $soapval;
  674. } else if (is_array($soapval->value)) {
  675. if ($SOAP_OBJECT_STRUCT && $soapval->type != 'Array') {
  676. $classname = 'stdclass';
  677. if (isset($this->_type_translation[$soapval->tqn->fqn()])) {
  678. // this will force an error in php if the
  679. // class does not exist
  680. $classname = $this->_type_translation[$soapval->tqn->fqn()];
  681. } else if (isset($this->_type_translation[$soapval->type])) {
  682. // this will force an error in php if the
  683. // class does not exist
  684. $classname = $this->_type_translation[$soapval->type];
  685. } else if ($this->_auto_translation) {
  686. if (class_exists($soapval->type)) {
  687. $classname = $soapval->type;
  688. } else if ($this->_wsdl) {
  689. $t = $this->_wsdl->getComplexTypeNameForElement($soapval->name, $soapval->namespace);
  690. if ($t && class_exists($t)) $classname = $t;
  691. }
  692. }
  693. $return =& new $classname;
  694. } else {
  695. $return = array();
  696. }
  697. $counter = 1;
  698. $isstruct = !$SOAP_OBJECT_STRUCT || !is_array($return);
  699. foreach ($soapval->value as $item) {
  700. if (is_object($return)) {
  701. if ($this->_wsdl) {
  702. // get this childs wsdl information
  703. // /$soapval->ns/$soapval->type/$item->ns/$item->name
  704. $child_type = $this->_wsdl->getComplexTypeChildType(
  705. $soapval->namespace,
  706. $soapval->name,
  707. $item->namespace,
  708. $item->name);
  709. if ($child_type) $item->type = $child_type;
  710. }
  711. if (!$isstruct || $item->type == 'Array') {
  712. if (isset($return->{$item->name}) &&
  713. is_object($return->{$item->name})) {
  714. $return->{$item->name} =& $this->_decode($item);
  715. } else if (isset($return->{$item->name}) &&
  716. is_array($return->{$item->name})) {
  717. $return->{$item->name}[] =& $this->_decode($item);
  718. } else if (is_array($return)) {
  719. $return[] =& $this->_decode($item);
  720. } else {
  721. $return->{$item->name} =& $this->_decode($item);
  722. }
  723. } else if (isset($return->{$item->name})) {
  724. $isstruct = FALSE;
  725. if (count(get_object_vars($return)) == 1) {
  726. $d =& $this->_decode($item);
  727. $return = array($return->{$item->name}, $d);
  728. } else {
  729. $d =& $this->_decode($item);
  730. $return->{$item->name} = array($return->{$item->name}, $d);
  731. }
  732. } else {
  733. $return->{$item->name} =& $this->_decode($item);
  734. }
  735. /* set the attributes as members in the class */
  736. if (method_exists($return,'__set_attribute')) {
  737. foreach ($soapval->attributes as $key=>$value) {
  738. call_user_func_array(array(&$return,'__set_attribute'),array($key,$value));
  739. }
  740. }
  741. } else {
  742. if ($soapval->arrayType && $this->_isSoapValue($item)) {
  743. $item->type = $soapval->arrayType;
  744. }
  745. if (!$isstruct) {
  746. $return[] =& $this->_decode($item);
  747. } else if (isset($return[$item->name])) {
  748. $isstruct = FALSE;
  749. $d =& $this->_decode($item);
  750. $return = array($return[$item->name], $d);
  751. } else {
  752. $return[$item->name] =& $this->_decode($item);
  753. }
  754. }
  755. }
  756. return $return;
  757. }
  758. if ($soapval->type == 'boolean') {
  759. if ($soapval->value != '0' && strcasecmp($soapval->value,'false') !=0) {
  760. $soapval->value = TRUE;
  761. } else {
  762. $soapval->value = FALSE;
  763. }
  764. } else if ($soapval->type && array_key_exists($soapval->type, $this->_typemap[SOAP_XML_SCHEMA_VERSION])) {
  765. # if we can, lets set php's variable type
  766. settype($soapval->value, $this->_typemap[SOAP_XML_SCHEMA_VERSION][$soapval->type]);
  767. }
  768. return $soapval->value;
  769. }
  770. /**
  771. * creates the soap envelope with the soap envelop data
  772. *
  773. * @param string $payload soap data (in xml)
  774. * @return associative array (headers,body)
  775. * @access private
  776. */
  777. function &_makeEnvelope(&$method, &$headers, $encoding = SOAP_DEFAULT_ENCODING,$options = array())
  778. {
  779. $smsg = $header_xml = $ns_string = '';
  780. if ($headers) {
  781. $c = count($headers);
  782. for ($i=0; $i < $c; $i++) {
  783. $header_xml .= $headers[$i]->serialize($this);
  784. }
  785. $header_xml = "<SOAP-ENV:Header>\r\n$header_xml\r\n</SOAP-ENV:Header>\r\n";
  786. }
  787. if (!isset($options['input']) || $options['input'] == 'parse') {
  788. if (is_array($method)) {
  789. $c = count($method);
  790. for ($i = 0; $i < $c; $i++) {
  791. $smsg .= $method[$i]->serialize($this);
  792. }
  793. } else {
  794. $smsg =& $method->serialize($this);
  795. }
  796. } else {
  797. $smsg =& $method;
  798. }
  799. $body = "<SOAP-ENV:Body>\r\n".$smsg."\r\n</SOAP-ENV:Body>\r\n";
  800. foreach ($this->_namespaces as $k => $v) {
  801. $ns_string .= " xmlns:$v=\"$k\"\r\n";
  802. }
  803. /* if use='literal', we do not put in the encodingStyle. This is denoted by
  804. $this->_section5 being false.
  805. XXX use can be defined at a more granular level than we are dealing with
  806. here, so this does not work for all services.
  807. */
  808. $xml = "<?xml version=\"1.0\" encoding=\"$encoding\"?>\r\n\r\n".
  809. "<SOAP-ENV:Envelope $ns_string".
  810. ($this->_section5?" SOAP-ENV:encodingStyle=\"" . SOAP_SCHEMA_ENCODING . "\"":'').
  811. ">\r\n".
  812. "$header_xml$body</SOAP-ENV:Envelope>\r\n";
  813. return $xml;
  814. }
  815. function &_makeMimeMessage(&$xml, $encoding = SOAP_DEFAULT_ENCODING)
  816. {
  817. global $SOAP_options;
  818. if (!isset($SOAP_options['Mime'])) {
  819. return $this->_raiseSoapFault('Mime is not installed');
  820. }
  821. // encode any attachments
  822. // see http://www.w3.org/TR/SOAP-attachments
  823. // now we have to mime encode the message
  824. $params = array('content_type' => 'multipart/related; type=text/xml');
  825. $msg =& new Mail_mimePart('', $params);
  826. // add the xml part
  827. $params['content_type'] = 'text/xml';
  828. $params['charset'] = $encoding;
  829. $params['encoding'] = 'base64';
  830. $msg->addSubPart($xml, $params);
  831. // add the attachements
  832. $c = count($this->__attachments);
  833. for ($i=0; $i < $c; $i++) {
  834. $attachment =& $this->__attachments[$i];
  835. $msg->addSubPart($attachment['body'],$attachment);
  836. }
  837. return $msg->encode();
  838. }
  839. // XXX this needs to be used from the Transport system
  840. function &_makeDIMEMessage(&$xml)
  841. {
  842. global $SOAP_options;
  843. if (!isset($SOAP_options['DIME'])) {
  844. return $this->_raiseSoapFault('DIME is not installed');
  845. }
  846. // encode any attachments
  847. // see http://search.ietf.org/internet-drafts/draft-nielsen-dime-soap-00.txt
  848. // now we have to DIME encode the message
  849. $dime =& new Net_DIME_Message();
  850. $msg =& $dime->encodeData($xml,SOAP_ENVELOP,NULL,NET_DIME_TYPE_URI);
  851. // add the attachements
  852. $c = count($this->__attachments);
  853. for ($i=0; $i < $c; $i++) {
  854. $attachment =& $this->__attachments[$i];
  855. $msg .= $dime->encodeData($attachment['body'],$attachment['content_type'],$attachment['cid'],NET_DIME_TYPE_MEDIA);
  856. }
  857. $msg .= $dime->endMessage();
  858. return $msg;
  859. }
  860. function _decodeMimeMessage(&$data, &$headers, &$attachments)
  861. {
  862. global $SOAP_options;
  863. if (!isset($SOAP_options['Mime'])) {
  864. $this->_raiseSoapFault('Mime Unsupported, install PEAR::Mail::Mime','','','Server');
  865. return;
  866. }
  867. $params['include_bodies'] = TRUE;
  868. $params['decode_bodies'] = TRUE;
  869. $params['decode_headers'] = TRUE;
  870. // XXX lame thing to have to do for decoding
  871. $decoder =& new Mail_mimeDecode($data);
  872. $structure = $decoder->decode($params);
  873. if (isset($structure->body)) {
  874. $data = $structure->body;
  875. $headers = $structure->headers;
  876. return;
  877. } else if (isset($structure->parts)) {
  878. $data = $structure->parts[0]->body;
  879. $headers = array_merge($structure->headers,$structure->parts[0]->headers);
  880. if (count($structure->parts) > 1) {
  881. $mime_parts = array_splice($structure->parts,1);
  882. // prepare the parts for the soap parser
  883. $c = count($mime_parts);
  884. for ($i = 0; $i < $c; $i++) {
  885. $p =& $mime_parts[$i];
  886. if (isset($p->headers['content-location'])) {
  887. // XXX TODO: modify location per SwA note section 3
  888. // http://www.w3.org/TR/SOAP-attachments
  889. $attachments[$p->headers['content-location']] = $p->body;
  890. } else {
  891. $cid = 'cid:'.substr($p->headers['content-id'],1,strlen($p->headers['content-id'])-2);
  892. $attachments[$cid] = $p->body;
  893. }
  894. }
  895. }
  896. return;
  897. }
  898. $this->_raiseSoapFault('Mime parsing error','','','Server');
  899. }
  900. function _decodeDIMEMessage(&$data, &$headers, &$attachments)
  901. {
  902. global $SOAP_options;
  903. if (!isset($SOAP_options['DIME'])) {
  904. $this->_raiseSoapFault('DIME Unsupported, install PEAR::Net::DIME','','','Server');
  905. return;
  906. }
  907. // XXX this SHOULD be moved to the transport layer, e.g. PHP itself
  908. // should handle parsing DIME ;)
  909. $dime =& new Net_DIME_Message();
  910. $err = $dime->decodeData($data);
  911. if ( PEAR::isError($err) ) {
  912. $this->_raiseSoapFault('Failed to decode the DIME message!','','','Server');
  913. return;
  914. }
  915. if (strcasecmp($dime->parts[0]['type'],SOAP_ENVELOP) !=0) {
  916. $this->_raiseSoapFault('DIME record 1 is not a SOAP envelop!','','','Server');
  917. return;
  918. }
  919. $data = $dime->parts[0]['data'];
  920. $headers['content-type'] = 'text/xml'; // fake it for now
  921. $c = count($dime->parts);
  922. for ($i = 0; $i < $c; $i++) {
  923. $part =& $dime->parts[$i];
  924. // XXX we need to handle URI's better
  925. $id = strncmp( $part['id'], 'cid:', 4 ) ? 'cid:'.$part['id'] : $part['id'];
  926. $attachments[$id] = $part['data'];
  927. }
  928. }
  929. function __set_type_translation($type, $class=NULL)
  930. {
  931. $tq =& new QName($type);
  932. if (!$class) {
  933. $class = $tq->name;
  934. }
  935. $this->_type_translation[$type]=$class;
  936. }
  937. }
  938. /**
  939. * QName
  940. * class used to handle QNAME values in XML
  941. *
  942. * @access public
  943. * @version $Id: Base.php,v 1.40.2.4 2004/01/17 14:22:58 arnaud Exp $
  944. * @package SOAP::Client
  945. * @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates
  946. */
  947. class QName
  948. {
  949. var $name = '';
  950. var $ns = '';
  951. var $namespace='';
  952. #var $arrayInfo = '';
  953. function QName($name, $namespace = '') {
  954. if ($name && $name[0] == '{') {
  955. preg_match('/\{(.*?)\}(.*)/',$name, $m);
  956. $this->name = $m[2];
  957. $this->namespace = $m[1];
  958. } else if (strpos($name, ':') != FALSE) {
  959. $s = split(':',$name);
  960. $s = array_reverse($s);
  961. $this->name = $s[0];
  962. $this->ns = $s[1];
  963. $this->namespace = $namespace;
  964. } else {
  965. $this->name = $name;
  966. $this->namespace = $namespace;
  967. }
  968. # a little more magic than should be in a qname
  969. $p = strpos($this->name, '[');
  970. if ($p) {
  971. # XXX need to re-examine this logic later
  972. # chop off []
  973. $this->arraySize = split(',',substr($this->name,$p+1, strlen($this->name)-$p-2));
  974. $this->arrayInfo = substr($this->name, $p);
  975. $this->name = substr($this->name, 0, $p);
  976. }
  977. }
  978. function fqn()
  979. {
  980. if ($this->namespace) {
  981. return '{'.$this->namespace.'}'.$this->name;
  982. } else if ($this->ns) {
  983. return $this->ns.':'.$this->name;
  984. }
  985. return $this->name;
  986. }
  987. }
  988. ?>