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

/lib/PEAR/XML/Serializer.php

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