Support Joomla!

Joomla! 1.5 Documentation

Packages

Package: Joomla-Framework

Developer Network License

The Joomla! Developer Network content is © copyright 2006 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution- NonCommercial- ShareAlike 2.5
Source code for file /joomla/utilities/simplexml.php

Documentation is available at simplexml.php

  1. <?php
  2. /**
  3. @version        $Id: simplexml.php 6673 2007-02-19 05:12:48Z louis $
  4. @package        Joomla.Framework
  5. @subpackage    Utilities
  6. @copyright    Copyright (C) 2005 - 2007 Open Source Matters. All rights reserved.
  7. @license        GNU/GPL, see LICENSE.php
  8. *  Joomla! is free software. This version may have been modified pursuant
  9. *  to the GNU General Public License, and as distributed it includes or
  10. *  is derivative of works licensed under the GNU General Public License or
  11. *  other free or open source software licenses.
  12. *  See COPYRIGHT.php for copyright notices and details.
  13. */
  14.  
  15. // Check to ensure this file is within the rest of the framework
  16. defined('JPATH_BASE'or die();
  17.  
  18. /**
  19.  * SimpleXML implementation.
  20.  *
  21.  * The XML Parser extension (expat) is required to use JSimpleXML.
  22.  *
  23.  * The class provides a pure PHP4 implementation of the PHP5
  24.  * interface SimpleXML. As with PHP5's SimpleXML it is what it says:
  25.  * simple. Nevertheless, it is an easy way to deal with XML data,
  26.  * especially for read only access.
  27.  *
  28.  * Because it's not possible to use the PHP5 ArrayIterator interface
  29.  * with PHP4 there are some differences between this implementation
  30.  * and that of PHP5:
  31.  *
  32.  * <ul>
  33.  * <li>The access to the root node has to be explicit in
  34.  * JSimpleXML, not implicit as with PHP5. Write
  35.  * $xml->document->node instead of $xml->node</li>
  36.  * <li>You cannot acces CDATA using array syntax. Use the method data() instead</li>
  37.  * <li>You cannot access attributes directly with array syntax. use attributes()
  38.  * to read them.</li>
  39.  * <li>Comments are ignored.</li>
  40.  * <li>Last and least, this is not as fast as PHP5 SimpleXML--it is pure PHP4.</li>
  41.  * </ul>
  42.  *
  43.  * Example:
  44.  * <code>
  45.  * :simple.xml:
  46.  * <?xml version="1.0" encoding="utf-8" standalon="yes"?>
  47.  * <document>
  48.  *   <node>
  49.  *     <child gender="m">Tom Foo</child>
  50.  *     <child gender="f">Tamara Bar</child>
  51.  *   <node>
  52.  * </document>
  53.  *
  54.  * ---
  55.  *
  56.  * // read and write a document
  57.  * $xml = new JSimpleXML;
  58.  * $xml->loadFile('simple.xml');
  59.  * print $xml->asXML();
  60.  *
  61.  * // access a given node's CDATA
  62.  * print $xml->root->node->child[0]->data(); // Tom Foo
  63.  *
  64.  * // access attributes
  65.  * $attr = $xml->root->node->child[1]->attributes();
  66.  * print $attr['gender']; // f
  67.  *
  68.  * // access children
  69.  * foreach( $xml->root->node->children() as $child ) {
  70.  *   print $child->data();
  71.  * }
  72.  * </code>
  73.  *
  74.  * Note: JSimpleXML cannot be used to access sophisticated XML doctypes
  75.  * using datatype ANY (e.g. XHTML). With a DOM implementation you can
  76.  * handle this.
  77.  *
  78.  *
  79.  * @author        Johan Janssens <[email protected]>
  80.  * @package     Joomla.Framework
  81.  * @subpackage    Utilities
  82.  * @since 1.5
  83.  */
  84. class JSimpleXML extends JObject
  85. {
  86.     /**
  87.      * The XML parser
  88.      *
  89.      * @var resource 
  90.      */
  91.     var $_parser = null;
  92.  
  93.     /**
  94.     * The XML document
  95.     *
  96.     * @var string 
  97.     */
  98.     var $_xml = '';
  99.  
  100.     /**
  101.     * Document element
  102.     *
  103.     * @var object 
  104.     */
  105.     var $document = null;
  106.  
  107.     /**
  108.     * Current object depth
  109.     *
  110.     * @var array 
  111.     */
  112.     var $_stack = array();
  113.  
  114.  
  115.     /**
  116.      * Constructor.
  117.      *
  118.      * @access protected
  119.      */
  120.     function __construct($options null)
  121.     {
  122.         if(function_exists('xml_parser_create')) {
  123.             return false//TODO throw warning
  124.         }
  125.  
  126.         //Create the parser resource and make sure both versions of PHP autodetect the format.
  127.         $this->_parser = xml_parser_create('');
  128.  
  129.         // check parser resource
  130.         xml_set_object($this->_parser$this);
  131.         xml_parser_set_option($this->_parserXML_OPTION_CASE_FOLDING0);
  132.         ifis_array($options) )
  133.         {
  134.             foreach$options as $option => $value {
  135.                 xml_parser_set_option($this->_parser$option$value);
  136.             }
  137.         }
  138.  
  139.         //Set the handlers
  140.         xml_set_element_handler($this->_parser'_startElement''_endElement');
  141.         xml_set_character_data_handler($this->_parser'_characterData');
  142.     }
  143.  
  144.      /**
  145.      * Interprets a string of XML into an object
  146.      *
  147.      * This function will take the well-formed xml string data and return an object of class
  148.      * JSimpleXMLElement with properties containing the data held within the xml document.
  149.      * If any errors occur, it returns FALSE.
  150.      *
  151.      * @param string  Well-formed xml string data
  152.      * @param string  currently ignored
  153.      * @return object JSimpleXMLElement 
  154.      */
  155.     function loadString($string$classname null{
  156.         $this->_parse($string);
  157.         return true;
  158.     }
  159.  
  160.      /**
  161.      * Interprets an XML file into an object
  162.      *
  163.      * This function will convert the well-formed XML document in the file specified by filename
  164.      * to an object  of class JSimpleXMLElement. If any errors occur during file access or
  165.      * interpretation, the function returns FALSE.
  166.      *
  167.      * @param string  Path to xml file containing a well-formed XML document
  168.      * @param string  currently ignored
  169.      * @return boolean True if successful, false if file empty
  170.      */
  171.     function loadFile($path$classname null)
  172.     {
  173.         //Get the XML document loaded into a variable
  174.         $xml trimfile_get_contents($path) );
  175.         if ($xml == '')
  176.         {
  177.             return false;
  178.         }
  179.         else
  180.         {
  181.             $this->_parse($xml);
  182.             return true;
  183.         }
  184.     }
  185.  
  186.     /**
  187.      * Get a JSimpleXMLElement object from a DOM node.
  188.      *
  189.      * This function takes a node of a DOM  document and makes it into a JSimpleXML node.
  190.      * This new object can then be used as a native JSimpleXML element. If any errors occur,
  191.      * it returns FALSE.
  192.      *
  193.      * @param string    DOM  document
  194.      * @param string       currently ignored
  195.      * @return object     JSimpleXMLElement 
  196.      */
  197.     function importDom($node$classname null{
  198.         return false;
  199.     }
  200.  
  201.     /**
  202.      * Get the parser
  203.      *
  204.      * @access public
  205.      * @return resource XML parser resource handle
  206.      */
  207.     function getParser({
  208.         return $this->_parser;
  209.     }
  210.  
  211.     /**
  212.      * Set the parser
  213.      *
  214.      * @access public
  215.      * @param resource    XML parser resource handle
  216.      */
  217.     function setParser($parser{
  218.         $this->_parser = $parser;
  219.     }
  220.  
  221.     /**
  222.      * Start parsing an XML document
  223.      *
  224.      * Parses an XML document. The handlers for the configured events are called as many times as necessary.
  225.      *
  226.      * @param $xml     string     data to parse
  227.      */
  228.     function _parse($data '')
  229.     {
  230.         //Error handling
  231.         if (!xml_parse($this->_parser$data)) {
  232.             $this->_handleError(
  233.                 xml_get_error_code($this->_parser),
  234.                 xml_get_current_line_number($this->_parser),
  235.                 xml_get_current_column_number($this->_parser)
  236.             );
  237.         }
  238.  
  239.         //Free the parser
  240.         xml_parser_free($this->_parser);
  241.     }
  242.  
  243.     /**
  244.      * Handles an XML parsing error
  245.      *
  246.      * @access protected
  247.      * @param int $code XML Error Code
  248.      * @param int $line Line on which the error happened
  249.      * @param int $col Column on which the error happened
  250.      */
  251.     function _handleError($code$line$col)
  252.     {
  253.         JError::raiseError'SOME_ERROR_CODE' 'XML Parsing Error at '.$line.':'.$col.'. Error '.$code.': '.xml_error_string($code));
  254.     }
  255.  
  256.     /**
  257.      * Gets the reference to the current direct parent
  258.      *
  259.      * @return object 
  260.      */
  261.     function _getStackLocation()
  262.     {
  263.         $return '';
  264.         foreach($this->_stack as $stack{
  265.             $return .= $stack.'->';
  266.         }
  267.  
  268.         return rtrim($return'->');
  269.     }
  270.  
  271.     /**
  272.      * Handler function for the start of a tag
  273.      *
  274.      * @access protected
  275.      * @param resource $parser 
  276.      * @param string $name 
  277.      * @param array $attrs 
  278.      */
  279.     function _startElement($parser$name$attrs array())
  280.     {
  281.         //Make the name of the tag lower case
  282.         $name strtolower($name);
  283.  
  284.         //Check to see if tag is root-level
  285.         if (count($this->_stack== 0)
  286.         {
  287.             //If so, set the document as the current tag
  288.             $this->document = new JSimpleXMLElement($name$attrs);
  289.  
  290.             //And start out the stack with the document tag
  291.             $this->_stack = array('document');
  292.         }
  293.         //If it isn't root level, use the stack to find the parent
  294.         else
  295.         {
  296.              //Get the name which points to the current direct parent, relative to $this
  297.             $parent $this->_getStackLocation();
  298.  
  299.             //Add the child
  300.             eval('$this->'.$parent.'->addChild($name, $attrs, '.count($this->_stack).');');
  301.  
  302.             //Update the stack
  303.             eval('$this->_stack[] = $name.\'[\'.(count($this->'.$parent.'->'.$name.') - 1).\']\';');
  304.         }
  305.     }
  306.  
  307.     /**
  308.      * Handler function for the end of a tag
  309.      *
  310.      * @access protected
  311.      * @param resource $parser 
  312.      * @param string $name 
  313.      */
  314.     function _endElement($parser$name)
  315.     {
  316.         //Update stack by removing the end value from it as the parent
  317.         array_pop($this->_stack);
  318.     }
  319.  
  320.     /**
  321.      * Handler function for the character data within a tag
  322.      *
  323.      * @access protected
  324.      * @param resource $parser 
  325.      * @param string $data 
  326.      */
  327.     function _characterData($parser$data)
  328.     {
  329.         //Get the reference to the current parent object
  330.         $tag $this->_getStackLocation();
  331.  
  332.         //Assign data to it
  333.         eval('$this->'.$tag.'->_data .= trim($data);');
  334.     }
  335. }
  336.  
  337.  
  338. /**
  339.  * SimpleXML Element
  340.  *
  341.  * This object stores all of the direct children of itself in the $children array.
  342.  * They are also stored by type as arrays. So, if, for example, this tag had 2 <font>
  343.  * tags as children, there would be a class member called $font created as an array.
  344.  * $font[0] would be the first font tag, and $font[1] would be the second.
  345.  *
  346.  * To loop through all of the direct children of this object, the $children member
  347.  *  should be used.
  348.  *
  349.  * To loop through all of the direct children of a specific tag for this object, it
  350.  * is probably easier to use the arrays of the specific tag names, as explained above.
  351.  *
  352.  * @author        Johan Janssens <[email protected]>
  353.  * @package     Joomla.Framework
  354.  * @subpackage    Utilities
  355.  * @since 1.5
  356.  */
  357. class JSimpleXMLElement extends JObject
  358. {
  359.     /**
  360.      * Array with the attributes of this XML element
  361.      *
  362.      * @var array 
  363.      */
  364.     var $_attributes = array();
  365.  
  366.     /**
  367.      * The name of the element
  368.      *
  369.      * @var string 
  370.      */
  371.     var $_name = '';
  372.  
  373.     /**
  374.      * The data the element contains
  375.      *
  376.      * @var string 
  377.      */
  378.     var $_data = '';
  379.  
  380.     /**
  381.      * Array of references to the objects of all direct children of this XML object
  382.      *
  383.      * @var array 
  384.      */
  385.     var $_children = array();
  386.  
  387.     /**
  388.      * The level of this XML element
  389.      *
  390.      * @var int 
  391.      */
  392.     var $_level = 0;
  393.  
  394.     /**
  395.      * Constructor, sets up all the default values
  396.      *
  397.      * @param string $name 
  398.      * @param array $attrs 
  399.      * @param int $parents 
  400.      * @return JSimpleXMLElement 
  401.      */
  402.     function __construct($name$attrs array()$level 0)
  403.     {
  404.         //Make the keys of the attr array lower case, and store the value
  405.         $this->_attributes = array_change_key_case($attrsCASE_LOWER);
  406.  
  407.         //Make the name lower case and store the value
  408.         $this->_name = strtolower($name);
  409.  
  410.         //Set the level
  411.         $this->_level = $level;
  412.     }
  413.  
  414.     /**
  415.      * Get the name of the element
  416.      *
  417.      * @access public
  418.      * @return string 
  419.      */
  420.     function name({
  421.         return $this->_name;
  422.     }
  423.  
  424.     /**
  425.      * Get the an attribute of the element
  426.      *
  427.      * @param string $attribute     The name of the attribute
  428.      *
  429.      * @access public
  430.      * @return mixed If an attribute is given will return the attribute if it exist.
  431.      *                   If no attribute is given will return the complete attributes array
  432.      */
  433.     function attributes($attribute null)
  434.     {
  435.         if(!isset($attribute)) {
  436.             return $this->_attributes;
  437.         }
  438.  
  439.         return isset($this->_attributes[$attribute]$this->_attributes[$attributenull;
  440.     }
  441.  
  442.     /**
  443.      * Get the data of the element
  444.      *
  445.      * @access public
  446.      * @return string 
  447.      */
  448.     function data({
  449.         return $this->_data;
  450.     }
  451.  
  452.     /**
  453.      * Set the data of the element
  454.      *
  455.      * @access public
  456.      * @param    string $data 
  457.      * @return string 
  458.      */
  459.     function setData($data{
  460.         $this->_data = $data;
  461.     }
  462.  
  463.     /**
  464.      * Get the children of the element
  465.      *
  466.      * @access public
  467.      * @return array 
  468.      */
  469.     function children({
  470.         return $this->_children;
  471.     }
  472.  
  473.     /**
  474.      * Get the level of the element
  475.      *
  476.      * @access public
  477.      * @return int 
  478.      */
  479.     function level({
  480.         return $this->_level;
  481.     }
  482.  
  483.      /**
  484.      * Adds an attribute to the element
  485.      *
  486.      * @param string $name 
  487.      * @param array  $attrs 
  488.      */
  489.     function addAttribute($name$value)
  490.     {
  491.         //add the attribute to the element, override if it already exists
  492.         $this->_attributes[$name$value;
  493.     }
  494.  
  495.      /**
  496.      * Removes an attribute from the element
  497.      *
  498.      * @param string $name 
  499.      */
  500.     function removeAttribute($name)
  501.     {
  502.         unset($this->_attributes[$name]);
  503.     }
  504.  
  505.     /**
  506.      * Adds a direct child to the element
  507.      *
  508.      * @param string $name 
  509.      * @param array  $attrs 
  510.      * @param int      $level 
  511.      */
  512.     function addChild($name$attrs$level)
  513.     {
  514.         //If there is no array already set for the tag name being added,
  515.         //create an empty array for it
  516.         if(!isset($this->$name))
  517.             $this->$name array();
  518.  
  519.         //Create the child object itself
  520.         $child new JSimpleXMLElement($name$attrs$level);
  521.  
  522.         //Add the reference of it to the end of an array member named for the elements name
  523.         $this->{$name}[=$child;
  524.  
  525.         //Add the reference to the children array member
  526.                 $this->_children[=$child;
  527.     }
  528.  
  529.     function removeChild(&$child)
  530.     {
  531.         $name $child->name();
  532.         for ($i=0,$n=count($this->_children);$i<$n;$i++)
  533.         {
  534.             if ($this->_children[$i== $child{
  535.                 unset($this->_children[$i]);
  536.             }
  537.         }
  538.         for ($i=0,$n=count($this->{$name});$i<$n;$i++)
  539.         {
  540.             if ($this->{$name}[$i== $child{
  541.                 unset($this->{$name}[$i]);
  542.             }
  543.         }
  544.         $this->_children array_values($this->_children);
  545.         $this->{$namearray_values($this->{$name});
  546.         unset($child);
  547.     }
  548.  
  549.     /**
  550.      * Get an element in the document by / separated path
  551.      *
  552.      * @param    string    $path    The / separated path to the element
  553.      * @return    object    JSimpleXMLElement 
  554.      */
  555.     function &getElementByPath($path)
  556.     {
  557.         $tmp    =$this;
  558.         $false    false;
  559.         $parts    explode('/'trim($path'/'));
  560.  
  561.         foreach ($parts as $node)
  562.         {
  563.             $found false;
  564.             foreach ($tmp->_children as $child)
  565.             {
  566.                 if ($child->_name == $node)
  567.                 {
  568.                     $tmp =$child;
  569.                     $found true;
  570.                     break;
  571.                 }
  572.             }
  573.             if (!$found{
  574.                 break;
  575.             }
  576.         }
  577.  
  578.         if ($found{
  579.             $ref =$tmp;
  580.         else {
  581.             $ref =$false;
  582.         }
  583.         return $ref;
  584.     }
  585.  
  586.     function map($callback$args=array())
  587.     {
  588.         $callback($this$args);
  589.         // Map to all children
  590.         if ($n count($this->_children)) {
  591.             for($i=0;$i<$n;$i++)
  592.             {
  593.                 $this->_children[$i]->map($callback$args);
  594.             }
  595.         }
  596.     }
  597.  
  598.     /**
  599.      * Return a well-formed XML string based on SimpleXML element
  600.      *
  601.      * @return string 
  602.      */
  603.     function asXML($whitespace=true)
  604.     {
  605.         //Start a new line, indent by the number indicated in $this->level, add a <, and add the name of the tag
  606.         if ($whitespace{
  607.             $out "\n".str_repeat("\t"$this->_level).'<'.$this->_name;
  608.         else {
  609.             $out '<'.$this->_name;
  610.         }
  611.  
  612.         //For each attribute, add attr="value"
  613.         foreach($this->_attributes as $attr => $value)
  614.             $out .= ' '.$attr.'="'.$value.'"';
  615.  
  616.         //If there are no children and it contains no data, end it off with a />
  617.         if(empty($this->_children&& empty($this->_data))
  618.             $out .= " />";
  619.  
  620.         //Otherwise...
  621.         else
  622.         {
  623.             //If there are children
  624.             if(!empty($this->_children))
  625.             {
  626.                 //Close off the start tag
  627.                 $out .= '>';
  628.  
  629.                 //For each child, call the asXML function (this will ensure that all children are added recursively)
  630.                 foreach($this->_children as $child)
  631.                     $out .= $child->asXML($whitespace);
  632.  
  633.                 //Add the newline and indentation to go along with the close tag
  634.                 if ($whitespace{
  635.                     $out .= "\n".str_repeat("\t"$this->_level);
  636.                 }
  637.             }
  638.  
  639.             //If there is data, close off the start tag and add the data
  640.             elseif(!empty($this->_data))
  641.                 $out .= '>'.$this->_data;
  642.  
  643.             //Add the end tag
  644.             $out .= '</'.$this->_name.'>';
  645.         }
  646.  
  647.         //Return the final output
  648.         return $out;
  649.     }
  650. }
  651. ?>

Documentation generated on Mon, 05 Mar 2007 21:25:19 +0000 by phpDocumentor 1.3.1