mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-30 11:53:57 +01:00
3384 lines
117 KiB
PHP
3384 lines
117 KiB
PHP
<?php
|
|
|
|
//
|
|
// +----------------------------------------------------------------------+
|
|
// | <phpXML/> 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 <phpXML/>. |
|
|
// | |
|
|
// | 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 <mpm@phpxml.org> |
|
|
// +----------------------------------------------------------------------+
|
|
//
|
|
|
|
/**
|
|
* 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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @param array $highlight Array containing a list of full document
|
|
* paths of nodes to be highlighted by <font>...</font> 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 = "<font color=\"#FF0000\"><b>";
|
|
$highlight_end = "</b></font>";
|
|
|
|
// 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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 ( preg_match('/'."\\/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 <mpm@phpxml.org>
|
|
* @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 ( preg_match('/'."\\/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 <mpm@phpxml.org>
|
|
* @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 ( preg_match('/'."\\/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 <mpm@phpxml.org>
|
|
* @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 ( preg_match('/'."\\/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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 ( preg_match('/'."\[".'/', $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 ( preg_match('/'."\(".'/', $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 ( preg_match('/'."^@".'/i', $step) )
|
|
{
|
|
// Use the attribute axis and select the attribute.
|
|
$axis["axis"] = "attribute";
|
|
$axis["node-test"] = substr($step, 1);
|
|
}
|
|
elseif ( preg_match('/'."\]$".'/i', $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 ( preg_match('/'."^[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, "<b>".$step."</b>", $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 <mpm@phpxml.org>
|
|
* @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, "<b>".$term."</b>", $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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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_func(array( &$this,$method), $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 <mpm@phpxml.org>
|
|
* @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_func(array( &$this,$method), $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 <mpm@phpxml.org>
|
|
* @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 ( preg_match('/'."-".'/', $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 ( preg_match('/'."\(".'/', $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,
|
|
"<b>".$predicate."</b>", $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,
|
|
"<b>".$before."</b>", $this->xpath));
|
|
}
|
|
}
|
|
|
|
// Check whether the predicate is just a digit.
|
|
if ( preg_match('/'."^[0-9]+(\.[0-9]+)?$".'/', $predicate) ||
|
|
preg_match('/'."^\.[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 <mpm@phpxml.org>
|
|
* @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 ( preg_match('/'."^[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 <mpm@phpxml.org>
|
|
* @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 ( preg_match('/'."\(".'/', $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, "<b>".$function."</b>",
|
|
$this->xpath));
|
|
}
|
|
}
|
|
elseif ( $node_test == "*" )
|
|
{
|
|
// Add this node to the node-set.
|
|
return true;
|
|
}
|
|
elseif ( preg_match('/'."^[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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 ( preg_match('/'."^[0-9]+(\.[0-9]+)?$".'/', $arguments) ||
|
|
preg_match('/'."^\.[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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 ( preg_match('/'."^".$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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 ( preg_match('/'."^[0-9]+(\.[0-9]+)?$".'/', $arguments) ||
|
|
preg_match('/'."^\.[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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 ( preg_match('/'."^[0-9]+(\.[0-9]+)?$".'/', $arguments) ||
|
|
preg_match('/'."^\.[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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 <mpm@phpxml.org>
|
|
* @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 = preg_replace('/'.", $".'/i', ");", $command);
|
|
|
|
// Execute the command.
|
|
eval($command);
|
|
}
|
|
|
|
// Display the error message.
|
|
echo "<b>phpXML error:</b> ".$message;
|
|
|
|
// End the execution of this script.
|
|
exit;
|
|
}
|
|
}
|
|
|
|
?>
|