2 // +----------------------------------------------------------------------+
3 // | PEAR :: XML_Tree |
4 // +----------------------------------------------------------------------+
5 // | Copyright (c) 1997-2003 The PHP Group |
6 // +----------------------------------------------------------------------+
7 // | This source file is subject to version 2.02 of the PHP license, |
8 // | that is bundled with this package in the file LICENSE, and is |
9 // | available at through the world-wide-web at |
10 // | http://www.php.net/license/2_02.txt. |
11 // | If you did not receive a copy of the PHP license and are unable to |
12 // | obtain it through the world-wide-web, please send a note to |
13 // | license@php.net so we can mail you a copy immediately. |
14 // +----------------------------------------------------------------------+
15 // | Authors: Bernd Römer <berndr@bonn.edu> |
16 // | Sebastian Bergmann <sb@sebastian-bergmann.de> |
17 // | Christian Kühn <ck@chkuehn.de> (escape xml entities) |
18 // | Michele Manzato <michele.manzato@verona.miz.it> |
19 // +----------------------------------------------------------------------+
21 // $Id: Node.php,v 1.18 2003/09/11 19:03:27 cox Exp $
27 * @author Bernd Römer <berndr@bonn.edu>
29 * @version 1.0 16-Aug-2001
31 #[AllowDynamicProperties]
35 * Attributes of this node
42 * Children of this node
49 * Content (text) of this node
66 * @param string name Node name
67 * @param string content Node content (text)
68 * @param array attributes Attribute-hash for the node
70 function __construct($name, $content = '', $attributes = array(), $lineno = null)
73 if ($content !== null) {
74 $this->setContent($content);
76 $this->attributes = $attributes;
77 $this->children = array();
78 $this->lineno = $lineno;
82 * Append a child node to this node, after all other nodes
84 * @param mixed child Child to insert (XML_Tree or XML_Tree_Node),
85 * or name of child node
86 * @param string content Content (text) for the new node (only if
87 * $child is the node name)
88 * @param array attributes Attribute-hash for new node
90 * @return object reference to new child node
93 function &addChild($child, $content = '', $attributes = array(), $lineno = null)
95 $index = sizeof($this->children);
97 if (is_object($child)) {
98 if (strtolower(get_class($child)) == 'xml_tree_node') {
99 $this->children[$index] = $child;
102 if (strtolower(get_class($child)) == 'xml_tree' && isset($child->root)) {
103 $this->children[$index] = $child->root->getElement();
106 $this->children[$index] = new XML_Tree_Node($child, $content, $attributes, $lineno);
109 return $this->children[$index];
113 * Get a copy of this node by clone this node and all of its children,
116 * @return object Reference to the cloned copy.
119 function &cloneNode()
121 $clone = new XML_Tree_Node($this->name,$this->content,$this->attributes);
123 $max_child=count($this->children);
124 for($i=0;$i<$max_child;$i++) {
125 $clone->children[]=$this->children[$i]->cloneNode();
128 /* for future use....
129 // clone all other vars
130 $temp=get_object_vars($this);
131 foreach($temp as $varname => $value)
132 if (!in_array($varname,array('name','content','attributes','children')))
133 $clone->$varname=$value;
140 * Inserts child ($child) to a specified child-position ($pos)
142 * @param mixed path Path to parent node to add child (see getNodeAt()
143 * for format). If null child is inserted in the
145 * @param integer pos Position where to insert the new child.
146 * 0 < means |$pos| elements before the end,
147 * e.g. -1 appends as last child.
148 * @param mixed child Child to insert (XML_Tree or XML_Tree_Node),
149 * or name of child node
150 * @param string content Content (text) for the new node (only if
151 * $child is the node name)
152 * @param array attributes Attribute-hash for new node
154 * @return Reference to the newly inserted node, or PEAR_Error upon insertion error.
157 function &insertChild($path,$pos,&$child, $content = '', $attributes = array())
159 $parent =& $this->getNodeAt($path);
160 if (PEAR::isError($parent)) {
161 // $path was not found
163 } elseif ($parent != $this) {
164 // Insert at the node found
165 return $parent->insertChild(null, $pos, $child, $content, $attributes);
168 if (($pos < -count($this->children)) || ($pos > count($this->children))) {
169 return new PEAR_Error("Invalid insert position.");
172 if (is_object($child)) { // child is an object
173 // insert a single node
174 if (strtolower(get_class($child)) == 'xml_tree_node') {
175 array_splice($this->children, $pos, 0, 'dummy');
177 $pos = count($this->children) + $pos - 1;
179 $this->children[$pos] = &$child;
180 // insert a tree i.e insert root-element of tree
181 } elseif (strtolower(get_class($child)) == 'xml_tree' && isset($child->root)) {
182 array_splice($this->children, $pos, 0, 'dummy');
184 $pos = count($this->children) + $pos - 1;
186 $this->children[$pos] = $child->root;
188 return new PEAR_Error("Bad node (must be a XML_Tree or an XML_Tree_Node)");
190 } else { // child offered is a string
191 array_splice($this->children, $pos, 0, 'dummy');
193 $pos = count($this->children) + $pos - 1;
195 $this->children[$pos] = new XML_Tree_Node($child, $content, $attributes);
201 * Removes child at a given position
203 * @param integer pos position of child to remove in children-list.
204 * 0 < means |$pos| elements before the end,
205 * e.g. -1 removes the last child.
207 * @return mixed The removed node, or PEAR_Error upon removal error.
210 function &removeChild($pos)
212 if (($pos < -count($this->children)) || ($pos >= count($this->children))) {
213 return new PEAR_Error("Invalid remove position.");
216 // Using array_splice() instead of a simple unset() to maintain index-integrity
217 return array_splice($this->children, $pos, 1);
221 * Returns text representation of this node.
223 * @return string text (xml) representation of this node. Each tag is
224 * indented according to its level.
230 static $do_ident = true;
232 if ($this->name !== null) {
233 $ident = str_repeat(' ', $deep);
235 $out = $ident . '<' . $this->name;
237 $out = '<' . $this->name;
239 foreach ($this->attributes as $name => $value) {
240 $out .= ' ' . $name . '="' . $value . '"';
243 $out .= '>' . $this->content;
245 if (sizeof($this->children) > 0) {
247 foreach ($this->children as $child) {
248 $out .= $child->get();
254 $out .= $ident . '</' . $this->name . ">\n";
256 $out .= '</' . $this->name . '>';
260 $out = $this->content;
268 * Get an attribute by its name.
270 * @param string $name Name of attribute to retrieve
272 * @return string attribute, or null if attribute is unset.
275 function getAttribute($name)
277 if (isset($this->attributes[strtolower($name)])) {
278 return $this->attributes[strtolower($name)];
283 function hasAttribute($name)
285 return isset($this->attributes[strtolower($name)]);
289 * Sets an attribute for this node.
291 * @param string name Name of attribute to set
292 * @param string value Value of attribute
296 function setAttribute($name, $value = '')
298 $this->attributes[strtolower($name)] = $value;
302 * Unsets an attribute of this node.
304 * @param string $name Name of attribute to unset
308 function unsetAttribute($name)
310 if (isset($this->attributes[strtolower($name)])) {
311 unset($this->attributes[strtolower($name)]);
316 * Sets the content for this node.
318 * @param string content Node content to assign
322 function setContent($content)
325 $this->content = $this->encodeXmlEntities($content);
326 // var_dump(array($this->content, $content));
330 * Gets an element by its 'path'.
332 * @param array path path to element: sequence of indexes to the
333 * children. E.g. array(1, 2, 3) means "third
334 * child of second child of first child" of the node.
336 * @return object reference to element found, or PEAR_Error if node can't
340 function &getElement($path)
342 if (!is_array($path)) {
343 $path = array($path);
345 if (sizeof($path) == 0) {
350 $next = array_shift($path1);
351 if (isset($this->children[$next])) {
352 $x =& $this->children[$next]->getElement($path1);
353 if (!PEAR::isError($x)) {
358 return new PEAR_Error("Bad path to node: [".implode('-', $path)."]");
362 * Get a reference to a node by its 'path'.
364 * @param mixed path Path to node. Can be either a string (slash-separated
365 * children names) or an array (sequence of children names) both
366 * starting from this node. The first name in sequence
367 * is a child name, not the name of this node.
369 * @return object Reference to the XML_Tree_Node found, or PEAR_Error if
370 * the path does not match any node. Note that if more than
371 * one element matches then only the first matching node is
375 function &getNodeAt($path)
377 if (is_string($path))
378 $path = explode("/", $path);
380 if (sizeof($path) == 0) {
385 $next = array_shift($path1);
387 // Get the first children of this node whose name is '$next'
389 for ($i = 0; $i < count($this->children); $i++) {
390 if ($this->children[$i]->name == $next) {
391 $child =& $this->children[$i];
395 if (!is_null($child)) {
396 $x =& $child->getNodeAt($path1);
397 if (!PEAR::isError($x)) {
402 // No node with that name found
403 return new PEAR_Error("Bad path to node: [".implode('/', $path)."]");
407 * Escape XML entities.
409 * @param string xml Text string to escape.
414 function encodeXmlEntities($xml)
416 $xml = preg_replace_callback("/[\x{0080}-\x{FFFF}]/u", function($v) {
418 return '&#'. mb_ord($v[0]).';';
420 //"'&#'.ord('\\1').';'"
425 $xml = str_replace(array('ü', 'Ü', 'ö',
430 array('ü', 'Ü', 'ö',
431 'Ö', 'ä', 'Ä',
432 'ß', '<', '>',
438 $xml = preg_replace(array("/\&([a-z\d\#]+)\;/i",
440 "/\#\|\|([a-z\d\#]+)\|\|\#/i",
451 $xml = preg_replace_callback("/[^a-zA-Z\d\s\<\>\&\;\.\:\=\"\-\/\%\?\!\'\(\)\[\]\{\}\$\#\+\,\@_]/u",
454 return '&#'. mb_ord($v[0]).';';
456 //"'&#'.ord('\\1').';'"
466 * Decode XML entities in a text string.
468 * @param string xml Text to decode
470 * @return string Decoded text
473 function decodeXmlEntities($xml)
475 static $trans_tbl = null;
477 $trans_tbl = get_html_translation_table(HTML_ENTITIES);
478 $trans_tbl = array_flip($trans_tbl);
480 for ($i = 1; $i <= 255; $i++) {
481 $ent = sprintf("&#%03d;", $i);
483 $xml = str_replace($ent, $ch, $xml);
486 return strtr($xml, $trans_tbl);
491 * Print text representation of XML_Tree_Node.