【问题标题】:PHP5 : Applying a method from an extended class on an object from the original (parent) classPHP5:将扩展类的方法应用于原始(父)类的对象
【发布时间】:2011-02-04 20:46:31
【问题描述】:

我正在尝试扩展两个原生 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的重述)

【问题讨论】:

标签: php oop domdocument


【解决方案1】:

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

我说你应该继续你的实验,因为你会在这个过程中学到一些很好的 OOP 课程(尽管很艰难)。没有错。

【讨论】:

    【解决方案2】:

    我认为您可能需要在这里花费很长的时间来安装指向向下、向上、向左和向右的指向属性,这有点像 DOM 为 JavaScript 所做的那样。这些必须在创建结构时分配。

    【讨论】:

      【解决方案3】:

      面向对象方法的后果之一是继承是一条单向的街道。也就是说,没有办法让父母知道孩子的方法。因此,虽然您可以遍历对象集合,但您只能可靠地调用父类的方法。 PHP 确实有办法在运行时测试方法是否存在,这可能很有用,但也会很快变得丑陋。

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

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

      【讨论】:

        【解决方案4】:

        我认为你最好用包装对象来装饰现有的库,而不是直接将它们子类化。正如您已经看到的,您不能轻易地将辅助方法附加到 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
        

        希望这会有所帮助。

        【讨论】:

        • 感谢您的回答,这是我所期望的,我学到了很多。
        【解决方案5】:

        您可以通过DOMDocument::registerNodeClass()“告诉”一个DOMDocument,在为某个节点类型实例化对象时,它应该使用哪个类而不是默认类。 例如

        $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') 并期望从现在开始继承自MyDOMNode 而不是DOMNode 的DOMElement。 IE。您必须注册所有要覆盖的节点类型。

        【讨论】:

        • 非常感谢您的回答。看到 DOMDocument 有专门实现这一点的方法,很有趣也很有用。
        • 为了让 nDOMDocument 以及扩展的 DOMElement 或 DOMNode 使用,您可以考虑使用特征来创建您的自定义方法,并根据它的节点类遗产使用它们...php.net/manual/en/class.domimplementation.php
        猜你喜欢
        • 1970-01-01
        • 2015-03-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-28
        • 1970-01-01
        相关资源
        最近更新 更多