home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / XML / Serializer.php < prev    next >
Encoding:
PHP Script  |  2006-04-07  |  37.3 KB  |  1,025 lines

  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.47 2005/09/30 13:40:30 schst 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. /**
  295.  * error code for no serialization done
  296.  */
  297. define('XML_SERIALIZER_ERROR_NO_SERIALIZATION', 51);
  298.  
  299. /**
  300.  * do not replace entitites
  301.  */
  302. define('XML_SERIALIZER_ENTITIES_NONE', XML_UTIL_ENTITIES_NONE);
  303.  
  304. /**
  305.  * replace all XML entitites
  306.  * This setting will replace <, >, ", ' and &
  307.  */
  308. define('XML_SERIALIZER_ENTITIES_XML', XML_UTIL_ENTITIES_XML);
  309.  
  310. /**
  311.  * replace only required XML entitites
  312.  * This setting will replace <, " and &
  313.  */
  314. define('XML_SERIALIZER_ENTITIES_XML_REQUIRED', XML_UTIL_ENTITIES_XML_REQUIRED);
  315.  
  316. /**
  317.  * replace HTML entitites
  318.  * @link    http://www.php.net/htmlentities
  319.  */
  320. define('XML_SERIALIZER_ENTITIES_HTML', XML_UTIL_ENTITIES_HTML);
  321.  
  322. /**
  323.  * Creates XML documents from PHP data structures like arrays, objects or scalars.
  324.  *
  325.  * this class can be used in two modes:
  326.  *
  327.  *  1. create an XML document from an array or object that is processed by other
  328.  *    applications. That means, you can create a RDF document from an array in the
  329.  *    following format:
  330.  *
  331.  *    $data = array(
  332.  *              'channel' => array(
  333.  *                            'title' => 'Example RDF channel',
  334.  *                            'link'  => 'http://www.php-tools.de',
  335.  *                            'image' => array(
  336.  *                                        'title' => 'Example image',
  337.  *                                        'url'   => 'http://www.php-tools.de/image.gif',
  338.  *                                        'link'  => 'http://www.php-tools.de'
  339.  *                                           ),
  340.  *                            array(
  341.  *                                 'title' => 'Example item',
  342.  *                                 'link'  => 'http://example.com'
  343.  *                                 ),
  344.  *                            array(
  345.  *                                 'title' => 'Another Example item',
  346.  *                                 'link'  => 'http://example.org'
  347.  *                                 )
  348.  *                              )
  349.  *             );
  350.  *
  351.  *   to create a RDF document from this array do the following:
  352.  *
  353.  *   require_once 'XML/Serializer.php';
  354.  *
  355.  *   $options = array(
  356.  *                     XML_SERIALIZER_OPTION_INDENT      => "\t",        // indent with tabs
  357.  *                     XML_SERIALIZER_OPTION_LINEBREAKS  => "\n",        // use UNIX line breaks
  358.  *                     XML_SERIALIZER_OPTION_ROOT_NAME   => 'rdf:RDF',   // root tag
  359.  *                     XML_SERIALIZER_OPTION_DEFAULT_TAG => 'item'       // tag for values with numeric keys
  360.  *   );
  361.  *
  362.  *   $serializer = new XML_Serializer($options);
  363.  *   $rdf        = $serializer->serialize($data);
  364.  *
  365.  * You will get a complete XML document that can be processed like any RDF document.
  366.  *
  367.  * 2. this classes can be used to serialize any data structure in a way that it can
  368.  *    later be unserialized again.
  369.  *    XML_Serializer will store the type of the value and additional meta information
  370.  *    in attributes of the surrounding tag. This meat information can later be used
  371.  *    to restore the original data structure in PHP. If you want XML_Serializer
  372.  *    to add meta information to the tags, add
  373.  *
  374.  *      XML_SERIALIZER_OPTION_TYPEHINTS => true
  375.  *
  376.  *    to the options array in the constructor.
  377.  *
  378.  * @category   XML
  379.  * @package    XML_Serializer
  380.  * @author     Stephan Schmidt <schst@php.net>
  381.  * @copyright  1997-2005 The PHP Group
  382.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  383.  * @version    Release: 0.18.0
  384.  * @link       http://pear.php.net/package/XML_Serializer
  385.  * @see        XML_Unserializer
  386.  */
  387. class XML_Serializer extends PEAR
  388. {
  389.    /**
  390.     * list of all available options
  391.     *
  392.     * @access private
  393.     * @var    array
  394.     */
  395.     var $_knownOptions = array(
  396.                                  XML_SERIALIZER_OPTION_INDENT,
  397.                                  XML_SERIALIZER_OPTION_LINEBREAKS,
  398.                                  XML_SERIALIZER_OPTION_TYPEHINTS,
  399.                                  XML_SERIALIZER_OPTION_XML_DECL_ENABLED,
  400.                                  XML_SERIALIZER_OPTION_XML_ENCODING,
  401.                                  XML_SERIALIZER_OPTION_DEFAULT_TAG,
  402.                                  XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME,
  403.                                  XML_SERIALIZER_OPTION_ATTRIBUTE_KEY,
  404.                                  XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE,
  405.                                  XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS,
  406.                                  XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES,
  407.                                  XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES,
  408.                                  XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES,
  409.                                  XML_SERIALIZER_OPTION_MODE,
  410.                                  XML_SERIALIZER_OPTION_DOCTYPE_ENABLED,
  411.                                  XML_SERIALIZER_OPTION_DOCTYPE,
  412.                                  XML_SERIALIZER_OPTION_ROOT_NAME,
  413.                                  XML_SERIALIZER_OPTION_ROOT_ATTRIBS,
  414.                                  XML_SERIALIZER_OPTION_ATTRIBUTES_KEY,
  415.                                  XML_SERIALIZER_OPTION_CONTENT_KEY,
  416.                                  XML_SERIALIZER_OPTION_COMMENT_KEY,
  417.                                  XML_SERIALIZER_OPTION_TAGMAP,
  418.                                  XML_SERIALIZER_OPTION_ENCODE_FUNC,
  419.                                  XML_SERIALIZER_OPTION_NAMESPACE,
  420.                                  XML_SERIALIZER_OPTION_ENTITIES,
  421.                                  XML_SERIALIZER_OPTION_RETURN_RESULT,
  422.                                  XML_SERIALIZER_OPTION_IGNORE_NULL,
  423.                                  XML_SERIALIZER_OPTION_CDATA_SECTIONS
  424.                                 );
  425.     
  426.    /**
  427.     * default options for the serialization
  428.     *
  429.     * @access private
  430.     * @var    array
  431.     */
  432.     var $_defaultOptions = array(
  433.                          XML_SERIALIZER_OPTION_INDENT               => '',                    // string used for indentation
  434.                          XML_SERIALIZER_OPTION_LINEBREAKS           => "\n",                  // string used for newlines
  435.                          XML_SERIALIZER_OPTION_TYPEHINTS            => false,                 // automatically add type hin attributes
  436.                          XML_SERIALIZER_OPTION_XML_DECL_ENABLED     => false,                 // add an XML declaration
  437.                          XML_SERIALIZER_OPTION_XML_ENCODING         => null,                  // encoding specified in the XML declaration
  438.                          XML_SERIALIZER_OPTION_DEFAULT_TAG          => 'XML_Serializer_Tag',  // tag used for indexed arrays or invalid names
  439.                          XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME => false,                 // use classname for objects in indexed arrays
  440.                          XML_SERIALIZER_OPTION_ATTRIBUTE_KEY        => '_originalKey',        // attribute where original key is stored
  441.                          XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE       => '_type',               // attribute for type (only if typeHints => true)
  442.                          XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS      => '_class',              // attribute for class of objects (only if typeHints => true)
  443.                          XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES => false,                 // scalar values (strings, ints,..) will be serialized as attribute
  444.                          XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES   => '',                    // prepend string for attributes
  445.                          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
  446.                          XML_SERIALIZER_OPTION_MODE                 => XML_SERIALIZER_MODE_DEFAULT,             // use XML_SERIALIZER_MODE_SIMPLEXML to use parent name as tagname if transforming an indexed array
  447.                          XML_SERIALIZER_OPTION_DOCTYPE_ENABLED      => false,                 // add a doctype declaration
  448.                          XML_SERIALIZER_OPTION_DOCTYPE              => null,                  // supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()}
  449.                          XML_SERIALIZER_OPTION_ROOT_NAME            => null,                  // name of the root tag
  450.                          XML_SERIALIZER_OPTION_ROOT_ATTRIBS         => array(),               // attributes of the root tag
  451.                          XML_SERIALIZER_OPTION_ATTRIBUTES_KEY       => null,                  // all values in this key will be treated as attributes
  452.                          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
  453.                          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
  454.                          XML_SERIALIZER_OPTION_TAGMAP               => array(),               // tag names that will be changed
  455.                          XML_SERIALIZER_OPTION_ENCODE_FUNC          => null,                  // function that will be applied before serializing
  456.                          XML_SERIALIZER_OPTION_NAMESPACE            => null,                  // namespace to use
  457.                          XML_SERIALIZER_OPTION_ENTITIES             => XML_SERIALIZER_ENTITIES_XML, // type of entities to replace,
  458.                          XML_SERIALIZER_OPTION_RETURN_RESULT        => false,                 // serialize() returns the result of the serialization instead of true
  459.                          XML_SERIALIZER_OPTION_IGNORE_NULL          => false,                 // ignore properties that are set to null
  460.                          XML_SERIALIZER_OPTION_CDATA_SECTIONS       => false                  // Whether to use cdata sections for plain character data
  461.                         );
  462.  
  463.    /**
  464.     * options for the serialization
  465.     *
  466.     * @access public
  467.     * @var    array
  468.     */
  469.     var $options = array();
  470.  
  471.    /**
  472.     * current tag depth
  473.     *
  474.     * @access private
  475.     * @var    integer
  476.     */
  477.     var $_tagDepth = 0;
  478.  
  479.    /**
  480.     * serilialized representation of the data
  481.     *
  482.     * @access private
  483.     * @var    string
  484.     */
  485.     var $_serializedData = null;
  486.     
  487.    /**
  488.     * constructor
  489.     *
  490.     * @access   public
  491.     * @param    mixed   $options    array containing options for the serialization
  492.     */
  493.     function XML_Serializer( $options = null )
  494.     {
  495.         $this->PEAR();
  496.         if (is_array($options)) {
  497.             $this->options = array_merge($this->_defaultOptions, $options);
  498.         } else {
  499.             $this->options = $this->_defaultOptions;
  500.         }
  501.     }
  502.  
  503.    /**
  504.     * return API version
  505.     *
  506.     * @access   public
  507.     * @static
  508.     * @return   string  $version API version
  509.     */
  510.     function apiVersion()
  511.     {
  512.         return '0.18.0';
  513.     }
  514.  
  515.    /**
  516.     * reset all options to default options
  517.     *
  518.     * @access   public
  519.     * @see      setOption(), XML_Serializer()
  520.     */
  521.     function resetOptions()
  522.     {
  523.         $this->options = $this->_defaultOptions;
  524.     }
  525.  
  526.    /**
  527.     * set an option
  528.     *
  529.     * You can use this method if you do not want to set all options in the constructor
  530.     *
  531.     * @access   public
  532.     * @see      resetOption(), XML_Serializer()
  533.     */
  534.     function setOption($name, $value)
  535.     {
  536.         $this->options[$name] = $value;
  537.     }
  538.     
  539.    /**
  540.     * sets several options at once
  541.     *
  542.     * You can use this method if you do not want to set all options in the constructor
  543.     *
  544.     * @access   public
  545.     * @see      resetOption(), XML_Unserializer(), setOption()
  546.     */
  547.     function setOptions($options)
  548.     {
  549.         $this->options = array_merge($this->options, $options);
  550.     }
  551.  
  552.    /**
  553.     * serialize data
  554.     *
  555.     * @access   public
  556.     * @param    mixed    $data data to serialize
  557.     * @return   boolean  true on success, pear error on failure
  558.     */
  559.     function serialize($data, $options = null)
  560.     {
  561.         // if options have been specified, use them instead
  562.         // of the previously defined ones
  563.         if (is_array($options)) {
  564.             $optionsBak = $this->options;
  565.             if (isset($options['overrideOptions']) && $options['overrideOptions'] == true) {
  566.                 $this->options = array_merge($this->_defaultOptions, $options);
  567.             } else {
  568.                 $this->options = array_merge($this->options, $options);
  569.             }
  570.         } else {
  571.             $optionsBak = null;
  572.         }
  573.         
  574.         //  start depth is zero
  575.         $this->_tagDepth = 0;
  576.  
  577.         $rootAttributes = $this->options[XML_SERIALIZER_OPTION_ROOT_ATTRIBS];
  578.         if (isset($this->options[XML_SERIALIZER_OPTION_NAMESPACE]) && is_array($this->options[XML_SERIALIZER_OPTION_NAMESPACE])) {
  579.             $rootAttributes['xmlns:'.$this->options[XML_SERIALIZER_OPTION_NAMESPACE][0]] = $this->options[XML_SERIALIZER_OPTION_NAMESPACE][1];
  580.         }
  581.         
  582.         $this->_serializedData = '';
  583.         // serialize an array
  584.         if (is_array($data)) {
  585.             if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) {
  586.                 $tagName = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME];
  587.             } else {
  588.                 $tagName = 'array';
  589.             }
  590.  
  591.             $this->_serializedData .= $this->_serializeArray($data, $tagName, $rootAttributes);
  592.         } elseif (is_object($data)) {
  593.             // serialize an object
  594.             if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) {
  595.                 $tagName = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME];
  596.             } else {
  597.                 $tagName = get_class($data);
  598.             }
  599.             $this->_serializedData .= $this->_serializeObject($data, $tagName, $rootAttributes);
  600.         } else {
  601.             $tag = array();
  602.             if (isset($this->options[XML_SERIALIZER_OPTION_ROOT_NAME])) {
  603.                 $tag['qname'] = $this->options[XML_SERIALIZER_OPTION_ROOT_NAME];
  604.             } else {
  605.                 $tag['qname'] = gettype($data);
  606.             }
  607.             if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
  608.                 $rootAttributes[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = gettype($data);
  609.             }
  610.             @settype($data, 'string');
  611.             $tag['content']    = $data;
  612.             $tag['attributes'] = $rootAttributes;
  613.             $this->_serializedData = $this->_createXMLTag($tag);
  614.         }
  615.         
  616.         // add doctype declaration
  617.         if ($this->options[XML_SERIALIZER_OPTION_DOCTYPE_ENABLED] === true) {
  618.             $this->_serializedData = XML_Util::getDoctypeDeclaration($tagName, $this->options[XML_SERIALIZER_OPTION_DOCTYPE])
  619.                                    . $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]
  620.                                    . $this->_serializedData;
  621.         }
  622.  
  623.         //  build xml declaration
  624.         if ($this->options[XML_SERIALIZER_OPTION_XML_DECL_ENABLED]) {
  625.             $atts = array();
  626.             $this->_serializedData = XML_Util::getXMLDeclaration('1.0', $this->options[XML_SERIALIZER_OPTION_XML_ENCODING])
  627.                                    . $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]
  628.                                    . $this->_serializedData;
  629.         }
  630.  
  631.         if ($this->options[XML_SERIALIZER_OPTION_RETURN_RESULT] === true) {
  632.             $result = $this->_serializedData;
  633.         } else {
  634.             $result = true;
  635.         }
  636.         
  637.         if ($optionsBak !== null) {
  638.             $this->options = $optionsBak;
  639.         }
  640.  
  641.         return $result;
  642.     }
  643.  
  644.    /**
  645.     * get the result of the serialization
  646.     *
  647.     * @access public
  648.     * @return string serialized XML
  649.     */
  650.     function getSerializedData()
  651.     {
  652.         if ($this->_serializedData == null) {
  653.             return  $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.', XML_SERIALIZER_ERROR_NO_SERIALIZATION);
  654.         }
  655.         return $this->_serializedData;
  656.     }
  657.     
  658.    /**
  659.     * serialize any value
  660.     *
  661.     * This method checks for the type of the value and calls the appropriate method
  662.     *
  663.     * @access private
  664.     * @param  mixed     $value
  665.     * @param  string    $tagName
  666.     * @param  array     $attributes
  667.     * @return string
  668.     */
  669.     function _serializeValue($value, $tagName = null, $attributes = array())
  670.     {
  671.         if (is_array($value)) {
  672.             $xml = $this->_serializeArray($value, $tagName, $attributes);
  673.         } elseif (is_object($value)) {
  674.             $xml = $this->_serializeObject($value, $tagName);
  675.         } else {
  676.             $tag = array(
  677.                           'qname'      => $tagName,
  678.                           'attributes' => $attributes,
  679.                           'content'    => $value
  680.                         );
  681.             $xml = $this->_createXMLTag($tag);
  682.         }
  683.         return $xml;
  684.     }
  685.     
  686.    /**
  687.     * serialize an array
  688.     *
  689.     * @access   private
  690.     * @param    array   $array       array to serialize
  691.     * @param    string  $tagName     name of the root tag
  692.     * @param    array   $attributes  attributes for the root tag
  693.     * @return   string  $string      serialized data
  694.     * @uses     XML_Util::isValidName() to check, whether key has to be substituted
  695.     */
  696.     function _serializeArray(&$array, $tagName = null, $attributes = array())
  697.     {
  698.         $_content = null;
  699.         $_comment = null;
  700.  
  701.         // check for comment
  702.         if ($this->options[XML_SERIALIZER_OPTION_COMMENT_KEY] !== null) {
  703.             if (isset($array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]])) {
  704.                 $_comment = $array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]];
  705.                 unset($array[$this->options[XML_SERIALIZER_OPTION_COMMENT_KEY]]);
  706.             }
  707.         }
  708.         
  709.         /**
  710.          * check for special attributes
  711.          */
  712.         if ($this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY] !== null) {
  713.             if (isset($array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]])) {
  714.                 $attributes = $array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]];
  715.                 unset($array[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTES_KEY]]);
  716.             }
  717.             /**
  718.              * check for special content
  719.              */
  720.             if ($this->options[XML_SERIALIZER_OPTION_CONTENT_KEY] !== null) {
  721.                 if (isset($array[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]])) {
  722.                     $_content = $array[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]];
  723.                     unset($array[$this->options[XML_SERIALIZER_OPTION_CONTENT_KEY]]);
  724.                 }
  725.             }
  726.         }
  727.  
  728.         if ($this->options[XML_SERIALIZER_OPTION_IGNORE_NULL] === true) {
  729.             foreach (array_keys($array) as $key) {
  730.                 if (is_null($array[$key])) {
  731.                     unset($array[$key]);
  732.                 }
  733.             }
  734.         }
  735.  
  736.         /*
  737.         * if mode is set to simpleXML, check whether
  738.         * the array is associative or indexed
  739.         */
  740.         if (is_array($array) && !empty($array) && $this->options[XML_SERIALIZER_OPTION_MODE] == XML_SERIALIZER_MODE_SIMPLEXML) {
  741.             $indexed = true;
  742.             foreach ($array as $key => $val) {
  743.                 if (!is_int($key)) {
  744.                     $indexed = false;
  745.                     break;
  746.                 }
  747.             }
  748.  
  749.             if ($indexed && $this->options[XML_SERIALIZER_OPTION_MODE] == XML_SERIALIZER_MODE_SIMPLEXML) {
  750.                 $string = '';
  751.                 foreach ($array as $key => $val) {
  752.                     $string .= $this->_serializeValue( $val, $tagName, $attributes);
  753.                     
  754.                     $string .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
  755.                     // do indentation
  756.                     if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null && $this->_tagDepth>0) {
  757.                         $string .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth);
  758.                     }
  759.                 }
  760.                 return rtrim($string);
  761.             }
  762.         }
  763.         
  764.         $scalarAsAttributes = false;
  765.         if (is_array($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES]) && isset($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES][$tagName])) {
  766.             $scalarAsAttributes = $this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES][$tagName];
  767.         } elseif ($this->options[XML_SERIALIZER_OPTION_SCALAR_AS_ATTRIBUTES] === true) {
  768.             $scalarAsAttributes = true;
  769.         }
  770.         
  771.         if ($scalarAsAttributes === true) {
  772.             $this->expectError('*');
  773.             foreach ($array as $key => $value) {
  774.                 if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
  775.                     unset($array[$key]);
  776.                     $attributes[$this->options[XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES].$key] = $value;
  777.                 }
  778.             }
  779.             $this->popExpect();
  780.         } elseif (is_array($scalarAsAttributes)) {
  781.             $this->expectError('*');
  782.             foreach ($scalarAsAttributes as $key) {
  783.                 if (!isset($array[$key])) {
  784.                     continue;
  785.                 }
  786.                 $value = $array[$key];
  787.                 if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
  788.                     unset($array[$key]);
  789.                     $attributes[$this->options[XML_SERIALIZER_OPTION_PREPEND_ATTRIBUTES].$key] = $value;
  790.                 }
  791.             }
  792.             $this->popExpect();
  793.         }
  794.  
  795.         // check for empty array => create empty tag
  796.         if (empty($array)) {
  797.             $tag = array(
  798.                             'qname'      => $tagName,
  799.                             'content'    => $_content,
  800.                             'attributes' => $attributes
  801.                         );
  802.         } else {
  803.             $this->_tagDepth++;
  804.             $tmp = $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
  805.             foreach ($array as $key => $value) {
  806.                 // do indentation
  807.                 if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null && $this->_tagDepth>0) {
  808.                     $tmp .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth);
  809.                 }
  810.  
  811.                 if (isset($this->options[XML_SERIALIZER_OPTION_TAGMAP][$key])) {
  812.                     $key = $this->options[XML_SERIALIZER_OPTION_TAGMAP][$key];
  813.                 }
  814.  
  815.                 // copy key
  816.                 $origKey = $key;
  817.                 $this->expectError('*');
  818.                 // key cannot be used as tagname => use default tag
  819.                 $valid = XML_Util::isValidName($key);
  820.                 $this->popExpect();
  821.                 if (PEAR::isError($valid)) {
  822.                     if ($this->options[XML_SERIALIZER_OPTION_CLASSNAME_AS_TAGNAME] && is_object($value)) {
  823.                         $key = get_class($value);
  824.                     } else {
  825.                         $key = $this->_getDefaultTagname($tagName);
  826.                     }
  827.                 }
  828.                 $atts = array();
  829.                 if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
  830.                     $atts[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = gettype($value);
  831.                     if ($key !== $origKey) {
  832.                         $atts[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_KEY]] = (string)$origKey;
  833.                     }
  834.                 }
  835.  
  836.                 $tmp .= $this->_createXMLTag(array(
  837.                                                     'qname'      => $key,
  838.                                                     'attributes' => $atts,
  839.                                                     'content'    => $value )
  840.                                             );
  841.                 $tmp .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
  842.             }
  843.             
  844.             $this->_tagDepth--;
  845.             if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null && $this->_tagDepth>0) {
  846.                 $tmp .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth);
  847.             }
  848.     
  849.             if (trim($tmp) === '') {
  850.                 $tmp = null;
  851.             }
  852.             
  853.             $tag = array(
  854.                           'qname'      => $tagName,
  855.                           'content'    => $tmp,
  856.                           'attributes' => $attributes
  857.                         );
  858.         }
  859.         if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
  860.             if (!isset($tag['attributes'][$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]])) {
  861.                 $tag['attributes'][$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]] = 'array';
  862.             }
  863.         }
  864.  
  865.         $string = '';
  866.         if (!is_null($_comment)) {
  867.             $string .= XML_Util::createComment($_comment);
  868.             $string .= $this->options[XML_SERIALIZER_OPTION_LINEBREAKS];
  869.             if ($this->options[XML_SERIALIZER_OPTION_INDENT]!==null && $this->_tagDepth>0) {
  870.                 $string .= str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth);
  871.             }
  872.         }
  873.         $string .= $this->_createXMLTag($tag, false);
  874.         return $string;
  875.     }
  876.  
  877.    /**
  878.     * get the name of the default tag.
  879.     *
  880.     * The name of the parent tag needs to be passed as the
  881.     * default name can depend on the context.
  882.     *
  883.     * @param  string     name of the parent tag
  884.     * @return string     default tag name
  885.     */
  886.     function _getDefaultTagname($parent)
  887.     {
  888.         if (is_string($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG])) {
  889.             return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG];
  890.         }
  891.         if (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG][$parent])) {
  892.             return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG][$parent];
  893.         } elseif (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['#default'])) {
  894.             return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['#default'];
  895.         } elseif (isset($this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['__default'])) {
  896.             // keep this for BC
  897.             return $this->options[XML_SERIALIZER_OPTION_DEFAULT_TAG]['__default'];
  898.         }
  899.         return 'XML_Serializer_Tag';
  900.     }
  901.     
  902.    /**
  903.     * serialize an object
  904.     *
  905.     * @access   private
  906.     * @param    object  $object object to serialize
  907.     * @return   string  $string serialized data
  908.     */
  909.     function _serializeObject(&$object, $tagName = null, $attributes = array())
  910.     {
  911.         // check for magic function
  912.         if (method_exists($object, '__sleep')) {
  913.             $propNames = $object->__sleep();
  914.             if (is_array($propNames)) {
  915.                 $properties = array();
  916.                 foreach ($propNames as $propName) {
  917.                     $properties[$propName] = $object->$propName;
  918.                 }
  919.             } else {
  920.                 $properties = get_object_vars($object);
  921.             }
  922.         } else {
  923.             $properties = get_object_vars($object);
  924.         }
  925.  
  926.         if (empty($tagName)) {
  927.             $tagName = get_class($object);
  928.         }
  929.  
  930.         // typehints activated?
  931.         if ($this->options[XML_SERIALIZER_OPTION_TYPEHINTS] === true) {
  932.             $attributes[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_TYPE]]  = 'object';
  933.             $attributes[$this->options[XML_SERIALIZER_OPTION_ATTRIBUTE_CLASS]] =  get_class($object);
  934.         }
  935.         $string = $this->_serializeArray($properties, $tagName, $attributes);
  936.         return $string;
  937.     }
  938.   
  939.    /**
  940.     * create a tag from an array
  941.     * this method awaits an array in the following format
  942.     * array(
  943.     *       'qname'        => $tagName,
  944.     *       'attributes'   => array(),
  945.     *       'content'      => $content,      // optional
  946.     *       'namespace'    => $namespace     // optional
  947.     *       'namespaceUri' => $namespaceUri  // optional
  948.     *   )
  949.     *
  950.     * @access   private
  951.     * @param    array   $tag tag definition
  952.     * @param    boolean $replaceEntities whether to replace XML entities in content or not
  953.     * @return   string  $string XML tag
  954.     */
  955.     function _createXMLTag($tag, $firstCall = true)
  956.     {
  957.         // build fully qualified tag name
  958.         if ($this->options[XML_SERIALIZER_OPTION_NAMESPACE] !== null) {
  959.             if (is_array($this->options[XML_SERIALIZER_OPTION_NAMESPACE])) {
  960.                 $tag['qname'] = $this->options[XML_SERIALIZER_OPTION_NAMESPACE][0] . ':' . $tag['qname'];
  961.             } else {
  962.                 $tag['qname'] = $this->options[XML_SERIALIZER_OPTION_NAMESPACE] . ':' . $tag['qname'];
  963.             }
  964.         }
  965.  
  966.         // attribute indentation
  967.         if ($this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES] !== false) {
  968.             $multiline = true;
  969.             $indent    = str_repeat($this->options[XML_SERIALIZER_OPTION_INDENT], $this->_tagDepth);
  970.  
  971.             if ($this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES] == '_auto') {
  972.                 $indent .= str_repeat(' ', (strlen($tag['qname'])+2));
  973.  
  974.             } else {
  975.                 $indent .= $this->options[XML_SERIALIZER_OPTION_INDENT_ATTRIBUTES];
  976.             }
  977.         } else {
  978.             $multiline = false;
  979.             $indent    = false;
  980.         }
  981.  
  982.         if (is_array($tag['content'])) {
  983.             if (empty($tag['content'])) {
  984.                 $tag['content'] =   '';
  985.             }
  986.         } elseif(is_scalar($tag['content']) && (string)$tag['content'] == '') {
  987.             $tag['content'] =   '';
  988.         }
  989.  
  990.         // replace XML entities (only needed, if this is not a nested call)
  991.         if ($firstCall === true) {
  992.             if ($this->options[XML_SERIALIZER_OPTION_CDATA_SECTIONS] === true) {
  993.                 $replaceEntities = XML_UTIL_CDATA_SECTION;
  994.             } else {
  995.                    $replaceEntities = $this->options[XML_SERIALIZER_OPTION_ENTITIES];
  996.             }
  997.         } else {
  998.             $replaceEntities = XML_SERIALIZER_ENTITIES_NONE;
  999.         }
  1000.         if (is_scalar($tag['content']) || is_null($tag['content'])) {
  1001.             if ($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC]) {
  1002.                 if ($firstCall === true) {
  1003.                     $tag['content'] = call_user_func($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['content']);
  1004.                 }
  1005.                 $tag['attributes'] = array_map($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['attributes']);
  1006.             }
  1007.             $tag = XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options[XML_SERIALIZER_OPTION_LINEBREAKS]);
  1008.         } elseif (is_array($tag['content'])) {
  1009.             $tag    =   $this->_serializeArray($tag['content'], $tag['qname'], $tag['attributes']);
  1010.         } elseif (is_object($tag['content'])) {
  1011.             $tag    =   $this->_serializeObject($tag['content'], $tag['qname'], $tag['attributes']);
  1012.         } elseif (is_resource($tag['content'])) {
  1013.             settype($tag['content'], 'string');
  1014.             if ($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC]) {
  1015.                 if ($replaceEntities === true) {
  1016.                     $tag['content'] = call_user_func($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['content']);
  1017.                 }
  1018.                 $tag['attributes'] = array_map($this->options[XML_SERIALIZER_OPTION_ENCODE_FUNC], $tag['attributes']);
  1019.             }
  1020.             $tag = XML_Util::createTagFromArray($tag, $replaceEntities);
  1021.         }
  1022.         return  $tag;
  1023.     }
  1024. }
  1025. ?>