我正在尝试扩展两个本机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)
答案 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一样。这些必须在创建结构时分配。