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.26 2004/11/06 15:49:52 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.13.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()
  131.                         );
  132.  
  133.    /**
  134.     * options for the serialization
  135.     * @access private
  136.     * @var array $options 
  137.     */
  138.     var $options = array();
  139.  
  140.    /**
  141.     * current tag depth
  142.     * @var integer $_tagDepth 
  143.     */
  144.     var $_tagDepth = 0;
  145.  
  146.    /**
  147.     * serilialized representation of the data
  148.     * @var string $_serializedData 
  149.     */
  150.     var $_serializedData = null;
  151.     
  152.    /**
  153.     * constructor
  154.     *
  155.     * @access   public
  156.     * @param    mixed   $options    array containing options for the serialization
  157.     */
  158.     function XML_Serializer$options = null )
  159.     {
  160.         $this->PEAR();
  161.         if (is_array($options)) {
  162.             $this->options array_merge($this->_defaultOptions$options);
  163.         else {
  164.             $this->options $this->_defaultOptions;
  165.         }
  166.     }
  167.  
  168.    /**
  169.     * return API version
  170.     *
  171.     * @access   public
  172.     * @static
  173.     * @return   string  $version API version
  174.     */
  175.     function apiVersion()
  176.     {
  177.         return '0.13';
  178.     }
  179.  
  180.    /**
  181.     * reset all options to default options
  182.     *
  183.     * @access   public
  184.     * @see      setOption(), XML_Unserializer()
  185.     */
  186.     function resetOptions()
  187.     {
  188.         $this->options $this->_defaultOptions;
  189.     }
  190.  
  191.    /**
  192.     * set an option
  193.     *
  194.     * You can use this method if you do not want to set all options in the constructor
  195.     *
  196.     * @access   public
  197.     * @see      resetOption(), XML_Serializer()
  198.     */
  199.     function setOption($name$value)
  200.     {
  201.         $this->options[$name$value;
  202.     }
  203.     
  204.    /**
  205.     * sets several options at once
  206.     *
  207.     * You can use this method if you do not want to set all options in the constructor
  208.     *
  209.     * @access   public
  210.     * @see      resetOption(), XML_Unserializer(), setOption()
  211.     */
  212.     function setOptions($options)
  213.     {
  214.         $this->options array_merge($this->options$options);
  215.     }
  216.  
  217.    /**
  218.     * serialize data
  219.     *
  220.     * @access   public
  221.     * @param    mixed    $data data to serialize
  222.     * @return   boolean  true on success, pear error on failure
  223.     */
  224.     function serialize($data$options = null)
  225.     {
  226.         // if options have been specified, use them instead
  227.         // of the previously defined ones
  228.         if (is_array($options)) {
  229.             $optionsBak $this->options;
  230.             if (isset($options['overrideOptions']&& $options['overrideOptions'== true{
  231.                 $this->options array_merge($this->_defaultOptions$options);
  232.             else {
  233.                 $this->options array_merge($this->options$options);
  234.             }
  235.         }
  236.         else {
  237.             $optionsBak = null;
  238.         }
  239.         
  240.         // maintain BC
  241.         if (isset($this->options['tagName'])) {
  242.             $this->options['rootName'$this->options['tagName'];
  243.         }
  244.         
  245.         //  start depth is zero
  246.         $this->_tagDepth = 0;
  247.  
  248.         $this->_serializedData '';
  249.         // serialize an array
  250.         if (is_array($data)) {
  251.             if (isset($this->options['rootName'])) {
  252.                 $tagName $this->options['rootName'];
  253.             else {
  254.                 $tagName 'array';
  255.             }
  256.  
  257.             $this->_serializedData .= $this->_serializeArray($data$tagName$this->options['rootAttributes']);
  258.         }
  259.         // serialize an object
  260.         elseif (is_object($data)) {
  261.             if (isset($this->options['rootName'])) {
  262.                 $tagName $this->options['rootName'];
  263.             else {
  264.                 $tagName get_class($data);
  265.             }
  266.             $this->_serializedData .= $this->_serializeObject($data$tagName$this->options['rootAttributes']);
  267.         }
  268.         
  269.         // add doctype declaration
  270.         if ($this->options['addDoctype'=== true{
  271.             $this->_serializedData = XML_Util::getDoctypeDeclaration($tagName$this->options['doctype'])
  272.                                    . $this->options['linebreak']
  273.                                    . $this->_serializedData;
  274.         }
  275.  
  276.         //  build xml declaration
  277.         if ($this->options['addDecl']{
  278.             $atts = array();
  279.             if (isset($this->options['encoding']) ) {
  280.                 $encoding $this->options['encoding'];
  281.             else {
  282.                 $encoding = null;
  283.             }
  284.             $this->_serializedData = XML_Util::getXMLDeclaration('1.0'$encoding)
  285.                                    . $this->options['linebreak']
  286.                                    . $this->_serializedData;
  287.         }
  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.             foreach ($array as $key => $value{
  402.                 if (is_scalar($value&& (XML_Util::isValidName($key=== true)) {
  403.                     unset($array[$key]);
  404.                     $attributes[$this->options['prependAttributes'].$key$value;
  405.                 }
  406.             }
  407.         }
  408.  
  409.         // check for empty array => create empty tag
  410.         if (empty($array)) {
  411.             $tag = array(
  412.                             'qname'      => $tagName,
  413.                             'content'    => $_content,
  414.                             'attributes' => $attributes
  415.                         );
  416.  
  417.         else {
  418.             $this->_tagDepth++;
  419.             $tmp $this->options['linebreak'];
  420.             foreach ($array as $key => $value{
  421.                 //    do indentation
  422.                 if ($this->options['indent']!==null && $this->_tagDepth>0{
  423.                     $tmp .= str_repeat($this->options['indent']$this->_tagDepth);
  424.                 }
  425.  
  426.                 if (isset($this->options['tagMap'][$key])) {
  427.                     $key $this->options['tagMap'][$key];
  428.                 }
  429.  
  430.                 //    copy key
  431.                 $origKey    =    $key;
  432.                 //    key cannot be used as tagname => use default tag
  433.                 $valid = XML_Util::isValidName($key);
  434.                 if (PEAR::isError($valid)) {
  435.                     if ($this->options['classAsTagName'&& is_object($value)) {
  436.                         $key get_class($value);
  437.                     else {
  438.                         $key $this->options['defaultTagName'];
  439.                     }
  440.                     }
  441.                 $atts = array();
  442.                 if ($this->options['typeHints'=== true{
  443.                     $atts[$this->options['typeAttribute']] gettype($value);
  444.                     if ($key !== $origKey{
  445.                         $atts[$this->options['keyAttribute']] = (string)$origKey;
  446.                     }
  447.     
  448.                 }
  449.                 
  450.                 $tmp .= $this->_createXMLTag(array(
  451.                                                     'qname'      => $key,
  452.                                                     'attributes' => $atts,
  453.                                                     'content'    => $value )
  454.                                             );
  455.                 $tmp .= $this->options['linebreak'];
  456.             }
  457.             
  458.             $this->_tagDepth--;
  459.             if ($this->options['indent']!==null && $this->_tagDepth>0{
  460.                 $tmp .= str_repeat($this->options['indent']$this->_tagDepth);
  461.             }
  462.     
  463.             if (trim($tmp=== ''{
  464.                 $tmp = null;
  465.             }
  466.             
  467.             $tag = array(
  468.                             'qname'      => $tagName,
  469.                             'content'    => $tmp,
  470.                             'attributes' => $attributes
  471.                         );
  472.         }
  473.         if ($this->options['typeHints'=== true{
  474.             if (!isset($tag['attributes'][$this->options['typeAttribute']])) {
  475.                 $tag['attributes'][$this->options['typeAttribute']] 'array';
  476.             }
  477.         }
  478.  
  479.         $string $this->_createXMLTag($tagfalse);
  480.         return $string;
  481.     }
  482.  
  483.    /**
  484.     * serialize an object
  485.     *
  486.     * @access   private
  487.     * @param    object  $object object to serialize
  488.     * @return   string  $string serialized data
  489.     */
  490.     function _serializeObject(&$object$tagName = null$attributes = array())
  491.     {
  492.         //  check for magic function
  493.         if (method_exists($object'__sleep')) {
  494.             $object->__sleep();
  495.         }
  496.  
  497.         $tmp $this->options['linebreak'];
  498.         $properties get_object_vars($object);
  499.         if (empty($tagName)) {
  500.             $tagName get_class($object);
  501.         }
  502.         
  503.         // typehints activated?
  504.         if ($this->options['typeHints'=== true{
  505.             $attributes[$this->options['typeAttribute']]  'object';
  506.             $attributes[$this->options['classAttribute']] =  get_class($object);
  507.         }
  508.         
  509.         $string $this->_serializeArray($properties$tagName$attributes);
  510.         return $string;
  511.     }
  512.   
  513.    /**
  514.     * create a tag from an array
  515.     * this method awaits an array in the following format
  516.     * array(
  517.     *       'qname'        => $tagName,
  518.     *       'attributes'   => array(),
  519.     *       'content'      => $content,      // optional
  520.     *       'namespace'    => $namespace     // optional
  521.     *       'namespaceUri' => $namespaceUri  // optional
  522.     *   )
  523.     *
  524.     * @access   private
  525.     * @param    array   $tag tag definition
  526.     * @param    boolean $replaceEntities whether to replace XML entities in content or not
  527.     * @return   string  $string XML tag
  528.     */
  529.     function _createXMLTag$tag$replaceEntities = true )
  530.     {
  531.         if ($this->options['indentAttributes'!== false{
  532.             $multiline = true;
  533.             $indent    str_repeat($this->options['indent']$this->_tagDepth);
  534.  
  535.             if ($this->options['indentAttributes'== '_auto'{
  536.                 $indent .= str_repeat(' '(strlen($tag['qname'])+2));
  537.  
  538.             else {
  539.                 $indent .= $this->options['indentAttributes'];
  540.             }
  541.         else {
  542.             $multiline = false;
  543.             $indent    = false;
  544.         }
  545.     
  546.         if (is_array($tag['content'])) {
  547.             if (empty($tag['content'])) {
  548.                 $tag['content'=   '';
  549.             }
  550.         elseif(is_scalar($tag['content']&& (string)$tag['content'== ''{
  551.             $tag['content'=   '';
  552.         }
  553.     
  554.         if (is_scalar($tag['content']|| is_null($tag['content'])) {
  555.             $tag = XML_Util::createTagFromArray($tag$replaceEntities$multiline$indent$this->options['linebreak']);
  556.         elseif (is_array($tag['content'])) {
  557.             $tag    =   $this->_serializeArray($tag['content']$tag['qname']$tag['attributes']);
  558.         elseif (is_object($tag['content'])) {
  559.             $tag    =   $this->_serializeObject($tag['content']$tag['qname']$tag['attributes']);
  560.         elseif (is_resource($tag['content'])) {
  561.             settype($tag['content']'string');
  562.             $tag    =   XML_Util::createTagFromArray($tag$replaceEntities);
  563.         }
  564.         return  $tag;
  565.     }
  566. }
  567. ?>

Documentation generated on Mon, 11 Mar 2019 13:58:25 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.