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

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