【问题标题】:Constructor not called on extended PHP DOMElement未在扩展的 PHP DOMElement 上调用构造函数
【发布时间】:2017-06-07 14:15:54
【问题描述】:

在 PHP 中扩展 DOMElement 时,不调用子类的构造函数。就预期的行为而言,文档中没有任何东西让我跳出来,但也许我错过了一些东西。这是一个简单的测试用例......

 class SillyTestClass extends DOMElement{
    public $foo=null;
    public function __construct($name,$value=null,$namespace=null){
        echo "calling custom construct....";
        $this->foo="bar";
        parent::__construct($name,$value,$namespace);
    }
    public function sayHello(){
        echo "Why, hello there!";
    }
}

$doc=new DOMDocument();
$doc->registerNodeClass('DOMElement','SillyTestClass');
$doc->loadHTML("<div><h1>Sample</h1></div>");
//THIS WORKS! CUSTOM CLASS BEING USED
$doc->documentElement->firstChild->sayHello();

//THIS IS STILL NULL:( Never set by construct, no message saying construct was called either  
echo $doc->documentElement->firstChild->foo; 

当然,如果我自己实例化它也没关系...

$elm=new SillyTestClass("foo","Hi there");
//WORKS! Outputs "bar";
echo $elm->foo;

为什么当我向 DOMDocument 注册节点类时,它不调用__construct,即使它以其他方式为我提供了适当的继承?

更新对于真正好奇的人或了解 C 的人

============================================== ============================= 调查....

这是取自 PHP src on github的DOM扩展源码

如果你要创建一个元素,这就是发生的事件链::

document.c :: dom_document_create_element
    |  //uses libxml to generate a new DOMNode
    | node = xmlNewDocNode(docp, NULL, (xmlChar *) name, (xmlChar *) value);

    // that node is then sent to 
  php_dom.c :: php_dom_create_object
   |
   |  //the node type is used to figure out what extension class to use
   |     switch (obj->type) {...
   |    
   |     //that class is used to instance an object
   |     if (domobj && domobj->document) {
   |          ce = dom_get_doc_classmap(domobj->document, ce);
   |     }
        object_init_ex(return_value, ce);

如果 DOMDocument 实例化它们,您似乎无法从扩展 DOMNode 或内置扩展类(DOMElement、DOMText)中获得真正的继承。在这种情况下,首先创建 libxml 节点,然后添加我们的类属性。

这很不幸,而且似乎无法解决,因为即使您将 importNode 导入到文档中,它也会实例化一个新节点。例子

class extendsDE extends DOMElement{
   public $constructWasCalled=false;
    public function __construct($name){
       parent::__construct($name);
       $this->constructWasCalled=true;
   }
}

 class extendsDD extends DOMDocument{

    public function __construct(){
        parent::__construct();
        $this->registerNodeClass("DOMElement","extendsDE");
    }
    //@override
    public function createElement($name){
        $elm=new extendsDE($name);
        echo "Element construct called when we create=";
        echo $elm->constructWasCalled?"true":"false";
        return $this->importNode($elm);
   }
}

$doc=new extendsDD();
$node=$doc->createElement("div");
echo "<br/>";
echo "But then when we import into document, a new element is created  and construct called= ";
echo $node->constructWasCalled?"true":"false"; 

现在的争论 - 这是开发人员的意图并且文档具有误导性,还是应该发生错误和真正的继承?

【问题讨论】:

  • 文档中甚至有一条注释显示构造函数应该调用parent::__construct()。但我得到的结果和你一样。
  • 您出于好奇使用了哪个版本的 PHP?
  • 我使用 PHP 5.5.38
  • 我一直尝试到 7.0 没有变化。并查看了 DOM ext 源代码,它看起来似乎在很长一段时间内都没有更改,因此 php 版本不太重要。

标签: php oop domdocument


【解决方案1】:

到目前为止,我已经在某些情况下找到了解决此问题的方法。 DOMNodes 保存在内存中,因此只要您可以将其一次性放入文档中(您的构造函数调用),那么无论您如何处理它或之后如何访问它都可以......

这是一个示例,我们可以让构造调用并且仍然可以处理文档

class extendsDE extends DOMElement{
    public $constructWasCalled=false;
    public function __construct($name){
         parent::__construct($name);
          $this->constructWasCalled=true;
   }
  }

   $doc=new DOMDocument();
   $doc->registerNodeClass("DOMElement","extendsDE");
   $doc->loadHTML("<div></div>");

   //append a node we create manually rather than through createElement
   $node=$doc->getElementsByTagName('div')->item(0)->appendChild(new extendsDE("p"));
    $node->nodeValue="Was my construct called?";
     echo "<br/>";
    echo "A new element was appended and construct called= ";
    echo $node->constructWasCalled?"true":"false";
    echo "<br/>";
    echo "Okay but what happens if I retrieve that node some other way..";
    echo "<br/>";
    echo "what if I get that element through selectors. Custom property still set from constructor=";
   echo $doc->getElementsByTagName('p')->item(0)->constructWasCalled?"true":"false";
  echo "<br/>";
  echo "what if I get that element through relationships. Custom property still set from constructor=";
  echo $doc->getElementsByTagName('div')->item(0)->childNodes->item(0)->constructWasCalled?"true":"false";

关键:

只有在创建所有元素时才有效。如果您使用 $document->loadHTML($html) 加载 HTML 标记,您的扩展构造函数将不会被调用。到目前为止,我只能想到一些破解方法,比如加载标记,然后将每个节点循环到实例副本并插入它们。绝对有可能,但速度很慢......

【讨论】:

    猜你喜欢
    • 2017-01-14
    • 2014-05-29
    • 1970-01-01
    • 2014-04-18
    • 2016-08-29
    • 2016-08-26
    • 1970-01-01
    • 2022-11-24
    • 2018-09-26
    相关资源
    最近更新 更多