Source for file Serializer.php

Documentation is available at Serializer.php

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

Documentation generated on Mon, 04 Dec 2006 11:10:08 -0500 by phpDocumentor 1.3.0RC3