OSDN Git Service

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