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

Revision 7655, 117.4 KB checked in by douglasz, 11 years ago (diff)

Ticket #3236 - Melhorias de performance no codigo do Expresso.

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