是否可以直接在PHP对象上使用XPath?

时间:2014-07-29 11:16:57

标签: php object xpath

是否可以直接在PHP对象上使用XPath语法来浏览对象的层次结构?

也就是说,我可以使用(2)代替(1):

  1. $object->subObject1->subObject2
  2. $object['subObject1/subObject2'](括号中的表达式是XPath。)
  3. 其他问题:

    根据我目前的理解,将对象转换为ArrayObject没有意义,因为XPath不能与ArrayObjects一起使用。这是对的吗?

2 个答案:

答案 0 :(得分:2)

如果您只需要基于/ - 分隔路径的基本遍历,那么您可以使用这样的简单循环实现它:

  public function getDescendant($path) {
        // Separate the path into an array of components
        $path_parts = explode('/', $path);

        // Start by pointing at the current object
        $var = $this;

        // Loop over the parts of the path specified
        foreach($path_parts as $property)
        {
              // Check that it's a valid access
              if ( is_object($var) && isset($var->$property) )
              {
                    // Traverse to the specified property, 
                    // overwriting the same variable
                    $var = $var->$property;
              }
              else
              {
                    return null;
              }
        }

        // Our variable has now traversed the specified path
        return $var;
  }

设置一个值是类似的,但是我们需要一个额外的技巧:为了能够在循环退出后分配一个值,我们需要每次通过引用分配变量:

  public function setDescendant($path, $value) {
        // Separate the path into an array of components
        $path_parts = explode('/', $path);

        // Start by pointing at the current object
        $var =& $this;

        // Loop over the parts of the path specified
        foreach($path_parts as $property)
        {
              // Traverse to the specified property, 
              // overwriting the same variable with a *reference*
              $var =& $var->$property;
        }

        // Our variable has now traversed the specified path,
        // and is a reference to the variable we want to overwrite
        $var = $value;
  }

将这些添加到名为Test的类中,允许我们执行以下操作:

$foo = new Test;
$foo->setDescendant('A/B', 42);

$bar = new Test;
$bar->setDescendant('One/Two', $foo);
echo $bar->getDescendant('One/Two/A/B'), ' is the same as ', $bar->One->Two->A->B;

要在问题中使用数组访问表示法来允许此操作,您需要创建一个实现the ArrayAccess interface的类:

  • 上述功能可以直接用作offsetGetoffsetSet
  • offsetExistsgetDescendant / offsetGet类似,只是返回false而不是null,而true代替$var }}。
  • 正确实现offsetUnset稍微麻烦一点,因为您无法使用引用分配技巧从其父对象中实际删除属性。相反,您需要特别处理指定路径的最后一部分,例如用array_pop($path_parts)
  • 抓住它
  • 有点小心,4种方法可能会使用共同的基础。

另一个想法是,这可能是a Trait的一个很好的候选者,它基本上允许您将函数复制并粘贴到不相关的类中。请注意,Traits无法直接实现接口,因此每个类都需要特性的implements ArrayAccessuse语句。

(当我有时间时,我可以回来编辑ArrayAccess方法的完整示例。)

答案 1 :(得分:0)

对于某些依赖项,它应该(很容易)支持完整的XPath表达式集。唯一的困难是从 完全限定的XPath实现遍历对象。

  1. 使用来自PEAR的XML_Serializer将对象序列化为XML。
  2. 将创建的文档加载为DOMDocument,运行您的任意XPath表达式并从所选元素中获取节点路径($node->getNodePath()),如图所示here
  3. 使用类似/blah/example[2]/x[3]的节点路径,您现在可以使用object attribute iteration以递归方式在对象上实现遍历。这在很大程度上取决于1中的序列化器的实际工作方式。
  4. 注意:我不知道实际是否需要实施ArrayAccess界面,因为您可以使用$obj->$key访问$key等对象属性是一些从节点路径切成的字符串。