2017-01-23 85 views
5

在PHP中擴展DOMElement時,未調用子類的構造函數。就預期的行爲而言,文檔中沒有任何內容跳出來,但也許我錯過了一些東西。這裏有一個簡單的測試用例....未在擴展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; 

爲什麼當我與DOM文檔註冊節點類它不叫__construct儘管它以其他方式給我適當的繼承?

UPDATE對於真正好奇的人或誰懂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); 

看來,你不要從延伸的DOMNode獲得真正的繼承,或者它內置的擴展類(一個DOMElement,一個DOMText)如果DOM文檔實例他們。在這種情況下,首先創建libxml節點,然後再次添加我們的類屬性。

這是不幸的,不可能繞過它似乎,因爲即使當您將導入節點到文檔中時,它實例化新節點。例如

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"; 

現在debate-這是開發商打算什麼和文檔是誤導性的,或者是一個錯誤,真正的繼承是應該發生的呢?

+0

甚至在文檔中還有一個註釋,表明構造函數應該調用'parent :: __ construct()'。但是我得到和你一樣的結果。 – Barmar

+0

哪個版本的PHP你出於好奇使用? – user2782001

+0

我用PHP 5.5.38 – Barmar

回答

1

到目前爲止,我在某些情況下已經找到了解決這個問題的方法。 DOMNode被保存在內存中,所以只要你可以將它整合到文檔中(你的構造函數叫做),那麼無論你用它做什麼或者你如何訪問它,它都可以。

下面是一個例子,我們可以得到的結構打電話,仍然是好的與

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標記,則不會調用您的擴展構造函數。到目前爲止,我只能想到如何加載標記,然後將每個節點循環到實例副本並插入它們。肯定可能,但是很慢......