2017-01-23 13 views
5

При расширении DOMElement в PHP конструктор дочернего класса не вызывается. Ничто не выскочило из меня в документах, насколько это ожидалось, но, может быть, я что-то упустил. Вот простой тест ....Конструктор не вызвал расширенный 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, хотя он дает мне надлежащее наследование любым другим способом?

UPDATE Для очень любопытных людей или людей, которые знают C

============================= ========================================= Расследование ....

Это является источником расширения DOM взяты из PHP src on github

Если вы должны были создать элемент, это цепь событий, которая происходит ::

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), если экземпляры DomDocument им. В этом случае узел libxml создается первым, а свойства нашего класса привязаны вторым.

Это, к сожалению, невозможно обойти, потому что даже когда вы импортируетеNode в документ, он вводит новый узел. Пример

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

В какой-то момент я выяснил это в некоторых обстоятельствах. 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"; 

загвоздка:

это работает только если вы создаете все элементы. Если вы загружаете HTML-разметку, используя $ document-> loadHTML ($ html), ваши конструкторы расширений не будут вызваны. До сих пор я могу только думать о том, чтобы взломать все это, например, загружать разметку, а затем перебирать каждый узел на экземпляры экземпляров и вставлять их. Определенно возможно, но медленно ...

 Смежные вопросы

  • Нет связанных вопросов^_^