PageRenderTime 50ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/include/XML/Serializer.php

https://github.com/radicaldesigns/amp
PHP | 1025 lines | 469 code | 88 blank | 468 comment | 103 complexity | be52ecbd59c4b60d09e12025ec691ddd MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0, BSD-3-Clause, LGPL-2.0, CC-BY-SA-3.0, AGPL-1.0
  1. <?PHP
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * XML_Serializer
  5. *
  6. * Creates XML documents from PHP data structures like arrays, objects or scalars.
  7. *
  8. * PHP versions 4 and 5
  9. *
  10. * LICENSE: This source file is subject to version 3.0 of the PHP license
  11. * that is available through the world-wide-web at the following URI:
  12. * http://www.php.net/license/3_0.txt. If you did not receive a copy of
  13. * the PHP License and are unable to obtain it through the web, please
  14. * send a note to license@php.net so we can mail you a copy immediately.
  15. *
  16. * @category XML
  17. * @package XML_Serializer
  18. * @author Stephan Schmidt <schst@php.net>
  19. * @copyright 1997-2005 The PHP Group
  20. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  21. * @version CVS: $Id: Serializer.php,v 1.47 2005/09/30 13:40:30 schst Exp $
  22. * @link http://pear.php.net/package/XML_Serializer
  23. * @see XML_Unserializer
  24. */
  25. /**
  26. * uses PEAR error management
  27. */
  28. require_once 'PEAR.php';
  29. /**
  30. * uses XML_Util to create XML tags
  31. */
  32. require_once 'XML/Util.php';
  33. /**
  34. * option: string used for indentation
  35. *
  36. * Possible values:
  37. * - any string (default is any string)
  38. */
  39. define('XML_SERIALIZER_OPTION_INDENT', 'indent');
  40. /**
  41. * option: string used for linebreaks
  42. *
  43. * Possible values:
  44. * - any string (default is \n)
  45. */
  46. define('XML_SERIALIZER_OPTION_LINEBREAKS', 'linebreak');
  47. /**
  48. * option: enable type hints
  49. *
  50. * Possible values:
  51. * - true
  52. * - false
  53. */
  54. define('XML_SERIALIZER_OPTION_TYPEHINTS', 'typeHints');
  55. /**
  56. * option: add an XML declaration
  57. *
  58. * Possible values:
  59. * - true
  60. * - false
  61. */
  62. define('XML_SERIALIZER_OPTION_XML_DECL_ENABLED', 'addDecl');
  63. /**
  64. * option: encoding of the document
  65. *
  66. * Possible values:
  67. * - any valid encoding
  68. * - null (default)
  69. */
  70. define('XML_SERIALIZER_OPTION_XML_ENCODING', 'encoding');
  71. /**
  72. * option: default name for tags
  73. *
  74. * Possible values:
  75. * - any string (XML_Serializer_Tag is default)
  76. */
  77. define('XML_SERIALIZER_OPTION_DEFAULT_TAG', 'defaultTagName');
  78. /**
  79. * option: use classname for objects in indexed arrays
  80. *
  81. * Possible values:
  82. * - true (default)
  83. * - false
  84. */
  85. define('XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME', 'classAsTagName');
  86. /**
  87. * option: attribute where original key is stored
  88. *
  89. * Possible values:
  90. * - any string (default is _originalKey)
  91. */
  92. define('XML_SERIALIZER_OPTION_ATTRIBUTE_KEY', 'keyAttribute');
  93. /**
  94. * option: attribute for type (only if typeHints => true)
  95. *
  96. * Possible values:
  97. * - any string (default is _type)
  98. */
  99. define('XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE', 'typeAttribute');
  100. /**
  101. * option: attribute for class (only if typeHints => true)
  102. *
  103. * Possible values:
  104. * - any string (default is _class)
  105. */
  106. define('XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS', 'classAttribute');
  107. /**
  108. * option: scalar values (strings, ints,..) will be serialized as attribute
  109. *
  110. * Possible values:
  111. * - true
  112. * - false (default)
  113. * - array which sets this option on a per-tag basis
  114. */
  115. define('XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES', 'scalarAsAttributes');
  116. /**
  117. * option: prepend string for attributes
  118. *
  119. * Possible values:
  120. * - any string (default is any string)
  121. */
  122. define('XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES', 'prependAttributes');
  123. /**
  124. * option: indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column
  125. *
  126. * Possible values:
  127. * - true
  128. * - false (default)
  129. */
  130. define('XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES', 'indentAttributes');
  131. /**
  132. * option: use 'simplexml' to use parent name as tagname if transforming an indexed array
  133. *
  134. * Possible values:
  135. * - XML_SERIALIZER_MODE_DEFAULT (default)
  136. * - XML_SERIALIZER_MODE_SIMPLEXML
  137. */
  138. define('XML_SERIALIZER_OPTION_MODE', 'mode');
  139. /**
  140. * option: add a doctype declaration
  141. *
  142. * Possible values:
  143. * - true
  144. * - false (default)
  145. */
  146. define('XML_SERIALIZER_OPTION_DOCTYPE_ENABLED', 'addDoctype');
  147. /**
  148. * option: supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()}
  149. *
  150. * Possible values:
  151. * - string
  152. * - array
  153. */
  154. define('XML_SERIALIZER_OPTION_DOCTYPE', 'doctype');
  155. /**
  156. * option: name of the root tag
  157. *
  158. * Possible values:
  159. * - string
  160. * - null (default)
  161. */
  162. define('XML_SERIALIZER_OPTION_ROOT_NAME', 'rootName');
  163. /**
  164. * option: attributes of the root tag
  165. *
  166. * Possible values:
  167. * - array
  168. */
  169. define('XML_SERIALIZER_OPTION_ROOT_ATTRIBS', 'rootAttributes');
  170. /**
  171. * option: all values in this key will be treated as attributes
  172. *
  173. * Possible values:
  174. * - array
  175. */
  176. define('XML_SERIALIZER_OPTION_ATTRIBUTES_KEY', 'attributesArray');
  177. /**
  178. * option: this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray
  179. *
  180. * Possible values:
  181. * - string
  182. * - null (default)
  183. */
  184. define('XML_SERIALIZER_OPTION_CONTENT_KEY', 'contentName');
  185. /**
  186. * option: this value will be used in a comment, instead of creating a new tag
  187. *
  188. * Possible values:
  189. * - string
  190. * - null (default)
  191. */
  192. define('XML_SERIALIZER_OPTION_COMMENT_KEY', 'commentName');
  193. /**
  194. * option: tag names that will be changed
  195. *
  196. * Possible values:
  197. * - array
  198. */
  199. define('XML_SERIALIZER_OPTION_TAGMAP', 'tagMap');
  200. /**
  201. * option: function that will be applied before serializing
  202. *
  203. * Possible values:
  204. * - any valid PHP callback
  205. */
  206. define('XML_SERIALIZER_OPTION_ENCODE_FUNC', 'encodeFunction');
  207. /**
  208. * option: function that will be applied before serializing
  209. *
  210. * Possible values:
  211. * - string
  212. * - null (default)
  213. */
  214. define('XML_SERIALIZER_OPTION_NAMESPACE', 'namespace');
  215. /**
  216. * option: type of entities to replace
  217. *
  218. * Possible values:
  219. * - XML_SERIALIZER_ENTITIES_NONE
  220. * - XML_SERIALIZER_ENTITIES_XML (default)
  221. * - XML_SERIALIZER_ENTITIES_XML_REQUIRED
  222. * - XML_SERIALIZER_ENTITIES_HTML
  223. */
  224. define('XML_SERIALIZER_OPTION_ENTITIES', 'replaceEntities');
  225. /**
  226. * option: whether to return the result of the serialization from serialize()
  227. *
  228. * Possible values:
  229. * - true
  230. * - false (default)
  231. */
  232. define('XML_SERIALIZER_OPTION_RETURN_RESULT', 'returnResult');
  233. /**
  234. * option: whether to ignore properties that are set to null
  235. *
  236. * Possible values:
  237. * - true
  238. * - false (default)
  239. */
  240. define('XML_SERIALIZER_OPTION_IGNORE_NULL', 'ignoreNull');
  241. /**
  242. * option: whether to use cdata sections for character data
  243. *
  244. * Possible values:
  245. * - true
  246. * - false (default)
  247. */
  248. define('XML_SERIALIZER_OPTION_CDATA_SECTIONS', 'cdata');
  249. /**
  250. * default mode
  251. */
  252. define('XML_SERIALIZER_MODE_DEFAULT', 'default');
  253. /**
  254. * SimpleXML mode
  255. *
  256. * When serializing indexed arrays, the key of the parent value is used as a tagname.
  257. */
  258. define('XML_SERIALIZER_MODE_SIMPLEXML', 'simplexml');
  259. /**
  260. * error code for no serialization done
  261. */
  262. define('XML_SERIALIZER_ERROR_NO_SERIALIZATION', 51);
  263. /**
  264. * do not replace entitites
  265. */
  266. define('XML_SERIALIZER_ENTITIES_NONE', XML_UTIL_ENTITIES_NONE);
  267. /**
  268. * replace all XML entitites
  269. * This setting will replace <, >, ", ' and &
  270. */
  271. define('XML_SERIALIZER_ENTITIES_XML', XML_UTIL_ENTITIES_XML);
  272. /**
  273. * replace only required XML entitites
  274. * This setting will replace <, " and &
  275. */
  276. define('XML_SERIALIZER_ENTITIES_XML_REQUIRED', XML_UTIL_ENTITIES_XML_REQUIRED);
  277. /**
  278. * replace HTML entitites
  279. * @link http://www.php.net/htmlentities
  280. */
  281. define('XML_SERIALIZER_ENTITIES_HTML', XML_UTIL_ENTITIES_HTML);
  282. /**
  283. * Creates XML documents from PHP data structures like arrays, objects or scalars.
  284. *
  285. * this class can be used in two modes:
  286. *
  287. * 1. create an XML document from an array or object that is processed by other
  288. * applications. That means, you can create a RDF document from an array in the
  289. * following format:
  290. *
  291. * $data = array(
  292. * 'channel' => array(
  293. * 'title' => 'Example RDF channel',
  294. * 'link' => 'http://www.php-tools.de',
  295. * 'image' => array(
  296. * 'title' => 'Example image',
  297. * 'url' => 'http://www.php-tools.de/image.gif',
  298. * 'link' => 'http://www.php-tools.de'
  299. * ),
  300. * array(
  301. * 'title' => 'Example item',
  302. * 'link' => 'http://example.com'
  303. * ),
  304. * array(
  305. * 'title' => 'Another Example item',
  306. * 'link' => 'http://example.org'
  307. * )
  308. * )
  309. * );
  310. *
  311. * to create a RDF document from this array do the following:
  312. *
  313. * require_once 'XML/Serializer.php';
  314. *
  315. * $options = array(
  316. * XML_SERIALIZER_OPTION_INDENT => "\t", // indent with tabs
  317. * XML_SERIALIZER_OPTION_LINEBREAKS => "\n", // use UNIX line breaks
  318. * XML_SERIALIZER_OPTION_ROOT_NAME => 'rdf:RDF', // root tag
  319. * XML_SERIALIZER_OPTION_DEFAULT_TAG => 'item' // tag for values with numeric keys
  320. * );
  321. *
  322. * $serializer = new XML_Serializer($options);
  323. * $rdf = $serializer->serialize($data);
  324. *
  325. * You will get a complete XML document that can be processed like any RDF document.
  326. *
  327. * 2. this classes can be used to serialize any data structure in a way that it can
  328. * later be unserialized again.
  329. * XML_Serializer will store the type of the value and additional meta information
  330. * in attributes of the surrounding tag. This meat information can later be used
  331. * to restore the original data structure in PHP. If you want XML_Serializer
  332. * to add meta information to the tags, add
  333. *
  334. * XML_SERIALIZER_OPTION_TYPEHINTS => true
  335. *
  336. * to the options array in the constructor.
  337. *
  338. * @category XML
  339. * @package XML_Serializer
  340. * @author Stephan Schmidt <schst@php.net>
  341. * @copyright 1997-2005 The PHP Group
  342. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  343. * @version Release: @package_version@
  344. * @link http://pear.php.net/package/XML_Serializer
  345. * @see XML_Unserializer
  346. */
  347. class XML_Serializer extends PEAR
  348. {
  349. /**
  350. * list of all available options
  351. *
  352. * @access private
  353. * @var array
  354. */
  355. var $_knownOptions = array(
  356. XML_SERIALIZER_OPTION_INDENT,
  357. XML_SERIALIZER_OPTION_LINEBREAKS,
  358. XML_SERIALIZER_OPTION_TYPEHINTS,
  359. XML_SERIALIZER_OPTION_XML_DECL_ENABLED,
  360. XML_SERIALIZER_OPTION_XML_ENCODING,
  361. XML_SERIALIZER_OPTION_DEFAULT_TAG,
  362. XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME,
  363. XML_SERIALIZER_OPTION_ATTRIBUTE_KEY,
  364. XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE,
  365. XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS,
  366. XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES,
  367. XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES,
  368. XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES,
  369. XML_SERIALIZER_OPTION_MODE,
  370. XML_SERIALIZER_OPTION_DOCTYPE_ENABLED,
  371. XML_SERIALIZER_OPTION_DOCTYPE,
  372. XML_SERIALIZER_OPTION_ROOT_NAME,
  373. XML_SERIALIZER_OPTION_ROOT_ATTRIBS,
  374. XML_SERIALIZER_OPTION_ATTRIBUTES_KEY,
  375. XML_SERIALIZER_OPTION_CONTENT_KEY,
  376. XML_SERIALIZER_OPTION_COMMENT_KEY,
  377. XML_SERIALIZER_OPTION_TAGMAP,
  378. XML_SERIALIZER_OPTION_ENCODE_FUNC,
  379. XML_SERIALIZER_OPTION_NAMESPACE,
  380. XML_SERIALIZER_OPTION_ENTITIES,
  381. XML_SERIALIZER_OPTION_RETURN_RESULT,
  382. XML_SERIALIZER_OPTION_IGNORE_NULL,
  383. XML_SERIALIZER_OPTION_CDATA_SECTIONS
  384. );
  385. /**
  386. * default options for the serialization
  387. *
  388. * @access private
  389. * @var array
  390. */
  391. var $_defaultOptions = array(
  392. XML_SERIALIZER_OPTION_INDENT => '', // string used for indentation
  393. XML_SERIALIZER_OPTION_LINEBREAKS => "\n", // string used for newlines
  394. XML_SERIALIZER_OPTION_TYPEHINTS => false, // automatically add type hin attributes
  395. XML_SERIALIZER_OPTION_XML_DECL_ENABLED => false, // add an XML declaration
  396. XML_SERIALIZER_OPTION_XML_ENCODING => null, // encoding specified in the XML declaration
  397. XML_SERIALIZER_OPTION_DEFAULT_TAG => 'XML_Serializer_Tag', // tag used for indexed arrays or invalid names
  398. XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME => false, // use classname for objects in indexed arrays
  399. XML_SERIALIZER_OPTION_ATTRIBUTE_KEY => '_originalKey', // attribute where original key is stored
  400. XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE => '_type', // attribute for type (only if typeHints => true)
  401. XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS => '_class', // attribute for class of objects (only if typeHints => true)
  402. XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES => false, // scalar values (strings, ints,..) will be serialized as attribute
  403. XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES => '', // prepend string for attributes
  404. XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES => false, // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column
  405. XML_SERIALIZER_OPTION_MODE => XML_SERIALIZER_MODE_DEFAULT, // use XML_SERIALIZER_MODE_SIMPLEXML to use parent name as tagname if transforming an indexed array
  406. XML_SERIALIZER_OPTION_DOCTYPE_ENABLED => false, // add a doctype declaration
  407. XML_SERIALIZER_OPTION_DOCTYPE => null, // supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()}
  408. XML_SERIALIZER_OPTION_ROOT_NAME => null, // name of the root tag
  409. XML_SERIALIZER_OPTION_ROOT_ATTRIBS => array(), // attributes of the root tag
  410. XML_SERIALIZER_OPTION_ATTRIBUTES_KEY => null, // all values in this key will be treated as attributes
  411. XML_SERIALIZER_OPTION_CONTENT_KEY => null, // this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray
  412. XML_SERIALIZER_OPTION_COMMENT_KEY => null, // this value will be used directly as comment, instead of creating a new tag, may only be used in conjuction with attributesArray
  413. XML_SERIALIZER_OPTION_TAGMAP => array(), // tag names that will be changed
  414. XML_SERIALIZER_OPTION_ENCODE_FUNC => null, // function that will be applied before serializing
  415. XML_SERIALIZER_OPTION_NAMESPACE => null, // namespace to use
  416. XML_SERIALIZER_OPTION_ENTITIES => XML_SERIALIZER_ENTITIES_XML, // type of entities to replace,
  417. XML_SERIALIZER_OPTION_RETURN_RESULT => false, // serialize() returns the result of the serialization instead of true
  418. XML_SERIALIZER_OPTION_IGNORE_NULL => false, // ignore properties that are set to null
  419. XML_SERIALIZER_OPTION_CDATA_SECTIONS => false // Whether to use cdata sections for plain character data
  420. );
  421. /**
  422. * options for the serialization
  423. *
  424. * @access public
  425. * @var array
  426. */
  427. var $options = array();
  428. /**
  429. * current tag depth
  430. *
  431. * @access private
  432. * @var integer
  433. */
  434. var $_tagDepth = 0;
  435. /**
  436. * serilialized representation of the data
  437. *
  438. * @access private
  439. * @var string
  440. */
  441. var $_serializedData = null;
  442. /**
  443. * constructor
  444. *
  445. * @access public
  446. * @param mixed $options array containing options for the serialization
  447. */
  448. function XML_Serializer( $options = null )
  449. {
  450. $this->PEAR();
  451. if (is_array($options)) {
  452. $this->options = array_merge($this->_defaultOptions, $options);
  453. } else {
  454. $this->options = $this->_defaultOptions;
  455. }
  456. }
  457. /**
  458. * return API version
  459. *
  460. * @access public
  461. * @static
  462. * @return string $version API version
  463. */
  464. function apiVersion()
  465. {
  466. return '@package_version@';
  467. }
  468. /**
  469. * reset all options to default options
  470. *
  471. * @access public
  472. * @see setOption(), XML_Serializer()
  473. */
  474. function resetOptions()
  475. {
  476. $this->options = $this->_defaultOptions;
  477. }
  478. /**
  479. * set an option
  480. *
  481. * You can use this method if you do not want to set all options in the constructor
  482. *
  483. * @access public
  484. * @see resetOption(), XML_Serializer()
  485. */
  486. function setOption($name, $value)
  487. {
  488. $this->options[$name] = $value;
  489. }
  490. /**
  491. * sets several options at once
  492. *
  493. * You can use this method if you do not want to set all options in the constructor
  494. *
  495. * @access public
  496. * @see resetOption(), XML_Unserializer(), setOption()
  497. */
  498. function setOptions($options)
  499. {
  500. $this->options = array_merge($this->options, $options);
  501. }
  502. /**
  503. * serialize data
  504. *
  505. * @access public
  506. * @param mixed $data data to serialize
  507. * @return boolean true on success, pear error on failure
  508. */
  509. function serialize($data, $options = null)
  510. {
  511. // if options have been specified, use them instead
  512. // of the previously defined ones
  513. if (is_array($options)) {
  514. $optionsBak = $this->options;
  515. if (isset($options['overrideOptions']) && $options['overrideOptions'] == true) {
  516. $this->options = array_merge($this->_defaultOptions, $options);
  517. } else {
  518. $this->options = array_merge($this->options, $options);
  519. }
  520. } else {
  521. $optionsBak = null;
  522. }
  523. // start depth is zero
  524. $this->_tagDepth = 0;
  525. $rootAttributes = $this->options[XML_SERIALIZER_OPTION_ROOT_ATTRIBS];
  526. if (isset($this->options[XML_SERIALIZER_OPTION_NAMESPACE]) && is_array($this->options[XML_SERIALIZER_OPTION_NAMESPACE])) {
  527. $rootAttributes['xmlns:'.$this->options[XML_SERIALIZER_OPTION_NAMESPACE][0]] = $this->options[XML_SERIALIZER_OPTION_NAMESPACE][1];
  528. }
  529. $this->_serializedData = '';
  530. // serialize an array
  531. if (is_array($data)) {
  532. if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) {
  533. $tagName = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME];
  534. } else {
  535. $tagName = 'array';
  536. }
  537. $this->_serializedData .= $this->_serializeArray($data, $tagName, $rootAttributes);
  538. } elseif (is_object($data)) {
  539. // serialize an object
  540. if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) {
  541. $tagName = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME];
  542. } else {
  543. $tagName = get_class($data);
  544. }
  545. $this->_serializedData .= $this->_serializeObject($data, $tagName, $rootAttributes);
  546. } else {
  547. $tag = array();
  548. if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) {
  549. $tag['qname'] = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME];
  550. } else {
  551. $tag['qname'] = gettype($data);
  552. }
  553. if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
  554. $rootAttributes[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = gettype($data);
  555. }
  556. @settype($data, 'string');
  557. $tag['content'] = $data;
  558. $tag['attributes'] = $rootAttributes;
  559. $this->_serializedData = $this->_createXMLTag($tag);
  560. }
  561. // add doctype declaration
  562. if ($this->options[XML_SERIALIZER_OPTION_DOCTYPE_ENABLED] === true) {
  563. $this->_serializedData = XML_Util::getDoctypeDeclaration($tagName, $this->options[XML_SERIALIZER_OPTION_DOCTYPE])
  564. . $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]
  565. . $this->_serializedData;
  566. }
  567. // build xml declaration
  568. if ($this->options[XML_SERIALIZER_OPTION_XML_DECL_ENABLED]) {
  569. $atts = array();
  570. $this->_serializedData = XML_Util::getXMLDeclaration('1.0', $this->options[XML_SERIALIZER_OPTION_XML_ENCODING])
  571. . $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]
  572. . $this->_serializedData;
  573. }
  574. if ($this->options[XML_SERIALIZER_OPTION_RETURN_RESULT] === true) {
  575. $result = $this->_serializedData;
  576. } else {
  577. $result = true;
  578. }
  579. if ($optionsBak !== null) {
  580. $this->options = $optionsBak;
  581. }
  582. return $result;
  583. }
  584. /**
  585. * get the result of the serialization
  586. *
  587. * @access public
  588. * @return string serialized XML
  589. */
  590. function getSerializedData()
  591. {
  592. if ($this->_serializedData == null) {
  593. return $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.', XML_SERIALIZER_ERROR_NO_SERIALIZATION);
  594. }
  595. return $this->_serializedData;
  596. }
  597. /**
  598. * serialize any value
  599. *
  600. * This method checks for the type of the value and calls the appropriate method
  601. *
  602. * @access private
  603. * @param mixed $value
  604. * @param string $tagName
  605. * @param array $attributes
  606. * @return string
  607. */
  608. function _serializeValue($value, $tagName = null, $attributes = array())
  609. {
  610. if (is_array($value)) {
  611. $xml = $this->_serializeArray($value, $tagName, $attributes);
  612. } elseif (is_object($value)) {
  613. $xml = $this->_serializeObject($value, $tagName);
  614. } else {
  615. $tag = array(
  616. 'qname' => $tagName,
  617. 'attributes' => $attributes,
  618. 'content' => $value
  619. );
  620. $xml = $this->_createXMLTag($tag);
  621. }
  622. return $xml;
  623. }
  624. /**
  625. * serialize an array
  626. *
  627. * @access private
  628. * @param array $array array to serialize
  629. * @param string $tagName name of the root tag
  630. * @param array $attributes attributes for the root tag
  631. * @return string $string serialized data
  632. * @uses XML_Util::isValidName() to check, whether key has to be substituted
  633. */
  634. function _serializeArray(&$array, $tagName = null, $attributes = array())
  635. {
  636. $_content = null;
  637. $_comment = null;
  638. // check for comment
  639. if ($this->options[XML_SERIALIZER_OPTION_COMMENT_KEY] !== null) {
  640. if (isset($array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]])) {
  641. $_comment = $array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]];
  642. unset($array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]]);
  643. }
  644. }
  645. /**
  646. * check for special attributes
  647. */
  648. if ($this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY] !== null) {
  649. if (isset($array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]])) {
  650. $attributes = $array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]];
  651. unset($array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]]);
  652. }
  653. /**
  654. * check for special content
  655. */
  656. if ($this->options[XML_SERIALIZER_OPTION_CONTENT_KEY] !== null) {
  657. if (isset($array[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]])) {
  658. $_content = $array[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]];
  659. unset($array[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]]);
  660. }
  661. }
  662. }
  663. if ($this->options[XML_SERIALIZER_OPTION_IGNORE_NULL] === true) {
  664. foreach (array_keys($array) as $key) {
  665. if (is_null($array[$key])) {
  666. unset($array[$key]);
  667. }
  668. }
  669. }
  670. /*
  671. * if mode is set to simpleXML, check whether
  672. * the array is associative or indexed
  673. */
  674. if (is_array($array) && !empty($array) && $this->options[XML_SERIALIZER_OPTION_MODE] == XML_SERIALIZER_MODE_SIMPLEXML) {
  675. $indexed = true;
  676. foreach ($array as $key => $val) {
  677. if (!is_int($key)) {
  678. $indexed = false;
  679. break;
  680. }
  681. }
  682. if ($indexed && $this->options[XML_SERIALIZER_OPTION_MODE] == XML_SERIALIZER_MODE_SIMPLEXML) {
  683. $string = '';
  684. foreach ($array as $key => $val) {
  685. $string .= $this->_serializeValue( $val, $tagName, $attributes);
  686. $string .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
  687. // do indentation
  688. if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null && $this->_tagDepth>0) {
  689. $string .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth);
  690. }
  691. }
  692. return rtrim($string);
  693. }
  694. }
  695. $scalarAsAttributes = false;
  696. if (is_array($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES]) && isset($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES][$tagName])) {
  697. $scalarAsAttributes = $this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES][$tagName];
  698. } elseif ($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES] === true) {
  699. $scalarAsAttributes = true;
  700. }
  701. if ($scalarAsAttributes === true) {
  702. $this->expectError('*');
  703. foreach ($array as $key => $value) {
  704. if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
  705. unset($array[$key]);
  706. $attributes[$this->options[XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES].$key] = $value;
  707. }
  708. }
  709. $this->popExpect();
  710. } elseif (is_array($scalarAsAttributes)) {
  711. $this->expectError('*');
  712. foreach ($scalarAsAttributes as $key) {
  713. if (!isset($array[$key])) {
  714. continue;
  715. }
  716. $value = $array[$key];
  717. if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
  718. unset($array[$key]);
  719. $attributes[$this->options[XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES].$key] = $value;
  720. }
  721. }
  722. $this->popExpect();
  723. }
  724. // check for empty array => create empty tag
  725. if (empty($array)) {
  726. $tag = array(
  727. 'qname' => $tagName,
  728. 'content' => $_content,
  729. 'attributes' => $attributes
  730. );
  731. } else {
  732. $this->_tagDepth++;
  733. $tmp = $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
  734. foreach ($array as $key => $value) {
  735. // do indentation
  736. if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null && $this->_tagDepth>0) {
  737. $tmp .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth);
  738. }
  739. if (isset($this->options[XML_SERIALIZER_OPTION_TAGMAP][$key])) {
  740. $key = $this->options[XML_SERIALIZER_OPTION_TAGMAP][$key];
  741. }
  742. // copy key
  743. $origKey = $key;
  744. $this->expectError('*');
  745. // key cannot be used as tagname => use default tag
  746. $valid = XML_Util::isValidName($key);
  747. $this->popExpect();
  748. if (PEAR::isError($valid)) {
  749. if ($this->options[XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME] && is_object($value)) {
  750. $key = get_class($value);
  751. } else {
  752. $key = $this->_getDefaultTagname($tagName);
  753. }
  754. }
  755. $atts = array();
  756. if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
  757. $atts[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = gettype($value);
  758. if ($key !== $origKey) {
  759. $atts[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_KEY]] = (string)$origKey;
  760. }
  761. }
  762. $tmp .= $this->_createXMLTag(array(
  763. 'qname' => $key,
  764. 'attributes' => $atts,
  765. 'content' => $value )
  766. );
  767. $tmp .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
  768. }
  769. $this->_tagDepth--;
  770. if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null && $this->_tagDepth>0) {
  771. $tmp .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth);
  772. }
  773. if (trim($tmp) === '') {
  774. $tmp = null;
  775. }
  776. $tag = array(
  777. 'qname' => $tagName,
  778. 'content' => $tmp,
  779. 'attributes' => $attributes
  780. );
  781. }
  782. if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
  783. if (!isset($tag['attributes'][$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]])) {
  784. $tag['attributes'][$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = 'array';
  785. }
  786. }
  787. $string = '';
  788. if (!is_null($_comment)) {
  789. $string .= XML_Util::createComment($_comment);
  790. $string .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
  791. if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null && $this->_tagDepth>0) {
  792. $string .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth);
  793. }
  794. }
  795. $string .= $this->_createXMLTag($tag, false);
  796. return $string;
  797. }
  798. /**
  799. * get the name of the default tag.
  800. *
  801. * The name of the parent tag needs to be passed as the
  802. * default name can depend on the context.
  803. *
  804. * @param string name of the parent tag
  805. * @return string default tag name
  806. */
  807. function _getDefaultTagname($parent)
  808. {
  809. if (is_string($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG])) {
  810. return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG];
  811. }
  812. if (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG][$parent])) {
  813. return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG][$parent];
  814. } elseif (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['#default'])) {
  815. return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['#default'];
  816. } elseif (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['__default'])) {
  817. // keep this for BC
  818. return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['__default'];
  819. }
  820. return 'XML_Serializer_Tag';
  821. }
  822. /**
  823. * serialize an object
  824. *
  825. * @access private
  826. * @param object $object object to serialize
  827. * @return string $string serialized data
  828. */
  829. function _serializeObject(&$object, $tagName = null, $attributes = array())
  830. {
  831. // check for magic function
  832. if (method_exists($object, '__sleep')) {
  833. $propNames = $object->__sleep();
  834. if (is_array($propNames)) {
  835. $properties = array();
  836. foreach ($propNames as $propName) {
  837. $properties[$propName] = $object->$propName;
  838. }
  839. } else {
  840. $properties = get_object_vars($object);
  841. }
  842. } else {
  843. $properties = get_object_vars($object);
  844. }
  845. if (empty($tagName)) {
  846. $tagName = get_class($object);
  847. }
  848. // typehints activated?
  849. if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
  850. $attributes[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = 'object';
  851. $attributes[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS]] = get_class($object);
  852. }
  853. $string = $this->_serializeArray($properties, $tagName, $attributes);
  854. return $string;
  855. }
  856. /**
  857. * create a tag from an array
  858. * this method awaits an array in the following format
  859. * array(
  860. * 'qname' => $tagName,
  861. * 'attributes' => array(),
  862. * 'content' => $content, // optional
  863. * 'namespace' => $namespace // optional
  864. * 'namespaceUri' => $namespaceUri // optional
  865. * )
  866. *
  867. * @access private
  868. * @param array $tag tag definition
  869. * @param boolean $replaceEntities whether to replace XML entities in content or not
  870. * @return string $string XML tag
  871. */
  872. function _createXMLTag($tag, $firstCall = true)
  873. {
  874. // build fully qualified tag name
  875. if ($this->options[XML_SERIALIZER_OPTION_NAMESPACE] !== null) {
  876. if (is_array($this->options[XML_SERIALIZER_OPTION_NAMESPACE])) {
  877. $tag['qname'] = $this->options[XML_SERIALIZER_OPTION_NAMESPACE][0] . ':' . $tag['qname'];
  878. } else {
  879. $tag['qname'] = $this->options[XML_SERIALIZER_OPTION_NAMESPACE] . ':' . $tag['qname'];
  880. }
  881. }
  882. // attribute indentation
  883. if ($this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES] !== false) {
  884. $multiline = true;
  885. $indent = str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth);
  886. if ($this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES] == '_auto') {
  887. $indent .= str_repeat(' ', (strlen($tag['qname'])+2));
  888. } else {
  889. $indent .= $this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES];
  890. }
  891. } else {
  892. $multiline = false;
  893. $indent = false;
  894. }
  895. if (is_array($tag['content'])) {
  896. if (empty($tag['content'])) {
  897. $tag['content'] = '';
  898. }
  899. } elseif(is_scalar($tag['content']) && (string)$tag['content'] == '') {
  900. $tag['content'] = '';
  901. }
  902. // replace XML entities (only needed, if this is not a nested call)
  903. if ($firstCall === true) {
  904. if ($this->options[XML_SERIALIZER_OPTION_CDATA_SECTIONS] === true) {
  905. $replaceEntities = XML_UTIL_CDATA_SECTION;
  906. } else {
  907. $replaceEntities = $this->options[XML_SERIALIZER_OPTION_ENTITIES];
  908. }
  909. } else {
  910. $replaceEntities = XML_SERIALIZER_ENTITIES_NONE;
  911. }
  912. if (is_scalar($tag['content']) || is_null($tag['content'])) {
  913. if ($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC]) {
  914. if ($firstCall === true) {
  915. $tag['content'] = call_user_func($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['content']);
  916. }
  917. $tag['attributes'] = array_map($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['attributes']);
  918. }
  919. $tag = XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]);
  920. } elseif (is_array($tag['content'])) {
  921. $tag = $this->_serializeArray($tag['content'], $tag['qname'], $tag['attributes']);
  922. } elseif (is_object($tag['content'])) {
  923. $tag = $this->_serializeObject($tag['content'], $tag['qname'], $tag['attributes']);
  924. } elseif (is_resource($tag['content'])) {
  925. settype($tag['content'], 'string');
  926. if ($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC]) {
  927. if ($replaceEntities === true) {
  928. $tag['content'] = call_user_func($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['content']);
  929. }
  930. $tag['attributes'] = array_map($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['attributes']);
  931. }
  932. $tag = XML_Util::createTagFromArray($tag, $replaceEntities);
  933. }
  934. return $tag;
  935. }
  936. }
  937. ?>