diff --git a/phpgwapi/inc/class.xml.inc.php b/phpgwapi/inc/class.xml.inc.php new file mode 100644 index 0000000000..31893581c0 --- /dev/null +++ b/phpgwapi/inc/class.xml.inc.php @@ -0,0 +1,3385 @@ + version 1.0 | +// | Copyright (c) 2001 Michael P. Mehl. All rights reserved. | +// +----------------------------------------------------------------------+ +// | Latest releases are available at http://phpxml.org/. For feedback or | +// | bug reports, please contact the author at mpm@phpxml.org. Thanks! | +// +----------------------------------------------------------------------+ +// | The contents of this file are subject to the Mozilla Public License | +// | Version 1.1 (the "License"); you may not use this file except in | +// | compliance with the License. You may obtain a copy of the License at | +// | http://www.mozilla.org/MPL/ | +// | | +// | Software distributed under the License is distributed on an "AS IS" | +// | basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See | +// | the License for the specific language governing rights and | +// | limitations under the License. | +// | | +// | The Original Code is . | +// | | +// | The Initial Developer of the Original Code is Michael P. Mehl. | +// | Portions created by Michael P. Mehl are Copyright (C) 2001 Michael | +// | P. Mehl. All Rights Reserved. | +// +----------------------------------------------------------------------+ +// | Authors: | +// | Michael P. Mehl | +// +----------------------------------------------------------------------+ +// + +/** +* Class for accessing XML data through the XPath language. +* +* This class offers methods for accessing the nodes of a XML document using +* the XPath language. You can add or remove nodes, set or modify their +* content and their attributes. No additional PHP extensions like DOM XML +* or something similar are required to use these features. +* +* @link http://www.phpxml.org/ Latest release of this class +* @link http://www.w3.org/TR/xpath W3C XPath Recommendation +* @copyright Copyright (c) 2001 Michael P. Mehl. All rights reserved. +* @author Michael P. Mehl +* @version 1.0 (2001-03-08) +* @access public +*/ + +class XML +{ + /** + * List of all document nodes. + * + * This array contains a list of all document nodes saved as an + * associative array. + * + * @access private + * @var array + */ + var $nodes = array(); + + /** + * List of document node IDs. + * + * This array contains a list of all IDs of all document nodes that + * are used for counting when adding a new node. + * + * @access private + * @var array + */ + var $ids = array(); + + /** + * Current document path. + * + * This variable saves the current path while parsing a XML file and adding + * the nodes being read from the file. + * + * @access private + * @var string + */ + var $path = ""; + + /** + * Current document position. + * + * This variable counts the current document position while parsing a XML + * file and adding the nodes being read from the file. + * + * @access private + * @var int + */ + var $position = 0; + + /** + * Path of the document root. + * + * This string contains the full path to the node that acts as the root + * node of the whole document. + * + * @access private + * @var string + */ + var $root = ""; + + /** + * Current XPath expression. + * + * This string contains the full XPath expression being parsed currently. + * + * @access private + * @var string + */ + var $xpath = ""; + + /** + * List of entities to be converted. + * + * This array contains a list of entities to be converted when an XPath + * expression is evaluated. + * + * @access private + * @var array + */ + var $entities = array ( "&" => "&", "<" => "<", ">" => ">", + "'" => "&apos", '"' => """ ); + + /** + * List of supported XPath axes. + * + * This array contains a list of all valid axes that can be evaluated in an + * XPath expression. + * + * @access private + * @var array + */ + var $axes = array ( "child", "descendant", "parent", "ancestor", + "following-sibling", "preceding-sibling", "following", "preceding", + "attribute", "namespace", "self", "descendant-or-self", + "ancestor-or-self" ); + + /** + * List of supported XPath functions. + * + * This array contains a list of all valid functions that can be evaluated + * in an XPath expression. + * + * @access private + * @var array + */ + var $functions = array ( "last", "position", "count", "id", "name", + "string", "concat", "starts-with", "contains", "substring-before", + "substring-after", "substring", "string-length", "translate", + "boolean", "not", "true", "false", "lang", "number", "sum", "floor", + "ceiling", "round", "text" ); + + /** + * List of supported XPath operators. + * + * This array contains a list of all valid operators that can be evaluated + * in a predicate of an XPath expression. The list is ordered by the + * precedence of the operators (lowest precedence first). + * + * @access private + * @var array + */ + var $operators = array( " or ", " and ", "=", "!=", "<=", "<", ">=", ">", + "+", "-", "*", " div ", " mod " ); + + /** + * Constructor of the class. + * + * This constructor initializes the class and, when a filename is given, + * tries to read and parse the given file. + * + * @access public + * @author Michael P. Mehl + * @param string $file Path and name of the file to read and parsed. + * @see load_file() + */ + function XML ( $file = "" ) + { + // Check whether a file was given. + if ( !empty($file) ) + { + // Load the XML file. + $this->load_file($file); + } + } + + /** + * Reads a file and parses the XML data. + * + * This method reads the content of a XML file, tries to parse its + * content and upon success stores the information retrieved from + * the file into an array. + * + * @access public + * @author Michael P. Mehl + * @param string $file Path and name of the file to be read and parsed. + * @see handle_start_element(), handle_end_element(), + * handle_character_data() + */ + function load_file ( $file ) + { + // Check whether the file exists and is readable. + if ( file_exists($file) && is_readable($file) ) + { + // Read the content of the file. + $content = implode("", file($file)); + + // Check whether content has been read. + if ( !empty($content) ) + { + // Create an XML parser. + $parser = xml_parser_create(); + + // Set the options for parsing the XML data. + xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); + xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); + + // Set the object for the parser. + xml_set_object($parser, &$this); + + // Set the element handlers for the parser. + xml_set_element_handler($parser, "handle_start_element", + "handle_end_element"); + xml_set_character_data_handler($parser, + "handle_character_data"); + + // Parse the XML file. + if ( !xml_parse($parser, $content, true) ) + { + // Display an error message. + $this->display_error("XML error in file %s, line %d: %s", + $file, xml_get_current_line_number($parser), + xml_error_string(xml_get_error_code($parser))); + } + + // Free the parser. + xml_parser_free($parser); + } + } + else + { + // Display an error message. + $this->display_error("File %s could not be found or read.", $file); + } + } + + /** + * Generates a XML file with the content of the current document. + * + * This method creates a string containing the XML data being read + * and modified by this class before. This string can be used to save + * a modified document back to a file or doing other nice things with + * it. + * + * @access public + * @author Michael P. Mehl + * @param array $highlight Array containing a list of full document + * paths of nodes to be highlighted by ... tags + * in the generated XML string. + * @param string $root While doing a recursion with this method, this + * parameter is used for internal purpose. + * @param int $level While doing a recursion with this method, this + * parameter is used for internal purpose. + * @return string The returned string contains well-formed XML data + * representing the content of this document. + * @see load_file(), evaluate(), get_content() + */ + function get_file ( $highlight = array(), $root = "", $level = 0 ) + { + // Create a string to save the generated XML data. + $xml = ""; + + // Create two strings containing the tags for highlighting a node. + $highlight_start = ""; + $highlight_end = ""; + + // Generate a string to be displayed before the tags. + $before = ""; + + // Calculate the amount of whitespaces to display. + for ( $i = 0; $i < ( $level * 2 ); $i++ ) + { + // Add a whitespaces to the string. + $before .= " "; + } + + // Check whether a root node is given. + if ( empty($root) ) + { + // Set it to the document root. + $root = $this->root; + } + + // Check whether the node is selected. + $selected = in_array($root, $highlight); + + // Now add the whitespaces to the XML data. + $xml .= $before; + + // Check whether the node is selected. + if ( $selected ) + { + // Add the highlight code to the XML data. + $xml .= $highlight_start; + } + + // Now open the tag. + $xml .= "<".$this->nodes[$root]["name"]; + + // Check whether there are attributes for this node. + if ( count($this->nodes[$root]["attributes"]) > 0 ) + { + // Run through all attributes. + foreach ( $this->nodes[$root]["attributes"] as $key => $value ) + { + // Check whether this attribute is highlighted. + if ( in_array($root."/attribute::".$key, $highlight) ) + { + // Add the highlight code to the XML data. + $xml .= $highlight_start; + } + + // Add the attribute to the XML data. + $xml .= " ".$key."=\"".trim(stripslashes($value))."\""; + + // Check whether this attribute is highlighted. + if ( in_array($root."/attribute::".$key, $highlight) ) + { + // Add the highlight code to the XML data. + $xml .= $highlight_end; + } + } + } + + // Check whether the node contains character data or has children. + if ( empty($this->nodes[$root]["text"]) && + !isset($this->nodes[$root]["children"]) ) + { + // Add the end to the tag. + $xml .= "/"; + } + + // Close the tag. + $xml .= ">\n"; + + // Check whether the node is selected. + if ( $selected ) + { + // Add the highlight code to the XML data. + $xml .= $highlight_end; + } + + // Check whether the node contains character data. + if ( !empty($this->nodes[$root]["text"]) ) + { + // Add the character data to the XML data. + $xml .= $before." ".$this->nodes[$root]["text"]."\n"; + } + + // Check whether the node has children. + if ( isset($this->nodes[$root]["children"]) ) + { + // Run through all children with different names. + foreach ( $this->nodes[$root]["children"] as $child => $pos ) + { + // Run through all children with the same name. + for ( $i = 1; $i <= $pos; $i++ ) + { + // Generate the full path of the child. + $fullchild = $root."/".$child."[".$i."]"; + + // Add the child's XML data to the existing data. + $xml .= $this->get_file($highlight, $fullchild, + $level + 1); + } + } + } + + // Check whether there are attributes for this node. + if ( !empty($this->nodes[$root]["text"]) || + isset($this->nodes[$root]["children"]) ) + { + // Add the whitespaces to the XML data. + $xml .= $before; + + // Check whether the node is selected. + if ( $selected ) + { + // Add the highlight code to the XML data. + $xml .= $highlight_start; + } + + // Add the closing tag. + $xml .= "</".$this->nodes[$root]["name"].">"; + + // Check whether the node is selected. + if ( $selected ) + { + // Add the highlight code to the XML data. + $xml .= $highlight_end; + } + + // Add a linebreak. + $xml .= "\n"; + } + + // Return the XML data. + return $xml; + } + + /** + * Adds a new node to the XML document. + * + * This method adds a new node to the tree of nodes of the XML document + * being handled by this class. The new node is created according to the + * parameters passed to this method. + * + * @access public + * @author Michael P. Mehl + * @param string $content Full path of the parent, to which the new + * node should be added as a child. + * @param string $name Name of the new node. + * @return string The string returned by this method will contain the + * full document path of the created node. + * @see remove_node(), evaluate() + */ + function add_node ( $context, $name ) + { + // Check whether a name for this element is already set. + if ( empty($this->root) ) + { + // Use this tag as the root element. + $this->root = "/".$name."[1]"; + } + + // Calculate the full path for this element. + $path = $context."/".$name; + + // Set the relative context and the position. + $position = ++$this->ids[$path]; + $relative = $name."[".$position."]"; + + // Calculate the full path. + $fullpath = $context."/".$relative; + + // Calculate the context position, which is the position of this + // element within elements of the same name in the parent node. + $this->nodes[$fullpath]["context-position"] = $position; + + // Calculate the position for the following and preceding axis + // detection. + $this->nodes[$fullpath]["document-position"] = + $this->nodes[$context]["document-position"] + 1; + + // Save the information about the node. + $this->nodes[$fullpath]["name"] = $name; + $this->nodes[$fullpath]["text"] = ""; + $this->nodes[$fullpath]["parent"] = $context; + + // Add this element to the element count array. + if ( !$this->nodes[$context]["children"][$name] ) + { + // Set the default name. + $this->nodes[$context]["children"][$name] = 1; + } + else + { + // Calculate the name. + $this->nodes[$context]["children"][$name] = + $this->nodes[$context]["children"][$name] + 1; + } + + // Return the path of the new node. + return $fullpath; + } + + /** + * Removes a node from the XML document. + * + * This method removes a node from the tree of nodes of the XML document. + * If the node is a document node, all children of the node and its + * character data will be removed. If the node is an attribute node, + * only this attribute will be removed, the node to which the attribute + * belongs as well as its children will remain unmodified. + * + * @access public + * @author Michael P. Mehl + * @param string $node Full path of the node to be removed. + * @see add_node(), evaluate() + */ + function remove_node ( $node ) + { + // Check whether the node is an attribute node. + if ( ereg("/attribute::", $node) ) + { + // Get the path to the attribute node's parent. + $parent = $this->prestr($node, "/attribute::"); + + // Get the name of the attribute. + $attribute = $this->afterstr($node, "/attribute::"); + + // Check whether the attribute exists. + if ( isset($this->nodes[$parent]["attributes"][$attribute]) ) + { + // Create a new array. + $new = array(); + + // Run through the existing attributes. + foreach ( $this->nodes[$parent]["attributes"] + as $key => $value ) + { + // Check whether it's the attribute to remove. + if ( $key != $attribute ) + { + // Add it to the new array again. + $new[$key] = $value; + } + } + + // Save the new attributes. + $this->nodes[$parent]["attributes"] = $new; + } + } + else + { + // Create an associative array, which contains information about + // all nodes that required to be renamed. + $rename = array(); + + // Get the name, the parent and the siblings of current node. + $name = $this->nodes[$node]["name"]; + $parent = $this->nodes[$node]["parent"]; + $siblings = $this->nodes[$parent]["children"][$name]; + + // Decrease the number of children. + $this->nodes[$parent]["children"][$name]--; + + // Create a counter for renumbering the siblings. + $counter = 1; + + // Now run through the siblings. + for ( $i = 1; $i <= $siblings; $i++ ) + { + // Create the name of the sibling. + $sibling = $parent."/".$name."[".$i."]"; + + // Check whether it's the name of the current node. + if ( $sibling != $node ) + { + // Create the new name for the sibling. + $new = $parent."/".$name."[".$counter."]"; + + // Increase the counter. + $counter++; + + // Add the old and the new name to the list of nodes + // to be renamed. + $rename[$sibling] = $new; + } + } + + // Create an array for saving the new node-list. + $nodes = array(); + + // Now run through through the existing nodes. + foreach ( $this->nodes as $name => $values ) + { + // Check the position of the path of the node to be deleted + // in the path of the current node. + $position = strpos($name, $node); + + // Check whether it's not the node to be deleted. + if ( $position === false ) + { + // Run through the array of nodes to be renamed. + foreach ( $rename as $old => $new ) + { + // Check whether this node and it's parent requires to + // be renamed. + $name = str_replace($old, $new, $name); + $values["parent"] = str_replace($old, $new, + $values["parent"]); + } + + // Add the node to the list of nodes. + $nodes[$name] = $values; + } + } + + // Save the new array of nodes. + $this->nodes = $nodes; + } + } + + /** + * Add content to a node. + * + * This method adds content to a node. If it's an attribute node, then + * the value of the attribute will be set, otherwise the character data of + * the node will be set. The content is appended to existing content, + * so nothing will be overwritten. + * + * @access public + * @author Michael P. Mehl + * @param string $path Full document path of the node. + * @param string $value String containing the content to be added. + * @see get_content(), evaluate() + */ + function add_content ( $path, $value ) + { + // Check whether it's an attribute node. + if ( ereg("/attribute::", $path) ) + { + // Get the path to the attribute node's parent. + $parent = $this->prestr($path, "/attribute::"); + + // Get the parent node. + $parent = $this->nodes[$parent]; + + // Get the name of the attribute. + $attribute = $this->afterstr($path, "/attribute::"); + + // Set the attribute. + $parent["attributes"][$attribute] .= $value; + } + else + { + // Set the character data of the node. + $this->nodes[$path]["text"] .= $value; + } + } + + /** + * Set the content of a node. + * + * This method sets the content of a node. If it's an attribute node, then + * the value of the attribute will be set, otherwise the character data of + * the node will be set. Existing content will be overwritten. + * + * @access public + * @author Michael P. Mehl + * @param string $path Full document path of the node. + * @param string $value String containing the content to be set. + * @see get_content(), evaluate() + */ + function set_content ( $path, $value ) + { + // Check whether it's an attribute node. + if ( ereg("/attribute::", $path) ) + { + // Get the path to the attribute node's parent. + $parent = $this->prestr($path, "/attribute::"); + + // Get the parent node. + $parent = $this->nodes[$parent]; + + // Get the name of the attribute. + $attribute = $this->afterstr($path, "/attribute::"); + + // Set the attribute. + $parent["attributes"][$attribute] = $value; + } + else + { + // Set the character data of the node. + $this->nodes[$path]["text"] = $value; + } + } + + /** + * Retrieves the content of a node. + * + * This method retrieves the content of a node. If it's an attribute + * node, then the value of the attribute will be retrieved, otherwise + * it'll be the character data of the node. + * + * @access public + * @author Michael P. Mehl + * @param string $path Full document path of the node, from which the + * content should be retrieved. + * @return string The returned string contains either the value or the + * character data of the node. + * @see set_content(), evaluate() + */ + function get_content ( $path ) + { + // Check whether it's an attribute node. + if ( ereg("/attribute::", $path) ) + { + // Get the path to the attribute node's parent. + $parent = $this->prestr($path, "/attribute::"); + + // Get the parent node. + $parent = $this->nodes[$parent]; + + // Get the name of the attribute. + $attribute = $this->afterstr($path, "/attribute::"); + + // Get the attribute. + $attribute = $parent["attributes"][$attribute]; + + // Return the value of the attribute. + return $attribute; + } + else + { + // Return the cdata of the node. + return stripslashes($this->nodes[$path]["text"]); + } + } + + /** + * Add attributes to a node. + * + * This method adds attributes to a node. Existing attributes will not be + * overwritten. + * + * @access public + * @author Michael P. Mehl + * @param string $path Full document path of the node, the attributes + * should be added to. + * @param array $attributes Associative array containing the new + * attributes for the node. + * @see set_content(), get_content() + */ + function add_attributes ( $path, $attributes ) + { + // Add the attributes to the node. + $this->nodes[$path]["attributes"] = array_merge($attributes, + $this->nodes[$path]["attributes"]); + } + + /** + * Sets the attributes of a node. + * + * This method sets the attributes of a node and overwrites all existing + * attributes by doing this. + * + * @access public + * @author Michael P. Mehl + * @param string $path Full document path of the node, the attributes + * of which should be set. + * @param array $attributes Associative array containing the new + * attributes for the node. + * @see set_content(), get_content() + */ + function set_attributes ( $path, $attributes ) + { + // Set the attributes of the node. + $this->nodes[$path]["attributes"] = $attributes; + } + + /** + * Retrieves a list of all attributes of a node. + * + * This method retrieves a list of all attributes of the node specified in + * the argument. + * + * @access public + * @author Michael P. Mehl + * @param string $path Full document path of the node, from which the + * list of attributes should be retrieved. + * @return array The returned associative array contains the all + * attributes of the specified node. + * @see get_content(), $nodes, $ids + */ + function get_attributes ( $path ) + { + // Return the attributes of the node. + return $this->nodes[$path]["attributes"]; + } + + /** + * Retrieves the name of a document node. + * + * This method retrieves the name of document node specified in the + * argument. + * + * @access public + * @author Michael P. Mehl + * @param string $path Full document path of the node, from which the + * name should be retrieved. + * @return string The returned array contains the name of the specified + * node. + * @see get_content(), $nodes, $ids + */ + function get_name ( $path ) + { + // Return the name of the node. + return $this->nodes[$path]["name"]; + } + + /** + * Evaluates an XPath expression. + * + * This method tries to evaluate an XPath expression by parsing it. A + * XML document has to be read before this method is able to work. + * + * @access public + * @author Michael P. Mehl + * @param string $path XPath expression to be evaluated. + * @param string $context Full path of a document node, starting + * from which the XPath expression should be evaluated. + * @return array The returned array contains a list of the full + * document paths of all nodes that match the evaluated + * XPath expression. + * @see $nodes, $ids + */ + function evaluate ( $path, $context = "" ) + { + // Remove slashes and quote signs. + $path = stripslashes($path); + $path = str_replace("\"", "", $path); + $path = str_replace("'", "", $path); + + // Split the paths into different paths. + $paths = $this->split_paths($path); + + // Create an empty set to save the result. + $result = array(); + + // Run through all paths. + foreach ( $paths as $path ) + { + // Trim the path. + $path = trim($path); + + // Save the current path. + $this->xpath = $path; + + // Convert all entities. + $path = strtr($path, array_flip($this->entities)); + + // Split the path at every slash. + $steps = $this->split_steps($path); + + // Check whether the first element is empty. + if ( empty($steps[0]) ) + { + // Remove the first and empty element. + array_shift($steps); + } + + // Start to evaluate the steps. + $nodes = $this->evaluate_step($context, $steps); + + // Remove duplicated nodes. + $nodes = array_unique($nodes); + + // Add the nodes to the result set. + $result = array_merge($result, $nodes); + } + + // Return the result. + return $result; + } + + /** + * Handles opening XML tags while parsing. + * + * While parsing a XML document for each opening tag this method is + * called. It'll add the tag found to the tree of document nodes. + * + * @access private + * @author Michael P. Mehl + * @param int $parser Handler for accessing the current XML parser. + * @param string $name Name of the opening tag found in the document. + * @param array $attributes Associative array containing a list of + * all attributes of the tag found in the document. + * @see handle_end_element(), handle_character_data(), $nodes, $ids + */ + function handle_start_element ( $parser, $name, $attributes ) + { + // Add a node. + $this->path = $this->add_node($this->path, $name); + + // Set the attributes. + $this->set_attributes($this->path, $attributes); + } + + /** + * Handles closing XML tags while parsing. + * + * While parsing a XML document for each closing tag this method is + * called. + * + * @access private + * @author Michael P. Mehl + * @param int $parser Handler for accessing the current XML parser. + * @param string $name Name of the closing tag found in the document. + * @see handle_start_element(), handle_character_data(), $nodes, $ids + */ + function handle_end_element ( $parser, $name ) + { + // Jump back to the parent element. + $this->path = substr($this->path, 0, strrpos($this->path, "/")); + } + + /** + * Handles character data while parsing. + * + * While parsing a XML document for each character data this method + * is called. It'll add the character data to the document tree. + * + * @access private + * @author Michael P. Mehl + * @param int $parser Handler for accessing the current XML parser. + * @param string $text Character data found in the document. + * @see handle_start_element(), handle_end_element(), $nodes, $ids + */ + function handle_character_data ( $parser, $text ) + { + // Replace entities. + $text = strtr($text, $this->entities); + + // Save the text. + $this->add_content($this->path, addslashes(trim($text))); + } + + /** + * Splits an XPath expression into its different expressions. + * + * This method splits an XPath expression. Each expression can consists of + * list of expression being separated from each other by a | character. + * + * @access private + * @author Michael P. Mehl + * @param string $expression The complete expression to be splitted + * into its different expressions. + * @return array The array returned from this method contains a list + * of all expressions found in the expression passed to this + * method as a parameter. + * @see evalute() + */ + function split_paths ( $expression ) + { + // Create an empty array. + $paths = array(); + + // Save the position of the slash. + $position = -1; + + // Run through the expression. + do + { + // Search for a slash. + $position = $this->search_string($expression, "|"); + + // Check whether a | was found. + if ( $position >= 0 ) + { + // Get the left part of the expression. + $left = substr($expression, 0, $position); + $right = substr($expression, $position + 1); + + // Add the left value to the steps. + $paths[] = $left; + + // Reduce the expression to the right part. + $expression = $right; + } + } + while ( $position > -1 ); + + // Add the remaing expression to the list of steps. + $paths[] = $expression; + + // Return the steps. + return $paths; + } + + /** + * Splits an XPath expression into its different steps. + * + * This method splits an XPath expression. Each expression can consists of + * list of steps being separated from each other by a / character. + * + * @access private + * @author Michael P. Mehl + * @param string $expression The complete expression to be splitted + * into its different steps. + * @return array The array returned from this method contains a list + * of all steps found in the expression passed to this + * method as a parameter. + * @see evalute() + */ + function split_steps ( $expression ) + { + // Create an empty array. + $steps = array(); + + // Replace a double slashes, because they'll cause problems otherwise. + $expression = str_replace("//@", "/descendant::*/@", $expression); + $expression = str_replace("//", "/descendant::", $expression); + + // Save the position of the slash. + $position = -1; + + // Run through the expression. + do + { + // Search for a slash. + $position = $this->search_string($expression, "/"); + + // Check whether a slash was found. + if ( $position >= 0 ) + { + // Get the left part of the expression. + $left = substr($expression, 0, $position); + $right = substr($expression, $position + 1); + + // Add the left value to the steps. + $steps[] = $left; + + // Reduce the expression to the right part. + $expression = $right; + } + } + while ( $position > -1 ); + + // Add the remaing expression to the list of steps. + $steps[] = $expression; + + // Return the steps. + return $steps; + } + + /** + * Retrieves axis information from an XPath expression step. + * + * This method tries to extract the name of the axis and its node-test + * from a given step of an XPath expression at a given node. + * + * @access private + * @author Michael P. Mehl + * @param string $step String containing a step of an XPath expression. + * @param string $node Full document path of the node on which the + * step is executed. + * @return array This method returns an array containing information + * about the axis found in the step. + * @see evaluate_step() + */ + function get_axis ( $step, $node ) + { + // Create an array to save the axis information. + $axis = array( + "axis" => "", + "node-test" => "", + "predicate" => array() + ); + + // Check whether there are predicates. + if ( ereg("\[", $step) ) + { + // Get the predicates. + $predicates = substr($step, strpos($step, "[")); + + // Reduce the step. + $step = $this->prestr($step, "["); + + // Try to split the predicates. + $predicates = str_replace("][", "]|[", $predicates); + $predicates = explode("|", $predicates); + + // Run through all predicates. + foreach ( $predicates as $predicate ) + { + // Remove the brackets. + $predicate = substr($predicate, 1, strlen($predicate) - 2); + + // Add the predicate to the list of predicates. + $axis["predicate"][] = $predicate; + } + } + + // Check whether the axis is given in plain text. + if ( $this->search_string($step, "::") > -1 ) + { + // Split the step to extract axis and node-test. + $axis["axis"] = $this->prestr($step, "::"); + $axis["node-test"] = $this->afterstr($step, "::"); + } + else + { + // Check whether the step is empty. + if ( empty($step) ) + { + // Set it to the default value. + $step = "."; + } + + // Check whether is an abbreviated syntax. + if ( $step == "*" ) + { + // Use the child axis and select all children. + $axis["axis"] = "child"; + $axis["node-test"] = "*"; + } + elseif ( ereg("\(", $step) ) + { + // Check whether it's a function. + if ( $this->is_function($this->prestr($step, "(")) ) + { + // Get the position of the first bracket. + $start = strpos($step, "("); + $end = strpos($step, ")", $start); + + // Get everything before, between and after the brackets. + $before = substr($step, 0, $start); + $between = substr($step, $start + 1, $end - $start - 1); + $after = substr($step, $end + 1); + + // Trim each string. + $before = trim($before); + $between = trim($between); + $after = trim($after); + + // Save the evaluated function. + $axis["axis"] = "function"; + $axis["node-test"] = $this->evaluate_function($before, + $between, $node); + } + else + { + // Use the child axis and a function. + $axis["axis"] = "child"; + $axis["node-test"] = $step; + } + } + elseif ( eregi("^@", $step) ) + { + // Use the attribute axis and select the attribute. + $axis["axis"] = "attribute"; + $axis["node-test"] = substr($step, 1); + } + elseif ( eregi("\]$", $step) ) + { + // Use the child axis and select a position. + $axis["axis"] = "child"; + $axis["node-test"] = substr($step, strpos($step, "[")); + } + elseif ( $step == "." ) + { + // Select the self axis. + $axis["axis"] = "self"; + $axis["node-test"] = "*"; + } + elseif ( $step == ".." ) + { + // Select the parent axis. + $axis["axis"] = "parent"; + $axis["node-test"] = "*"; + } + elseif ( ereg("^[a-zA-Z0-9\-_]+$", $step) ) + { + // Select the child axis and the child. + $axis["axis"] = "child"; + $axis["node-test"] = $step; + } + else + { + // Use the child axis and a name. + $axis["axis"] = "child"; + $axis["node-test"] = $step; + } + } + + // Check whether it's a valid axis. + if ( !in_array($axis["axis"], array_merge($this->axes, + array("function"))) ) + { + // Display an error message. + $this->display_error("While parsing an XPath expression, in ". + "the step \"%s\" the invalid axis \"%s\" was found.", + str_replace($step, "".$step."", $this->xpath),# + $axis["axis"]); + } + + // Return the axis information. + return $axis; + } + + /** + * Looks for a string within another string. + * + * This method looks for a string within another string. Brackets in the + * string the method is looking through will be respected, which means that + * only if the string the method is looking for is located outside of + * brackets, the search will be successful. + * + * @access private + * @author Michael P. Mehl + * @param string $term String in which the search shall take place. + * @param string $expression String that should be searched. + * @return int This method returns -1 if no string was found, otherwise + * the offset at which the string was found. + * @see evaluate_step() + */ + function search_string ( $term, $expression ) + { + // Create a new counter for the brackets. + $brackets = 0; + + // Run through the string. + for ( $i = 0; $i < strlen($term); $i++ ) + { + // Get the character at the position of the string. + $character = substr($term, $i, 1); + + // Check whether it's a breacket. + if ( ( $character == "(" ) || ( $character == "[" ) ) + { + // Increase the number of brackets. + $brackets++; + } + elseif ( ( $character == ")" ) || ( $character == "]" ) ) + { + // Decrease the number of brackets. + $brackets--; + } + elseif ( $brackets == 0 ) + { + // Check whether we can find the expression at this index. + if ( substr($term, $i, strlen($expression)) == $expression ) + { + // Return the current index. + return $i; + } + } + } + + // Check whether we had a valid number of brackets. + if ( $brackets != 0 ) + { + // Display an error message. + $this->display_error("While parsing an XPath expression, in the ". + "predicate \"%s\", there was an invalid number of brackets.", + str_replace($term, "".$term."", $this->xpath)); + } + + // Nothing was found. + return (-1); + } + + /** + * Checks for a valid function name. + * + * This method check whether an expression contains a valid name of an + * XPath function. + * + * @access private + * @author Michael P. Mehl + * @param string $expression Name of the function to be checked. + * @return boolean This method returns true if the given name is a valid + * XPath function name, otherwise false. + * @see evaluate() + */ + function is_function ( $expression ) + { + // Check whether it's in the list of supported functions. + if ( in_array($expression, $this->functions) ) + { + // It's a function. + return true; + } + else + { + // It's not a function. + return false; + } + } + + /** + * Evaluates a step of an XPath expression. + * + * This method tries to evaluate a step from an XPath expression at a + * specific context. + * + * @access private + * @author Michael P. Mehl + * @param string $context Full document path of the context from + * which starting the step should be evaluated. + * @param array $steps Array containing the remaining steps of the + * current XPath expression. + * @return array This method returns an array containing all nodes + * that are the result of evaluating the given XPath step. + * @see evaluate() + */ + function evaluate_step ( $context, $steps ) + { + // Create an empty array for saving the nodes found. + $nodes = array(); + + // Check whether the context is an array of contexts. + if ( is_array($context) ) + { + // Run through the array. + foreach ( $context as $path ) + { + // Call this method for this single path. + $nodes = array_merge($nodes, + $this->evaluate_step($path, $steps)); + } + } + else + { + // Get this step. + $step = array_shift($steps); + + // Create an array to save the new contexts. + $contexts = array(); + + // Get the axis of the current step. + $axis = $this->get_axis($step, $context); + + // Check whether it's a function. + if ( $axis["axis"] == "function" ) + { + // Check whether an array was return by the function. + if ( is_array($axis["node-test"]) ) + { + // Add the results to the list of contexts. + $contexts = array_merge($contexts, $axis["node-test"]); + } + else + { + // Add the result to the list of contexts. + $contexts[] = $axis["node-test"]; + } + } + else + { + // Create the name of the method. + $method = "handle_axis_".str_replace("-", "_", $axis["axis"]); + + // Check whether the axis handler is defined. + if ( !method_exists(&$this, $method) ) + { + // Display an error message. + $this->display_error("While parsing an XPath expression, ". + "the axis \"%s\" could not be handled, because this ". + "version does not support this axis.", $axis["axis"]); + } + + // Perform an axis action. + $contexts = call_user_method($method, &$this, $axis, $context); + + // Check whether there are predicates. + if ( count($axis["predicate"]) > 0 ) + { + // Check whether each node fits the predicates. + $contexts = $this->check_predicates($contexts, + $axis["predicate"]); + } + } + + // Check whether there are more steps left. + if ( count($steps) > 0 ) + { + // Continue the evaluation of the next steps. + $nodes = $this->evaluate_step($contexts, $steps); + } + else + { + // Save the found contexts. + $nodes = $contexts; + } + } + + // Return the nodes found. + return $nodes; + } + + /** + * Evaluates an XPath function + * + * This method evaluates a given XPath function with its arguments on a + * specific node of the document. + * + * @access private + * @author Michael P. Mehl + * @param string $function Name of the function to be evaluated. + * @param string $arguments String containing the arguments being + * passed to the function. + * @param string $node Full path to the document node on which the + * function should be evaluated. + * @return mixed This method returns the result of the evaluation of + * the function. Depending on the function the type of the + * return value can be different. + * @see evaluate() + */ + function evaluate_function ( $function, $arguments, $node ) + { + // Remove whitespaces. + $function = trim($function); + $arguments = trim($arguments); + + // Create the name of the function handling function. + $method = "handle_function_".str_replace("-", "_", $function); + + // Check whether the function handling function is available. + if ( !method_exists(&$this, $method) ) + { + // Display an error message. + $this->display_error("While parsing an XPath expression, ". + "the function \"%s\" could not be handled, because this ". + "version does not support this function.", $function); + } + + // Return the result of the function. + return call_user_method($method, &$this, $node, $arguments); + } + + /** + * Evaluates a predicate on a node. + * + * This method tries to evaluate a predicate on a given node. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the predicate + * should be evaluated. + * @param string $predicate String containing the predicate expression + * to be evaluated. + * @return mixed This method is called recursively. The first call should + * return a boolean value, whether the node matches the predicate + * or not. Any call to the method being made during the recursion + * may also return other types for further processing. + * @see evaluate() + */ + function evaluate_predicate ( $node, $predicate ) + { + // Set the default position and the type of the operator. + $position = 0; + $operator = ""; + + // Run through all operators and try to find them. + foreach ( $this->operators as $expression ) + { + // Check whether a position was already found. + if ( $position <= 0 ) + { + // Try to find the operator. + $position = $this->search_string($predicate, $expression); + + // Check whether a operator was found. + if ( $position > 0 ) + { + // Save the operator. + $operator = $expression; + + // Check whether it's the equal operator. + if ( $operator == "=" ) + { + // Also look for other operators containing the + // equal sign. + if ( $this->search_string($predicate, "!=") == + ( $position - 1 ) ) + { + // Get the new position. + $position = $this->search_string($predicate, "!="); + + // Save the new operator. + $operator = "!="; + } + if ( $this->search_string($predicate, "<=") == + ( $position - 1 ) ) + { + // Get the new position. + $position = $this->search_string($predicate, "<="); + + // Save the new operator. + $operator = "<="; + } + if ( $this->search_string($predicate, ">=") == + ( $position - 1 ) ) + { + // Get the new position. + $position = $this->search_string($predicate, ">="); + + // Save the new operator. + $operator = ">="; + } + } + } + } + } + + // Check whether the operator is a - sign. + if ( $operator == "-" ) + { + // Check whether it's not a function containing a - in its name. + foreach ( $this->functions as $function ) + { + // Check whether there's a - sign in the function name. + if ( ereg("-", $function) ) + { + // Get the position of the - in the function name. + $sign = strpos($function, "-"); + + // Extract a substring from the predicate. + $sub = substr($predicate, $position - $sign, + strlen($function)); + + // Check whether it's the function. + if ( $sub == $function ) + { + // Don't use the operator. + $operator = ""; + $position = -1; + } + } + } + } + elseif ( $operator == "*" ) + { + // Get some substrings. + $character = substr($predicate, $position - 1, 1); + $attribute = substr($predicate, $position - 11, 11); + + // Check whether it's an attribute selection. + if ( ( $character == "@" ) || ( $attribute == "attribute::" ) ) + { + // Don't use the operator. + $operator = ""; + $position = -1; + } + } + + // Check whether an operator was found. + if ( $position > 0 ) + { + // Get the left and the right part of the expression. + $left = substr($predicate, 0, $position); + $right = substr($predicate, $position + strlen($operator)); + + // Remove whitespaces. + $left = trim($left); + $right = trim($right); + + // Evaluate the left and the right part. + $left = $this->evaluate_predicate($node, $left); + $right = $this->evaluate_predicate($node, $right); + + // Check the kind of operator. + switch ( $operator ) + { + case " or ": + // Return the two results connected by an "or". + return ( $left or $right ); + + case " and ": + // Return the two results connected by an "and". + return ( $left and $right ); + + case "=": + // Compare the two results. + return ( $left == $right ); + + case "!=": + // Check whether the two results are not equal. + return ( $left != $right ); + + case "<=": + // Compare the two results. + return ( $left <= $right ); + + case "<": + // Compare the two results. + return ( $left < $right ); + + case ">=": + // Compare the two results. + return ( $left >= $right ); + + case ">": + // Compare the two results. + return ( $left > $right ); + + case "+": + // Return the result by adding one result to the other. + return ( $left + $right ); + + case "-": + // Return the result by decrease one result by the other. + return ( $left - $right ); + + case "*": + // Return a multiplication of the two results. + return ( $left * $right ); + + case " div ": + // Return a division of the two results. + if ( $right == 0 ) + { + // Display an error message. + $this->display_error("While parsing an XPath ". + "predicate, a error due a division by zero ". + "occured."); + } + else + { + // Return the result of the division. + return ( $left / $right ); + } + break; + + case " mod ": + // Return a modulo of the two results. + return ( $left % $right ); + } + } + + // Check whether the predicate is a function. + if ( ereg("\(", $predicate) ) + { + // Get the position of the first bracket. + $start = strpos($predicate, "("); + $end = strpos($predicate, ")", $start); + + // Get everything before, between and after the brackets. + $before = substr($predicate, 0, $start); + $between = substr($predicate, $start + 1, $end - $start - 1); + $after = substr($predicate, $end + 1); + + // Trim each string. + $before = trim($before); + $between = trim($between); + $after = trim($after); + + // Check whether there's something after the bracket. + if ( !empty($after) ) + { + // Display an error message. + $this->display_error("While parsing an XPath expression ". + "there was found an error in the predicate \"%s\", ". + "because after a closing bracket there was found ". + "something unknown.", str_replace($predicate, + "".$predicate."", $this->xpath)); + } + + // Check whether it's a function. + if ( empty($before) && empty($after) ) + { + // Evaluate the content of the brackets. + return $this->evaluate_predicate($node, $between); + } + elseif ( $this->is_function($before) ) + { + // Return the evaluated function. + return $this->evaluate_function($before, $between, $node); + } + else + { + // Display an error message. + $this->display_error("While parsing a predicate in an XPath ". + "expression, a function \"%s\" was found, which is not ". + "yet supported by the parser.", str_replace($before, + "".$before."", $this->xpath)); + } + } + + // Check whether the predicate is just a digit. + if ( ereg("^[0-9]+(\.[0-9]+)?$", $predicate) || + ereg("^\.[0-9]+$", $predicate) ) + { + // Return the value of the digit. + return doubleval($predicate); + } + + // Check whether it's an XPath expression. + $result = $this->evaluate($predicate, $node); + if ( count($result) > 0 ) + { + // Convert the array. + $result = explode("|", implode("|", $result)); + + // Get the value of the first result. + $value = $this->get_content($result[0]); + + // Return the value. + return $value; + } + + // Return the predicate as a string. + return $predicate; + } + + /** + * Checks whether a node matches predicates. + * + * This method checks whether a list of nodes passed to this method match + * a given list of predicates. + * + * @access private + * @author Michael P. Mehl + * @param array $nodes Array of full paths of all nodes to be tested. + * @param array $predicates Array of predicates to use. + * @return array The array returned by this method contains a list of + * all nodes matching the given predicates. + * @see evaluate_step() + */ + function check_predicates ( $nodes, $predicates ) + { + // Create an empty set of nodes. + $result = array(); + + // Run through all nodes. + foreach ( $nodes as $node ) + { + // Create a variable whether to add this node to the node-set. + $add = true; + + // Run through all predicates. + foreach ( $predicates as $predicate ) + { + // Check whether the predicate is just an number. + if ( ereg("^[0-9]+$", $predicate) ) + { + // Enhance the predicate. + $predicate .= "=position()"; + } + + // Do the predicate check. + $check = $this->evaluate_predicate($node, $predicate); + + // Check whether it's a string. + if ( is_string($check) && ( ( $check == "" ) || + ( $check == $predicate ) ) ) + { + // Set the result to false. + $check = false; + } + + // Check whether it's an integer. + if ( is_int($check) ) + { + // Check whether it's the current position. + if ( $check == $this->handle_function_position($node, "") ) + { + // Set it to true. + $check = true; + } + else + { + // Set it to false. + $check = false; + } + } + + // Check whether the predicate is OK for this node. + $add = $add && $check; + } + + // Check whether to add this node to the node-set. + if ( $add ) + { + // Add the node to the node-set. + $result[] = $node; + } + } + + // Return the array of nodes. + return $result; + } + + /** + * Checks whether a node matches a node-test. + * + * This method checks whether a node in the document matches a given + * node-test. + * + * @access private + * @author Michael P. Mehl + * @param string $context Full path of the node, which should be tested + * for matching the node-test. + * @param string $node_test String containing the node-test for the + * node. + * @return boolean This method returns true if the node matches the + * node-test, otherwise false. + * @see evaluate() + */ + function check_node_test ( $context, $node_test ) + { + // Check whether it's a function. + if ( ereg("\(", $node_test) ) + { + // Get the type of function to use. + $function = $this->prestr($node_test, "("); + + // Check whether the node fits the method. + switch ( $function ) + { + case "node": + // Add this node to the list of nodes. + return true; + + case "text": + // Check whether the node has some text. + if ( !empty($this->nodes[$context]["text"]) ) + { + // Add this node to the list of nodes. + return true; + } + break; + + case "comment": + // Check whether the node has some comment. + if ( !empty($this->nodes[$context]["comment"]) ) + { + // Add this node to the list of nodes. + return true; + } + break; + + case "processing-instruction": + // Get the literal argument. + $literal = $this->afterstr($axis["node-test"], "("); + + // Cut the literal. + $literal = substr($literal, 0, strlen($literal) - 1); + + // Check whether a literal was given. + if ( !empty($literal) ) + { + // Check whether the node's processing instructions + // are matching the literals given. + if ( $this->nodes[$context] + ["processing-instructions"] == $literal ) + { + // Add this node to the node-set. + return true; + } + } + else + { + // Check whether the node has processing + // instructions. + if ( !empty($this->nodes[$context] + ["processing-instructions"]) ) + { + // Add this node to the node-set. + return true; + } + } + break; + + default: + // Display an error message. + $this->display_error("While parsing an XPath ". + "expression there was found an undefined ". + "function called \"%s\".", + str_replace($function, "".$function."", + $this->xpath)); + } + } + elseif ( $node_test == "*" ) + { + // Add this node to the node-set. + return true; + } + elseif ( ereg("^[a-zA-Z0-9\-_]+", $node_test) ) + { + // Check whether the node-test can be fulfilled. + if ( $this->nodes[$context]["name"] == $node_test ) + { + // Add this node to the node-set. + return true; + } + } + else + { + // Display an error message. + $this->display_error("While parsing the XPath expression \"%s\" ". + "an empty and therefore invalid node-test has been found.", + $this->xpath); + } + + // Don't add this context. + return false; + } + + /** + * Handles the XPath child axis. + * + * This method handles the XPath child axis. + * + * @access private + * @author Michael P. Mehl + * @param array $axis Array containing information about the axis. + * @param string $context Node from which starting the axis should + * be processed. + * @return array This method returns an array containing all nodes + * that were found during the evaluation of the given axis. + * @see evaluate() + */ + function handle_axis_child ( $axis, $context ) + { + // Create an empty node-set. + $nodes = array(); + + // Get a list of all children. + $children = $this->nodes[$context]["children"]; + + // Check whether there are children. + if ( !empty($children) ) + { + // Run through all children. + foreach ( $children as $child_name => $child_position ) + { + // Run through all childs with this name. + for ( $i = 1; $i <= $child_position; $i++ ) + { + // Create the path of the child. + $child = $context."/".$child_name."[".$i."]"; + + // Check whether + if ( $this->check_node_test($child, $axis["node-test"]) ) + { + // Add the child to the node-set. + $nodes[] = $child; + } + } + } + } + + // Return the nodeset. + return $nodes; + } + + /** + * Handles the XPath parent axis. + * + * This method handles the XPath parent axis. + * + * @access private + * @author Michael P. Mehl + * @param array $axis Array containing information about the axis. + * @param string $context Node from which starting the axis should + * be processed. + * @return array This method returns an array containing all nodes + * that were found during the evaluation of the given axis. + * @see evaluate() + */ + function handle_axis_parent ( $axis, $context ) + { + // Create an empty node-set. + $nodes = array(); + + // Check whether the parent matches the node-test. + if ( $this->check_node_test($this->nodes[$context]["parent"], + $axis["node-test"]) ) + { + // Add this node to the list of nodes. + $nodes[] = $this->nodes[$context]["parent"]; + } + + // Return the nodeset. + return $nodes; + } + + /** + * Handles the XPath attribute axis. + * + * This method handles the XPath attribute axis. + * + * @access private + * @author Michael P. Mehl + * @param array $axis Array containing information about the axis. + * @param string $context Node from which starting the axis should + * be processed. + * @return array This method returns an array containing all nodes + * that were found during the evaluation of the given axis. + * @see evaluate() + */ + function handle_axis_attribute ( $axis, $context ) + { + // Create an empty node-set. + $nodes = array(); + + // Check whether all nodes should be selected. + if ( $axis["node-test"] == "*" ) + { + // Check whether there are attributes. + if ( count($this->nodes[$context]["attributes"]) > 0 ) + { + // Run through the attributes. + foreach ( $this->nodes[$context]["attributes"] as + $key => $value ) + { + // Add this node to the node-set. + $nodes[] = $context."/attribute::".$key; + } + } + } + elseif ( !empty($this->nodes[$context]["attributes"] + [$axis["node-test"]]) ) + { + // Add this node to the node-set. + $nodes[] = $context."/attribute::".$axis["node-test"]; + } + + // Return the nodeset. + return $nodes; + } + + /** + * Handles the XPath self axis. + * + * This method handles the XPath self axis. + * + * @access private + * @author Michael P. Mehl + * @param array $axis Array containing information about the axis. + * @param string $context Node from which starting the axis should + * be processed. + * @return array This method returns an array containing all nodes + * that were found during the evaluation of the given axis. + * @see evaluate() + */ + function handle_axis_self ( $axis, $context ) + { + // Create an empty node-set. + $nodes = array(); + + // Check whether the context match the node-test. + if ( $this->check_node_test($context, $axis["node-test"]) ) + { + // Add this node to the node-set. + $nodes[] = $context; + } + + // Return the nodeset. + return $nodes; + } + + /** + * Handles the XPath descendant axis. + * + * This method handles the XPath descendant axis. + * + * @access private + * @author Michael P. Mehl + * @param array $axis Array containing information about the axis. + * @param string $context Node from which starting the axis should + * be processed. + * @return array This method returns an array containing all nodes + * that were found during the evaluation of the given axis. + * @see evaluate() + */ + function handle_axis_descendant ( $axis, $context ) + { + // Create an empty node-set. + $nodes = array(); + + // Check whether the current node has children. + if ( count($this->nodes[$context]["children"]) > 0 ) + { + // Get a list of children. + $children = $this->nodes[$context]["children"]; + + // Run through all children. + foreach ( $children as $child_name => $child_position ) + { + // Run through all children of this name. + for ( $i = 1; $i <= $child_position; $i++ ) + { + // Create the full path for the children. + $child = $context."/".$child_name."[".$i."]"; + + // Check whether the child matches the node-test. + if ( $this->check_node_test($child, $axis["node-test"]) ) + { + // Add the child to the list of nodes. + $nodes[] = $child; + } + + // Recurse to the next level. + $nodes = array_merge($nodes, + $this->handle_axis_descendant($axis, $child)); + } + } + } + + // Return the nodeset. + return $nodes; + } + + /** + * Handles the XPath ancestor axis. + * + * This method handles the XPath ancestor axis. + * + * @access private + * @author Michael P. Mehl + * @param array $axis Array containing information about the axis. + * @param string $context Node from which starting the axis should + * be processed. + * @return array This method returns an array containing all nodes + * that were found during the evaluation of the given axis. + * @see evaluate() + */ + function handle_axis_ancestor ( $axis, $context ) + { + // Create an empty node-set. + $nodes = array(); + + // Get the parent of the current node. + $parent = $this->nodes[$context]["parent"]; + + // Check whether the parent isn't empty. + if ( !empty($parent) ) + { + // Check whether the parent matches the node-test. + if ( $this->check_node_test($parent, $axis["node-test"]) ) + { + // Add the parent to the list of nodes. + $nodes[] = $parent; + } + + // Handle all other ancestors. + $nodes = array_merge($nodes, + $this->handle_axis_ancestor($axis, $parent)); + } + + // Return the nodeset. + return $nodes; + } + + /** + * Handles the XPath namespace axis. + * + * This method handles the XPath namespace axis. + * + * @access private + * @author Michael P. Mehl + * @param array $axis Array containing information about the axis. + * @param string $context Node from which starting the axis should + * be processed. + * @return array This method returns an array containing all nodes + * that were found during the evaluation of the given axis. + * @see evaluate() + */ + function handle_axis_namespace ( $axis, $context ) + { + // Create an empty node-set. + $nodes = array(); + + // Check whether all nodes should be selected. + if ( !empty($this->nodes[$context]["namespace"]) ) + { + // Add this node to the node-set. + $nodes[] = $context; + } + + // Return the nodeset. + return $nodes; + } + + /** + * Handles the XPath following axis. + * + * This method handles the XPath following axis. + * + * @access private + * @author Michael P. Mehl + * @param array $axis Array containing information about the axis. + * @param string $context Node from which starting the axis should + * be processed. + * @return array This method returns an array containing all nodes + * that were found during the evaluation of the given axis. + * @see evaluate() + */ + function handle_axis_following ( $axis, $context ) + { + // Create an empty node-set. + $nodes = array(); + + // Get the current document position. + $position = $this->nodes[$context]["document-position"]; + + // Create a flag, whether we already found the context node. + $found = false; + + // Run through all nodes of the document. + foreach ( $this->nodes as $node => $data ) + { + // Check whether the context node has already been found. + if ( $found ) + { + // Check whether the position is correct. + if ( $this->nodes[$node]["document-position"] == $position ) + { + // Check whether the node fits the node-test. + if ( $this->check_node_test($node, $axis["node-test"]) ) + { + // Add the node to the list of nodes. + $nodes[] = $node; + } + } + } + + // Check whether this is the context node. + if ( $node == $context ) + { + // After this we'll look for more nodes. + $found = true; + } + } + + // Return the nodeset. + return $nodes; + } + + /** + * Handles the XPath preceding axis. + * + * This method handles the XPath preceding axis. + * + * @access private + * @author Michael P. Mehl + * @param array $axis Array containing information about the axis. + * @param string $context Node from which starting the axis should + * be processed. + * @return array This method returns an array containing all nodes + * that were found during the evaluation of the given axis. + * @see evaluate() + */ + function handle_axis_preceding ( $axis, $context ) + { + // Create an empty node-set. + $nodes = array(); + + // Get the current document position. + $position = $this->nodes[$context]["document-position"]; + + // Create a flag, whether we already found the context node. + $found = true; + + // Run through all nodes of the document. + foreach ( $this->nodes as $node => $data ) + { + // Check whether this is the context node. + if ( $node == $context ) + { + // After this we won't look for more nodes. + $found = false; + } + + // Check whether the context node has already been found. + if ( $found ) + { + // Check whether the position is correct. + if ( $this->nodes[$node]["document-position"] == $position ) + { + // Check whether the node fits the node-test. + if ( $this->check_node_test($node, $axis["node-test"]) ) + { + // Add the node to the list of nodes. + $nodes[] = $node; + } + } + } + } + + // Return the nodeset. + return $nodes; + } + + /** + * Handles the XPath following-sibling axis. + * + * This method handles the XPath following-sibling axis. + * + * @access private + * @author Michael P. Mehl + * @param array $axis Array containing information about the axis. + * @param string $context Node from which starting the axis should + * be processed. + * @return array This method returns an array containing all nodes + * that were found during the evaluation of the given axis. + * @see evaluate() + */ + function handle_axis_following_sibling ( $axis, $context ) + { + // Create an empty node-set. + $nodes = array(); + + // Get all children from the parent. + $siblings = $this->handle_axis_child($axis, + $this->nodes[$context]["parent"]); + + // Create a flag whether the context node was already found. + $found = false; + + // Run through all siblings. + foreach ( $siblings as $sibling ) + { + // Check whether the context node was already found. + if ( $found ) + { + // Check whether the sibling is a real sibling. + if ( $this->nodes[$sibling]["name"] == + $this->nodes[$context]["name"] ) + { + // Check whether the sibling matches the node-test. + if ( $this->check_node_test($sibling, $axis["node-test"]) ) + { + // Add the sibling to the list of nodes. + $nodes[] = $sibling; + } + } + } + + // Check whether this is the context node. + if ( $sibling == $context ) + { + // Continue looking for other siblings. + $found = true; + } + } + + // Return the nodeset. + return $nodes; + } + + /** + * Handles the XPath preceding-sibling axis. + * + * This method handles the XPath preceding-sibling axis. + * + * @access private + * @author Michael P. Mehl + * @param array $axis Array containing information about the axis. + * @param string $context Node from which starting the axis should + * be processed. + * @return array This method returns an array containing all nodes + * that were found during the evaluation of the given axis. + * @see evaluate() + */ + function handle_axis_preceding_sibling ( $axis, $context ) + { + // Create an empty node-set. + $nodes = array(); + + // Get all children from the parent. + $siblings = $this->handle_axis_child($axis, + $this->nodes[$context]["parent"]); + + // Create a flag whether the context node was already found. + $found = true; + + // Run through all siblings. + foreach ( $siblings as $sibling ) + { + // Check whether this is the context node. + if ( $sibling == $context ) + { + // Don't continue looking for other siblings. + $found = false; + } + + // Check whether the context node was already found. + if ( $found ) + { + // Check whether the sibling is a real sibling. + if ( $this->nodes[$sibling]["name"] == + $this->nodes[$context]["name"] ) + { + // Check whether the sibling matches the node-test. + if ( $this->check_node_test($sibling, $axis["node-test"]) ) + { + // Add the sibling to the list of nodes. + $nodes[] = $sibling; + } + } + } + } + + // Return the nodeset. + return $nodes; + } + + /** + * Handles the XPath descendant-or-self axis. + * + * This method handles the XPath descendant-or-self axis. + * + * @access private + * @author Michael P. Mehl + * @param array $axis Array containing information about the axis. + * @param string $context Node from which starting the axis should + * be processed. + * @return array This method returns an array containing all nodes + * that were found during the evaluation of the given axis. + * @see evaluate() + */ + function handle_axis_descendant_or_self ( $axis, $context ) + { + // Create an empty node-set. + $nodes = array(); + + // Read the nodes. + $nodes = array_merge( + $this->handle_axis_descendant($axis, $context), + $this->handle_axis_self($axis, $context)); + + // Return the nodeset. + return $nodes; + } + + /** + * Handles the XPath ancestor-or-self axis. + * + * This method handles the XPath ancestor-or-self axis. + * + * @access private + * @author Michael P. Mehl + * @param array $axis Array containing information about the axis. + * @param string $context Node from which starting the axis should + * be processed. + * @return array This method returns an array containing all nodes + * that were found during the evaluation of the given axis. + * @see evaluate() + */ + function handle_axis_ancestor_or_self ( $axis, $context ) + { + // Create an empty node-set. + $nodes = array(); + + // Read the nodes. + $nodes = array_merge( + $this->handle_axis_ancestor($axis, $context), + $this->handle_axis_self($axis, $context)); + + // Return the nodeset. + return $nodes; + } + + /** + * Handles the XPath function last. + * + * This method handles the XPath function last. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_last ( $node, $arguments ) + { + // Calculate the size of the context. + $parent = $this->nodes[$node]["parent"]; + $children = $this->nodes[$parent]["children"]; + $context = $children[$this->nodes[$node]["name"]]; + + // Return the size. + return $context; + } + + /** + * Handles the XPath function position. + * + * This method handles the XPath function position. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_position ( $node, $arguments ) + { + // return the context-position. + return $this->nodes[$node]["context-position"]; + } + + /** + * Handles the XPath function count. + * + * This method handles the XPath function count. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_count ( $node, $arguments ) + { + // Evaluate the argument of the method as an XPath and return + // the number of results. + return count($this->evaluate($arguments, $node)); + } + + /** + * Handles the XPath function id. + * + * This method handles the XPath function id. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_id ( $node, $arguments ) + { + // Trim the arguments. + $arguments = trim($arguments); + + // Now split the arguments. + $arguments = explode(" ", $arguments); + + // Check whether + + // Create a list of nodes. + $nodes = array(); + + // Run through all document node. + foreach ( $this->nodes as $node => $position ) + { + // Check whether the node has the ID we're looking for. + if ( in_array($this->nodes[$node]["attributes"]["id"], + $arguments) ) + { + // Add this node to the list of nodes. + $nodes[] = $node; + } + } + + // Return the list of nodes. + return $nodes; + } + + /** + * Handles the XPath function name. + * + * This method handles the XPath function name. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_name ( $node, $arguments ) + { + // Return the name of the node. + return $this->nodes[$node]["name"]; + } + + /** + * Handles the XPath function string. + * + * This method handles the XPath function string. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_string ( $node, $arguments ) + { + // Check what type of parameter is given + if ( ereg("^[0-9]+(\.[0-9]+)?$", $arguments) || + ereg("^\.[0-9]+$", $arguments) ) + { + // Convert the digits to a number. + $number = doubleval($arguments); + + // Return the number. + return strval($number); + } + elseif ( is_bool($arguments) ) + { + // Check whether it's true. + if ( $arguments == true ) + { + // Return true as a string. + return "true"; + } + else + { + // Return false as a string. + return "false"; + } + } + elseif ( !empty($arguments) ) + { + // Use the argument as an XPath. + $result = $this->evaluate($arguments, $node); + + // Get the first argument. + $result = explode("|", implode("|", $result)); + + // Return the first result as a string. + return $result[0]; + } + elseif ( empty($arguments) ) + { + // Return the current node. + return $node; + } + else + { + // Return an empty string. + return ""; + } + } + + /** + * Handles the XPath function concat. + * + * This method handles the XPath function concat. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_concat ( $node, $arguments ) + { + // Split the arguments. + $arguments = explode(",", $arguments); + + // Run through each argument and evaluate it. + for ( $i = 0; $i < sizeof($arguments); $i++ ) + { + // Trim each argument. + $arguments[$i] = trim($arguments[$i]); + + // Evaluate it. + $arguments[$i] = $this->evaluate_predicate($node, $arguments[$i]); + } + + // Put the string together. + $arguments = implode("", $arguments); + + // Return the string. + return $arguments; + } + + /** + * Handles the XPath function starts-with. + * + * This method handles the XPath function starts-with. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_starts_with ( $node, $arguments ) + { + // Get the arguments. + $first = trim($this->prestr($arguments, ",")); + $second = trim($this->afterstr($arguments, ",")); + + // Evaluate each argument. + $first = $this->evaluate_predicate($node, $first); + $second = $this->evaluate_predicate($node, $second); + + // Check whether the first string starts with the second one. + if ( ereg("^".$second, $first) ) + { + // Return true. + return true; + } + else + { + // Return false. + return false; + } + } + + /** + * Handles the XPath function contains. + * + * This method handles the XPath function contains. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_contains ( $node, $arguments ) + { + // Get the arguments. + $first = trim($this->prestr($arguments, ",")); + $second = trim($this->afterstr($arguments, ",")); + + // Evaluate each argument. + $first = $this->evaluate_predicate($node, $first); + $second = $this->evaluate_predicate($node, $second); + + // Check whether the first string starts with the second one. + if ( ereg($second, $first) ) + { + // Return true. + return true; + } + else + { + // Return false. + return false; + } + } + + /** + * Handles the XPath function substring-before. + * + * This method handles the XPath function substring-before. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_substring_before ( $node, $arguments ) + { + // Get the arguments. + $first = trim($this->prestr($arguments, ",")); + $second = trim($this->afterstr($arguments, ",")); + + // Evaluate each argument. + $first = $this->evaluate_predicate($node, $first); + $second = $this->evaluate_predicate($node, $second); + + // Return the substring. + return $this->prestr(strval($first), strval($second)); + } + + /** + * Handles the XPath function substring-after. + * + * This method handles the XPath function substring-after. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_substring_after ( $node, $arguments ) + { + // Get the arguments. + $first = trim($this->prestr($arguments, ",")); + $second = trim($this->afterstr($arguments, ",")); + + // Evaluate each argument. + $first = $this->evaluate_predicate($node, $first); + $second = $this->evaluate_predicate($node, $second); + + // Return the substring. + return $this->afterstr(strval($first), strval($second)); + } + + /** + * Handles the XPath function substring. + * + * This method handles the XPath function substring. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_substring ( $node, $arguments ) + { + // Split the arguments. + $arguments = explode(",", $arguments); + + // Run through all arguments. + for ( $i = 0; $i < sizeof($arguments); $i++ ) + { + // Trim the string. + $arguments[$i] = trim($arguments[$i]); + + // Evaluate each argument. + $arguments[$i] = $this->evaluate_predicate($node, $arguments[$i]); + } + + // Check whether a third argument was given. + if ( !empty($arguments[2]) ) + { + // Return the substring. + return substr(strval($arguments[0]), $arguments[1] - 1, + $arguments[2]); + } + else + { + // Return the substring. + return substr(strval($arguments[0]), $arguments[1] - 1); + } + } + + /** + * Handles the XPath function string-length. + * + * This method handles the XPath function string-length. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_string_length ( $node, $arguments ) + { + // Trim the argument. + $arguments = trim($arguments); + + // Evaluate the argument. + $arguments = $this->evaluate_predicate($node, $arguments); + + // Return the length of the string. + return strlen(strval($arguments)); + } + + /** + * Handles the XPath function translate. + * + * This method handles the XPath function translate. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_translate ( $node, $arguments ) + { + // Split the arguments. + $arguments = explode(",", $arguments); + + // Run through all arguments. + for ( $i = 0; $i < sizeof($arguments); $i++ ) + { + // Trim the argument. + $arguments[$i] = trim($arguments[$i]); + + // Evaluate the argument. + $arguments[$i] = $this->evaluate_predicate($node, $arguments[$i]); + } + + // Return the translated string. + return strtr($arguments[0], $arguments[1], $arguments[2]); + } + + /** + * Handles the XPath function boolean. + * + * This method handles the XPath function boolean. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_boolean ( $node, $arguments ) + { + // Trim the arguments. + $arguments = trim($arguments); + + // Check what type of parameter is given + if ( ereg("^[0-9]+(\.[0-9]+)?$", $arguments) || + ereg("^\.[0-9]+$", $arguments) ) + { + // Convert the digits to a number. + $number = doubleval($arguments); + + // Check whether the number zero. + if ( $number == 0 ) + { + // Return false. + return false; + } + else + { + // Return true. + return true; + } + } + elseif ( empty($arguments) ) + { + // Sorry, there were no arguments. + return false; + } + else + { + // Try to evaluate the argument as an XPath. + $result = $this->evaluate($arguments, $node); + + // Check whether we found something. + if ( count($result) > 0 ) + { + // Return true. + return true; + } + else + { + // Return false. + return false; + } + } + } + + /** + * Handles the XPath function not. + * + * This method handles the XPath function not. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_not ( $node, $arguments ) + { + // Trim the arguments. + $arguments = trim($arguments); + + // Return the negative value of the content of the brackets. + return !$this->evaluate_predicate($node, $arguments); + } + + /** + * Handles the XPath function true. + * + * This method handles the XPath function true. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_true ( $node, $arguments ) + { + // Return true. + return true; + } + + /** + * Handles the XPath function false. + * + * This method handles the XPath function false. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_false ( $node, $arguments ) + { + // Return false. + return false; + } + + /** + * Handles the XPath function lang. + * + * This method handles the XPath function lang. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_lang ( $node, $arguments ) + { + // Trim the arguments. + $arguments = trim($arguments); + + // Check whether the node has an language attribute. + if ( empty($this->nodes[$node]["attributes"]["xml:lang"]) ) + { + // Run through the ancestors. + while ( !empty($node) ) + { + // Select the parent node. + $node = $this->nodes[$node]["parent"]; + + // Check whether there's a language definition. + if ( !empty($this->nodes[$node]["attributes"]["xml:lang"]) ) + { + // Check whether it's the language, the user asks for. + if ( eregi("^".$arguments, $this->nodes[$node] + ["attributes"]["xml:lang"]) ) + { + // Return true. + return true; + } + else + { + // Return false. + return false; + } + } + } + + // Return false. + return false; + } + else + { + // Check whether it's the language, the user asks for. + if ( eregi("^".$arguments, $this->nodes[$node]["attributes"] + ["xml:lang"]) ) + { + // Return true. + return true; + } + else + { + // Return false. + return false; + } + } + } + + /** + * Handles the XPath function number. + * + * This method handles the XPath function number. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_number ( $node, $arguments ) + { + // Check the type of argument. + if ( ereg("^[0-9]+(\.[0-9]+)?$", $arguments) || + ereg("^\.[0-9]+$", $arguments) ) + { + // Return the argument as a number. + return doubleval($arguments); + } + elseif ( is_bool($arguments) ) + { + // Check whether it's true. + if ( $arguments == true ) + { + // Return 1. + return 1; + } + else + { + // Return 0. + return 0; + } + } + } + + /** + * Handles the XPath function sum. + * + * This method handles the XPath function sum. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_sum ( $node, $arguments ) + { + // Trim the arguments. + $arguments = trim($arguments); + + // Evaluate the arguments as an XPath expression. + $results = $this->evaluate($arguments, $node); + + // Create a variable to save the sum. + $sum = 0; + + // Run through all results. + foreach ( $results as $result ) + { + // Get the value of the node. + $result = $this->get_content($result); + + // Add it to the sum. + $sum += doubleval($result); + } + + // Return the sum. + return $sum; + } + + /** + * Handles the XPath function floor. + * + * This method handles the XPath function floor. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_floor ( $node, $arguments ) + { + // Trim the arguments. + $arguments = trim($arguments); + + // Convert the arguments to a number. + $arguments = doubleval($arguments); + + // Return the result + return floor($arguments); + } + + /** + * Handles the XPath function ceiling. + * + * This method handles the XPath function ceiling. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_ceiling ( $node, $arguments ) + { + // Trim the arguments. + $arguments = trim($arguments); + + // Convert the arguments to a number. + $arguments = doubleval($arguments); + + // Return the result + return ceil($arguments); + } + + /** + * Handles the XPath function round. + * + * This method handles the XPath function round. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_round ( $node, $arguments ) + { + // Trim the arguments. + $arguments = trim($arguments); + + // Convert the arguments to a number. + $arguments = doubleval($arguments); + + // Return the result + return round($arguments); + } + + /** + * Handles the XPath function text. + * + * This method handles the XPath function text. + * + * @access private + * @author Michael P. Mehl + * @param string $node Full path of the node on which the function + * should be processed. + * @param string $arguments String containing the arguments that were + * passed to the function. + * @return mixed Depending on the type of function being processed this + * method returns different types. + * @see evaluate() + */ + function handle_function_text ( $node, $arguments ) + { + // Return the character data of the node. + return $this->nodes[$node]["text"]; + } + + /** + * Retrieves a substring before a delimiter. + * + * This method retrieves everything from a string before a given delimiter, + * not including the delimiter. + * + * @access private + * @author Michael P. Mehl + * @param string $string String, from which the substring should be + * extracted. + * @param string $delimiter String containing the delimiter to use. + * @return string Substring from the original string before the + * delimiter. + * @see afterstr() + */ + function prestr ( $string, $delimiter ) + { + // Return the substring. + return substr($string, 0, strlen($string) - strlen(strstr($string, + "$delimiter"))); + } + + /** + * Retrieves a substring after a delimiter. + * + * This method retrieves everything from a string after a given delimiter, + * not including the delimiter. + * + * @access private + * @author Michael P. Mehl + * @param string $string String, from which the substring should be + * extracted. + * @param string $delimiter String containing the delimiter to use. + * @return string Substring from the original string after the + * delimiter. + * @see prestr() + */ + function afterstr ( $string, $delimiter ) + { + // Return the substring. + return substr($string, + strpos($string, $delimiter) + strlen($delimiter)); + } + + /** + * Displays an error message. + * + * This method displays an error messages and stops the execution of the + * script. This method is called exactly in the same way as the printf + * function. The first argument contains the message and additional + * arguments of various types may be passed to this method to be inserted + * into the message. + * + * @access private + * @author Michael P. Mehl + * @param string $message Error message to be displayed. + */ + function display_error ( $message ) + { + // Check whether more than one argument was given. + if ( func_num_args() > 1 ) + { + // Read all arguments. + $arguments = func_get_args(); + + // Create a new string for the inserting command. + $command = "\$message = sprintf(\$message, "; + + // Run through the array of arguments. + for ( $i = 1; $i < sizeof($arguments); $i++ ) + { + // Add the number of the argument to the command. + $command .= "\$arguments[".$i."], "; + } + + // Replace the last separator. + $command = eregi_replace(", $", ");", $command); + + // Execute the command. + eval($command); + } + + // Display the error message. + echo "phpXML error: ".$message; + + // End the execution of this script. + exit; + } +} + +?> \ No newline at end of file