简体   繁体   English

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

[英]PHP5 : Applying a method from an extended class on an object from the original (parent) class

I'm trying to extend two native PHP5 classes (DOMDocument and DOMNode) to implement 2 methods (selectNodes and selectSingleNode) in order to make XPath queries easier. 我正在尝试扩展两个本机PHP5类(DOMDocument和DOMNode)以实现2个方法(selectNodes和selectSingleNode),以使XPath查询更容易。 I thought it would be rather straighforward, but I'm stuck in a problem which I think is an OOP beginner's issue. 我以为这很简单,但是我陷入了一个问题,我认为这是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);
  }
}

Then I tried to do extend DOMNode to implement the same methods so I can perform an XPath query directly on a node: 然后,我尝试扩展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);
  }
}

Now if I execute the following code (on an arbitrary XMLDocument): 现在,如果我在任意XMLDocument上执行以下代码:

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

The third line works and returns a DOMNode object $node1. 第三行工作并返回DOMNode对象$ node1。 However, the fourth line doesn't work because the selectSingleNode method belongs to the nDOMNode class, not DOMNode. 但是,第四行不起作用,因为selectSingleNode方法属于nDOMNode类,而不是DOMNode。 So my question: is there a way at some point to "transform" the returned DOMNode object into a nDOMNode object? 所以我的问题是:是否有办法将返回的DOMNode对象“转换”为nDOMNode对象? I feel I'm missing some essential point here and I'd greatly appreciate your help. 我觉得我在这里缺少一些要点,非常感谢您的帮助。

(Sorry, this is a restatement of my question Extending DOMDocument and DOMNode: problem with return object ) (对不起,这是我的问题的重述扩展DOMDocument和DOMNode:返回对象的问题

You can "tell" a DOMDocument via DOMDocument::registerNodeClass() which class it shall use instead of the default classes when instantiating an object for a certain node type. 您可以通过DOMDocument :: registerNodeClass() “讲述”一个DOMDocument,当实例化某个节点类型的对象时,它将使用哪个类而不是默认类。 Eg 例如

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

each time the dom would "normally" instantiate a DOMElement it will now create an instance of 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);
  }
}

prints content: foo . 打印content: foo

But you can't simply set registerNodeClass('DOMNode', 'MyDOMNode') and expect a DOMElement from now on to inherit from MyDOMNode instead of DOMNode. 但是您不能简单地设置registerNodeClass('DOMNode', 'MyDOMNode')并期望从现在起DOMElement将从MyDOMNode继承而不是DOMNode继承。 Ie you have to register all node types you want to overwrite. 即,您必须注册要覆盖的所有节点类型。

You'd need to code nDOMDocument::selectSingleNode() to return a nDOMNode object. 您需要对nDOMDocument :: selectSingleNode()进行编码,以返回nDOMNode对象。 There's no magical transform that can happen. 不可能发生任何神奇的转变。

I say you should continue with your experiment as you will learn some good OOP lessons along the way (albeit, the hard way). 我说您应该继续进行实验,因为您将在此过程中学习一些很好的OOP课程(尽管很困难)。 Nothing wrong with that. 没有错。

One of the consequences of the object-oriented approach is that inheritance is a one-way street. 面向对象方法的后果之一是继承是一条单向路。 That is, there is no way to make the parent aware of the methods of the children. 也就是说,没有办法让父母意识到孩子的方法。 Thus while you may be able to iterate through a collection of objects, you can only reliably call the methods of the parent class. 因此,尽管您可以遍历对象的集合,但只能可靠地调用父类的方法。 PHP does have ways to test if a method exists at runtime, and this can be useful but it can also get ugly quickly. PHP确实有方法可以在运行时测试某个方法是否存在,这可能很有用,但也会很快变得丑陋。

That means that when you extend a built-in class, you need to completely extend it so that your application is never working with instances of the built-in class--only your version. 这意味着扩展内置类时,需要完全扩展它,以便您的应用程序永远无法使用内置类的实例(仅适用于您的版本)。 In your case, you need to expand your extended classes to override all of the parent's methods that return the parent's class type: 在您的情况下,您需要扩展扩展类以覆盖所有返回父级类类型的父级方法:

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

I think you'll be better off here decorating the existing library with wrapper objects, rather than directly subclassing them. 我认为您最好在这里使用包装对象来装饰现有库,而不是直接对它们进行子类化。 As you can see already, you can't easily append helper methods to DOMDocument and have it return your subclassed objects. 正如您已经看到的那样,您无法轻松地将辅助方法添加到DOMDocument并使它返回您的子类对象。

Specifically, the problem here is DOMXPath::item() does not know to return a nDOMNode . 具体来说,这里的问题是DOMXPath :: item()不知道返回nDOMNode

If you tried to do something like the following, it would fail (many properties appear to be 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;
    }

}

If you wrap the object instead you could quickly expose the existing DOMNode interface using the __get() and __call() magic methods , and include your additional functionality and elect to return your own wrapped/custom classes to achieve your original goal. 如果包装对象,则可以使用__get()__call() 魔术方法快速公开现有的DOMNode接口,并包括其他功能并选择返回自己的包装/自定义类以实现您的原始目标。

Example: 例:

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

Hope this helps. 希望这可以帮助。

I think you may have to take the long way here and install pointing properties that point down, up, left and right, somewhat like the DOM does for JavaScript. 我认为您可能需要花很长的时间来安装指向属性,该属性指向下,上,左,右,就像DOM对JavaScript所做的那样。 These will necessarily be assigned when creating the structure. 这些在创建结构时必须分配。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM