现在的位置: 首页 > 综合 > 正文

phpXML类

2013年09月05日 ⁄ 综合 ⁄ 共 38347字 ⁄ 字号 评论关闭

同事翻译的phpXML类.

<?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
{
    /**
    * 文档的所有结点列表.
    *
    * 此数组包含了保存为关联数组的文档的所有结点列表
    *
    * @access private
    * @var    array
    */
    var $nodes = array();
   
    /**
    * 文档的结点ID列表.
    *
    * 此数组包含了文档中所有结点的IDs列表,其在用来在添加新结点时计数
    *
    * @access private
    * @var    array
    */
    var $ids = array();
   
    /**
    * 当前文档路径.
    *
    * 在分析XML文件和添加从文件中读取的结点时,此变量用于保存当前的路径
    *
    * @access private
    * @var    string
    */
    var $path = "";
   
    /**
    * 当前文档位置.
    *
    * 在分析XML文件和添加从文件中读取的结点时,此变量用于对当前文档位置计数
    *
    * @access private
    * @var    int
    */
    var $position = 0;
   
    /**
    * 文档根结点的路径.
    *
 * 此字符串包含了整个文档的根结点的完整路径
    *
 * @access private
    * @var    string
    */
 var $root = "";
   
    /**
    * 当前的 XPath 表达式.
    *
    * 此字符串包含了正在被解析的完整XPath表达式
    *
    * @access private
    * @var    string
    */
    var $xpath    = "";
                                                                               
    /**
    * 被转换的实体列表.
    *
    * 此数组包含了在进行XPath表达式运算时,被用来进行转换的一个实体列表
    *
    * @access private
    * @var    array
    */
    var $entities = array ( "&" => "&amp;", "<" => "&lt;", ">" => "&gt;",
        "'" => "&apos;", '"' => "&quot;" );
   
    /**
    * 支持的 XPath 轴列表.
    *
    * 此数组包含了可以被用在XPath表达式运算中的所有有效的轴列表
    *
    * @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" );
   
    /**
    * 支持的 XPath 函数列表.
    *
    * 此数组包含了可以被用在XPath表达式运算中的所有有效的函数列表
    *
    * @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" );
   
    /**
    * 支持的 XPath 运算符号列表.
    *
    * 此数组包含了可以被用在XPath表达式运算中的所有有效的运算符号列表.
 * 此列表按照运算符号的运算级别进行排序(最低的排在最前面,按由低到高排序)
    *
    * @access private
    * @var    array
    */
    var $operators = array( " or ", " and ", "=", "!=", "<=", "<", ">=", ">",
        "+", "-", "*", " div ", " mod " );

    /**
    * 构造函数.
    *
    * 此构造函数初始化类,当指定文件名时,读取和解析指定文件
    *
    * @access    public
    * @author    Michael P. Mehl <mpm@phpxml.org>
    * @param     string $file 指定的被读取和解析的文件路径名.
    * @see       load_file()
    */
 function XML ( $file = "" )
    {
        // 测试是否提供文件名
  if ( !empty($file) )
        {
            // 装载XML文件
   $this->load_file($file);
        }
 }

 /**
 * 解析 XML 数据.
 *
 * 此方法对提供的XML数据解析其内容,并且一旦成功就将检索出的信息保存到数组中.
 *
 * @access    public
 * @author    lzlhero <lzlhero_at_163.com>
 * @param     string $content 指定的被解析的XML数据.
 * @see       handle_start_element(), handle_end_element(),
 *            handle_character_data()
 */
 function load_xml ( $content )
 {
  // 测试内容是否为空
  if ( !empty($content) )
  {
   // 创建 XML 解析器
   $parser = xml_parser_create();

   // 设置解析 XML 数据的选项
   xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
   xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);

   // 设置用来解析的对象
   xml_set_object($parser, &$this);

   // 设置用来解析的元素句柄
   xml_set_element_handler($parser, "handle_start_element",
    "handle_end_element");
   xml_set_character_data_handler($parser,
    "handle_character_data");

   // 解析 XML 文件
   if ( !xml_parse($parser, $content, true) )
   {
    // 显示错误信息
    $this->display_error("XML 文件错误 %s, 第 %d 行: %s, 这是由于您的文件格式不对造成的. /n请重新选择导入文件!",
     $file, xml_get_current_line_number($parser),
     xml_error_string(xml_get_error_code($parser)));
   }

   // 释放解析器
   xml_parser_free($parser);
  }
 }

 /**
 * 读文件并且解析 XML 数据.
 *
 * 此方法读取 XML 文件内容,尝试解析其内容,并且一旦成功就将其从文件中检索出
 * 的信息保存到数组中.
 *
 * @access    public
 * @author    Michael P. Mehl <mpm@phpxml.org>
 * @param     string $file 指定的被读取和解析的文件路径名.
 * @see       load_xml()
 */
 function load_file ( $file )
 {
  // 测试文件是否存在并且是否可读
  if ( file_exists($file) && is_readable($file) )
  {
   // 读取文件的内容.
   // $content = implode("", file($file));
   $content = file_get_contents($file);
   $this->load_xml($content);
  }
  else
  {
   // 显示文件找不到错误
   $this->display_error("File %s could not be found or read.", $file);
  }
 }

 /**
 * 从当前文档的内容生成 XML 格式的字符串.
 *
 * 此方法用于将当前的XML数据以XML格式输入,不提供XML头标识和编码方式,
 * 在使用时,可以根据你的需要设置XML头标识和对当前方法的返回值进行编码
 *
 * @access    public
 * @author    lzlhero <lzlhero_at_163.com>
 * @param     string $root 当此方法对自身进行递归调用时,此参数被用于传送中间量(当前根结点)
 * @param     int $level 当此方法对自身进行递归调用时,此参数被用于传送中间量(当前缩进级别)
 * @return    string 此返回值为字符串,其中包含当前XML文档的格式化良好数据
 * @see       load_file(), get_file(), evaluate(), get_content()
 */
 function get_xml ( $root="" , $level = 0 )
 {
  // 测试是否提供了根结点.
  if ( empty($root) )
  {
   // 设置成文档的根结点.
   $root = $this->root;
  }

  // 创建标识前面的缩进字符串.
  $before = "";

  // 计算缩进字符串长.
  for ( $i = 0; $i < ( $level * 1 ); $i++ )
  {
   // 添加空格到字符串.
   $before .= "/t";
  }

  // 建立字符串用来保存生成的XML数据.
  // 现在打开根标识.
  $xml = $before."<".$this->nodes[$root]["name"];

  // 测试根结点下是否有属性.
  if ( count($this->nodes[$root]["attributes"]) > 0 )
  {
   // 循环所有的属性.
   foreach ( $this->nodes[$root]["attributes"] as $key => $value )
   {
    // 添加属性到XML数据中.
    $xml .= " ".$key."=/"".trim(stripslashes($value))."/"";
   }
  }

  // 测试根结点下是否含有文本内容或子结点.
  if ( empty($this->nodes[$root]["text"]) &&
   !isset($this->nodes[$root]["children"]) )
  {
   // 添加结束标识.
   $xml .= "/";
  }

  // 关闭标识.
  $xml .= ">/n";

  // 测试根结点是否含有文本内容.
  if ( !empty($this->nodes[$root]["text"]) )
  {
   // 添加文本内容到XML数据中.
   $xml .= $before."/t".$this->nodes[$root]["text"]."/n";
  }

  // 测试根结点下是否含有子结点.
  if ( isset($this->nodes[$root]["children"]) )
  {
   // 循环所有不同名称的子结点.
   foreach ( $this->nodes[$root]["children"] as $child => $pos )
   {
    // 循环具有相同名称,但索引不同的子结点.
    for ( $i = 1; $i <= $pos; $i++ )
    {
     // 生成子结点的完整路径.
     $fullchild = $root."/".$child."[".$i."]";

     // 将子结点的 XML 数据添加到当前 XML 串中.
     $xml .= $this->get_xml($fullchild , $level + 1);
    }
   }
  }

  // 测试根结点下是否含有文本内容或子结点.
  if ( !empty($this->nodes[$root]["text"]) ||
   isset($this->nodes[$root]["children"]) )
  {
   // 添加缩进量到 XML 数据中.
   $xml .= $before;

   // 添加关闭标识.
   $xml .= "</".$this->nodes[$root]["name"].">";

   // 添加一个回车.
   $xml .= "/n";
  }

  // 返回 XML 数据.
  return $xml;
 }

 /**
 * 从当前文档的内容生成 XML 文件.
 *
 * 此方法返回一个字符串,此字符串包含被当前类读取和修改后的XML数据,并且此XML数据是
 * HTML格式的数据,可以通过输入参数将此HTML格式化.
 * 可以用此字符串可以将修改后的文档保存到文件中,或用于其它的用途
 *
 * @access    public
 * @author    Michael P. Mehl <mpm@phpxml.org>
 * @param     array $highlight 此数组包含一个位于整个文档中的路径结点列表,
 *            其将在生成后的XML数据中被<font>...</font>这样的内容标识为高亮显示.
 * @param     string $root 当此方法对自身进行递归调用时,此参数被用于传送中间量(当前根结点)
 * @param     int $level 当此方法对自身进行递归调用时,此参数被用于传送中间量(当前缩进级别)
 * @return    string 此返回值为字符串,其中包含当前XML文档的格式化良好数据
 * @see       load_file(), get_xml(), evaluate(), get_content()
 */
 function get_file ( $highlight = array(), $root = "", $level = 0 )
 {
  // 建立一个字符串用来保存生成的XML数据.
  $xml = "";

  // 建立两个字符串保存高亮显示的标识.
  $highlight_start = "<font color=/"#FF0000/"><b>";
  $highlight_end   = "</b></font>";

  // 创建标识前面的缩进字符串.
  $before = "";

  // 计算缩进字符串长.
  for ( $i = 0; $i < ( $level * 2 ); $i++ )
  {
   // 添加空格到字符串.
   $before .= " ";
  }

  // 测试是否提供了根结点.
  if ( empty($root) )
  {
   // 设置成文档的根结点.
   $root = $this->root;
  }

  // 测试当前根结点是否被选中了.
  $selected = in_array($root, $highlight);

  // 添加空白缩进到XML数据中.
  $xml .= $before;

  // 测试当前根结点是否被选中了.
  if ( $selected )
  {
   // 添加高亮代码到XML数据中.
   $xml .= $highlight_start;
  }

  // 现在打开根标识.
  $xml .= "&lt;".$this->nodes[$root]["name"];

  // 测试根结点下是否有属性.
  if ( count($this->nodes[$root]["attributes"]) > 0 )
  {
   // 循环所有的属性.
   foreach ( $this->nodes[$root]["attributes"] as $key => $value )
   {
    // 测试当前属性是否被设成高亮显示.
    if ( in_array($root."/attribute::".$key, $highlight) )
    {
     // 添加高亮代码到XML数据中.
                    $xml .= $highlight_start;
    }
               
    // 添加属性到XML数据中.
                $xml .= " ".$key."=/"".trim(stripslashes($value))."/"";

    // 测试当前属性是否被设成高亮显示.
    if ( in_array($root."/attribute::".$key, $highlight) )
    {
     // 添加高亮代码到XML数据中.
     $xml .= $highlight_end;
    }
   }
  }

  // 测试根结点下是否含有文本内容或子结点.
  if ( empty($this->nodes[$root]["text"]) &&
            !isset($this->nodes[$root]["children"]) )
  {
   // 添加结束标识.
   $xml .= "/";
        }

  // 关闭标识.
  $xml .= "&gt;/n";

  // 测试当前根结点是否被选中了.
  if ( $selected )
  {
   // 添加高亮代码到XML数据中.
   $xml .= $highlight_end;
  }

  // 测试根结点是否含有文本内容.
  if ( !empty($this->nodes[$root]["text"]) )
  {
   // 添加文本内容到XML数据中.
   $xml .= $before."  ".$this->nodes[$root]["text"]."/n";
  }

  // 测试根结点下是否含有子结点.
  if ( isset($this->nodes[$root]["children"]) )
  {
   // 循环所有不同名称的子结点.
   foreach ( $this->nodes[$root]["children"] as $child => $pos )
   {
    // 循环具有相同名称,但索引不同的子结点.
    for ( $i = 1; $i <= $pos; $i++ )
    {
     // 生成子结点的完整路径.
     $fullchild = $root."/".$child."[".$i."]";

     // 将子结点的 XML 数据添加到当前 XML 串中.
     $xml .= $this->get_file($highlight, $fullchild,
      $level + 1);
    }
   }
  }

  // 测试根结点下是否含有文本内容或子结点.
  if ( !empty($this->nodes[$root]["text"]) ||
   isset($this->nodes[$root]["children"]) )
  {
   // 添加缩进量到 XML 数据中.
   $xml .= $before;
           
   // 测试当前根结点是否被选中了.
   if ( $selected )
   {
    // 添加高亮代码到XML数据中.
    $xml .= $highlight_start;
   }

   // 添加关闭标识.
   $xml .= "&lt;/".$this->nodes[$root]["name"]."&gt;";

   // 测试当前根结点是否被选中了.
   if ( $selected )
   {
    // 添加高亮代码到XML数据中.
    $xml .= $highlight_end;
   }

   // 添加一个回车.
   $xml .= "/n";
  }

  // 返回 XML 数据.
  return $xml;
 }
   
    /**
    * 在 XML 文档中添加新结点.
    *
    * 此方法添加一个新结点到当前XML文档的树结点中,此新结点通过输入参数进入建立
    *
    * @access    public
    * @author    Michael P. Mehl <mpm@phpxml.org>
    * @param     string $content 父结点的完整路径,其指定了作为子的新结点被添加何处
    * @param     string $name 新结点的命名
    * @return    string 此返回值为字符串,其包含了被建立新结点的完整路径
    * @see       remove_node(), evaluate()
    */
 function add_node ( $context, $name )
    {
  // 测试当前XML数据的根是否已经存在
  if ( empty($this->root) )
        {
   // 将此标识作为根元素使用
   $this->root = "/".$name."[1]";
  }
       
  // 设置此元素的相对位置到ids数组中
  $path = $context."/".$name;
  $position = ++$this->ids[$path];

  // 计算完整路径
  $relative = $name."[".$position."]";
  $fullpath = $context."/".$relative;
       
  // 计算在父结点下和此元素具有相同命名的元素的上下文位置
  $this->nodes[$fullpath]["context-position"] = $position;

  // 计算用于 following 和 preceding 轴测试的位置
  $this->nodes[$fullpath]["document-position"] =
            $this->nodes[$context]["document-position"] + 1;
       
  // 保存此添加结点的有关信息
  $this->nodes[$fullpath]["name"]   = $name;
  $this->nodes[$fullpath]["text"]   = "";
  $this->nodes[$fullpath]["parent"] = $context;

  // 添加此元素到父元素的子元素数组中,并且进行累加
  if ( !$this->nodes[$context]["children"][$name] )
  {
   // 设置默认名称
   $this->nodes[$context]["children"][$name] = 1;
        }
        else
        {
   // 计算名称
            $this->nodes[$context]["children"][$name] =
    $this->nodes[$context]["children"][$name] + 1;
        }
       
  // 返回新结点的完整路径
  return $fullpath;
    }

    /**
    * 从XML文档中删除一个结点.
    *
    * 此方法从XML文档的树结点中删除一个结点.如果此结点是文档结点,其下的所有子结点和字符
 * 数据将被删除.如果此结点是属性结点,只有此属性将被删除,作为此属性所在结点的子结点将不会
 * 被修改.
    *
    * @access    public
    * @author    Michael P. Mehl <mpm@phpxml.org>
    * @param     string $node 指定的被删除结点的完整路径.
    * @see       add_node(), evaluate()
    */
    function remove_node ( $node )
    {
  // 测试此结点是否为属性结点
        if ( ereg("/attribute::", $node) )
        {
            // Get the path to the attribute node's parent.
            $parent = $this->prestr($node, "/attribute::");
           
            // Get the name of the attribute.
            $attribute = $this->afterstr($node, "/attribute::");
           
            // Check whether the attribute exists.
            if ( isset($this->nodes[$parent]["attributes"][$attribute]) )
            {
                // Create a new array.
                $new = array();
               
                // Run through the existing attributes.
                foreach ( $this->nodes[$parent]["attributes"]
                    as $key => $value )
                {
                    // Check whether it's the attribute to remove.
                    if ( $key != $attribute )
                    {
                        // Add it to the new array again.
                        $new[$key] = $value;
                    }
                }
               
                // Save the new attributes.
                $this->nodes[$parent]["attributes"] = $new;
            }
        }
        else
        {
            // Create an associative array, which contains information about
            // all nodes that required to be renamed.
            $rename = array();
           
            // Get the name, the parent and the siblings of current node.
            $name     = $this->nodes[$node]["name"];
            $parent   = $this->nodes[$node]["parent"];
            $siblings = $this->nodes[$parent]["children"][$name];
           
            // Decrease the number of children.
            $this->nodes[$parent]["children"][$name]--;
           
            // Create a counter for renumbering the siblings.
            $counter = 1;
           
            // Now run through the siblings.
            for ( $i = 1; $i <= $siblings; $i++ )
            {
                // Create the name of the sibling.
                $sibling = $parent."/".$name."[".$i."]";
               
                // Check whether it's the name of the current node.
                if ( $sibling != $node )
                {
                    // Create the new name for the sibling.
                    $new = $parent."/".$name."[".$counter."]";
                   
                    // Increase the counter.
                    $counter++;
                   
                    // Add the old and the new name to the list of nodes
                    // to be renamed.
                    $rename[$sibling] = $new;
                }
            }
           
            // Create an array for saving the new node-list.
            $nodes = array();
           
            // Now run through through the existing nodes.
            foreach ( $this->nodes as $name => $values )
            {
                // Check the position of the path of the node to be deleted
                // in the path of the current node.
                $position = strpos($name, $node);

                // Check whether it's not the node to be deleted.
                if ( $position === false )
                {
                    // Run through the array of nodes to be renamed.
                    foreach ( $rename as $old => $new )
                    {
                        // Check whether this node and it's parent requires to
                        // be renamed.
                        $name             = str_replace($old, $new, $name);
                        $values["parent"] = str_replace($old, $new,
                            $values["parent"]);
                    }
                   
                    // Add the node to the list of nodes.
                    $nodes[$name] = $values;
                }
            }
           
            // Save the new array of nodes.
            $this->nodes = $nodes;
        }
    }

    /**
    * 为结点添加文本内容.
    *
    * 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 )
    {
  // 测试此路径是否为属性结点
        if ( ereg("/attribute::", $path) )
        {
   // 获取此属性结点的父结点路径
            $parent = $this->prestr($path, "/attribute::");
           
   // 获取父结点
            $parent = $this->nodes[$parent];
           
   // 获取属性命名
            $attribute = $this->afterstr($path, "/attribute::");
           
   // 设置属性
            $parent["attributes"][$attribute] .= $value;
        }
        else
        {
   // 为此结点添加文本数据
            $this->nodes[$path]["text"] .= $value;
        }
    }
   
    /**
    * 为结点设置文本内容.
    *
    * 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 )
    {
  // 测试此路径是否为属性结点
        if ( ereg("/attribute::", $path) )
        {
   // 获取此属性结点的父结点路径
            $parent = $this->prestr($path, "/attribute::");
           
   // 获取父结点
            $parent = $this->nodes[$parent];
           
   // 获取属性命名
            $attribute = $this->afterstr($path, "/attribute::");
           
   // 设置属性
            $parent["attributes"][$attribute] = $value;
        }
        else
        {
   // 设置此结点的文本数据
            $this->nodes[$path]["text"] = $value;
        }
    }
   
    /**
    * 获取结点的文本内容.
    *
    * 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 ( ereg("/attribute::", $path) )
        {
            // Get the path to the attribute node's parent.
            $parent = $this->prestr($path, "/attribute::");
           
            // Get the parent node.
            $parent = $this->nodes[$parent];
           
            // Get the name of the attribute.
            $attribute = $this->afterstr($path, "/attribute::");
           
            // Get the attribute.
            $attribute = $parent["attributes"][$attribute];
           
            // Return the value of the attribute.
            return $attribute;
        }
  else
        {
            // Return the cdata of the node.
            return stripslashes($this->nodes[$path]["text"]);
        }
    }
   
    /**
    * 为结点添加属性.
    *
    * 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"]);
    }
   
    /**
    * 设置结点属性.
    *
    * 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;
    }
   
    /**
    * 获取结点的所有属性列表.
    *
    * 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"];
    }
   
    /**
    * 获取文档中的结点命名.
    *
    * 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"];
    }
   
    /**
    * XPath表达式运算.
    *
    * 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;
    }
   
    /**
 * 当解析时,处理打开的 XML 标识.
 *
    * 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 )
    {
  // 添加结点
  $this->path = $this->add_node($this->path, $name);
       
  // 设置属性
        $this->set_attributes($this->path, $attributes);
    }
   
    /**
 * 当解析时,处理关闭的 XML 标识.
    *
    * 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, "/"));
    }
   
    /**
 * 当解析时,处理字符数据.
    *
    * 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 )
    {
        // 转换实体字符
        //$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 ( ereg("/[", $step) )
        {
            // Get the predicates.
            $predicates = substr($step, strpos($step, "["));
           
            // Reduce the step.
            $step = $this->prestr($step, "[");
           
            // Try to split the predicates.
            $predicates = str_replace("][", "]|[", $predicates);
            $predicates = explode("|", $predicates);
           
            // Run through all predicates.
            foreach ( $predicates as $predicate )
            {
                // Remove the brackets.
                $predicate = substr($predicate, 1, strlen($predicate) - 2);
               
                // Add the predicate to the list of predicates.
                $axis["predicate"][] = $predicate;
            }
        }
       
        // Check whether the axis is given in plain text.
        if ( $this->search_string($step, "::") > -1 )
        {
            // Split the step to extract axis and node-test.
            $axis["axis"]      = $this->prestr($step, "::");
            $axis["node-test"] = $this->afterstr($step, "::");
        }
        else
        {
            // Check whether the step is empty.
            if ( empty($step) )
            {
                // Set it to the default value.
                $step = ".";
            }
           
            // Check whether is an abbreviated syntax.
            if ( $step == "*" )
            {
                // Use the child axis and select all children.
                $axis["axis"]      = "child";
                $axis["node-test"] = "*";
            }
            elseif ( ereg("/(", $step) )
            {
                // Check whether it's a function.
                if ( $this->is_function($this->prestr($step, "(")) )
                {
                    // Get the position of the first bracket.
                    $start = strpos($step, "(");
                    $end   = strpos($step, ")", $start);
                   
                    // Get everything before, between and after the brackets.
                    $before  = substr($step, 0, $start);
                    $between = substr($step, $start + 1, $end - $start - 1);
                    $after   = substr($step, $end + 1);
                   
                    // Trim each string.
                    $before  = trim($before);
                    $between = trim($between);
                    $after   = trim($after);
                   
                    // Save the evaluated function.
                    $axis["axis"]      = "function";
                    $axis["node-test"] = $this->evaluate_function($before,
                        $between, $node);
                }
                else
                {
                    // Use the child axis and a function.
                    $axis["axis"]      = "child";
                    $axis["node-test"] = $step;
                }
            }
            elseif ( eregi("^@", $step) )
            {
                // Use the attribute axis and select the attribute.
                $axis["axis"]      = "attribute";
                $axis["node-test"] = substr($step, 1);
            }
            elseif ( eregi("/]$", $step) )
            {
                // Use the child axis and select a position.
                $axis["axis"]      = "child";
                $axis["node-test"] = substr($step, strpos($step, "["));
            }
            elseif ( $step == "." )
            {
                // Select the self axis.
                $axis["axis"]      = "self";
                $axis["node-test"] = "*";
            }
            elseif ( $step == ".." )
            {
                // Select the parent axis.
                $axis["axis"]      = "parent";
                $axis["node-test"] = "*";
            }
            elseif ( ereg("^[a-zA-Z0-9/-_]+$", $step) )
            {
                // Select the child axis and the child.
                $axis["axis"]      = "child";
                $axis["node-test"] = $step;
            }
            else
            {
                // Use the child axis and a name.
                $axis["axis"]      = "child";
                $axis["node-test"] = $step;
            }
        }

        // Check whether it's a valid axis.
        if ( !in_array($axis["axis"], array_merge($this->axes,
            array("function"))) )
        {
            // Display an error message.
            $this->display_error("While parsing an XPath expression, in ".
                "the step /"%s/" the invalid axis /"%s/" was found.",
                str_replace($step, "<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_method($method, &$this, $axis, $context);
           
                // Check whether there are predicates.
                if ( count($axis["predicate"]) > 0 )
                {
                    // Check whether each node fits the predicates.
                    $contexts = $this->check_predicates($contexts,
                        $axis["predicate"]);
                }
            }
           

抱歉!评论已关闭.