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.34 2004/12/28 16:55:54 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.                          'returnResult'       => false                  // serialize() returns the result of the serialization instead of true
  159.                         );
  160.  
  161.    /**
  162.     * options for the serialization
  163.     * @access private
  164.     * @var array $options 
  165.     */
  166.     var $options = array();
  167.  
  168.    /**
  169.     * current tag depth
  170.     * @var integer $_tagDepth 
  171.     */
  172.     var $_tagDepth = 0;
  173.  
  174.    /**
  175.     * serilialized representation of the data
  176.     * @var string $_serializedData 
  177.     */
  178.     var $_serializedData = null;
  179.     
  180.    /**
  181.     * constructor
  182.     *
  183.     * @access   public
  184.     * @param    mixed   $options    array containing options for the serialization
  185.     */
  186.     function XML_Serializer$options = null )
  187.     {
  188.         $this->PEAR();
  189.         if (is_array($options)) {
  190.             $this->options array_merge($this->_defaultOptions$options);
  191.         else {
  192.             $this->options $this->_defaultOptions;
  193.         }
  194.     }
  195.  
  196.    /**
  197.     * return API version
  198.     *
  199.     * @access   public
  200.     * @static
  201.     * @return   string  $version API version
  202.     */
  203.     function apiVersion()
  204.     {
  205.         return '0.14';
  206.     }
  207.  
  208.    /**
  209.     * reset all options to default options
  210.     *
  211.     * @access   public
  212.     * @see      setOption(), XML_Unserializer()
  213.     */
  214.     function resetOptions()
  215.     {
  216.         $this->options $this->_defaultOptions;
  217.     }
  218.  
  219.    /**
  220.     * set an option
  221.     *
  222.     * You can use this method if you do not want to set all options in the constructor
  223.     *
  224.     * @access   public
  225.     * @see      resetOption(), XML_Serializer()
  226.     */
  227.     function setOption($name$value)
  228.     {
  229.         $this->options[$name$value;
  230.     }
  231.     
  232.    /**
  233.     * sets several options at once
  234.     *
  235.     * You can use this method if you do not want to set all options in the constructor
  236.     *
  237.     * @access   public
  238.     * @see      resetOption(), XML_Unserializer(), setOption()
  239.     */
  240.     function setOptions($options)
  241.     {
  242.         $this->options array_merge($this->options$options);
  243.     }
  244.  
  245.    /**
  246.     * serialize data
  247.     *
  248.     * @access   public
  249.     * @param    mixed    $data data to serialize
  250.     * @return   boolean  true on success, pear error on failure
  251.     */
  252.     function serialize($data$options = null)
  253.     {
  254.         // if options have been specified, use them instead
  255.         // of the previously defined ones
  256.         if (is_array($options)) {
  257.             $optionsBak $this->options;
  258.             if (isset($options['overrideOptions']&& $options['overrideOptions'== true{
  259.                 $this->options array_merge($this->_defaultOptions$options);
  260.             else {
  261.                 $this->options array_merge($this->options$options);
  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 ($this->options['returnResult'=== true{
  317.             $result $this->_serializedData;
  318.         else {
  319.             $result = true;
  320.         }
  321.         
  322.         if ($optionsBak !== null{
  323.             $this->options $optionsBak;
  324.         }
  325.  
  326.         return $result;
  327.     }
  328.  
  329.    /**
  330.     * get the result of the serialization
  331.     *
  332.     * @access public
  333.     * @return string serialized XML
  334.     */
  335.     function getSerializedData()
  336.     {
  337.         if ($this->_serializedData == null {
  338.             return  $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.'XML_SERIALIZER_ERROR_NO_SERIALIZATION);
  339.         }
  340.         return $this->_serializedData;
  341.     }
  342.     
  343.    /**
  344.     * serialize any value
  345.     *
  346.     * This method checks for the type of the value and calls the appropriate method
  347.     *
  348.     * @access private
  349.     * @param  mixed     $value 
  350.     * @param  string    $tagName 
  351.     * @param  array     $attributes 
  352.     * @return string 
  353.     */
  354.     function _serializeValue($value$tagName = null$attributes = array())
  355.     {
  356.         if (is_array($value)) {
  357.             $xml $this->_serializeArray($value$tagName$attributes);
  358.         elseif (is_object($value)) {
  359.             $xml $this->_serializeObject($value$tagName);
  360.         else {
  361.             $tag = array(
  362.                           'qname'      => $tagName,
  363.                           'attributes' => $attributes,
  364.                           'content'    => $value
  365.                         );
  366.             $xml $this->_createXMLTag($tag);
  367.         }
  368.         return $xml;
  369.     }
  370.     
  371.    /**
  372.     * serialize an array
  373.     *
  374.     * @access   private
  375.     * @param    array   $array       array to serialize
  376.     * @param    string  $tagName     name of the root tag
  377.     * @param    array   $attributes  attributes for the root tag
  378.     * @return   string  $string      serialized data
  379.     * @uses     XML_Util::isValidName() to check, whether key has to be substituted
  380.     */
  381.     function _serializeArray(&$array$tagName = null$attributes = array())
  382.     {
  383.         $_content = null;
  384.         
  385.         /**
  386.          * check for special attributes
  387.          */
  388.         if ($this->options['attributesArray'!== null{
  389.             if (isset($array[$this->options['attributesArray']])) {
  390.                 $attributes $array[$this->options['attributesArray']];
  391.                 unset($array[$this->options['attributesArray']]);
  392.             }
  393.             /**
  394.              * check for special content
  395.              */
  396.             if ($this->options['contentName'!== null{
  397.                 if (isset($array[$this->options['contentName']])) {
  398.                     $_content $array[$this->options['contentName']];
  399.                     unset($array[$this->options['contentName']]);
  400.                 }
  401.             }
  402.         }
  403.  
  404.         /*
  405.         * if mode is set to simpleXML, check whether
  406.         * the array is associative or indexed
  407.         */
  408.         if (is_array($array&& !empty($array&& $this->options['mode'== 'simplexml'{
  409.             $indexed = true;
  410.             foreach ($array as $key => $val{
  411.                 if (!is_int($key)) {
  412.                     $indexed = false;
  413.                     break;
  414.                 }
  415.             }
  416.  
  417.             if ($indexed && $this->options['mode'== 'simplexml'{
  418.                 $string '';
  419.                 foreach ($array as $key => $val{
  420.                     $string .= $this->_serializeValue$val$tagName$attributes);
  421.                     
  422.                     $string .= $this->options['linebreak'];
  423.                     //    do indentation
  424.                     if ($this->options['indent']!==null && $this->_tagDepth>0{
  425.                         $string .= str_repeat($this->options['indent']$this->_tagDepth);
  426.                     }
  427.                 }
  428.                 return rtrim($string);
  429.             }
  430.         }
  431.         
  432.         if ($this->options['scalarAsAttributes'=== true{
  433.             $this->expectError('*');
  434.             foreach ($array as $key => $value{
  435.                 if (is_scalar($value&& (XML_Util::isValidName($key=== true)) {
  436.                     unset($array[$key]);
  437.                     $attributes[$this->options['prependAttributes'].$key$value;
  438.                 }
  439.             }
  440.             $this->popExpect();
  441.         }
  442.  
  443.         // check for empty array => create empty tag
  444.         if (empty($array)) {
  445.             $tag = array(
  446.                             'qname'      => $tagName,
  447.                             'content'    => $_content,
  448.                             'attributes' => $attributes
  449.                         );
  450.  
  451.         else {
  452.             $this->_tagDepth++;
  453.             $tmp $this->options['linebreak'];
  454.             foreach ($array as $key => $value{
  455.                 //    do indentation
  456.                 if ($this->options['indent']!==null && $this->_tagDepth>0{
  457.                     $tmp .= str_repeat($this->options['indent']$this->_tagDepth);
  458.                 }
  459.  
  460.                 if (isset($this->options['tagMap'][$key])) {
  461.                     $key $this->options['tagMap'][$key];
  462.                 }
  463.  
  464.                 // copy key
  465.                 $origKey    =    $key;
  466.                 $this->expectError('*');
  467.                 // key cannot be used as tagname => use default tag
  468.                 $valid = XML_Util::isValidName($key);
  469.                 $this->popExpect();
  470.                 if (PEAR::isError($valid)) {
  471.                     if ($this->options['classAsTagName'&& is_object($value)) {
  472.                         $key get_class($value);
  473.                     else {
  474.                         $key $this->options['defaultTagName'];
  475.                     }
  476.                     }
  477.                 $atts = array();
  478.                 if ($this->options['typeHints'=== true{
  479.                     $atts[$this->options['typeAttribute']] gettype($value);
  480.                     if ($key !== $origKey{
  481.                         $atts[$this->options['keyAttribute']] = (string)$origKey;
  482.                     }
  483.     
  484.                 }
  485.                 
  486.                 $tmp .= $this->_createXMLTag(array(
  487.                                                     'qname'      => $key,
  488.                                                     'attributes' => $atts,
  489.                                                     'content'    => $value )
  490.                                             );
  491.                 $tmp .= $this->options['linebreak'];
  492.             }
  493.             
  494.             $this->_tagDepth--;
  495.             if ($this->options['indent']!==null && $this->_tagDepth>0{
  496.                 $tmp .= str_repeat($this->options['indent']$this->_tagDepth);
  497.             }
  498.     
  499.             if (trim($tmp=== ''{
  500.                 $tmp = null;
  501.             }
  502.             
  503.             $tag = array(
  504.                             'qname'      => $tagName,
  505.                             'content'    => $tmp,
  506.                             'attributes' => $attributes
  507.                         );
  508.         }
  509.         if ($this->options['typeHints'=== true{
  510.             if (!isset($tag['attributes'][$this->options['typeAttribute']])) {
  511.                 $tag['attributes'][$this->options['typeAttribute']] 'array';
  512.             }
  513.         }
  514.  
  515.         $string $this->_createXMLTag($tagfalse);
  516.         return $string;
  517.     }
  518.  
  519.    /**
  520.     * serialize an object
  521.     *
  522.     * @access   private
  523.     * @param    object  $object object to serialize
  524.     * @return   string  $string serialized data
  525.     */
  526.     function _serializeObject(&$object$tagName = null$attributes = array())
  527.     {
  528.         //  check for magic function
  529.         if (method_exists($object'__sleep')) {
  530.             $object->__sleep();
  531.         }
  532.  
  533.         $tmp $this->options['linebreak'];
  534.         $properties get_object_vars($object);
  535.         if (empty($tagName)) {
  536.             $tagName get_class($object);
  537.         }
  538.         
  539.         // typehints activated?
  540.         if ($this->options['typeHints'=== true{
  541.             $attributes[$this->options['typeAttribute']]  'object';
  542.             $attributes[$this->options['classAttribute']] =  get_class($object);
  543.         }
  544.         
  545.         $string $this->_serializeArray($properties$tagName$attributes);
  546.         return $string;
  547.     }
  548.   
  549.    /**
  550.     * create a tag from an array
  551.     * this method awaits an array in the following format
  552.     * array(
  553.     *       'qname'        => $tagName,
  554.     *       'attributes'   => array(),
  555.     *       'content'      => $content,      // optional
  556.     *       'namespace'    => $namespace     // optional
  557.     *       'namespaceUri' => $namespaceUri  // optional
  558.     *   )
  559.     *
  560.     * @access   private
  561.     * @param    array   $tag tag definition
  562.     * @param    boolean $replaceEntities whether to replace XML entities in content or not
  563.     * @return   string  $string XML tag
  564.     */
  565.     function _createXMLTag$tag$replaceEntities = true )
  566.     {
  567.         if ($this->options['namespace'!== null{
  568.             if (is_array($this->options['namespace'])) {
  569.                 $tag['qname'$this->options['namespace'][0':' $tag['qname'];
  570.             else {
  571.                 $tag['qname'$this->options['namespace'':' $tag['qname'];
  572.             }
  573.         }
  574.  
  575.         if ($this->options['indentAttributes'!== false{
  576.             $multiline = true;
  577.             $indent    str_repeat($this->options['indent']$this->_tagDepth);
  578.  
  579.             if ($this->options['indentAttributes'== '_auto'{
  580.                 $indent .= str_repeat(' '(strlen($tag['qname'])+2));
  581.  
  582.             else {
  583.                 $indent .= $this->options['indentAttributes'];
  584.             }
  585.         else {
  586.             $multiline = false;
  587.             $indent    = false;
  588.         }
  589.  
  590.         if ($replaceEntities{
  591.                $replaceEntities $this->options['replaceEntities'];
  592.         }
  593.     
  594.         if (is_array($tag['content'])) {
  595.             if (empty($tag['content'])) {
  596.                 $tag['content'=   '';
  597.             }
  598.         elseif(is_scalar($tag['content']&& (string)$tag['content'== ''{
  599.             $tag['content'=   '';
  600.         }
  601.     
  602.         if (is_scalar($tag['content']|| is_null($tag['content'])) {
  603.             if ($this->options['encodeFunction']{
  604.                 if ($replaceEntities === true{
  605.                     $tag['content'call_user_func($this->options['encodeFunction']$tag['content']);
  606.                 }
  607.                 $tag['attributes'array_map($this->options['encodeFunction']$tag['attributes']);
  608.             }
  609.             $tag = XML_Util::createTagFromArray($tag$replaceEntities$multiline$indent$this->options['linebreak']);
  610.         elseif (is_array($tag['content'])) {
  611.             $tag    =   $this->_serializeArray($tag['content']$tag['qname']$tag['attributes']);
  612.         elseif (is_object($tag['content'])) {
  613.             $tag    =   $this->_serializeObject($tag['content']$tag['qname']$tag['attributes']);
  614.         elseif (is_resource($tag['content'])) {
  615.             settype($tag['content']'string');
  616.             if ($this->options['encodeFunction']{
  617.                 if ($replaceEntities === true{
  618.                     $tag['content'call_user_func($this->options['encodeFunction']$tag['content']);
  619.                 }
  620.                 $tag['attributes'array_map($this->options['encodeFunction']$tag['attributes']);
  621.             }
  622.             $tag = XML_Util::createTagFromArray($tag$replaceEntities);
  623.         }
  624.         return  $tag;
  625.     }
  626. }
  627. ?>

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