XML_Serializer
[ class tree: XML_Serializer ] [ index: XML_Serializer ] [ all elements ]

Source for file Serializer.php

Documentation is available at Serializer.php

  1. <?PHP
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2002 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.0 of the PHP license,       |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/2_02.txt.                                 |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Stephan Schmidt <schst@php-tools.net>                       |
  17. // +----------------------------------------------------------------------+
  18. //
  19. //    $Id: Serializer.php,v 1.32 2004/12/23 13:33:51 schst Exp $
  20.  
  21. /**
  22.  * uses PEAR error management
  23.  */
  24. require_once 'PEAR.php';
  25.  
  26. /**
  27.  * uses XML_Util to create XML tags
  28.  */
  29. require_once 'XML/Util.php';
  30.  
  31. /**
  32.  * error code for no serialization done
  33.  */
  34. define('XML_SERIALIZER_ERROR_NO_SERIALIZATION'51);
  35.  
  36. /**
  37.  * do not replace entitites
  38.  */
  39. define('XML_SERIALIZER_ENTITIES_NONE'XML_UTIL_ENTITIES_NONE);
  40.  
  41. /**
  42.  * replace all XML entitites
  43.  * This setting will replace <, >, ", ' and &
  44.  */
  45. define('XML_SERIALIZER_ENTITIES_XML'XML_UTIL_ENTITIES_XML);
  46.  
  47. /**
  48.  * replace only required XML entitites
  49.  * This setting will replace <, " and &
  50.  */
  51. define('XML_SERIALIZER_ENTITIES_XML_REQUIRED'XML_UTIL_ENTITIES_XML_REQUIRED);
  52.  
  53. /**
  54.  * replace HTML entitites
  55.  * @link    http://www.php.net/htmlentities
  56.  */
  57. define('XML_SERIALIZER_ENTITIES_HTML'XML_UTIL_ENTITIES_HTML);
  58.  
  59. /**
  60.  * XML_Serializer
  61.  * class that serializes various structures into an XML document
  62.  *
  63.  * this class can be used in two modes:
  64.  *
  65.  *  1. create an XML document from an array or object that is processed by other
  66.  *    applications. That means, you can create a RDF document from an array in the
  67.  *    following format:
  68.  *
  69.  *    $data = array(
  70.  *              'channel' => array(
  71.  *                            'title' => 'Example RDF channel',
  72.  *                            'link'  => 'http://www.php-tools.de',
  73.  *                            'image' => array(
  74.  *                                        'title' => 'Example image',
  75.  *                                        'url'   => 'http://www.php-tools.de/image.gif',
  76.  *                                        'link'  => 'http://www.php-tools.de'
  77.  *                                           ),
  78.  *                            array(
  79.  *                                 'title' => 'Example item',
  80.  *                                 'link'  => 'http://example.com'
  81.  *                                 ),
  82.  *                            array(
  83.  *                                 'title' => 'Another Example item',
  84.  *                                 'link'  => 'http://example.org'
  85.  *                                 )
  86.  *                              )
  87.  *             );
  88.  *
  89.  *   to create a RDF document from this array do the following:
  90.  *
  91.  *   require_once 'XML/Serializer.php';
  92.  *
  93.  *   $options = array(
  94.  *                     'indent'         => "\t",        // indent with tabs
  95.  *                     'linebreak'      => "\n",        // use UNIX line breaks
  96.  *                     'rootName'       => 'rdf:RDF',   // root tag
  97.  *                     'defaultTagName' => 'item'       // tag for values with numeric keys
  98.  *   );
  99.  *
  100.  *   $serializer = new XML_Serializer($options);
  101.  *   $rdf        = $serializer->serialize($data);
  102.  *
  103.  * You will get a complete XML document that can be processed like any RDF document.
  104.  *
  105.  *
  106.  * 2. this classes can be used to serialize any data structure in a way that it can
  107.  *    later be unserialized again.
  108.  *    XML_Serializer will store the type of the value and additional meta information
  109.  *    in attributes of the surrounding tag. This meat information can later be used
  110.  *    to restore the original data structure in PHP. If you want XML_Serializer
  111.  *    to add meta information to the tags, add
  112.  *
  113.  *      'typeHints' => true
  114.  *
  115.  *    to the options array in the constructor.
  116.  *
  117.  *    Future versions of this package will include an XML_Unserializer, that does
  118.  *    the unserialization automatically for you.
  119.  *
  120.  * @category XML
  121.  * @package  XML_Serializer
  122.  * @version  0.14.1
  123.  * @author   Stephan Schmidt <schst@php.net>
  124.  * @uses     XML_Util
  125.  */
  126. class XML_Serializer extends PEAR
  127. {
  128.    /**
  129.     * default options for the serialization
  130.     * @access private
  131.     * @var array $_defaultOptions 
  132.     */
  133.     var $_defaultOptions = array(
  134.                          'indent'             => '',                    // string used for indentation
  135.                          'linebreak'          => "\n",                  // string used for newlines
  136.                          'typeHints'          => false,                 // automatically add type hin attributes
  137.                          'addDecl'            => false,                 // add an XML declaration
  138.                          'encoding'           => null,                  // encoding specified in the XML declaration
  139.                          'defaultTagName'     => 'XML_Serializer_Tag',  // tag used for indexed arrays or invalid names
  140.                          'classAsTagName'     => false,                 // use classname for objects in indexed arrays
  141.                          'keyAttribute'       => '_originalKey',        // attribute where original key is stored
  142.                          'typeAttribute'      => '_type',               // attribute for type (only if typeHints => true)
  143.                          'classAttribute'     => '_class',              // attribute for class of objects (only if typeHints => true)
  144.                          'scalarAsAttributes' => false,                 // scalar values (strings, ints,..) will be serialized as attribute
  145.                          'prependAttributes'  => '',                    // prepend string for attributes
  146.                          'indentAttributes'   => false,                 // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column
  147.                          'mode'               => 'default',             // use 'simplexml' to use parent name as tagname if transforming an indexed array
  148.                          'addDoctype'         => false,                 // add a doctype declaration
  149.                          'doctype'            => null,                  // supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()}
  150.                          'rootName'           => null,                  // name of the root tag
  151.                          'rootAttributes'     => array(),               // attributes of the root tag
  152.                          'attributesArray'    => null,                  // all values in this key will be treated as attributes
  153.                          'contentName'        => null,                  // this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray
  154.                          'tagMap'             => array(),               // tag names that will be changed
  155.                          'encodeFunction'     => null,                  // function that will be applied before serializing
  156.                          'namespace'          => null,                  // namespace to use
  157.                          'replaceEntities'    => XML_UTIL_ENTITIES_XML  // type of entities to replace
  158.                         );
  159.  
  160.    /**
  161.     * options for the serialization
  162.     * @access private
  163.     * @var array $options 
  164.     */
  165.     var $options = array();
  166.  
  167.    /**
  168.     * current tag depth
  169.     * @var integer $_tagDepth 
  170.     */
  171.     var $_tagDepth = 0;
  172.  
  173.    /**
  174.     * serilialized representation of the data
  175.     * @var string $_serializedData 
  176.     */
  177.     var $_serializedData = null;
  178.     
  179.    /**
  180.     * constructor
  181.     *
  182.     * @access   public
  183.     * @param    mixed   $options    array containing options for the serialization
  184.     */
  185.     function XML_Serializer$options = null )
  186.     {
  187.         $this->PEAR();
  188.         if (is_array($options)) {
  189.             $this->options array_merge($this->_defaultOptions$options);
  190.         else {
  191.             $this->options $this->_defaultOptions;
  192.         }
  193.     }
  194.  
  195.    /**
  196.     * return API version
  197.     *
  198.     * @access   public
  199.     * @static
  200.     * @return   string  $version API version
  201.     */
  202.     function apiVersion()
  203.     {
  204.         return '0.14';
  205.     }
  206.  
  207.    /**
  208.     * reset all options to default options
  209.     *
  210.     * @access   public
  211.     * @see      setOption(), XML_Unserializer()
  212.     */
  213.     function resetOptions()
  214.     {
  215.         $this->options $this->_defaultOptions;
  216.     }
  217.  
  218.    /**
  219.     * set an option
  220.     *
  221.     * You can use this method if you do not want to set all options in the constructor
  222.     *
  223.     * @access   public
  224.     * @see      resetOption(), XML_Serializer()
  225.     */
  226.     function setOption($name$value)
  227.     {
  228.         $this->options[$name$value;
  229.     }
  230.     
  231.    /**
  232.     * sets several options at once
  233.     *
  234.     * You can use this method if you do not want to set all options in the constructor
  235.     *
  236.     * @access   public
  237.     * @see      resetOption(), XML_Unserializer(), setOption()
  238.     */
  239.     function setOptions($options)
  240.     {
  241.         $this->options array_merge($this->options$options);
  242.     }
  243.  
  244.    /**
  245.     * serialize data
  246.     *
  247.     * @access   public
  248.     * @param    mixed    $data data to serialize
  249.     * @return   boolean  true on success, pear error on failure
  250.     */
  251.     function serialize($data$options = null)
  252.     {
  253.         // if options have been specified, use them instead
  254.         // of the previously defined ones
  255.         if (is_array($options)) {
  256.             $optionsBak $this->options;
  257.             if (isset($options['overrideOptions']&& $options['overrideOptions'== true{
  258.                 $this->options array_merge($this->_defaultOptions$options);
  259.             else {
  260.                 $this->options array_merge($this->options$options);
  261.             }
  262.         }
  263.         else {
  264.             $optionsBak = null;
  265.         }
  266.         
  267.         // maintain BC
  268.         if (isset($this->options['tagName'])) {
  269.             $this->options['rootName'$this->options['tagName'];
  270.         }
  271.         
  272.         //  start depth is zero
  273.         $this->_tagDepth = 0;
  274.  
  275.         $rootAttributes $this->options['rootAttributes'];
  276.         if (is_array($this->options['namespace'])) {
  277.             $rootAttributes['xmlns:'.$this->options['namespace'][0]] $this->options['namespace'][1];
  278.         }
  279.         
  280.         $this->_serializedData '';
  281.         // serialize an array
  282.         if (is_array($data)) {
  283.             if (isset($this->options['rootName'])) {
  284.                 $tagName $this->options['rootName'];
  285.             else {
  286.                 $tagName 'array';
  287.             }
  288.  
  289.             $this->_serializedData .= $this->_serializeArray($data$tagName$rootAttributes);
  290.         }
  291.         // serialize an object
  292.         elseif (is_object($data)) {
  293.             if (isset($this->options['rootName'])) {
  294.                 $tagName $this->options['rootName'];
  295.             else {
  296.                 $tagName get_class($data);
  297.             }
  298.             $this->_serializedData .= $this->_serializeObject($data$tagName$rootAttributes);
  299.         }
  300.         
  301.         // add doctype declaration
  302.         if ($this->options['addDoctype'=== true{
  303.             $this->_serializedData = XML_Util::getDoctypeDeclaration($tagName$this->options['doctype'])
  304.                                    . $this->options['linebreak']
  305.                                    . $this->_serializedData;
  306.         }
  307.  
  308.         //  build xml declaration
  309.         if ($this->options['addDecl']{
  310.             $atts = array();
  311.             $this->_serializedData = XML_Util::getXMLDeclaration('1.0'$this->options['encoding'])
  312.                                    . $this->options['linebreak']
  313.                                    . $this->_serializedData;
  314.         }
  315.  
  316.         if ($optionsBak !== null{
  317.             $this->options $optionsBak;
  318.         }
  319.  
  320.         return  true;
  321.     }
  322.  
  323.    /**
  324.     * get the result of the serialization
  325.     *
  326.     * @access public
  327.     * @return string serialized XML
  328.     */
  329.     function getSerializedData()
  330.     {
  331.         if ($this->_serializedData == null {
  332.             return  $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.'XML_SERIALIZER_ERROR_NO_SERIALIZATION);
  333.         }
  334.         return $this->_serializedData;
  335.     }
  336.     
  337.    /**
  338.     * serialize any value
  339.     *
  340.     * This method checks for the type of the value and calls the appropriate method
  341.     *
  342.     * @access private
  343.     * @param  mixed     $value 
  344.     * @param  string    $tagName 
  345.     * @param  array     $attributes 
  346.     * @return string 
  347.     */
  348.     function _serializeValue($value$tagName = null$attributes = array())
  349.     {
  350.         if (is_array($value)) {
  351.             $xml $this->_serializeArray($value$tagName$attributes);
  352.         elseif (is_object($value)) {
  353.             $xml $this->_serializeObject($value$tagName);
  354.         else {
  355.             $tag = array(
  356.                           'qname'      => $tagName,
  357.                           'attributes' => $attributes,
  358.                           'content'    => $value
  359.                         );
  360.             $xml $this->_createXMLTag($tag);
  361.         }
  362.         return $xml;
  363.     }
  364.     
  365.    /**
  366.     * serialize an array
  367.     *
  368.     * @access   private
  369.     * @param    array   $array       array to serialize
  370.     * @param    string  $tagName     name of the root tag
  371.     * @param    array   $attributes  attributes for the root tag
  372.     * @return   string  $string      serialized data
  373.     * @uses     XML_Util::isValidName() to check, whether key has to be substituted
  374.     */
  375.     function _serializeArray(&$array$tagName = null$attributes = array())
  376.     {
  377.         $_content = null;
  378.         
  379.         /**
  380.          * check for special attributes
  381.          */
  382.         if ($this->options['attributesArray'!== null{
  383.             if (isset($array[$this->options['attributesArray']])) {
  384.                 $attributes $array[$this->options['attributesArray']];
  385.                 unset($array[$this->options['attributesArray']]);
  386.             }
  387.             /**
  388.              * check for special content
  389.              */
  390.             if ($this->options['contentName'!== null{
  391.                 if (isset($array[$this->options['contentName']])) {
  392.                     $_content $array[$this->options['contentName']];
  393.                     unset($array[$this->options['contentName']]);
  394.                 }
  395.             }
  396.         }
  397.  
  398.         /*
  399.         * if mode is set to simpleXML, check whether
  400.         * the array is associative or indexed
  401.         */
  402.         if (is_array($array&& !empty($array&& $this->options['mode'== 'simplexml'{
  403.             $indexed = true;
  404.             foreach ($array as $key => $val{
  405.                 if (!is_int($key)) {
  406.                     $indexed = false;
  407.                     break;
  408.                 }
  409.             }
  410.  
  411.             if ($indexed && $this->options['mode'== 'simplexml'{
  412.                 $string '';
  413.                 foreach ($array as $key => $val{
  414.                     $string .= $this->_serializeValue$val$tagName$attributes);
  415.                     
  416.                     $string .= $this->options['linebreak'];
  417.                     //    do indentation
  418.                     if ($this->options['indent']!==null && $this->_tagDepth>0{
  419.                         $string .= str_repeat($this->options['indent']$this->_tagDepth);
  420.                     }
  421.                 }
  422.                 return rtrim($string);
  423.             }
  424.         }
  425.         
  426.         if ($this->options['scalarAsAttributes'=== true{
  427.             $this->expectError('*');
  428.             foreach ($array as $key => $value{
  429.                 if (is_scalar($value&& (XML_Util::isValidName($key=== true)) {
  430.                     unset($array[$key]);
  431.                     $attributes[$this->options['prependAttributes'].$key$value;
  432.                 }
  433.             }
  434.             $this->popExpect();
  435.         }
  436.  
  437.         // check for empty array => create empty tag
  438.         if (empty($array)) {
  439.             $tag = array(
  440.                             'qname'      => $tagName,
  441.                             'content'    => $_content,
  442.                             'attributes' => $attributes
  443.                         );
  444.  
  445.         else {
  446.             $this->_tagDepth++;
  447.             $tmp $this->options['linebreak'];
  448.             foreach ($array as $key => $value{
  449.                 //    do indentation
  450.                 if ($this->options['indent']!==null && $this->_tagDepth>0{
  451.                     $tmp .= str_repeat($this->options['indent']$this->_tagDepth);
  452.                 }
  453.  
  454.                 if (isset($this->options['tagMap'][$key])) {
  455.                     $key $this->options['tagMap'][$key];
  456.                 }
  457.  
  458.                 // copy key
  459.                 $origKey    =    $key;
  460.                 $this->expectError('*');
  461.                 // key cannot be used as tagname => use default tag
  462.                 $valid = XML_Util::isValidName($key);
  463.                 $this->popExpect();
  464.                 if (PEAR::isError($valid)) {
  465.                     if ($this->options['classAsTagName'&& is_object($value)) {
  466.                         $key get_class($value);
  467.                     else {
  468.                         $key $this->options['defaultTagName'];
  469.                     }
  470.                     }
  471.                 $atts = array();
  472.                 if ($this->options['typeHints'=== true{
  473.                     $atts[$this->options['typeAttribute']] gettype($value);
  474.                     if ($key !== $origKey{
  475.                         $atts[$this->options['keyAttribute']] = (string)$origKey;
  476.                     }
  477.     
  478.                 }
  479.                 
  480.                 $tmp .= $this->_createXMLTag(array(
  481.                                                     'qname'      => $key,
  482.                                                     'attributes' => $atts,
  483.                                                     'content'    => $value )
  484.                                             );
  485.                 $tmp .= $this->options['linebreak'];
  486.             }
  487.             
  488.             $this->_tagDepth--;
  489.             if ($this->options['indent']!==null && $this->_tagDepth>0{
  490.                 $tmp .= str_repeat($this->options['indent']$this->_tagDepth);
  491.             }
  492.     
  493.             if (trim($tmp=== ''{
  494.                 $tmp = null;
  495.             }
  496.             
  497.             $tag = array(
  498.                             'qname'      => $tagName,
  499.                             'content'    => $tmp,
  500.                             'attributes' => $attributes
  501.                         );
  502.         }
  503.         if ($this->options['typeHints'=== true{
  504.             if (!isset($tag['attributes'][$this->options['typeAttribute']])) {
  505.                 $tag['attributes'][$this->options['typeAttribute']] 'array';
  506.             }
  507.         }
  508.  
  509.         $string $this->_createXMLTag($tagfalse);
  510.         return $string;
  511.     }
  512.  
  513.    /**
  514.     * serialize an object
  515.     *
  516.     * @access   private
  517.     * @param    object  $object object to serialize
  518.     * @return   string  $string serialized data
  519.     */
  520.     function _serializeObject(&$object$tagName = null$attributes = array())
  521.     {
  522.         //  check for magic function
  523.         if (method_exists($object'__sleep')) {
  524.             $object->__sleep();
  525.         }
  526.  
  527.         $tmp $this->options['linebreak'];
  528.         $properties get_object_vars($object);
  529.         if (empty($tagName)) {
  530.             $tagName get_class($object);
  531.         }
  532.         
  533.         // typehints activated?
  534.         if ($this->options['typeHints'=== true{
  535.             $attributes[$this->options['typeAttribute']]  'object';
  536.             $attributes[$this->options['classAttribute']] =  get_class($object);
  537.         }
  538.         
  539.         $string $this->_serializeArray($properties$tagName$attributes);
  540.         return $string;
  541.     }
  542.   
  543.    /**
  544.     * create a tag from an array
  545.     * this method awaits an array in the following format
  546.     * array(
  547.     *       'qname'        => $tagName,
  548.     *       'attributes'   => array(),
  549.     *       'content'      => $content,      // optional
  550.     *       'namespace'    => $namespace     // optional
  551.     *       'namespaceUri' => $namespaceUri  // optional
  552.     *   )
  553.     *
  554.     * @access   private
  555.     * @param    array   $tag tag definition
  556.     * @param    boolean $replaceEntities whether to replace XML entities in content or not
  557.     * @return   string  $string XML tag
  558.     */
  559.     function _createXMLTag$tag$replaceEntities = true )
  560.     {
  561.         if ($this->options['namespace'!== null{
  562.             if (is_array($this->options['namespace'])) {
  563.                 $tag['qname'$this->options['namespace'][0':' $tag['qname'];
  564.             else {
  565.                 $tag['qname'$this->options['namespace'':' $tag['qname'];
  566.             }
  567.         }
  568.  
  569.         if ($this->options['indentAttributes'!== false{
  570.             $multiline = true;
  571.             $indent    str_repeat($this->options['indent']$this->_tagDepth);
  572.  
  573.             if ($this->options['indentAttributes'== '_auto'{
  574.                 $indent .= str_repeat(' '(strlen($tag['qname'])+2));
  575.  
  576.             else {
  577.                 $indent .= $this->options['indentAttributes'];
  578.             }
  579.         else {
  580.             $multiline = false;
  581.             $indent    = false;
  582.         }
  583.  
  584.         if ($replaceEntities{
  585.                $replaceEntities $this->options['replaceEntities'];
  586.         }
  587.     
  588.         if (is_array($tag['content'])) {
  589.             if (empty($tag['content'])) {
  590.                 $tag['content'=   '';
  591.             }
  592.         elseif(is_scalar($tag['content']&& (string)$tag['content'== ''{
  593.             $tag['content'=   '';
  594.         }
  595.     
  596.         if (is_scalar($tag['content']|| is_null($tag['content'])) {
  597.             if ($this->options['encodeFunction']{
  598.                 if ($replaceEntities === true{
  599.                     $tag['content'call_user_func($this->options['encodeFunction']$tag['content']);
  600.                 }
  601.                 $tag['attributes'array_map($this->options['encodeFunction']$tag['attributes']);
  602.             }
  603.             $tag = XML_Util::createTagFromArray($tag$replaceEntities$multiline$indent$this->options['linebreak']);
  604.         elseif (is_array($tag['content'])) {
  605.             $tag    =   $this->_serializeArray($tag['content']$tag['qname']$tag['attributes']);
  606.         elseif (is_object($tag['content'])) {
  607.             $tag    =   $this->_serializeObject($tag['content']$tag['qname']$tag['attributes']);
  608.         elseif (is_resource($tag['content'])) {
  609.             settype($tag['content']'string');
  610.             if ($this->options['encodeFunction']{
  611.                 if ($replaceEntities === true{
  612.                     $tag['content'call_user_func($this->options['encodeFunction']$tag['content']);
  613.                 }
  614.                 $tag['attributes'array_map($this->options['encodeFunction']$tag['attributes']);
  615.             }
  616.             $tag = XML_Util::createTagFromArray($tag$replaceEntities);
  617.         }
  618.         return  $tag;
  619.     }
  620. }
  621. ?>

Documentation generated on Mon, 11 Mar 2019 14:05:07 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.