[ Index ]

PHP Cross Reference of Joomla 1.5.26 DE

title

Body

[close]

/libraries/joomla/utilities/ -> simplexml.php (source)

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


Generated: Wed Mar 28 15:54:07 2012 Cross-referenced by PHPXref 0.7.1