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.18 2004/05/26 13:22:23 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.9.1
  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.                          "keyAttribute"       => "_originalKey",        // attribute where original key is stored
  117.                          "typeAttribute"      => "_type",               // attribute for type (only if typeHints => true)
  118.                          "classAttribute"     => "_class",              // attribute for class of objects (only if typeHints => true)
  119.                          "scalarAsAttributes" => false,                 // scalar values (strings, ints,..) will be serialized as attribute
  120.                          "prependAttributes"  => "",                    // prepend string for attributes
  121.                          "indentAttributes"   => false,                 // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column
  122.                          "mode"               => 'default',             // use 'simplexml' to use parent name as tagname if transforming an indexed array
  123.                          "addDoctype"         => false,                 // add a doctype declaration
  124.                          "doctype"            => null,                  // supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()}
  125.                          "rootName"           => null,                  // name of the root tag
  126.                          "rootAttributes"     => array(),               // attributes of the root tag
  127.                          "attributesArray"    => null,                  // all values in this key will be treated as attributes
  128.                          "contentName"        => null                   // this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray
  129.                         );
  130.  
  131.    /**
  132.     * options for the serialization
  133.     * @access private
  134.     * @var array $options 
  135.     */
  136.     var $options = array();
  137.  
  138.    /**
  139.     * current tag depth
  140.     * @var integer $_tagDepth 
  141.     */
  142.     var $_tagDepth = 0;
  143.  
  144.    /**
  145.     * serilialized representation of the data
  146.     * @var string $_serializedData 
  147.     */
  148.     var $_serializedData = null;
  149.     
  150.    /**
  151.     * constructor
  152.     *
  153.     * @access   public
  154.     * @param    mixed   $options    array containing options for the serialization
  155.     */
  156.     function XML_Serializer$options = null )
  157.     {
  158.         $this->PEAR();
  159.         if (is_array($options)) {
  160.             $this->options array_merge($this->_defaultOptions$options);
  161.         else {
  162.             $this->options $this->_defaultOptions;
  163.         }
  164.     }
  165.  
  166.    /**
  167.     * return API version
  168.     *
  169.     * @access   public
  170.     * @static
  171.     * @return   string  $version API version
  172.     */
  173.     function apiVersion()
  174.     {
  175.         return "0.9";
  176.     }
  177.  
  178.    /**
  179.     * reset all options to default options
  180.     *
  181.     * @access   public
  182.     * @see      setOption(), XML_Unserializer()
  183.     */
  184.     function resetOptions()
  185.     {
  186.         $this->options $this->_defaultOptions;
  187.     }
  188.  
  189.    /**
  190.     * set an option
  191.     *
  192.     * You can use this method if you do not want to set all options in the constructor
  193.     *
  194.     * @access   public
  195.     * @see      resetOption(), XML_Serializer()
  196.     */
  197.     function setOption($name$value)
  198.     {
  199.         $this->options[$name$value;
  200.     }
  201.     
  202.    /**
  203.     * sets several options at once
  204.     *
  205.     * You can use this method if you do not want to set all options in the constructor
  206.     *
  207.     * @access   public
  208.     * @see      resetOption(), XML_Unserializer(), setOption()
  209.     */
  210.     function setOptions($options)
  211.     {
  212.         $this->options array_merge($this->options$options);
  213.     }
  214.  
  215.    /**
  216.     * serialize data
  217.     *
  218.     * @access   public
  219.     * @param    mixed    $data data to serialize
  220.     * @return   boolean  true on success, pear error on failure
  221.     */
  222.     function serialize($data$options = null)
  223.     {
  224.         // if options have been specified, use them instead
  225.         // of the previously defined ones
  226.         if (is_array($options)) {
  227.             $optionsBak $this->options;
  228.             if (isset($options["overrideOptions"]&& $options["overrideOptions"== true{
  229.                 $this->options array_merge($this->_defaultOptions$options);
  230.             else {
  231.                 $this->options array_merge($this->options$options);
  232.             }
  233.         }
  234.         else {
  235.             $optionsBak = null;
  236.         }
  237.         
  238.         // maintain BC
  239.         if (isset($this->options["tagName"])) {
  240.             $this->options["rootName"$this->options["tagName"];
  241.         }
  242.         
  243.         //  start depth is zero
  244.         $this->_tagDepth = 0;
  245.  
  246.         $this->_serializedData "";
  247.         // serialize an array
  248.         if (is_array($data)) {
  249.             if (isset($this->options["rootName"])) {
  250.                 $tagName $this->options["rootName"];
  251.             else {
  252.                 $tagName "array";
  253.             }
  254.  
  255.             $this->_serializedData .= $this->_serializeArray($data$tagName$this->options["rootAttributes"]);
  256.         }
  257.         // serialize an object
  258.         elseif (is_object($data)) {
  259.             if (isset($this->options["rootName"])) {
  260.                 $tagName $this->options["rootName"];
  261.             else {
  262.                 $tagName get_class($data);
  263.             }
  264.             $this->_serializedData .= $this->_serializeObject($data$tagName$this->options["rootAttributes"]);
  265.         }
  266.         
  267.         // add doctype declaration
  268.         if ($this->options["addDoctype"=== true{
  269.             $this->_serializedData = XML_Util::getDoctypeDeclaration($tagName$this->options["doctype"])
  270.                                    . $this->options["linebreak"]
  271.                                    . $this->_serializedData;
  272.         }
  273.  
  274.         //  build xml declaration
  275.         if ($this->options["addDecl"]{
  276.             $atts = array();
  277.             if (isset($this->options["encoding"]) ) {
  278.                 $encoding $this->options["encoding"];
  279.             else {
  280.                 $encoding = null;
  281.             }
  282.             $this->_serializedData = XML_Util::getXMLDeclaration("1.0"$encoding)
  283.                                    . $this->options["linebreak"]
  284.                                    . $this->_serializedData;
  285.         }
  286.         
  287.         
  288.         if ($optionsBak !== null{
  289.             $this->options $optionsBak;
  290.         }
  291.         
  292.         return  true;
  293.     }
  294.  
  295.    /**
  296.     * get the result of the serialization
  297.     *
  298.     * @access public
  299.     * @return string serialized XML
  300.     */
  301.     function getSerializedData()
  302.     {
  303.         if ($this->_serializedData == null {
  304.             return  $this->raiseError("No serialized data available. Use XML_Serializer::serialize() first."XML_SERIALIZER_ERROR_NO_SERIALIZATION);
  305.         }
  306.         return $this->_serializedData;
  307.     }
  308.     
  309.    /**
  310.     * serialize any value
  311.     *
  312.     * This method checks for the type of the value and calls the appropriate method
  313.     *
  314.     * @access private
  315.     * @param  mixed     $value 
  316.     * @param  string    $tagName 
  317.     * @param  array     $attributes 
  318.     * @return string 
  319.     */
  320.     function _serializeValue($value$tagName = null$attributes = array())
  321.     {
  322.         if (is_array($value)) {
  323.             $xml $this->_serializeArray($value$tagName$attributes);
  324.         elseif (is_object($value)) {
  325.             $xml $this->_serializeObject($value$tagName);
  326.         else {
  327.             $tag = array(
  328.                           "qname"      => $tagName,
  329.                           "attributes" => $attributes,
  330.                           "content"    => $value
  331.                         );
  332.             $xml $this->_createXMLTag($tag);
  333.         }
  334.         return $xml;
  335.     }
  336.     
  337.    /**
  338.     * serialize an array
  339.     *
  340.     * @access   private
  341.     * @param    array   $array       array to serialize
  342.     * @param    string  $tagName     name of the root tag
  343.     * @param    array   $attributes  attributes for the root tag
  344.     * @return   string  $string      serialized data
  345.     * @uses     XML_Util::isValidName() to check, whether key has to be substituted
  346.     */
  347.     function _serializeArray(&$array$tagName = null$attributes = array())
  348.     {
  349.         /**
  350.          * check for special attributes
  351.          */
  352.         if ($this->options['attributesArray'!== null{
  353.             $_content = null;
  354.             if (isset($array[$this->options['attributesArray']])) {
  355.                 $attributes $array[$this->options['attributesArray']];
  356.                 unset($array[$this->options['attributesArray']]);
  357.             }
  358.             /**
  359.              * check for special content
  360.              */
  361.             if ($this->options['contentName'!== null{
  362.                 if (isset($array[$this->options['contentName']])) {
  363.                     $_content $array[$this->options['contentName']];
  364.                     unset($array[$this->options['contentName']]);
  365.                 }
  366.             }
  367.         }
  368.  
  369.         /*
  370.         * if mode is set to simpleXML, check whether
  371.         * the array is associative or indexed
  372.         */
  373.         if (is_array($array&& $this->options["mode"== "simplexml"{
  374.             $indexed = true;
  375.             foreach ($array as $key => $val{
  376.                 if (!is_int($key)) {
  377.                     $indexed = false;
  378.                     break;
  379.                 }
  380.             }
  381.  
  382.             if ($indexed{
  383.                 $string "";
  384.                 foreach ($array as $key => $val{
  385.                     $string .= $this->_serializeValue$val$tagName$attributes);
  386.                     
  387.                     $string .= $this->options["linebreak"];
  388.                     //    do indentation
  389.                     if ($this->options["indent"]!==null && $this->_tagDepth>0{
  390.                         $string .= str_repeat($this->options["indent"]$this->_tagDepth);
  391.                     }
  392.                 }
  393.                 return rtrim($string);
  394.             }
  395.         }
  396.         
  397.         if ($this->options["scalarAsAttributes"=== true{
  398.             foreach ($array as $key => $value{
  399.                 if (is_scalar($value&& (XML_Util::isValidName($key=== true)) {
  400.                     unset($array[$key]);
  401.                     $attributes[$this->options["prependAttributes"].$key$value;
  402.                 }
  403.             }
  404.         }
  405.  
  406.         // check for empty array => create empty tag
  407.         if (empty($array)) {
  408.             $tag = array(
  409.                             "qname"      => $tagName,
  410.                             "content"    => $_content,
  411.                             "attributes" => $attributes
  412.                         );
  413.  
  414.         else {
  415.             $this->_tagDepth++;
  416.             $tmp $this->options["linebreak"];
  417.             foreach ($array as $key => $value{
  418.                 //    do indentation
  419.                 if ($this->options["indent"]!==null && $this->_tagDepth>0{
  420.                     $tmp .= str_repeat($this->options["indent"]$this->_tagDepth);
  421.                 }
  422.     
  423.                 //    copy key
  424.                 $origKey    =    $key;
  425.                 //    key cannot be used as tagname => use default tag
  426.                 $valid = XML_Util::isValidName($key);
  427.                 if (PEAR::isError($valid)) {
  428.                     $key $this->options["defaultTagName"];
  429.                     }
  430.                 $atts = array();
  431.                 if ($this->options["typeHints"=== true{
  432.                     $atts[$this->options["typeAttribute"]] gettype($value);
  433.                     if ($key !== $origKey{
  434.                         $atts[$this->options["keyAttribute"]] = (string)$origKey;
  435.                     }
  436.     
  437.                 }
  438.                 
  439.                 $tmp .= $this->_createXMLTag(array(
  440.                                                     "qname"      => $key,
  441.                                                     "attributes" => $atts,
  442.                                                     "content"    => $value )
  443.                                             );
  444.                 $tmp .= $this->options["linebreak"];
  445.             }
  446.             
  447.             $this->_tagDepth--;
  448.             if ($this->options["indent"]!==null && $this->_tagDepth>0{
  449.                 $tmp .= str_repeat($this->options["indent"]$this->_tagDepth);
  450.             }
  451.     
  452.             if (trim($tmp=== ''{
  453.                 $tmp = null;
  454.             }
  455.             
  456.             $tag = array(
  457.                             "qname"      => $tagName,
  458.                             "content"    => $tmp,
  459.                             "attributes" => $attributes
  460.                         );
  461.         }
  462.         if ($this->options["typeHints"=== true{
  463.             if (!isset($tag["attributes"][$this->options["typeAttribute"]])) {
  464.                 $tag["attributes"][$this->options["typeAttribute"]] "array";
  465.             }
  466.         }
  467.  
  468.         $string $this->_createXMLTag($tagfalse);
  469.         return $string;
  470.     }
  471.  
  472.    /**
  473.     * serialize an object
  474.     *
  475.     * @access   private
  476.     * @param    object  $object object to serialize
  477.     * @return   string  $string serialized data
  478.     */
  479.     function _serializeObject(&$object$tagName = null$attributes = array())
  480.     {
  481.         //  check for magic function
  482.         if (method_exists($object"__sleep")) {
  483.             $object->__sleep();
  484.         }
  485.  
  486.         $tmp $this->options["linebreak"];
  487.         $properties get_object_vars($object);
  488.         if (empty($tagName)) {
  489.             $tagName get_class($object);
  490.         }
  491.         
  492.         // typehints activated?
  493.         if ($this->options["typeHints"=== true{
  494.             $attributes[$this->options["typeAttribute"]]  "object";
  495.             $attributes[$this->options["classAttribute"]] =  get_class($object);
  496.         }
  497.         
  498.         $string $this->_serializeArray($properties$tagName$attributes);
  499.         return $string;
  500.     }
  501.   
  502.    /**
  503.     * create a tag from an array
  504.     * this method awaits an array in the following format
  505.     * array(
  506.     *       "qname"        => $tagName,
  507.     *       "attributes"   => array(),
  508.     *       "content"      => $content,      // optional
  509.     *       "namespace"    => $namespace     // optional
  510.     *       "namespaceUri" => $namespaceUri  // optional
  511.     *   )
  512.     *
  513.     * @access   private
  514.     * @param    array   $tag tag definition
  515.     * @param    boolean $replaceEntities whether to replace XML entities in content or not
  516.     * @return   string  $string XML tag
  517.     */
  518.     function _createXMLTag$tag$replaceEntities = true )
  519.     {
  520.         if ($this->options["indentAttributes"!== false{
  521.             $multiline = true;
  522.             $indent    str_repeat($this->options["indent"]$this->_tagDepth);
  523.  
  524.             if ($this->options["indentAttributes"== "_auto"{
  525.                 $indent .= str_repeat(" "(strlen($tag["qname"])+2));
  526.  
  527.             else {
  528.                 $indent .= $this->options["indentAttributes"];
  529.             }
  530.         else {
  531.             $multiline = false;
  532.             $indent    = false;
  533.         }
  534.     
  535.         if (is_array($tag["content"])) {
  536.             if (empty($tag["content"])) {
  537.                 $tag["content"=   '';
  538.             }
  539.         elseif(is_scalar($tag["content"]&& (string)$tag["content"== ''{
  540.             $tag["content"=   '';
  541.         }
  542.     
  543.         if (is_scalar($tag["content"]|| is_null($tag["content"])) {
  544.             $tag = XML_Util::createTagFromArray($tag$replaceEntities$multiline$indent$this->options["linebreak"]);
  545.         elseif (is_array($tag["content"])) {
  546.             $tag    =   $this->_serializeArray($tag["content"]$tag["qname"]$tag["attributes"]);
  547.         elseif (is_object($tag["content"])) {
  548.             $tag    =   $this->_serializeObject($tag["content"]$tag["qname"]$tag["attributes"]);
  549.         elseif (is_resource($tag["content"])) {
  550.             settype($tag["content"]"string");
  551.             $tag    =   XML_Util::createTagFromArray($tag$replaceEntities);
  552.         }
  553.         return  $tag;
  554.     }
  555. }
  556. ?>

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