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

Revision 2364, 117.2 KB checked in by amuller, 14 years ago (diff)

Ticket #1008 - Adicionando licença aos arquivos php

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