source: trunk/phpgwapi/inc/class.xml.inc.php @ 2

Revision 2, 116.5 KB checked in by niltonneto, 17 years ago (diff)

Removida todas as tags usadas pelo CVS ($Id, $Source).
Primeira versão no CVS externo.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
Line 
1<?php
2
3//
4// +----------------------------------------------------------------------+
5// | <phpXML/> version 1.0                                                |
6// | Copyright (c) 2001 Michael P. Mehl. All rights reserved.             |
7// +----------------------------------------------------------------------+
8// | Latest releases are available at http://phpxml.org/. For feedback or |
9// | bug reports, please contact the author at mpm@phpxml.org. Thanks!    |
10// +----------------------------------------------------------------------+
11// | The contents of this file are subject to the Mozilla Public License  |
12// | Version 1.1 (the "License"); you may not use this file except in     |
13// | compliance with the License. You may obtain a copy of the License at |
14// | http://www.mozilla.org/MPL/                                          |
15// |                                                                      |
16// | Software distributed under the License is distributed on an "AS IS"  |
17// | basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See  |
18// | the License for the specific language governing rights and           |
19// | limitations under the License.                                       |
20// |                                                                      |
21// | The Original Code is <phpXML/>.                                      |
22// |                                                                      |
23// | The Initial Developer of the Original Code is Michael P. Mehl.       |
24// | Portions created by Michael P. Mehl are Copyright (C) 2001 Michael   |
25// | P. Mehl. All Rights Reserved.                                        |
26// +----------------------------------------------------------------------+
27// | Authors:                                                             |
28// |   Michael P. Mehl <mpm@phpxml.org>                                   |
29// +----------------------------------------------------------------------+
30//
31
32/**
33* Class for accessing XML data through the XPath language.
34*
35* This class offers methods for accessing the nodes of a XML document using
36* the XPath language. You can add or remove nodes, set or modify their
37* content and their attributes. No additional PHP extensions like DOM XML
38* or something similar are required to use these features.
39*
40* @link      http://www.phpxml.org/ Latest release of this class
41* @link      http://www.w3.org/TR/xpath W3C XPath Recommendation
42* @copyright Copyright (c) 2001 Michael P. Mehl. All rights reserved.
43* @author    Michael P. Mehl <mpm@phpxml.org>
44* @version   1.0 (2001-03-08)
45* @access    public
46*/
47
48class XML
49{
50    /**
51    * List of all document nodes.
52    *
53    * This array contains a list of all document nodes saved as an
54    * associative array.
55    *
56    * @access private
57    * @var    array
58    */
59    var $nodes = array();
60   
61    /**
62    * List of document node IDs.
63    *
64    * This array contains a list of all IDs of all document nodes that
65    * are used for counting when adding a new node.
66    *
67    * @access private
68    * @var    array
69    */
70    var $ids = array();
71   
72    /**
73    * Current document path.
74    *
75    * This variable saves the current path while parsing a XML file and adding
76    * the nodes being read from the file.
77    *
78    * @access private
79    * @var    string
80    */
81    var $path = "";
82   
83    /**
84    * Current document position.
85    *
86    * This variable counts the current document position while parsing a XML
87    * file and adding the nodes being read from the file.
88    *
89    * @access private
90    * @var    int
91    */
92    var $position = 0;
93   
94    /**
95    * Path of the document root.
96    *
97    * This string contains the full path to the node that acts as the root
98    * node of the whole document.
99    *
100    * @access private
101    * @var    string
102    */
103    var $root = "";
104   
105    /**
106    * Current XPath expression.
107    *
108    * This string contains the full XPath expression being parsed currently.
109    *
110    * @access private
111    * @var    string
112    */
113    var $xpath    = "";
114                                                                               
115    /**
116    * List of entities to be converted.
117    *
118    * This array contains a list of entities to be converted when an XPath
119    * expression is evaluated.
120    *
121    * @access private
122    * @var    array
123    */
124    var $entities = array ( "&" => "&amp;", "<" => "&lt;", ">" => "&gt;",
125        "'" => "&apos", '"' => "&quot;" );
126   
127    /**
128    * List of supported XPath axes.
129    *
130    * This array contains a list of all valid axes that can be evaluated in an
131    * XPath expression.
132    *
133    * @access private
134    * @var    array
135    */
136    var $axes = array ( "child", "descendant", "parent", "ancestor",
137        "following-sibling", "preceding-sibling", "following", "preceding",
138        "attribute", "namespace", "self", "descendant-or-self",
139        "ancestor-or-self" );
140   
141    /**
142    * List of supported XPath functions.
143    *
144    * This array contains a list of all valid functions that can be evaluated
145    * in an XPath expression.
146    *
147    * @access private
148    * @var    array
149    */
150    var $functions = array ( "last", "position", "count", "id", "name",
151        "string", "concat", "starts-with", "contains", "substring-before",
152        "substring-after", "substring", "string-length", "translate",
153        "boolean", "not", "true", "false", "lang", "number", "sum", "floor",
154        "ceiling", "round", "text" );
155   
156    /**
157    * List of supported XPath operators.
158    *
159    * This array contains a list of all valid operators that can be evaluated
160    * in a predicate of an XPath expression. The list is ordered by the
161    * precedence of the operators (lowest precedence first).
162    *
163    * @access private
164    * @var    array
165    */
166    var $operators = array( " or ", " and ", "=", "!=", "<=", "<", ">=", ">",
167        "+", "-", "*", " div ", " mod " );
168
169    /**
170    * Constructor of the class.
171    *
172    * This constructor initializes the class and, when a filename is given,
173    * tries to read and parse the given file.
174    *
175    * @access    public
176    * @author    Michael P. Mehl <mpm@phpxml.org>
177    * @param     string $file Path and name of the file to read and parsed.
178    * @see       load_file()
179    */
180    function XML ( $file = "" )
181    {
182        // Check whether a file was given.
183        if ( !empty($file) )
184        {
185            // Load the XML file.
186            $this->load_file($file);
187        }
188    }
189
190    /**
191    * Reads a file and parses the XML data.
192    *
193    * This method reads the content of a XML file, tries to parse its
194    * content and upon success stores the information retrieved from
195    * the file into an array.
196    *
197    * @access    public
198    * @author    Michael P. Mehl <mpm@phpxml.org>
199    * @param     string $file Path and name of the file to be read and parsed.
200    * @see       handle_start_element(), handle_end_element(),
201    *            handle_character_data()
202    */
203    function load_file ( $file )
204    {
205        // Check whether the file exists and is readable.
206        if ( file_exists($file) && is_readable($file) )
207        {
208            // Read the content of the file.
209            $content = implode("", file($file));
210           
211            // Check whether content has been read.
212            if ( !empty($content) )
213            {
214                // Create an XML parser.
215                $parser = xml_parser_create();
216               
217                // Set the options for parsing the XML data.
218                xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
219                xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
220               
221                // Set the object for the parser.
222                xml_set_object($parser, &$this);
223               
224                // Set the element handlers for the parser.
225                xml_set_element_handler($parser, "handle_start_element",
226                    "handle_end_element");
227                xml_set_character_data_handler($parser,
228                    "handle_character_data");
229               
230                // Parse the XML file.
231                if ( !xml_parse($parser, $content, true) )
232                {
233                    // Display an error message.
234                    $this->display_error("XML error in file %s, line %d: %s",
235                        $file, xml_get_current_line_number($parser),
236                        xml_error_string(xml_get_error_code($parser)));
237                }
238               
239                // Free the parser.
240                xml_parser_free($parser);
241            }
242        }
243        else
244        {
245            // Display an error message.
246            $this->display_error("File %s could not be found or read.", $file);
247        }
248    }
249   
250    /**
251    * Generates a XML file with the content of the current document.
252    *
253    * This method creates a string containing the XML data being read
254    * and modified by this class before. This string can be used to save
255    * a modified document back to a file or doing other nice things with
256    * it.
257    *
258    * @access    public
259    * @author    Michael P. Mehl <mpm@phpxml.org>
260    * @param     array $highlight Array containing a list of full document
261    *            paths of nodes to be highlighted by <font>...</font> tags
262    *            in the generated XML string.
263    * @param     string $root While doing a recursion with this method, this
264    *            parameter is used for internal purpose.
265    * @param     int $level While doing a recursion with this method, this
266    *            parameter is used for internal purpose.
267    * @return    string The returned string contains well-formed XML data
268    *            representing the content of this document.
269    * @see       load_file(), evaluate(), get_content()
270    */
271    function get_file ( $highlight = array(), $root = "", $level = 0 )
272    {
273        // Create a string to save the generated XML data.
274        $xml = "";
275       
276        // Create two strings containing the tags for highlighting a node.
277        $highlight_start = "<font color=\"#FF0000\"><b>";
278        $highlight_end   = "</b></font>";
279       
280        // Generate a string to be displayed before the tags.
281        $before = "";
282       
283        // Calculate the amount of whitespaces to display.
284        for ( $i = 0; $i < ( $level * 2 ); $i++ )
285        {
286            // Add a whitespaces to the string.
287            $before .= " ";
288        }
289       
290        // Check whether a root node is given.
291        if ( empty($root) )
292        {
293            // Set it to the document root.
294            $root = $this->root;
295        }
296       
297        // Check whether the node is selected.
298        $selected = in_array($root, $highlight);
299       
300        // Now add the whitespaces to the XML data.
301        $xml .= $before;
302       
303        // Check whether the node is selected.
304        if ( $selected )
305        {
306            // Add the highlight code to the XML data.
307            $xml .= $highlight_start;
308        }
309       
310        // Now open the tag.
311        $xml .= "&lt;".$this->nodes[$root]["name"];
312       
313        // Check whether there are attributes for this node.
314        if ( count($this->nodes[$root]["attributes"]) > 0 )
315        {
316            // Run through all attributes.
317            foreach ( $this->nodes[$root]["attributes"] as $key => $value )
318            {
319                // Check whether this attribute is highlighted.
320                if ( in_array($root."/attribute::".$key, $highlight) )
321                {
322                    // Add the highlight code to the XML data.
323                    $xml .= $highlight_start;
324                }
325               
326                // Add the attribute to the XML data.
327                $xml .= " ".$key."=\"".trim(stripslashes($value))."\"";
328               
329                // Check whether this attribute is highlighted.
330                if ( in_array($root."/attribute::".$key, $highlight) )
331                {
332                    // Add the highlight code to the XML data.
333                    $xml .= $highlight_end;
334                }
335            }
336        }
337       
338        // Check whether the node contains character data or has children.
339        if ( empty($this->nodes[$root]["text"]) &&
340            !isset($this->nodes[$root]["children"]) )
341        {
342            // Add the end to the tag.
343            $xml .= "/";
344        }
345       
346        // Close the tag.
347        $xml .= "&gt;\n";
348       
349        // Check whether the node is selected.
350        if ( $selected )
351        {
352            // Add the highlight code to the XML data.
353            $xml .= $highlight_end;
354        }
355       
356        // Check whether the node contains character data.
357        if ( !empty($this->nodes[$root]["text"]) )
358        {
359            // Add the character data to the XML data.
360            $xml .= $before."  ".$this->nodes[$root]["text"]."\n";
361        }
362       
363        // Check whether the node has children.
364        if ( isset($this->nodes[$root]["children"]) )
365        {
366            // Run through all children with different names.
367            foreach ( $this->nodes[$root]["children"] as $child => $pos )
368            {
369                // Run through all children with the same name.
370                for ( $i = 1; $i <= $pos; $i++ )
371                {
372                    // Generate the full path of the child.
373                    $fullchild = $root."/".$child."[".$i."]";
374                   
375                    // Add the child's XML data to the existing data.
376                    $xml .= $this->get_file($highlight, $fullchild,
377                        $level + 1);
378                }
379            }
380        }
381       
382        // Check whether there are attributes for this node.
383        if ( !empty($this->nodes[$root]["text"]) ||
384            isset($this->nodes[$root]["children"]) )
385        {
386            // Add the whitespaces to the XML data.
387            $xml .= $before;
388           
389            // Check whether the node is selected.
390            if ( $selected )
391            {
392                // Add the highlight code to the XML data.
393                $xml .= $highlight_start;
394            }
395           
396            // Add the closing tag.
397            $xml .= "&lt;/".$this->nodes[$root]["name"]."&gt;";
398           
399            // Check whether the node is selected.
400            if ( $selected )
401            {
402                // Add the highlight code to the XML data.
403                $xml .= $highlight_end;
404            }
405           
406            // Add a linebreak.
407            $xml .= "\n";
408        }
409       
410        // Return the XML data.
411        return $xml;
412    }
413   
414    /**
415    * Adds a new node to the XML document.
416    *
417    * This method adds a new node to the tree of nodes of the XML document
418    * being handled by this class. The new node is created according to the
419    * parameters passed to this method.
420    *
421    * @access    public
422    * @author    Michael P. Mehl <mpm@phpxml.org>
423    * @param     string $content Full path of the parent, to which the new
424    *            node should be added as a child.
425    * @param     string $name Name of the new node.
426    * @return    string The string returned by this method will contain the
427    *            full document path of the created node.
428    * @see       remove_node(), evaluate()
429    */
430    function add_node ( $context, $name )
431    {
432        // Check whether a name for this element is already set.
433        if ( empty($this->root) )
434        {
435            // Use this tag as the root element.
436            $this->root = "/".$name."[1]";
437        }
438       
439        // Calculate the full path for this element.
440        $path = $context."/".$name;
441       
442        // Set the relative context and the position.
443        $position = ++$this->ids[$path];
444        $relative = $name."[".$position."]";
445       
446        // Calculate the full path.
447        $fullpath = $context."/".$relative;
448       
449        // Calculate the context position, which is the position of this
450        // element within elements of the same name in the parent node.
451        $this->nodes[$fullpath]["context-position"] = $position;
452       
453        // Calculate the position for the following and preceding axis
454        // detection.
455        $this->nodes[$fullpath]["document-position"] =
456            $this->nodes[$context]["document-position"] + 1;
457       
458        // Save the information about the node.
459        $this->nodes[$fullpath]["name"]   = $name;
460        $this->nodes[$fullpath]["text"]   = "";
461        $this->nodes[$fullpath]["parent"] = $context;
462       
463        // Add this element to the element count array.
464        if ( !$this->nodes[$context]["children"][$name] )
465        {
466            // Set the default name.
467            $this->nodes[$context]["children"][$name] = 1;
468        }
469        else
470        {
471            // Calculate the name.
472            $this->nodes[$context]["children"][$name] =
473                $this->nodes[$context]["children"][$name] + 1;
474        }
475       
476        // Return the path of the new node.
477        return $fullpath;
478    }
479
480    /**
481    * Removes a node from the XML document.
482    *
483    * This method removes a node from the tree of nodes of the XML document.
484    * If the node is a document node, all children of the node and its
485    * character data will be removed. If the node is an attribute node,
486    * only this attribute will be removed, the node to which the attribute
487    * belongs as well as its children will remain unmodified.
488    *
489    * @access    public
490    * @author    Michael P. Mehl <mpm@phpxml.org>
491    * @param     string $node Full path of the node to be removed.
492    * @see       add_node(), evaluate()
493    */
494    function remove_node ( $node )
495    {
496        // Check whether the node is an attribute node.
497        if ( ereg("/attribute::", $node) )
498        {
499            // Get the path to the attribute node's parent.
500            $parent = $this->prestr($node, "/attribute::");
501           
502            // Get the name of the attribute.
503            $attribute = $this->afterstr($node, "/attribute::");
504           
505            // Check whether the attribute exists.
506            if ( isset($this->nodes[$parent]["attributes"][$attribute]) )
507            {
508                // Create a new array.
509                $new = array();
510               
511                // Run through the existing attributes.
512                foreach ( $this->nodes[$parent]["attributes"]
513                    as $key => $value )
514                {
515                    // Check whether it's the attribute to remove.
516                    if ( $key != $attribute )
517                    {
518                        // Add it to the new array again.
519                        $new[$key] = $value;
520                    }
521                }
522               
523                // Save the new attributes.
524                $this->nodes[$parent]["attributes"] = $new;
525            }
526        }
527        else
528        {
529            // Create an associative array, which contains information about
530            // all nodes that required to be renamed.
531            $rename = array();
532           
533            // Get the name, the parent and the siblings of current node.
534            $name     = $this->nodes[$node]["name"];
535            $parent   = $this->nodes[$node]["parent"];
536            $siblings = $this->nodes[$parent]["children"][$name];
537           
538            // Decrease the number of children.
539            $this->nodes[$parent]["children"][$name]--;
540           
541            // Create a counter for renumbering the siblings.
542            $counter = 1;
543           
544            // Now run through the siblings.
545            for ( $i = 1; $i <= $siblings; $i++ )
546            {
547                // Create the name of the sibling.
548                $sibling = $parent."/".$name."[".$i."]";
549               
550                // Check whether it's the name of the current node.
551                if ( $sibling != $node )
552                {
553                    // Create the new name for the sibling.
554                    $new = $parent."/".$name."[".$counter."]";
555                   
556                    // Increase the counter.
557                    $counter++;
558                   
559                    // Add the old and the new name to the list of nodes
560                    // to be renamed.
561                    $rename[$sibling] = $new;
562                }
563            }
564           
565            // Create an array for saving the new node-list.
566            $nodes = array();
567           
568            // Now run through through the existing nodes.
569            foreach ( $this->nodes as $name => $values )
570            {
571                // Check the position of the path of the node to be deleted
572                // in the path of the current node.
573                $position = strpos($name, $node);
574
575                // Check whether it's not the node to be deleted.
576                if ( $position === false )
577                {
578                    // Run through the array of nodes to be renamed.
579                    foreach ( $rename as $old => $new )
580                    {
581                        // Check whether this node and it's parent requires to
582                        // be renamed.
583                        $name             = str_replace($old, $new, $name);
584                        $values["parent"] = str_replace($old, $new,
585                            $values["parent"]);
586                    }
587                   
588                    // Add the node to the list of nodes.
589                    $nodes[$name] = $values;
590                }
591            }
592           
593            // Save the new array of nodes.
594            $this->nodes = $nodes;
595        }
596    }
597
598    /**
599    * Add content to a node.
600    *
601    * This method adds content to a node. If it's an attribute node, then
602    * the value of the attribute will be set, otherwise the character data of
603    * the node will be set. The content is appended to existing content,
604    * so nothing will be overwritten.
605    *
606    * @access    public
607    * @author    Michael P. Mehl <mpm@phpxml.org>
608    * @param     string $path Full document path of the node.
609    * @param     string $value String containing the content to be added.
610    * @see       get_content(), evaluate()
611    */
612    function add_content ( $path, $value )
613    {
614        // Check whether it's an attribute node.
615        if ( ereg("/attribute::", $path) )
616        {
617            // Get the path to the attribute node's parent.
618            $parent = $this->prestr($path, "/attribute::");
619           
620            // Get the parent node.
621            $parent = $this->nodes[$parent];
622           
623            // Get the name of the attribute.
624            $attribute = $this->afterstr($path, "/attribute::");
625           
626            // Set the attribute.
627            $parent["attributes"][$attribute] .= $value;
628        }
629        else
630        {
631            // Set the character data of the node.
632            $this->nodes[$path]["text"] .= $value;
633        }
634    }
635   
636    /**
637    * Set the content of a node.
638    *
639    * This method sets the content of a node. If it's an attribute node, then
640    * the value of the attribute will be set, otherwise the character data of
641    * the node will be set. Existing content will be overwritten.
642    *
643    * @access    public
644    * @author    Michael P. Mehl <mpm@phpxml.org>
645    * @param     string $path Full document path of the node.
646    * @param     string $value String containing the content to be set.
647    * @see       get_content(), evaluate()
648    */
649    function set_content ( $path, $value )
650    {
651        // Check whether it's an attribute node.
652        if ( ereg("/attribute::", $path) )
653        {
654            // Get the path to the attribute node's parent.
655            $parent = $this->prestr($path, "/attribute::");
656           
657            // Get the parent node.
658            $parent = $this->nodes[$parent];
659           
660            // Get the name of the attribute.
661            $attribute = $this->afterstr($path, "/attribute::");
662           
663            // Set the attribute.
664            $parent["attributes"][$attribute] = $value;
665        }
666        else
667        {
668            // Set the character data of the node.
669            $this->nodes[$path]["text"] = $value;
670        }
671    }
672   
673    /**
674    * Retrieves the content of a node.
675    *
676    * This method retrieves the content of a node. If it's an attribute
677    * node, then the value of the attribute will be retrieved, otherwise
678    * it'll be the character data of the node.
679    *
680    * @access    public
681    * @author    Michael P. Mehl <mpm@phpxml.org>
682    * @param     string $path Full document path of the node, from which the
683    *            content should be retrieved.
684    * @return    string The returned string contains either the value or the
685    *            character data of the node.
686    * @see       set_content(), evaluate()
687    */
688    function get_content ( $path )
689    {
690        // Check whether it's an attribute node.
691        if ( ereg("/attribute::", $path) )
692        {
693            // Get the path to the attribute node's parent.
694            $parent = $this->prestr($path, "/attribute::");
695           
696            // Get the parent node.
697            $parent = $this->nodes[$parent];
698           
699            // Get the name of the attribute.
700            $attribute = $this->afterstr($path, "/attribute::");
701           
702            // Get the attribute.
703            $attribute = $parent["attributes"][$attribute];
704           
705            // Return the value of the attribute.
706            return $attribute;
707        }
708        else
709        {
710            // Return the cdata of the node.
711            return stripslashes($this->nodes[$path]["text"]);
712        }
713    }
714   
715    /**
716    * Add attributes to a node.
717    *
718    * This method adds attributes to a node. Existing attributes will not be
719    * overwritten.
720    *
721    * @access    public
722    * @author    Michael P. Mehl <mpm@phpxml.org>
723    * @param     string $path Full document path of the node, the attributes
724    *            should be added to.
725    * @param     array $attributes Associative array containing the new
726    *            attributes for the node.
727    * @see       set_content(), get_content()
728    */
729    function add_attributes ( $path, $attributes )
730    {
731        // Add the attributes to the node.
732        $this->nodes[$path]["attributes"] = array_merge($attributes,
733            $this->nodes[$path]["attributes"]);
734    }
735   
736    /**
737    * Sets the attributes of a node.
738    *
739    * This method sets the attributes of a node and overwrites all existing
740    * attributes by doing this.
741    *
742    * @access    public
743    * @author    Michael P. Mehl <mpm@phpxml.org>
744    * @param     string $path Full document path of the node, the attributes
745    *            of which should be set.
746    * @param     array $attributes Associative array containing the new
747    *            attributes for the node.
748    * @see       set_content(), get_content()
749    */
750    function set_attributes ( $path, $attributes )
751    {
752        // Set the attributes of the node.
753        $this->nodes[$path]["attributes"] = $attributes;
754    }
755   
756    /**
757    * Retrieves a list of all attributes of a node.
758    *
759    * This method retrieves a list of all attributes of the node specified in
760    * the argument.
761    *
762    * @access    public
763    * @author    Michael P. Mehl <mpm@phpxml.org>
764    * @param     string $path Full document path of the node, from which the
765    *            list of attributes should be retrieved.
766    * @return    array The returned associative array contains the all
767    *            attributes of the specified node.
768    * @see       get_content(), $nodes, $ids
769    */
770    function get_attributes ( $path )
771    {
772        // Return the attributes of the node.
773        return $this->nodes[$path]["attributes"];
774    }
775   
776    /**
777    * Retrieves the name of a document node.
778    *
779    * This method retrieves the name of document node specified in the
780    * argument.
781    *
782    * @access    public
783    * @author    Michael P. Mehl <mpm@phpxml.org>
784    * @param     string $path Full document path of the node, from which the
785    *            name should be retrieved.
786    * @return    string The returned array contains the name of the specified
787    *            node.
788    * @see       get_content(), $nodes, $ids
789    */
790    function get_name ( $path )
791    {
792        // Return the name of the node.
793        return $this->nodes[$path]["name"];
794    }
795   
796    /**
797    * Evaluates an XPath expression.
798    *
799    * This method tries to evaluate an XPath expression by parsing it. A
800    * XML document has to be read before this method is able to work.
801    *
802    * @access    public
803    * @author    Michael P. Mehl <mpm@phpxml.org>
804    * @param     string $path XPath expression to be evaluated.
805    * @param     string $context Full path of a document node, starting
806    *            from which the XPath expression should be evaluated.
807    * @return    array The returned array contains a list of the full
808    *            document paths of all nodes that match the evaluated
809    *            XPath expression.
810    * @see       $nodes, $ids
811    */
812    function evaluate ( $path, $context = "" )
813    {
814        // Remove slashes and quote signs.
815        $path = stripslashes($path);
816        $path = str_replace("\"", "", $path);
817        $path = str_replace("'", "", $path);
818       
819        // Split the paths into different paths.
820        $paths = $this->split_paths($path);
821       
822        // Create an empty set to save the result.
823        $result = array();
824       
825        // Run through all paths.
826        foreach ( $paths as $path )
827        {
828            // Trim the path.
829            $path = trim($path);
830           
831            // Save the current path.
832            $this->xpath = $path;
833       
834            // Convert all entities.
835            $path = strtr($path, array_flip($this->entities));
836       
837            // Split the path at every slash.
838            $steps = $this->split_steps($path);
839       
840            // Check whether the first element is empty.
841            if ( empty($steps[0]) )
842            {
843                // Remove the first and empty element.
844                array_shift($steps);
845            }
846       
847            // Start to evaluate the steps.
848            $nodes = $this->evaluate_step($context, $steps);
849       
850            // Remove duplicated nodes.
851            $nodes = array_unique($nodes);
852           
853            // Add the nodes to the result set.
854            $result = array_merge($result, $nodes);
855        }
856       
857        // Return the result.
858        return $result;
859    }
860   
861    /**
862    * Handles opening XML tags while parsing.
863    *
864    * While parsing a XML document for each opening tag this method is
865    * called. It'll add the tag found to the tree of document nodes.
866    *
867    * @access    private
868    * @author    Michael P. Mehl <mpm@phpxml.org>
869    * @param     int $parser Handler for accessing the current XML parser.
870    * @param     string $name Name of the opening tag found in the document.
871    * @param     array $attributes Associative array containing a list of
872    *            all attributes of the tag found in the document.
873    * @see       handle_end_element(), handle_character_data(), $nodes, $ids
874    */
875    function handle_start_element ( $parser, $name, $attributes )
876    {
877        // Add a node.
878        $this->path = $this->add_node($this->path, $name);
879       
880        // Set the attributes.
881        $this->set_attributes($this->path, $attributes);
882    }
883   
884    /**
885    * Handles closing XML tags while parsing.
886    *
887    * While parsing a XML document for each closing tag this method is
888    * called.
889    *
890    * @access    private
891    * @author    Michael P. Mehl <mpm@phpxml.org>
892    * @param     int $parser Handler for accessing the current XML parser.
893    * @param     string $name Name of the closing tag found in the document.
894    * @see       handle_start_element(), handle_character_data(), $nodes, $ids
895    */
896    function handle_end_element ( $parser, $name )
897    {
898        // Jump back to the parent element.
899        $this->path = substr($this->path, 0, strrpos($this->path, "/"));
900    }
901   
902    /**
903    * Handles character data while parsing.
904    *
905    * While parsing a XML document for each character data this method
906    * is called. It'll add the character data to the document tree.
907    *
908    * @access    private
909    * @author    Michael P. Mehl <mpm@phpxml.org>
910    * @param     int $parser Handler for accessing the current XML parser.
911    * @param     string $text Character data found in the document.
912    * @see       handle_start_element(), handle_end_element(), $nodes, $ids
913    */
914    function handle_character_data ( $parser, $text )
915    {
916        // Replace entities.
917        $text = strtr($text, $this->entities);
918       
919        // Save the text.
920        $this->add_content($this->path, addslashes(trim($text)));
921    }
922   
923    /**
924    * Splits an XPath expression into its different expressions.
925    *
926    * This method splits an XPath expression. Each expression can consists of
927    * list of expression being separated from each other by a | character.
928    *
929    * @access    private
930    * @author    Michael P. Mehl <mpm@phpxml.org>
931    * @param     string $expression The complete expression to be splitted
932    *            into its different expressions.
933    * @return    array The array returned from this method contains a list
934    *            of all expressions found in the expression passed to this
935    *            method as a parameter.
936    * @see       evalute()
937    */
938    function split_paths ( $expression )
939    {
940        // Create an empty array.
941        $paths = array();
942       
943        // Save the position of the slash.
944        $position = -1;
945       
946        // Run through the expression.
947        do
948        {
949            // Search for a slash.
950            $position = $this->search_string($expression, "|");
951           
952            // Check whether a | was found.
953            if ( $position >= 0 )
954            {
955                // Get the left part of the expression.
956                $left  = substr($expression, 0, $position);
957                $right = substr($expression, $position + 1);
958               
959                // Add the left value to the steps.
960                $paths[] = $left;
961               
962                // Reduce the expression to the right part.
963                $expression = $right;
964            }
965        }
966        while ( $position > -1 );
967       
968        // Add the remaing expression to the list of steps.
969        $paths[] = $expression;
970       
971        // Return the steps.
972        return $paths;
973    }
974   
975    /**
976    * Splits an XPath expression into its different steps.
977    *
978    * This method splits an XPath expression. Each expression can consists of
979    * list of steps being separated from each other by a / character.
980    *
981    * @access    private
982    * @author    Michael P. Mehl <mpm@phpxml.org>
983    * @param     string $expression The complete expression to be splitted
984    *            into its different steps.
985    * @return    array The array returned from this method contains a list
986    *            of all steps found in the expression passed to this
987    *            method as a parameter.
988    * @see       evalute()
989    */
990    function split_steps ( $expression )
991    {
992        // Create an empty array.
993        $steps = array();
994       
995        // Replace a double slashes, because they'll cause problems otherwise.
996        $expression = str_replace("//@", "/descendant::*/@", $expression);
997        $expression = str_replace("//", "/descendant::", $expression);
998       
999        // Save the position of the slash.
1000        $position = -1;
1001       
1002        // Run through the expression.
1003        do
1004        {
1005            // Search for a slash.
1006            $position = $this->search_string($expression, "/");
1007           
1008            // Check whether a slash was found.
1009            if ( $position >= 0 )
1010            {
1011                // Get the left part of the expression.
1012                $left  = substr($expression, 0, $position);
1013                $right = substr($expression, $position + 1);
1014               
1015                // Add the left value to the steps.
1016                $steps[] = $left;
1017               
1018                // Reduce the expression to the right part.
1019                $expression = $right;
1020            }
1021        }
1022        while ( $position > -1 );
1023       
1024        // Add the remaing expression to the list of steps.
1025        $steps[] = $expression;
1026       
1027        // Return the steps.
1028        return $steps;
1029    }
1030   
1031    /**
1032    * Retrieves axis information from an XPath expression step.
1033    *
1034    * This method tries to extract the name of the axis and its node-test
1035    * from a given step of an XPath expression at a given node.
1036    *
1037    * @access    private
1038    * @author    Michael P. Mehl <mpm@phpxml.org>
1039    * @param     string $step String containing a step of an XPath expression.
1040    * @param     string $node Full document path of the node on which the
1041    *            step is executed.
1042    * @return    array This method returns an array containing information
1043    *            about the axis found in the step.
1044    * @see       evaluate_step()
1045    */
1046    function get_axis ( $step, $node )
1047    {
1048        // Create an array to save the axis information.
1049        $axis = array(
1050            "axis"      => "",
1051            "node-test" => "",
1052            "predicate" => array()
1053        );
1054       
1055        // Check whether there are predicates.
1056        if ( ereg("\[", $step) )
1057        {
1058            // Get the predicates.
1059            $predicates = substr($step, strpos($step, "["));
1060           
1061            // Reduce the step.
1062            $step = $this->prestr($step, "[");
1063           
1064            // Try to split the predicates.
1065            $predicates = str_replace("][", "]|[", $predicates);
1066            $predicates = explode("|", $predicates);
1067           
1068            // Run through all predicates.
1069            foreach ( $predicates as $predicate )
1070            {
1071                // Remove the brackets.
1072                $predicate = substr($predicate, 1, strlen($predicate) - 2);
1073               
1074                // Add the predicate to the list of predicates.
1075                $axis["predicate"][] = $predicate;
1076            }
1077        }
1078       
1079        // Check whether the axis is given in plain text.
1080        if ( $this->search_string($step, "::") > -1 )
1081        {
1082            // Split the step to extract axis and node-test.
1083            $axis["axis"]      = $this->prestr($step, "::");
1084            $axis["node-test"] = $this->afterstr($step, "::");
1085        }
1086        else
1087        {
1088            // Check whether the step is empty.
1089            if ( empty($step) )
1090            {
1091                // Set it to the default value.
1092                $step = ".";
1093            }
1094           
1095            // Check whether is an abbreviated syntax.
1096            if ( $step == "*" )
1097            {
1098                // Use the child axis and select all children.
1099                $axis["axis"]      = "child";
1100                $axis["node-test"] = "*";
1101            }
1102            elseif ( ereg("\(", $step) )
1103            {
1104                // Check whether it's a function.
1105                if ( $this->is_function($this->prestr($step, "(")) )
1106                {
1107                    // Get the position of the first bracket.
1108                    $start = strpos($step, "(");
1109                    $end   = strpos($step, ")", $start);
1110                   
1111                    // Get everything before, between and after the brackets.
1112                    $before  = substr($step, 0, $start);
1113                    $between = substr($step, $start + 1, $end - $start - 1);
1114                    $after   = substr($step, $end + 1);
1115                   
1116                    // Trim each string.
1117                    $before  = trim($before);
1118                    $between = trim($between);
1119                    $after   = trim($after);
1120                   
1121                    // Save the evaluated function.
1122                    $axis["axis"]      = "function";
1123                    $axis["node-test"] = $this->evaluate_function($before,
1124                        $between, $node);
1125                }
1126                else
1127                {
1128                    // Use the child axis and a function.
1129                    $axis["axis"]      = "child";
1130                    $axis["node-test"] = $step;
1131                }
1132            }
1133            elseif ( eregi("^@", $step) )
1134            {
1135                // Use the attribute axis and select the attribute.
1136                $axis["axis"]      = "attribute";
1137                $axis["node-test"] = substr($step, 1);
1138            }
1139            elseif ( eregi("\]$", $step) )
1140            {
1141                // Use the child axis and select a position.
1142                $axis["axis"]      = "child";
1143                $axis["node-test"] = substr($step, strpos($step, "["));
1144            }
1145            elseif ( $step == "." )
1146            {
1147                // Select the self axis.
1148                $axis["axis"]      = "self";
1149                $axis["node-test"] = "*";
1150            }
1151            elseif ( $step == ".." )
1152            {
1153                // Select the parent axis.
1154                $axis["axis"]      = "parent";
1155                $axis["node-test"] = "*";
1156            }
1157            elseif ( ereg("^[a-zA-Z0-9\-_]+$", $step) )
1158            {
1159                // Select the child axis and the child.
1160                $axis["axis"]      = "child";
1161                $axis["node-test"] = $step;
1162            }
1163            else
1164            {
1165                // Use the child axis and a name.
1166                $axis["axis"]      = "child";
1167                $axis["node-test"] = $step;
1168            }
1169        }
1170
1171        // Check whether it's a valid axis.
1172        if ( !in_array($axis["axis"], array_merge($this->axes,
1173            array("function"))) )
1174        {
1175            // Display an error message.
1176            $this->display_error("While parsing an XPath expression, in ".
1177                "the step \"%s\" the invalid axis \"%s\" was found.",
1178                str_replace($step, "<b>".$step."</b>", $this->xpath),#
1179                $axis["axis"]); 
1180        }
1181       
1182        // Return the axis information.
1183        return $axis;
1184    }
1185   
1186    /**
1187    * Looks for a string within another string.
1188    *
1189    * This method looks for a string within another string. Brackets in the
1190    * string the method is looking through will be respected, which means that
1191    * only if the string the method is looking for is located outside of
1192    * brackets, the search will be successful.
1193    *
1194    * @access    private
1195    * @author    Michael P. Mehl <mpm@phpxml.org>
1196    * @param     string $term String in which the search shall take place.
1197    * @param     string $expression String that should be searched.
1198    * @return    int This method returns -1 if no string was found, otherwise
1199    *            the offset at which the string was found.
1200    * @see       evaluate_step()
1201    */
1202    function search_string ( $term, $expression )
1203    {
1204        // Create a new counter for the brackets.
1205        $brackets = 0;
1206       
1207        // Run through the string.
1208        for ( $i = 0; $i < strlen($term); $i++ )
1209        {
1210            // Get the character at the position of the string.
1211            $character = substr($term, $i, 1);
1212           
1213            // Check whether it's a breacket.
1214            if ( ( $character == "(" ) || ( $character == "[" ) )
1215            {
1216                // Increase the number of brackets.
1217                $brackets++;
1218            }
1219            elseif ( ( $character == ")" ) || ( $character == "]" ) )
1220            {
1221                // Decrease the number of brackets.
1222                $brackets--;
1223            }
1224            elseif ( $brackets == 0 )
1225            {
1226                // Check whether we can find the expression at this index.
1227                if ( substr($term, $i, strlen($expression)) == $expression )
1228                {
1229                    // Return the current index.
1230                    return $i;
1231                }
1232            }
1233        }
1234       
1235        // Check whether we had a valid number of brackets.
1236        if ( $brackets != 0 )
1237        {
1238            // Display an error message.
1239            $this->display_error("While parsing an XPath expression, in the ".
1240                "predicate \"%s\", there was an invalid number of brackets.",
1241                str_replace($term, "<b>".$term."</b>", $this->xpath));
1242        }
1243
1244        // Nothing was found.
1245        return (-1);
1246    }
1247   
1248    /**
1249    * Checks for a valid function name.
1250    *
1251    * This method check whether an expression contains a valid name of an
1252    * XPath function.
1253    *
1254    * @access    private
1255    * @author    Michael P. Mehl <mpm@phpxml.org>
1256    * @param     string $expression Name of the function to be checked.
1257    * @return    boolean This method returns true if the given name is a valid
1258    *            XPath function name, otherwise false.
1259    * @see       evaluate()
1260    */
1261    function is_function ( $expression )
1262    {
1263        // Check whether it's in the list of supported functions.
1264        if ( in_array($expression, $this->functions) )
1265        {
1266            // It's a function.
1267            return true;
1268        }
1269        else
1270        {
1271            // It's not a function.
1272            return false;
1273        }
1274    }
1275   
1276    /**
1277    * Evaluates a step of an XPath expression.
1278    *
1279    * This method tries to evaluate a step from an XPath expression at a
1280    * specific context.
1281    *
1282    * @access    private
1283    * @author    Michael P. Mehl <mpm@phpxml.org>
1284    * @param     string $context Full document path of the context from
1285    *            which starting the step should be evaluated.
1286    * @param     array $steps Array containing the remaining steps of the
1287    *            current XPath expression.
1288    * @return    array This method returns an array containing all nodes
1289    *            that are the result of evaluating the given XPath step.
1290    * @see       evaluate()
1291    */
1292    function evaluate_step ( $context, $steps )
1293    {
1294        // Create an empty array for saving the nodes found.
1295        $nodes = array();
1296
1297        // Check whether the context is an array of contexts.
1298        if ( is_array($context) )
1299        {
1300            // Run through the array.
1301            foreach ( $context as $path )
1302            {
1303                // Call this method for this single path.
1304                $nodes = array_merge($nodes,
1305                    $this->evaluate_step($path, $steps));
1306            }
1307        }
1308        else
1309        {
1310            // Get this step.
1311            $step = array_shift($steps);
1312           
1313            // Create an array to save the new contexts.
1314            $contexts = array();
1315           
1316            // Get the axis of the current step.
1317            $axis = $this->get_axis($step, $context);
1318           
1319            // Check whether it's a function.
1320            if ( $axis["axis"] == "function" )
1321            {
1322                // Check whether an array was return by the function.
1323                if ( is_array($axis["node-test"]) )
1324                {
1325                    // Add the results to the list of contexts.
1326                    $contexts = array_merge($contexts, $axis["node-test"]);
1327                }
1328                else
1329                {
1330                    // Add the result to the list of contexts.
1331                    $contexts[] = $axis["node-test"];
1332                }
1333            }
1334            else
1335            {
1336                // Create the name of the method.
1337                $method = "handle_axis_".str_replace("-", "_", $axis["axis"]);
1338           
1339                // Check whether the axis handler is defined.
1340                if ( !method_exists(&$this, $method) )
1341                {
1342                    // Display an error message.
1343                    $this->display_error("While parsing an XPath expression, ".
1344                        "the axis \"%s\" could not be handled, because this ".
1345                        "version does not support this axis.", $axis["axis"]);
1346                }
1347           
1348                // Perform an axis action.
1349                $contexts = call_user_method($method, &$this, $axis, $context);
1350           
1351                // Check whether there are predicates.
1352                if ( count($axis["predicate"]) > 0 )
1353                {
1354                    // Check whether each node fits the predicates.
1355                    $contexts = $this->check_predicates($contexts,
1356                        $axis["predicate"]);
1357                }
1358            }
1359           
1360            // Check whether there are more steps left.
1361            if ( count($steps) > 0 )
1362            {
1363                // Continue the evaluation of the next steps.
1364                $nodes = $this->evaluate_step($contexts, $steps);
1365            }
1366            else
1367            {
1368                // Save the found contexts.
1369                $nodes = $contexts;
1370            }
1371        }
1372       
1373        // Return the nodes found.
1374        return $nodes;
1375    }
1376   
1377    /**
1378    * Evaluates an XPath function
1379    *
1380    * This method evaluates a given XPath function with its arguments on a
1381    * specific node of the document.
1382    *
1383    * @access    private
1384    * @author    Michael P. Mehl <mpm@phpxml.org>
1385    * @param     string $function Name of the function to be evaluated.
1386    * @param     string $arguments String containing the arguments being
1387    *            passed to the function.
1388    * @param     string $node Full path to the document node on which the
1389    *            function should be evaluated.
1390    * @return    mixed This method returns the result of the evaluation of
1391    *            the function. Depending on the function the type of the
1392    *            return value can be different.
1393    * @see       evaluate()
1394    */
1395    function evaluate_function ( $function, $arguments, $node )
1396    {
1397        // Remove whitespaces.
1398        $function  = trim($function);
1399        $arguments = trim($arguments);
1400
1401        // Create the name of the function handling function.
1402        $method = "handle_function_".str_replace("-", "_", $function);
1403       
1404        // Check whether the function handling function is available.
1405        if ( !method_exists(&$this, $method) )
1406        {
1407            // Display an error message.
1408            $this->display_error("While parsing an XPath expression, ".
1409                "the function \"%s\" could not be handled, because this ".
1410                "version does not support this function.", $function);
1411        }
1412       
1413        // Return the result of the function.
1414        return call_user_method($method, &$this, $node, $arguments);
1415    }
1416   
1417    /**
1418    * Evaluates a predicate on a node.
1419    *
1420    * This method tries to evaluate a predicate on a given node.
1421    *
1422    * @access    private
1423    * @author    Michael P. Mehl <mpm@phpxml.org>
1424    * @param     string $node Full path of the node on which the predicate
1425    *            should be evaluated.
1426    * @param     string $predicate String containing the predicate expression
1427    *            to be evaluated.
1428    * @return    mixed This method is called recursively. The first call should
1429    *            return a boolean value, whether the node matches the predicate
1430    *            or not. Any call to the method being made during the recursion
1431    *            may also return other types for further processing.
1432    * @see       evaluate()
1433    */
1434    function evaluate_predicate ( $node, $predicate )
1435    {
1436        // Set the default position and the type of the operator.
1437        $position = 0;
1438        $operator = "";
1439       
1440        // Run through all operators and try to find them.
1441        foreach ( $this->operators as $expression )
1442        {
1443            // Check whether a position was already found.
1444            if ( $position <= 0 )
1445            {
1446                // Try to find the operator.
1447                $position = $this->search_string($predicate, $expression);
1448           
1449                // Check whether a operator was found.
1450                if ( $position > 0 )
1451                {
1452                    // Save the operator.
1453                    $operator = $expression;
1454                   
1455                    // Check whether it's the equal operator.
1456                    if ( $operator == "=" )
1457                    {
1458                        // Also look for other operators containing the
1459                        // equal sign.
1460                        if ( $this->search_string($predicate, "!=") ==
1461                            ( $position - 1 ) )
1462                        {
1463                            // Get the new position.
1464                            $position = $this->search_string($predicate, "!=");
1465                           
1466                            // Save the new operator.
1467                            $operator = "!=";
1468                        }
1469                        if ( $this->search_string($predicate, "<=") ==
1470                            ( $position - 1 ) )
1471                        {
1472                            // Get the new position.
1473                            $position = $this->search_string($predicate, "<=");
1474                           
1475                            // Save the new operator.
1476                            $operator = "<=";
1477                        }
1478                        if ( $this->search_string($predicate, ">=") ==
1479                            ( $position - 1 ) )
1480                        {
1481                            // Get the new position.
1482                            $position = $this->search_string($predicate, ">=");
1483                           
1484                            // Save the new operator.
1485                            $operator = ">=";
1486                        }
1487                    }
1488                }
1489            }
1490        }
1491       
1492        // Check whether the operator is a - sign.
1493        if ( $operator == "-" )
1494        {
1495            // Check whether it's not a function containing a - in its name.
1496            foreach ( $this->functions as $function )
1497            {
1498                // Check whether there's a - sign in the function name.
1499                if ( ereg("-", $function) )
1500                {
1501                    // Get the position of the - in the function name.
1502                    $sign = strpos($function, "-");
1503                   
1504                    // Extract a substring from the predicate.
1505                    $sub = substr($predicate, $position - $sign,
1506                        strlen($function));
1507                       
1508                    // Check whether it's the function.
1509                    if ( $sub == $function )
1510                    {
1511                        // Don't use the operator.
1512                        $operator = "";
1513                        $position = -1;
1514                    }
1515                }
1516            }
1517        }
1518        elseif ( $operator == "*" )
1519        {
1520            // Get some substrings.
1521            $character = substr($predicate, $position - 1, 1);
1522            $attribute = substr($predicate, $position - 11, 11);
1523           
1524            // Check whether it's an attribute selection.
1525            if ( ( $character == "@" ) || ( $attribute == "attribute::" ) )
1526            {
1527                // Don't use the operator.
1528                $operator = "";
1529                $position = -1;
1530            }
1531        }
1532       
1533        // Check whether an operator was found.       
1534        if ( $position > 0 )
1535        {
1536            // Get the left and the right part of the expression.
1537            $left  = substr($predicate, 0, $position);
1538            $right = substr($predicate, $position + strlen($operator));
1539           
1540            // Remove whitespaces.
1541            $left  = trim($left);
1542            $right = trim($right);
1543           
1544            // Evaluate the left and the right part.
1545            $left  = $this->evaluate_predicate($node, $left);
1546            $right = $this->evaluate_predicate($node, $right);
1547           
1548            // Check the kind of operator.
1549            switch ( $operator )
1550            {
1551                case " or ":
1552                    // Return the two results connected by an "or".
1553                    return ( $left or $right );
1554               
1555                case " and ":
1556                    // Return the two results connected by an "and".
1557                    return ( $left and $right );
1558               
1559                case "=":
1560                    // Compare the two results.
1561                    return ( $left == $right );
1562                   
1563                case "!=":
1564                    // Check whether the two results are not equal.
1565                    return ( $left != $right );
1566                   
1567                case "<=":
1568                    // Compare the two results.
1569                    return ( $left <= $right );
1570                   
1571                case "<":
1572                    // Compare the two results.
1573                    return ( $left < $right );
1574               
1575                case ">=":
1576                    // Compare the two results.
1577                    return ( $left >= $right );
1578                   
1579                case ">":
1580                    // Compare the two results.
1581                    return ( $left > $right );
1582                   
1583                case "+":
1584                    // Return the result by adding one result to the other.
1585                    return ( $left + $right );
1586               
1587                case "-":
1588                    // Return the result by decrease one result by the other.
1589                    return ( $left - $right );
1590               
1591                case "*":
1592                    // Return a multiplication of the two results.
1593                    return ( $left * $right );
1594                   
1595                case " div ":
1596                    // Return a division of the two results.
1597                    if ( $right == 0 )
1598                    {
1599                        // Display an error message.
1600                        $this->display_error("While parsing an XPath ".
1601                            "predicate, a error due a division by zero ".
1602                            "occured.");
1603                    }
1604                    else
1605                    {
1606                        // Return the result of the division.
1607                        return ( $left / $right );
1608                    }
1609                    break;
1610               
1611                case " mod ":
1612                    // Return a modulo of the two results.
1613                    return ( $left % $right );
1614            }
1615        }
1616       
1617        // Check whether the predicate is a function.
1618        if ( ereg("\(", $predicate) )
1619        {
1620            // Get the position of the first bracket.
1621            $start = strpos($predicate, "(");
1622            $end   = strpos($predicate, ")", $start);
1623           
1624            // Get everything before, between and after the brackets.
1625            $before  = substr($predicate, 0, $start);
1626            $between = substr($predicate, $start + 1, $end - $start - 1);
1627            $after   = substr($predicate, $end + 1);
1628           
1629            // Trim each string.
1630            $before  = trim($before);
1631            $between = trim($between);
1632            $after   = trim($after);
1633           
1634            // Check whether there's something after the bracket.
1635            if ( !empty($after) )
1636            {
1637                // Display an error message.
1638                $this->display_error("While parsing an XPath expression ".
1639                    "there was found an error in the predicate \"%s\", ".
1640                    "because after a closing bracket there was found ".
1641                    "something unknown.", str_replace($predicate,
1642                    "<b>".$predicate."</b>", $this->xpath));
1643            }
1644           
1645            // Check whether it's a function.
1646            if ( empty($before) && empty($after) )
1647            {
1648                // Evaluate the content of the brackets.
1649                return $this->evaluate_predicate($node, $between);
1650            }
1651            elseif ( $this->is_function($before) )
1652            {
1653                // Return the evaluated function.
1654                return $this->evaluate_function($before, $between, $node);
1655            }
1656            else
1657            {
1658                // Display an error message.
1659                $this->display_error("While parsing a predicate in an XPath ".
1660                    "expression, a function \"%s\" was found, which is not ".
1661                    "yet supported by the parser.", str_replace($before,
1662                    "<b>".$before."</b>", $this->xpath));
1663            }
1664        }
1665       
1666        // Check whether the predicate is just a digit.
1667        if ( ereg("^[0-9]+(\.[0-9]+)?$", $predicate) ||
1668            ereg("^\.[0-9]+$", $predicate) )
1669        {
1670            // Return the value of the digit.
1671            return doubleval($predicate);
1672        }
1673       
1674        // Check whether it's an XPath expression.
1675        $result = $this->evaluate($predicate, $node);
1676        if ( count($result) > 0 )
1677        {
1678            // Convert the array.
1679            $result = explode("|", implode("|", $result));
1680           
1681            // Get the value of the first result.
1682            $value = $this->get_content($result[0]);
1683           
1684            // Return the value.
1685            return $value;
1686        }
1687       
1688        // Return the predicate as a string.
1689        return $predicate;
1690    }
1691   
1692    /**
1693    * Checks whether a node matches predicates.
1694    *
1695    * This method checks whether a list of nodes passed to this method match
1696    * a given list of predicates.
1697    *
1698    * @access    private
1699    * @author    Michael P. Mehl <mpm@phpxml.org>
1700    * @param     array $nodes Array of full paths of all nodes to be tested.
1701    * @param     array $predicates Array of predicates to use.
1702    * @return    array The array returned by this method contains a list of
1703    *            all nodes matching the given predicates.
1704    * @see       evaluate_step()
1705    */
1706    function check_predicates ( $nodes, $predicates )
1707    {
1708        // Create an empty set of nodes.
1709        $result = array();
1710       
1711        // Run through all nodes.
1712        foreach ( $nodes as $node )
1713        {
1714            // Create a variable whether to add this node to the node-set.
1715            $add = true;
1716           
1717            // Run through all predicates.
1718            foreach ( $predicates as $predicate )
1719            {
1720                // Check whether the predicate is just an number.
1721                if ( ereg("^[0-9]+$", $predicate) )
1722                {
1723                    // Enhance the predicate.
1724                    $predicate .= "=position()";
1725                }
1726               
1727                // Do the predicate check.
1728                $check = $this->evaluate_predicate($node, $predicate);
1729               
1730                // Check whether it's a string.
1731                if ( is_string($check) && ( ( $check == "" ) ||
1732                    ( $check == $predicate ) ) )
1733                {
1734                    // Set the result to false.
1735                    $check = false;
1736                }
1737               
1738                // Check whether it's an integer.
1739                if ( is_int($check) )
1740                {
1741                    // Check whether it's the current position.
1742                    if ( $check == $this->handle_function_position($node, "") )
1743                    {
1744                        // Set it to true.
1745                        $check = true;
1746                    }
1747                    else
1748                    {
1749                        // Set it to false.
1750                        $check = false;
1751                    }
1752                }
1753               
1754                // Check whether the predicate is OK for this node.
1755                $add = $add && $check;
1756            }
1757           
1758            // Check whether to add this node to the node-set.
1759            if ( $add )
1760            {
1761                // Add the node to the node-set.
1762                $result[] = $node;
1763            }           
1764        }
1765       
1766        // Return the array of nodes.
1767        return $result;
1768    }
1769   
1770    /**
1771    * Checks whether a node matches a node-test.
1772    *
1773    * This method checks whether a node in the document matches a given
1774    * node-test.
1775    *
1776    * @access    private
1777    * @author    Michael P. Mehl <mpm@phpxml.org>
1778    * @param     string $context Full path of the node, which should be tested
1779    *            for matching the node-test.
1780    * @param     string $node_test String containing the node-test for the
1781    *            node.
1782    * @return    boolean This method returns true if the node matches the
1783    *            node-test, otherwise false.
1784    * @see       evaluate()
1785    */
1786    function check_node_test ( $context, $node_test )
1787    {
1788        // Check whether it's a function.
1789        if ( ereg("\(", $node_test) )
1790        {
1791            // Get the type of function to use.
1792            $function = $this->prestr($node_test, "(");
1793           
1794            // Check whether the node fits the method.
1795            switch ( $function )
1796            {
1797                case "node":
1798                    // Add this node to the list of nodes.
1799                    return true;
1800                   
1801                case "text":
1802                    // Check whether the node has some text.
1803                    if ( !empty($this->nodes[$context]["text"]) )
1804                    {
1805                        // Add this node to the list of nodes.
1806                        return true;
1807                    }
1808                    break;
1809                   
1810                case "comment":
1811                    // Check whether the node has some comment.
1812                    if ( !empty($this->nodes[$context]["comment"]) )
1813                    {
1814                        // Add this node to the list of nodes.
1815                        return true;
1816                    }
1817                    break;
1818               
1819                case "processing-instruction":
1820                    // Get the literal argument.
1821                    $literal = $this->afterstr($axis["node-test"], "(");
1822                   
1823                    // Cut the literal.
1824                    $literal = substr($literal, 0, strlen($literal) - 1);
1825                   
1826                    // Check whether a literal was given.
1827                    if ( !empty($literal) )
1828                    {
1829                        // Check whether the node's processing instructions
1830                        // are matching the literals given.
1831                        if ( $this->nodes[$context]
1832                            ["processing-instructions"] == $literal )
1833                        {
1834                            // Add this node to the node-set.
1835                            return true;
1836                        }
1837                    }
1838                    else
1839                    {
1840                        // Check whether the node has processing
1841                        // instructions.
1842                        if ( !empty($this->nodes[$context]
1843                            ["processing-instructions"]) )
1844                        {
1845                            // Add this node to the node-set.
1846                            return true;
1847                        }
1848                    }
1849                    break;
1850                   
1851                default:
1852                    // Display an error message.
1853                    $this->display_error("While parsing an XPath ".
1854                        "expression there was found an undefined ".
1855                        "function called \"%s\".",
1856                        str_replace($function, "<b>".$function."</b>",
1857                        $this->xpath));
1858            }
1859        }
1860        elseif ( $node_test == "*" )
1861        {
1862            // Add this node to the node-set.
1863            return true;
1864        }
1865        elseif ( ereg("^[a-zA-Z0-9\-_]+", $node_test) )
1866        {
1867            // Check whether the node-test can be fulfilled.
1868            if ( $this->nodes[$context]["name"] == $node_test )
1869            {
1870                // Add this node to the node-set.
1871                return true;
1872            }
1873        }
1874        else
1875        {
1876            // Display an error message.
1877            $this->display_error("While parsing the XPath expression \"%s\" ".
1878                "an empty and therefore invalid node-test has been found.",
1879                $this->xpath);
1880        }
1881       
1882        // Don't add this context.
1883        return false;
1884    }
1885   
1886    /**
1887    * Handles the XPath child axis.
1888    *
1889    * This method handles the XPath child axis.
1890    *
1891    * @access    private
1892    * @author    Michael P. Mehl <mpm@phpxml.org>
1893    * @param     array $axis Array containing information about the axis.
1894    * @param     string $context Node from which starting the axis should
1895    *            be processed.
1896    * @return    array This method returns an array containing all nodes
1897    *            that were found during the evaluation of the given axis.
1898    * @see       evaluate()
1899    */
1900    function handle_axis_child ( $axis, $context )
1901    {
1902        // Create an empty node-set.
1903        $nodes = array();
1904       
1905        // Get a list of all children.
1906        $children = $this->nodes[$context]["children"];
1907       
1908        // Check whether there are children.
1909        if ( !empty($children) )
1910        {
1911            // Run through all children.
1912            foreach ( $children as $child_name => $child_position )
1913            {
1914                // Run through all childs with this name.
1915                for ( $i = 1; $i <= $child_position; $i++ )
1916                {
1917                    // Create the path of the child.
1918                    $child = $context."/".$child_name."[".$i."]";
1919                   
1920                    // Check whether
1921                    if ( $this->check_node_test($child, $axis["node-test"]) )
1922                    {
1923                        // Add the child to the node-set.
1924                        $nodes[] = $child;
1925                    }
1926                }
1927            }
1928        }
1929       
1930        // Return the nodeset.
1931        return $nodes;
1932    }
1933   
1934    /**
1935    * Handles the XPath parent axis.
1936    *
1937    * This method handles the XPath parent axis.
1938    *
1939    * @access    private
1940    * @author    Michael P. Mehl <mpm@phpxml.org>
1941    * @param     array $axis Array containing information about the axis.
1942    * @param     string $context Node from which starting the axis should
1943    *            be processed.
1944    * @return    array This method returns an array containing all nodes
1945    *            that were found during the evaluation of the given axis.
1946    * @see       evaluate()
1947    */
1948    function handle_axis_parent ( $axis, $context )
1949    {
1950        // Create an empty node-set.
1951        $nodes = array();
1952       
1953        // Check whether the parent matches the node-test.
1954        if ( $this->check_node_test($this->nodes[$context]["parent"],
1955            $axis["node-test"]) )
1956        {
1957            // Add this node to the list of nodes.
1958            $nodes[] = $this->nodes[$context]["parent"];
1959        }
1960       
1961        // Return the nodeset.
1962        return $nodes;
1963    }
1964   
1965    /**
1966    * Handles the XPath attribute axis.
1967    *
1968    * This method handles the XPath attribute axis.
1969    *
1970    * @access    private
1971    * @author    Michael P. Mehl <mpm@phpxml.org>
1972    * @param     array $axis Array containing information about the axis.
1973    * @param     string $context Node from which starting the axis should
1974    *            be processed.
1975    * @return    array This method returns an array containing all nodes
1976    *            that were found during the evaluation of the given axis.
1977    * @see       evaluate()
1978    */
1979    function handle_axis_attribute ( $axis, $context )
1980    {
1981        // Create an empty node-set.
1982        $nodes = array();
1983       
1984        // Check whether all nodes should be selected.
1985        if ( $axis["node-test"] == "*" )
1986        {
1987            // Check whether there are attributes.
1988            if ( count($this->nodes[$context]["attributes"]) > 0 )
1989            {
1990                // Run through the attributes.
1991                foreach ( $this->nodes[$context]["attributes"] as
1992                    $key => $value )
1993                {
1994                    // Add this node to the node-set.
1995                    $nodes[] = $context."/attribute::".$key;
1996                }
1997            }
1998        }
1999        elseif ( !empty($this->nodes[$context]["attributes"]
2000            [$axis["node-test"]]) )
2001        {
2002            // Add this node to the node-set.
2003            $nodes[] = $context."/attribute::".$axis["node-test"];
2004        }
2005           
2006        // Return the nodeset.
2007        return $nodes;
2008    }
2009
2010    /**
2011    * Handles the XPath self axis.
2012    *
2013    * This method handles the XPath self axis.
2014    *
2015    * @access    private
2016    * @author    Michael P. Mehl <mpm@phpxml.org>
2017    * @param     array $axis Array containing information about the axis.
2018    * @param     string $context Node from which starting the axis should
2019    *            be processed.
2020    * @return    array This method returns an array containing all nodes
2021    *            that were found during the evaluation of the given axis.
2022    * @see       evaluate()
2023    */
2024    function handle_axis_self ( $axis, $context )
2025    {
2026        // Create an empty node-set.
2027        $nodes = array();
2028       
2029        // Check whether the context match the node-test.
2030        if ( $this->check_node_test($context, $axis["node-test"]) )
2031        {
2032            // Add this node to the node-set.
2033            $nodes[] = $context;
2034        }
2035
2036        // Return the nodeset.
2037        return $nodes;
2038    }
2039
2040    /**
2041    * Handles the XPath descendant axis.
2042    *
2043    * This method handles the XPath descendant axis.
2044    *
2045    * @access    private
2046    * @author    Michael P. Mehl <mpm@phpxml.org>
2047    * @param     array $axis Array containing information about the axis.
2048    * @param     string $context Node from which starting the axis should
2049    *            be processed.
2050    * @return    array This method returns an array containing all nodes
2051    *            that were found during the evaluation of the given axis.
2052    * @see       evaluate()
2053    */
2054    function handle_axis_descendant ( $axis, $context )
2055    {
2056        // Create an empty node-set.
2057        $nodes = array();
2058       
2059        // Check whether the current node has children.
2060        if ( count($this->nodes[$context]["children"]) > 0 )
2061        {
2062            // Get a list of children.
2063            $children = $this->nodes[$context]["children"];
2064           
2065            // Run through all children.
2066            foreach ( $children as $child_name => $child_position )
2067            {
2068                // Run through all children of this name.
2069                for ( $i = 1; $i <= $child_position; $i++ )
2070                {
2071                    // Create the full path for the children.
2072                    $child = $context."/".$child_name."[".$i."]";
2073                   
2074                    // Check whether the child matches the node-test.
2075                    if ( $this->check_node_test($child, $axis["node-test"]) )
2076                    {
2077                        // Add the child to the list of nodes.
2078                        $nodes[] = $child;
2079                    }
2080                   
2081                    // Recurse to the next level.
2082                    $nodes = array_merge($nodes,
2083                        $this->handle_axis_descendant($axis, $child));
2084                }
2085            }
2086        }
2087       
2088        // Return the nodeset.
2089        return $nodes;
2090    }
2091
2092    /**
2093    * Handles the XPath ancestor axis.
2094    *
2095    * This method handles the XPath ancestor axis.
2096    *
2097    * @access    private
2098    * @author    Michael P. Mehl <mpm@phpxml.org>
2099    * @param     array $axis Array containing information about the axis.
2100    * @param     string $context Node from which starting the axis should
2101    *            be processed.
2102    * @return    array This method returns an array containing all nodes
2103    *            that were found during the evaluation of the given axis.
2104    * @see       evaluate()
2105    */
2106    function handle_axis_ancestor ( $axis, $context )
2107    {
2108        // Create an empty node-set.
2109        $nodes = array();
2110       
2111        // Get the parent of the current node.
2112        $parent = $this->nodes[$context]["parent"];
2113       
2114        // Check whether the parent isn't empty.
2115        if ( !empty($parent) )
2116        {
2117            // Check whether the parent matches the node-test.
2118            if ( $this->check_node_test($parent, $axis["node-test"]) )
2119            {
2120                // Add the parent to the list of nodes.
2121                $nodes[] = $parent;
2122            }
2123           
2124            // Handle all other ancestors.
2125            $nodes = array_merge($nodes,
2126                $this->handle_axis_ancestor($axis, $parent));
2127        }
2128       
2129        // Return the nodeset.
2130        return $nodes;
2131    }
2132
2133    /**
2134    * Handles the XPath namespace axis.
2135    *
2136    * This method handles the XPath namespace axis.
2137    *
2138    * @access    private
2139    * @author    Michael P. Mehl <mpm@phpxml.org>
2140    * @param     array $axis Array containing information about the axis.
2141    * @param     string $context Node from which starting the axis should
2142    *            be processed.
2143    * @return    array This method returns an array containing all nodes
2144    *            that were found during the evaluation of the given axis.
2145    * @see       evaluate()
2146    */
2147    function handle_axis_namespace ( $axis, $context )
2148    {
2149        // Create an empty node-set.
2150        $nodes = array();
2151       
2152        // Check whether all nodes should be selected.
2153        if ( !empty($this->nodes[$context]["namespace"]) )
2154        {
2155            // Add this node to the node-set.
2156            $nodes[] = $context;
2157        }
2158           
2159        // Return the nodeset.
2160        return $nodes;
2161    }
2162   
2163    /**
2164    * Handles the XPath following axis.
2165    *
2166    * This method handles the XPath following axis.
2167    *
2168    * @access    private
2169    * @author    Michael P. Mehl <mpm@phpxml.org>
2170    * @param     array $axis Array containing information about the axis.
2171    * @param     string $context Node from which starting the axis should
2172    *            be processed.
2173    * @return    array This method returns an array containing all nodes
2174    *            that were found during the evaluation of the given axis.
2175    * @see       evaluate()
2176    */
2177    function handle_axis_following ( $axis, $context )
2178    {
2179        // Create an empty node-set.
2180        $nodes = array();
2181       
2182        // Get the current document position.
2183        $position = $this->nodes[$context]["document-position"];
2184       
2185        // Create a flag, whether we already found the context node.
2186        $found = false;
2187       
2188        // Run through all nodes of the document.
2189        foreach ( $this->nodes as $node => $data )
2190        {
2191            // Check whether the context node has already been found.
2192            if ( $found )
2193            {
2194                // Check whether the position is correct.
2195                if ( $this->nodes[$node]["document-position"] == $position )
2196                {
2197                    // Check whether the node fits the node-test.
2198                    if ( $this->check_node_test($node, $axis["node-test"]) )
2199                    {
2200                        // Add the node to the list of nodes.
2201                        $nodes[] = $node;
2202                    }
2203                }
2204            }
2205           
2206            // Check whether this is the context node.
2207            if ( $node == $context )
2208            {
2209                // After this we'll look for more nodes.
2210                $found = true;
2211            }
2212        }
2213           
2214        // Return the nodeset.
2215        return $nodes;
2216    }
2217   
2218    /**
2219    * Handles the XPath preceding axis.
2220    *
2221    * This method handles the XPath preceding axis.
2222    *
2223    * @access    private
2224    * @author    Michael P. Mehl <mpm@phpxml.org>
2225    * @param     array $axis Array containing information about the axis.
2226    * @param     string $context Node from which starting the axis should
2227    *            be processed.
2228    * @return    array This method returns an array containing all nodes
2229    *            that were found during the evaluation of the given axis.
2230    * @see       evaluate()
2231    */
2232    function handle_axis_preceding ( $axis, $context )
2233    {
2234        // Create an empty node-set.
2235        $nodes = array();
2236       
2237        // Get the current document position.
2238        $position = $this->nodes[$context]["document-position"];
2239       
2240        // Create a flag, whether we already found the context node.
2241        $found = true;
2242       
2243        // Run through all nodes of the document.
2244        foreach ( $this->nodes as $node => $data )
2245        {
2246            // Check whether this is the context node.
2247            if ( $node == $context )
2248            {
2249                // After this we won't look for more nodes.
2250                $found = false;
2251            }
2252           
2253            // Check whether the context node has already been found.
2254            if ( $found )
2255            {
2256                // Check whether the position is correct.
2257                if ( $this->nodes[$node]["document-position"] == $position )
2258                {
2259                    // Check whether the node fits the node-test.
2260                    if ( $this->check_node_test($node, $axis["node-test"]) )
2261                    {
2262                        // Add the node to the list of nodes.
2263                        $nodes[] = $node;
2264                    }
2265                }
2266            }
2267        }
2268           
2269        // Return the nodeset.
2270        return $nodes;
2271    }
2272   
2273    /**
2274    * Handles the XPath following-sibling axis.
2275    *
2276    * This method handles the XPath following-sibling axis.
2277    *
2278    * @access    private
2279    * @author    Michael P. Mehl <mpm@phpxml.org>
2280    * @param     array $axis Array containing information about the axis.
2281    * @param     string $context Node from which starting the axis should
2282    *            be processed.
2283    * @return    array This method returns an array containing all nodes
2284    *            that were found during the evaluation of the given axis.
2285    * @see       evaluate()
2286    */
2287    function handle_axis_following_sibling ( $axis, $context )
2288    {
2289        // Create an empty node-set.
2290        $nodes = array();
2291       
2292        // Get all children from the parent.
2293        $siblings = $this->handle_axis_child($axis,
2294            $this->nodes[$context]["parent"]);
2295       
2296        // Create a flag whether the context node was already found.
2297        $found = false;
2298       
2299        // Run through all siblings.
2300        foreach ( $siblings as $sibling )
2301        {
2302            // Check whether the context node was already found.
2303            if ( $found )
2304            {
2305                // Check whether the sibling is a real sibling.
2306                if ( $this->nodes[$sibling]["name"] ==
2307                    $this->nodes[$context]["name"] )
2308                {
2309                    // Check whether the sibling matches the node-test.
2310                    if ( $this->check_node_test($sibling, $axis["node-test"]) )
2311                    {
2312                        // Add the sibling to the list of nodes.
2313                        $nodes[] = $sibling;
2314                    }
2315                }
2316            }
2317           
2318            // Check whether this is the context node.
2319            if ( $sibling == $context )
2320            {
2321                // Continue looking for other siblings.
2322                $found = true;
2323            }
2324        }
2325           
2326        // Return the nodeset.
2327        return $nodes;
2328    }
2329   
2330    /**
2331    * Handles the XPath preceding-sibling axis.
2332    *
2333    * This method handles the XPath preceding-sibling axis.
2334    *
2335    * @access    private
2336    * @author    Michael P. Mehl <mpm@phpxml.org>
2337    * @param     array $axis Array containing information about the axis.
2338    * @param     string $context Node from which starting the axis should
2339    *            be processed.
2340    * @return    array This method returns an array containing all nodes
2341    *            that were found during the evaluation of the given axis.
2342    * @see       evaluate()
2343    */
2344    function handle_axis_preceding_sibling ( $axis, $context )
2345    {
2346        // Create an empty node-set.
2347        $nodes = array();
2348       
2349        // Get all children from the parent.
2350        $siblings = $this->handle_axis_child($axis,
2351            $this->nodes[$context]["parent"]);
2352       
2353        // Create a flag whether the context node was already found.
2354        $found = true;
2355       
2356        // Run through all siblings.
2357        foreach ( $siblings as $sibling )
2358        {
2359            // Check whether this is the context node.
2360            if ( $sibling == $context )
2361            {
2362                // Don't continue looking for other siblings.
2363                $found = false;
2364            }
2365           
2366            // Check whether the context node was already found.
2367            if ( $found )
2368            {
2369                // Check whether the sibling is a real sibling.
2370                if ( $this->nodes[$sibling]["name"] ==
2371                    $this->nodes[$context]["name"] )
2372                {
2373                    // Check whether the sibling matches the node-test.
2374                    if ( $this->check_node_test($sibling, $axis["node-test"]) )
2375                    {
2376                        // Add the sibling to the list of nodes.
2377                        $nodes[] = $sibling;
2378                    }
2379                }
2380            }
2381        }
2382           
2383        // Return the nodeset.
2384        return $nodes;
2385    }
2386   
2387    /**
2388    * Handles the XPath descendant-or-self axis.
2389    *
2390    * This method handles the XPath descendant-or-self axis.
2391    *
2392    * @access    private
2393    * @author    Michael P. Mehl <mpm@phpxml.org>
2394    * @param     array $axis Array containing information about the axis.
2395    * @param     string $context Node from which starting the axis should
2396    *            be processed.
2397    * @return    array This method returns an array containing all nodes
2398    *            that were found during the evaluation of the given axis.
2399    * @see       evaluate()
2400    */
2401    function handle_axis_descendant_or_self ( $axis, $context )
2402    {
2403        // Create an empty node-set.
2404        $nodes = array();
2405       
2406        // Read the nodes.
2407        $nodes = array_merge(
2408            $this->handle_axis_descendant($axis, $context),
2409            $this->handle_axis_self($axis, $context));
2410       
2411        // Return the nodeset.
2412        return $nodes;
2413    }
2414   
2415    /**
2416    * Handles the XPath ancestor-or-self axis.
2417    *
2418    * This method handles the XPath ancestor-or-self axis.
2419    *
2420    * @access    private
2421    * @author    Michael P. Mehl <mpm@phpxml.org>
2422    * @param     array $axis Array containing information about the axis.
2423    * @param     string $context Node from which starting the axis should
2424    *            be processed.
2425    * @return    array This method returns an array containing all nodes
2426    *            that were found during the evaluation of the given axis.
2427    * @see       evaluate()
2428    */
2429    function handle_axis_ancestor_or_self ( $axis, $context )
2430    {
2431        // Create an empty node-set.
2432        $nodes = array();
2433       
2434        // Read the nodes.
2435        $nodes = array_merge(
2436            $this->handle_axis_ancestor($axis, $context),
2437            $this->handle_axis_self($axis, $context));
2438       
2439        // Return the nodeset.
2440        return $nodes;
2441    }
2442   
2443    /**
2444    * Handles the XPath function last.
2445    *
2446    * This method handles the XPath function last.
2447    *
2448    * @access    private
2449    * @author    Michael P. Mehl <mpm@phpxml.org>
2450    * @param     string $node Full path of the node on which the function
2451    *            should be processed.
2452    * @param     string $arguments String containing the arguments that were
2453    *            passed to the function.
2454    * @return    mixed Depending on the type of function being processed this
2455    *            method returns different types.
2456    * @see       evaluate()
2457    */
2458    function handle_function_last ( $node, $arguments )
2459    {
2460        // Calculate the size of the context.
2461        $parent   = $this->nodes[$node]["parent"];
2462        $children = $this->nodes[$parent]["children"];
2463        $context  = $children[$this->nodes[$node]["name"]];
2464
2465        // Return the size.
2466        return $context;
2467    }
2468
2469    /**
2470    * Handles the XPath function position.
2471    *
2472    * This method handles the XPath function position.
2473    *
2474    * @access    private
2475    * @author    Michael P. Mehl <mpm@phpxml.org>
2476    * @param     string $node Full path of the node on which the function
2477    *            should be processed.
2478    * @param     string $arguments String containing the arguments that were
2479    *            passed to the function.
2480    * @return    mixed Depending on the type of function being processed this
2481    *            method returns different types.
2482    * @see       evaluate()
2483    */
2484    function handle_function_position ( $node, $arguments )
2485    {
2486        // return the context-position.
2487        return $this->nodes[$node]["context-position"];
2488    }
2489   
2490    /**
2491    * Handles the XPath function count.
2492    *
2493    * This method handles the XPath function count.
2494    *
2495    * @access    private
2496    * @author    Michael P. Mehl <mpm@phpxml.org>
2497    * @param     string $node Full path of the node on which the function
2498    *            should be processed.
2499    * @param     string $arguments String containing the arguments that were
2500    *            passed to the function.
2501    * @return    mixed Depending on the type of function being processed this
2502    *            method returns different types.
2503    * @see       evaluate()
2504    */
2505    function handle_function_count ( $node, $arguments )
2506    {
2507        // Evaluate the argument of the method as an XPath and return
2508        // the number of results.
2509        return count($this->evaluate($arguments, $node));
2510    }
2511   
2512    /**
2513    * Handles the XPath function id.
2514    *
2515    * This method handles the XPath function id.
2516    *
2517    * @access    private
2518    * @author    Michael P. Mehl <mpm@phpxml.org>
2519    * @param     string $node Full path of the node on which the function
2520    *            should be processed.
2521    * @param     string $arguments String containing the arguments that were
2522    *            passed to the function.
2523    * @return    mixed Depending on the type of function being processed this
2524    *            method returns different types.
2525    * @see       evaluate()
2526    */
2527    function handle_function_id ( $node, $arguments )
2528    {
2529        // Trim the arguments.
2530        $arguments = trim($arguments);
2531       
2532        // Now split the arguments.
2533        $arguments = explode(" ", $arguments);
2534       
2535        // Check whether
2536       
2537        // Create a list of nodes.
2538        $nodes = array();
2539       
2540        // Run through all document node.
2541        foreach ( $this->nodes as $node => $position )
2542        {
2543            // Check whether the node has the ID we're looking for.
2544            if ( in_array($this->nodes[$node]["attributes"]["id"],
2545                $arguments) )
2546            {
2547                // Add this node to the list of nodes.
2548                $nodes[] = $node;
2549            }
2550        }
2551       
2552        // Return the list of nodes.
2553        return $nodes;
2554    }
2555   
2556    /**
2557    * Handles the XPath function name.
2558    *
2559    * This method handles the XPath function name.
2560    *
2561    * @access    private
2562    * @author    Michael P. Mehl <mpm@phpxml.org>
2563    * @param     string $node Full path of the node on which the function
2564    *            should be processed.
2565    * @param     string $arguments String containing the arguments that were
2566    *            passed to the function.
2567    * @return    mixed Depending on the type of function being processed this
2568    *            method returns different types.
2569    * @see       evaluate()
2570    */
2571    function handle_function_name ( $node, $arguments )
2572    {
2573        // Return the name of the node.
2574        return $this->nodes[$node]["name"];
2575    }
2576   
2577    /**
2578    * Handles the XPath function string.
2579    *
2580    * This method handles the XPath function string.
2581    *
2582    * @access    private
2583    * @author    Michael P. Mehl <mpm@phpxml.org>
2584    * @param     string $node Full path of the node on which the function
2585    *            should be processed.
2586    * @param     string $arguments String containing the arguments that were
2587    *            passed to the function.
2588    * @return    mixed Depending on the type of function being processed this
2589    *            method returns different types.
2590    * @see       evaluate()
2591    */
2592    function handle_function_string ( $node, $arguments )
2593    {
2594        // Check what type of parameter is given
2595        if ( ereg("^[0-9]+(\.[0-9]+)?$", $arguments) ||
2596            ereg("^\.[0-9]+$", $arguments) )
2597        {
2598            // Convert the digits to a number.
2599            $number = doubleval($arguments);
2600               
2601            // Return the number.
2602            return strval($number);
2603        }
2604        elseif ( is_bool($arguments) )
2605        {
2606            // Check whether it's true.
2607            if ( $arguments == true )
2608            {
2609                // Return true as a string.
2610                return "true";
2611            }
2612            else
2613            {
2614                // Return false as a string.
2615                return "false";
2616            }
2617        }
2618        elseif ( !empty($arguments) )
2619        {
2620            // Use the argument as an XPath.
2621            $result = $this->evaluate($arguments, $node);
2622               
2623            // Get the first argument.
2624            $result = explode("|", implode("|", $result));
2625               
2626            // Return the first result as a string.
2627            return $result[0];
2628        }
2629        elseif ( empty($arguments) )
2630        {
2631            // Return the current node.
2632            return $node;
2633        }
2634        else
2635        {
2636            // Return an empty string.
2637            return "";
2638        }
2639    }
2640   
2641    /**
2642    * Handles the XPath function concat.
2643    *
2644    * This method handles the XPath function concat.
2645    *
2646    * @access    private
2647    * @author    Michael P. Mehl <mpm@phpxml.org>
2648    * @param     string $node Full path of the node on which the function
2649    *            should be processed.
2650    * @param     string $arguments String containing the arguments that were
2651    *            passed to the function.
2652    * @return    mixed Depending on the type of function being processed this
2653    *            method returns different types.
2654    * @see       evaluate()
2655    */
2656    function handle_function_concat ( $node, $arguments )
2657    {
2658        // Split the arguments.
2659        $arguments = explode(",", $arguments);
2660           
2661        // Run through each argument and evaluate it.
2662        for ( $i = 0; $i < sizeof($arguments); $i++ )
2663        {
2664            // Trim each argument.
2665            $arguments[$i] = trim($arguments[$i]);
2666               
2667            // Evaluate it.
2668            $arguments[$i] = $this->evaluate_predicate($node, $arguments[$i]);
2669        }
2670           
2671        // Put the string together.
2672        $arguments = implode("", $arguments);
2673           
2674        // Return the string.
2675        return $arguments;
2676    }
2677   
2678    /**
2679    * Handles the XPath function starts-with.
2680    *
2681    * This method handles the XPath function starts-with.
2682    *
2683    * @access    private
2684    * @author    Michael P. Mehl <mpm@phpxml.org>
2685    * @param     string $node Full path of the node on which the function
2686    *            should be processed.
2687    * @param     string $arguments String containing the arguments that were
2688    *            passed to the function.
2689    * @return    mixed Depending on the type of function being processed this
2690    *            method returns different types.
2691    * @see       evaluate()
2692    */
2693    function handle_function_starts_with ( $node, $arguments )
2694    {
2695        // Get the arguments.
2696        $first  = trim($this->prestr($arguments, ","));
2697        $second = trim($this->afterstr($arguments, ","));
2698           
2699        // Evaluate each argument.
2700        $first  = $this->evaluate_predicate($node, $first);
2701        $second = $this->evaluate_predicate($node, $second);
2702           
2703        // Check whether the first string starts with the second one.
2704        if ( ereg("^".$second, $first) )
2705        {
2706            // Return true.
2707            return true;
2708        }
2709        else
2710        {
2711            // Return false.
2712            return false;
2713        }
2714    }
2715   
2716    /**
2717    * Handles the XPath function contains.
2718    *
2719    * This method handles the XPath function contains.
2720    *
2721    * @access    private
2722    * @author    Michael P. Mehl <mpm@phpxml.org>
2723    * @param     string $node Full path of the node on which the function
2724    *            should be processed.
2725    * @param     string $arguments String containing the arguments that were
2726    *            passed to the function.
2727    * @return    mixed Depending on the type of function being processed this
2728    *            method returns different types.
2729    * @see       evaluate()
2730    */
2731    function handle_function_contains ( $node, $arguments )
2732    {
2733        // Get the arguments.
2734        $first  = trim($this->prestr($arguments, ","));
2735        $second = trim($this->afterstr($arguments, ","));
2736           
2737        // Evaluate each argument.
2738        $first  = $this->evaluate_predicate($node, $first);
2739        $second = $this->evaluate_predicate($node, $second);
2740           
2741        // Check whether the first string starts with the second one.
2742        if ( ereg($second, $first) )
2743        {
2744            // Return true.
2745            return true;
2746        }
2747        else
2748        {
2749            // Return false.
2750            return false;
2751        }
2752    }
2753   
2754    /**
2755    * Handles the XPath function substring-before.
2756    *
2757    * This method handles the XPath function substring-before.
2758    *
2759    * @access    private
2760    * @author    Michael P. Mehl <mpm@phpxml.org>
2761    * @param     string $node Full path of the node on which the function
2762    *            should be processed.
2763    * @param     string $arguments String containing the arguments that were
2764    *            passed to the function.
2765    * @return    mixed Depending on the type of function being processed this
2766    *            method returns different types.
2767    * @see       evaluate()
2768    */
2769    function handle_function_substring_before ( $node, $arguments )
2770    {
2771        // Get the arguments.
2772        $first  = trim($this->prestr($arguments, ","));
2773        $second = trim($this->afterstr($arguments, ","));
2774         
2775        // Evaluate each argument.
2776        $first  = $this->evaluate_predicate($node, $first);
2777        $second = $this->evaluate_predicate($node, $second);
2778           
2779        // Return the substring.
2780        return $this->prestr(strval($first), strval($second));
2781    }
2782   
2783    /**
2784    * Handles the XPath function substring-after.
2785    *
2786    * This method handles the XPath function substring-after.
2787    *
2788    * @access    private
2789    * @author    Michael P. Mehl <mpm@phpxml.org>
2790    * @param     string $node Full path of the node on which the function
2791    *            should be processed.
2792    * @param     string $arguments String containing the arguments that were
2793    *            passed to the function.
2794    * @return    mixed Depending on the type of function being processed this
2795    *            method returns different types.
2796    * @see       evaluate()
2797    */
2798    function handle_function_substring_after ( $node, $arguments )
2799    {
2800        // Get the arguments.
2801        $first  = trim($this->prestr($arguments, ","));
2802        $second = trim($this->afterstr($arguments, ","));
2803           
2804        // Evaluate each argument.
2805        $first  = $this->evaluate_predicate($node, $first);
2806        $second = $this->evaluate_predicate($node, $second);
2807           
2808        // Return the substring.
2809        return $this->afterstr(strval($first), strval($second));
2810    }
2811   
2812    /**
2813    * Handles the XPath function substring.
2814    *
2815    * This method handles the XPath function substring.
2816    *
2817    * @access    private
2818    * @author    Michael P. Mehl <mpm@phpxml.org>
2819    * @param     string $node Full path of the node on which the function
2820    *            should be processed.
2821    * @param     string $arguments String containing the arguments that were
2822    *            passed to the function.
2823    * @return    mixed Depending on the type of function being processed this
2824    *            method returns different types.
2825    * @see       evaluate()
2826    */
2827    function handle_function_substring ( $node, $arguments )
2828    {
2829        // Split the arguments.
2830        $arguments = explode(",", $arguments);
2831           
2832        // Run through all arguments.
2833        for ( $i = 0; $i < sizeof($arguments); $i++ )
2834        {
2835            // Trim the string.
2836            $arguments[$i] = trim($arguments[$i]);
2837               
2838            // Evaluate each argument.
2839            $arguments[$i] = $this->evaluate_predicate($node, $arguments[$i]);
2840        }
2841           
2842        // Check whether a third argument was given.
2843        if ( !empty($arguments[2]) )
2844        {
2845            // Return the substring.
2846            return substr(strval($arguments[0]), $arguments[1] - 1,
2847                $arguments[2]);
2848        }
2849        else
2850        {
2851            // Return the substring.
2852            return substr(strval($arguments[0]), $arguments[1] - 1);
2853        }
2854    }
2855   
2856    /**
2857    * Handles the XPath function string-length.
2858    *
2859    * This method handles the XPath function string-length.
2860    *
2861    * @access    private
2862    * @author    Michael P. Mehl <mpm@phpxml.org>
2863    * @param     string $node Full path of the node on which the function
2864    *            should be processed.
2865    * @param     string $arguments String containing the arguments that were
2866    *            passed to the function.
2867    * @return    mixed Depending on the type of function being processed this
2868    *            method returns different types.
2869    * @see       evaluate()
2870    */
2871    function handle_function_string_length ( $node, $arguments )
2872    {
2873        // Trim the argument.
2874        $arguments = trim($arguments);
2875           
2876        // Evaluate the argument.
2877        $arguments = $this->evaluate_predicate($node, $arguments);
2878           
2879        // Return the length of the string.
2880        return strlen(strval($arguments));
2881    }
2882   
2883    /**
2884    * Handles the XPath function translate.
2885    *
2886    * This method handles the XPath function translate.
2887    *
2888    * @access    private
2889    * @author    Michael P. Mehl <mpm@phpxml.org>
2890    * @param     string $node Full path of the node on which the function
2891    *            should be processed.
2892    * @param     string $arguments String containing the arguments that were
2893    *            passed to the function.
2894    * @return    mixed Depending on the type of function being processed this
2895    *            method returns different types.
2896    * @see       evaluate()
2897    */
2898    function handle_function_translate ( $node, $arguments )         
2899    {
2900        // Split the arguments.
2901        $arguments = explode(",", $arguments);
2902       
2903        // Run through all arguments.
2904        for ( $i = 0; $i < sizeof($arguments); $i++ )
2905        {
2906            // Trim the argument.
2907            $arguments[$i] = trim($arguments[$i]);
2908           
2909            // Evaluate the argument.
2910            $arguments[$i] = $this->evaluate_predicate($node, $arguments[$i]);
2911        }
2912           
2913        // Return the translated string.
2914        return strtr($arguments[0], $arguments[1], $arguments[2]);
2915    }
2916   
2917    /**
2918    * Handles the XPath function boolean.
2919    *
2920    * This method handles the XPath function boolean.
2921    *
2922    * @access    private
2923    * @author    Michael P. Mehl <mpm@phpxml.org>
2924    * @param     string $node Full path of the node on which the function
2925    *            should be processed.
2926    * @param     string $arguments String containing the arguments that were
2927    *            passed to the function.
2928    * @return    mixed Depending on the type of function being processed this
2929    *            method returns different types.
2930    * @see       evaluate()
2931    */
2932    function handle_function_boolean ( $node, $arguments )
2933    {
2934        // Trim the arguments.
2935        $arguments = trim($arguments);
2936       
2937        // Check what type of parameter is given
2938        if ( ereg("^[0-9]+(\.[0-9]+)?$", $arguments) ||
2939            ereg("^\.[0-9]+$", $arguments) )
2940        {
2941            // Convert the digits to a number.
2942            $number = doubleval($arguments);
2943           
2944            // Check whether the number zero.
2945            if ( $number == 0 )
2946            {
2947                // Return false.
2948                return false;
2949            }
2950            else
2951            {
2952                // Return true.
2953                return true;
2954            }
2955        }
2956        elseif ( empty($arguments) )
2957        {
2958            // Sorry, there were no arguments.
2959            return false;
2960        }
2961        else
2962        {
2963            // Try to evaluate the argument as an XPath.
2964            $result = $this->evaluate($arguments, $node);
2965           
2966            // Check whether we found something.
2967            if ( count($result) > 0 )
2968            {
2969                // Return true.
2970                return true;
2971            }
2972            else
2973            {
2974                // Return false.
2975                return false;
2976            }
2977        }
2978    }
2979   
2980    /**
2981    * Handles the XPath function not.
2982    *
2983    * This method handles the XPath function not.
2984    *
2985    * @access    private
2986    * @author    Michael P. Mehl <mpm@phpxml.org>
2987    * @param     string $node Full path of the node on which the function
2988    *            should be processed.
2989    * @param     string $arguments String containing the arguments that were
2990    *            passed to the function.
2991    * @return    mixed Depending on the type of function being processed this
2992    *            method returns different types.
2993    * @see       evaluate()
2994    */
2995    function handle_function_not ( $node, $arguments )
2996    {
2997        // Trim the arguments.
2998        $arguments = trim($arguments);
2999       
3000        // Return the negative value of the content of the brackets.
3001        return !$this->evaluate_predicate($node, $arguments);
3002    }
3003   
3004    /**
3005    * Handles the XPath function true.
3006    *
3007    * This method handles the XPath function true.
3008    *
3009    * @access    private
3010    * @author    Michael P. Mehl <mpm@phpxml.org>
3011    * @param     string $node Full path of the node on which the function
3012    *            should be processed.
3013    * @param     string $arguments String containing the arguments that were
3014    *            passed to the function.
3015    * @return    mixed Depending on the type of function being processed this
3016    *            method returns different types.
3017    * @see       evaluate()
3018    */
3019    function handle_function_true ( $node, $arguments )
3020    {
3021        // Return true.
3022        return true;
3023    }
3024   
3025    /**
3026    * Handles the XPath function false.
3027    *
3028    * This method handles the XPath function false.
3029    *
3030    * @access    private
3031    * @author    Michael P. Mehl <mpm@phpxml.org>
3032    * @param     string $node Full path of the node on which the function
3033    *            should be processed.
3034    * @param     string $arguments String containing the arguments that were
3035    *            passed to the function.
3036    * @return    mixed Depending on the type of function being processed this
3037    *            method returns different types.
3038    * @see       evaluate()
3039    */
3040    function handle_function_false ( $node, $arguments )
3041    {
3042        // Return false.
3043        return false;
3044    }
3045   
3046    /**
3047    * Handles the XPath function lang.
3048    *
3049    * This method handles the XPath function lang.
3050    *
3051    * @access    private
3052    * @author    Michael P. Mehl <mpm@phpxml.org>
3053    * @param     string $node Full path of the node on which the function
3054    *            should be processed.
3055    * @param     string $arguments String containing the arguments that were
3056    *            passed to the function.
3057    * @return    mixed Depending on the type of function being processed this
3058    *            method returns different types.
3059    * @see       evaluate()
3060    */
3061    function handle_function_lang ( $node, $arguments )
3062    {
3063        // Trim the arguments.
3064        $arguments = trim($arguments);
3065       
3066        // Check whether the node has an language attribute.
3067        if ( empty($this->nodes[$node]["attributes"]["xml:lang"]) )
3068        {
3069            // Run through the ancestors.
3070            while ( !empty($node) )
3071            {
3072                // Select the parent node.
3073                $node = $this->nodes[$node]["parent"];
3074               
3075                // Check whether there's a language definition.
3076                if ( !empty($this->nodes[$node]["attributes"]["xml:lang"]) )
3077                {
3078                    // Check whether it's the language, the user asks for.
3079                    if ( eregi("^".$arguments, $this->nodes[$node]
3080                        ["attributes"]["xml:lang"]) )
3081                    {
3082                        // Return true.
3083                        return true;
3084                    }
3085                    else
3086                    {
3087                        // Return false.
3088                        return false;
3089                    }
3090                }
3091            }
3092           
3093            // Return false.
3094            return false;
3095        }
3096        else
3097        {
3098            // Check whether it's the language, the user asks for.
3099            if ( eregi("^".$arguments, $this->nodes[$node]["attributes"]
3100                ["xml:lang"]) )
3101            {
3102                // Return true.
3103                return true;
3104            }
3105            else
3106            {
3107                // Return false.
3108                return false;
3109            }
3110        }
3111    }
3112   
3113    /**
3114    * Handles the XPath function number.
3115    *
3116    * This method handles the XPath function number.
3117    *
3118    * @access    private
3119    * @author    Michael P. Mehl <mpm@phpxml.org>
3120    * @param     string $node Full path of the node on which the function
3121    *            should be processed.
3122    * @param     string $arguments String containing the arguments that were
3123    *            passed to the function.
3124    * @return    mixed Depending on the type of function being processed this
3125    *            method returns different types.
3126    * @see       evaluate()
3127    */
3128    function handle_function_number ( $node, $arguments )
3129    {
3130        // Check the type of argument.
3131        if ( ereg("^[0-9]+(\.[0-9]+)?$", $arguments) ||
3132            ereg("^\.[0-9]+$", $arguments) )
3133        {
3134            // Return the argument as a number.
3135            return doubleval($arguments);
3136        }
3137        elseif ( is_bool($arguments) )
3138        {
3139            // Check whether it's true.
3140            if ( $arguments == true )
3141            {
3142                // Return 1.
3143                return 1;
3144            }
3145            else
3146            {
3147                // Return 0.
3148                return 0;
3149            }
3150        }
3151    }
3152   
3153    /**
3154    * Handles the XPath function sum.
3155    *
3156    * This method handles the XPath function sum.
3157    *
3158    * @access    private
3159    * @author    Michael P. Mehl <mpm@phpxml.org>
3160    * @param     string $node Full path of the node on which the function
3161    *            should be processed.
3162    * @param     string $arguments String containing the arguments that were
3163    *            passed to the function.
3164    * @return    mixed Depending on the type of function being processed this
3165    *            method returns different types.
3166    * @see       evaluate()
3167    */
3168    function handle_function_sum ( $node, $arguments )
3169    {
3170        // Trim the arguments.
3171        $arguments = trim($arguments);
3172       
3173        // Evaluate the arguments as an XPath expression.
3174        $results = $this->evaluate($arguments, $node);
3175       
3176        // Create a variable to save the sum.
3177        $sum = 0;
3178       
3179        // Run through all results.
3180        foreach ( $results as $result )
3181        {
3182            // Get the value of the node.
3183            $result = $this->get_content($result);
3184           
3185            // Add it to the sum.
3186            $sum += doubleval($result);
3187        }
3188       
3189        // Return the sum.
3190        return $sum;
3191    }
3192   
3193    /**
3194    * Handles the XPath function floor.
3195    *
3196    * This method handles the XPath function floor.
3197    *
3198    * @access    private
3199    * @author    Michael P. Mehl <mpm@phpxml.org>
3200    * @param     string $node Full path of the node on which the function
3201    *            should be processed.
3202    * @param     string $arguments String containing the arguments that were
3203    *            passed to the function.
3204    * @return    mixed Depending on the type of function being processed this
3205    *            method returns different types.
3206    * @see       evaluate()
3207    */
3208    function handle_function_floor ( $node, $arguments )
3209    {
3210        // Trim the arguments.
3211        $arguments = trim($arguments);
3212       
3213        // Convert the arguments to a number.
3214        $arguments = doubleval($arguments);
3215       
3216        // Return the result
3217        return floor($arguments);
3218    }
3219   
3220    /**
3221    * Handles the XPath function ceiling.
3222    *
3223    * This method handles the XPath function ceiling.
3224    *
3225    * @access    private
3226    * @author    Michael P. Mehl <mpm@phpxml.org>
3227    * @param     string $node Full path of the node on which the function
3228    *            should be processed.
3229    * @param     string $arguments String containing the arguments that were
3230    *            passed to the function.
3231    * @return    mixed Depending on the type of function being processed this
3232    *            method returns different types.
3233    * @see       evaluate()
3234    */
3235    function handle_function_ceiling ( $node, $arguments )
3236    {
3237        // Trim the arguments.
3238        $arguments = trim($arguments);
3239       
3240        // Convert the arguments to a number.
3241        $arguments = doubleval($arguments);
3242       
3243        // Return the result
3244        return ceil($arguments);
3245    }
3246   
3247    /**
3248    * Handles the XPath function round.
3249    *
3250    * This method handles the XPath function round.
3251    *
3252    * @access    private
3253    * @author    Michael P. Mehl <mpm@phpxml.org>
3254    * @param     string $node Full path of the node on which the function
3255    *            should be processed.
3256    * @param     string $arguments String containing the arguments that were
3257    *            passed to the function.
3258    * @return    mixed Depending on the type of function being processed this
3259    *            method returns different types.
3260    * @see       evaluate()
3261    */
3262    function handle_function_round ( $node, $arguments )
3263    {
3264        // Trim the arguments.
3265        $arguments = trim($arguments);
3266       
3267        // Convert the arguments to a number.
3268        $arguments = doubleval($arguments);
3269       
3270        // Return the result
3271        return round($arguments);
3272    }
3273   
3274    /**
3275    * Handles the XPath function text.
3276    *
3277    * This method handles the XPath function text.
3278    *
3279    * @access    private
3280    * @author    Michael P. Mehl <mpm@phpxml.org>
3281    * @param     string $node Full path of the node on which the function
3282    *            should be processed.
3283    * @param     string $arguments String containing the arguments that were
3284    *            passed to the function.
3285    * @return    mixed Depending on the type of function being processed this
3286    *            method returns different types.
3287    * @see       evaluate()
3288    */
3289    function handle_function_text ( $node, $arguments )
3290    {
3291        // Return the character data of the node.
3292        return $this->nodes[$node]["text"];
3293    }
3294   
3295    /**
3296    * Retrieves a substring before a delimiter.
3297    *
3298    * This method retrieves everything from a string before a given delimiter,
3299    * not including the delimiter.
3300    *
3301    * @access    private
3302    * @author    Michael P. Mehl <mpm@phpxml.org>
3303    * @param     string $string String, from which the substring should be
3304    *            extracted.
3305    * @param     string $delimiter String containing the delimiter to use.
3306    * @return    string Substring from the original string before the
3307    *            delimiter.
3308    * @see       afterstr()
3309    */
3310    function prestr ( $string, $delimiter )
3311    {
3312        // Return the substring.
3313                return substr($string, 0, strlen($string) - strlen(strstr($string,
3314            "$delimiter")));
3315        }
3316   
3317    /**
3318    * Retrieves a substring after a delimiter.
3319    *
3320    * This method retrieves everything from a string after a given delimiter,
3321    * not including the delimiter.
3322    *
3323    * @access    private
3324    * @author    Michael P. Mehl <mpm@phpxml.org>
3325    * @param     string $string String, from which the substring should be
3326    *            extracted.
3327    * @param     string $delimiter String containing the delimiter to use.
3328    * @return    string Substring from the original string after the
3329    *            delimiter.
3330    * @see       prestr()
3331    */
3332    function afterstr ( $string, $delimiter )
3333    {
3334        // Return the substring.
3335                return substr($string,
3336            strpos($string, $delimiter) + strlen($delimiter));
3337        }
3338   
3339    /**
3340    * Displays an error message.
3341    *
3342    * This method displays an error messages and stops the execution of the
3343    * script. This method is called exactly in the same way as the printf
3344    * function. The first argument contains the message and additional
3345    * arguments of various types may be passed to this method to be inserted
3346    * into the message.
3347    *
3348    * @access    private
3349    * @author    Michael P. Mehl <mpm@phpxml.org>
3350    * @param     string $message Error message to be displayed.
3351    */
3352    function display_error ( $message )
3353    {
3354                // Check whether more than one argument was given.
3355                if ( func_num_args() > 1 )
3356                {
3357                        // Read all arguments.
3358                        $arguments = func_get_args();
3359                       
3360                        // Create a new string for the inserting command.
3361                        $command = "\$message = sprintf(\$message, ";
3362                       
3363                        // Run through the array of arguments.
3364                        for ( $i = 1; $i < sizeof($arguments); $i++ )
3365                        {
3366                                // Add the number of the argument to the command.
3367                                $command .= "\$arguments[".$i."], ";
3368                        }
3369                       
3370                        // Replace the last separator.
3371                        $command = eregi_replace(", $", ");", $command);
3372                       
3373                        // Execute the command.
3374                        eval($command);
3375                }
3376               
3377        // Display the error message.
3378        echo "<b>phpXML error:</b> ".$message;
3379       
3380        // End the execution of this script.
3381        exit;
3382    }
3383}
3384
3385?>
Note: See TracBrowser for help on using the repository browser.