PHP5:从原始(父)类的对象应用扩展类中的方法

时间:2010-04-06 15:00:25

标签: php oop domdocument

我正在尝试扩展两个本机PHP5类(DOMDocument和DOMNode)来实现2个方法(selectNodes和selectSingleNode),以便更轻松地进行XPath查询。我认为它会相当直接,但我遇到了一个我认为是OOP初学者问题的问题。

class nDOMDocument extends DOMDocument {
    public function selectNodes($xpath){
    $oxpath = new DOMXPath($this);
    return $oxpath->query($xpath);
  }
  public function selectSingleNode($xpath){
    return $this->selectNodes($xpath)->item(0);
  }
}

然后我尝试扩展DOMNode以实现相同的方法,以便我可以直接在节点上执行XPath查询:

class nDOMNode extends DOMNode {
    public function selectNodes($xpath){
    $oxpath = new DOMXPath($this->ownerDocument,$this);
    return $oxpath->query($xpath);
  }
  public function selectSingleNode($xpath){
    return $this->selectNodes($xpath)->item(0);
  }
}

现在,如果我执行以下代码(在任意XMLDocument上):

$xmlDoc = new nDOMDocument;
$xmlDoc->loadXML(...some XML...);
$node1 = $xmlDoc->selectSingleNode("//item[@id=2]");
$node2 = $node1->selectSingleNode("firstname");

第三行工作并返回DOMNode对象$ node1。但是,第四行不起作用,因为selectSingleNode方法属于nDOMNode类,而不是DOMNode。 所以我的问题是:在某些方面是否有办法将返回的DOMNode对象“转换”为nDOMNode对象?我觉得我在这里缺少一些重点,我非常感谢你的帮助。

(对不起,这是对我的问题的重述Extending DOMDocument and DOMNode: problem with return object

5 个答案:

答案 0 :(得分:2)

您可以通过DOMDocument::registerNodeClass()“告诉”DOMDocument在实例化某个节点类型的对象时,它将使用哪个类而不是默认类。 E.g。

$doc->registerNodeClass('DOMElement', 'Foo');

每当dom“正常”实例化DOMElement时,它现在将创建Foo的实例。

$doc = new nDOMDocument;
$doc->loadxml('<a><b><c>foo</c></b></a>');

$a = $doc->selectSingleNode('//a');
$c = $a->selectSingleNode('//c');

echo 'content: ', $c->textContent;

class nDOMElement extends DOMElement {
  public function selectNodes($xpath){
    $oxpath = new DOMXPath($this->ownerDocument);
    return $oxpath->query($xpath);
  }
  public function selectSingleNode($xpath){
    return $this->selectNodes($xpath)->item(0);
  }
}


class nDOMDocument extends DOMDocument {
  public function __construct($version=NULL, $encoding=NULL) {
    parent::__construct($version, $encoding);
    $this->registerNodeClass('DOMElement', 'nDOMElement');
  }

  public function selectNodes($xpath){
    $oxpath = new DOMXPath($this);
    return $oxpath->query($xpath);
  }
  public function selectSingleNode($xpath){
    return $this->selectNodes($xpath)->item(0);
  }
}

打印content: foo

但是你不能简单地设置registerNodeClass('DOMNode', 'MyDOMNode')并期望从现在开始DOMElement继承MyDOMNode而不是DOMNode。即您必须注册要覆盖的所有节点类型。

答案 1 :(得分:1)

您需要编写nDOMDocument :: selectSingleNode()代码以返回nDOMNode对象。没有可能发生的神奇变革。

我说你应该继续你的实验,因为你将学习一些好的OOP课程(尽管很难)。没错。

答案 2 :(得分:1)

面向对象方法的一个后果是继承是一条单行道。也就是说,没有办法让父母意识到孩子的方法。因此,虽然您可以遍历一组对象,但您只能可靠地调用父类的方法。 PHP确实有办法测试一个方法是否在运行时存在,这可能很有用,但它也可能很快变得难看。

这意味着当您扩展内置类时,您需要完全扩展它,以便您的应用程序永远不会使用内置类的实例 - 只有您的版本。在您的情况下,您需要扩展扩展类以覆盖返回父类类型的所有父方法:

public myClass extends foo {
// override all methods that would normally return foo objects so that they
// return myClass objects.
}

答案 3 :(得分:1)

我认为你最好用包装器对象装饰现有的库,而不是直接对它们进行子类化。正如您已经看到的那样,您无法轻松地将辅助方法附加到DOMDocument并让它返回您的子类对象。

具体来说,问题是DOMXPath::item()无法返回nDOMNode

如果您尝试执行以下操作,则会失败(许多属性似乎为read only):

class nDOMDocument extends DOMDocument {

    ...

    public function selectSingleNode($xPath) {
        $node = $this->selectNodes($xPath)->item(0);
        $myNode = new nDOMNode;
        $myNode->set_name($node->get_name()); // fail?
        $myNode->set_content($node->get_content());
        // set namespace
        // can you set the owner document? etc

        return $myNode;
    }

}

如果你改为包装对象,你可以使用__get()__call() magic methods快速公开现有的DOMNode界面,并包含你的附加功能并选择返回你自己的包裹/自定义课程,以实现您的原始目标。

示例:

class nDOMNode {

    protected $_node;

    public function __construct(DOMNode $node) {
        $this->_node = $node;
    }

    // your methods

}

class nDOMDocument {

    protected $_doc;

    public function __construct(DOMDocument $doc) {
        $this->_doc = $doc;
    }

    ...

    public function selectNodes($xPath){
        $oxPath = new DOMXPath($this->_doc->ownerDocument, $this->_doc);
        return $oxPath->query($xPath);
    }

    public function selectSingleNode($xPath) {
        return new nDOMNode($this->selectNodes($xPath)->item(0));
    }

}

$doc = new nDOMDocument(new DOMDocument);
$doc->loadXML('<xml>');
$node = $doc->selectSingleNode("//item[@id=2]"); // returns nDOMNode

希望这有帮助。

答案 4 :(得分:0)

我认为你可能需要花很长时间来安装指向下,上,左,右的属性,就像DOM对JavaScript一样。这些必须在创建结构时分配。