| [ Index ] |
PHP Cross Reference of Joomla 1.5.26 DE |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Mar 28 15:54:07 2012 | Cross-referenced by PHPXref 0.7.1 |