2017-01-23 37 views
5

Podczas rozszerzania DOMElement w PHP, konstruktor klasy potomnej nie jest wywoływany. Nic nie wyskoczyło na mnie w dokumentach, o ile jest to oczekiwane zachowanie, ale może czegoś mi brakuje. Oto prosty przypadek testowy ....Konstruktor nie jest wywoływany w rozszerzonym DOMENIE PHP

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; 

Oczywiście gdybym oznacz ją sobie jest w porządku ...

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

Dlaczego kiedy zarejestrować klasę węzła z DOMDocument nie to nazwać __construct mimo że daje mi to właściwe dziedzictwo w każdy inny sposób?

UPDATE Dla naprawdę ciekawych ludzi lub ludzi znających C

============================= ========================================== Dochodzenie ....

jest to źródło rozszerzenie DOM zaczerpnięte z PHP src on github

Jeśli było utworzyć element, to jest łańcuch zdarzeń, które zdarza ::

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

Wydaje się, że nie dostać prawdziwe dziedziczenie z rozciągającej DOMNode lub jest zbudowany w klasach przedłużenie (DOMElement, DOMText) Jeżeli przypadkach nich domDocument. W takim przypadku węzeł libxml zostanie utworzony jako pierwszy, a właściwości klas zostaną zahaczone na drugim.

To jest niefortunne i niemożliwe do obejścia wydaje się, ponieważ nawet gdy importNode do dokumentu, instances nowy węzeł. Przykład

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

Teraz debate- jest to, co twórcy z przeznaczeniem oraz dokumentacja jest mylący, czy jest to błąd i prawdziwe dziedziczenie miało nastąpić?

+0

Jest nawet notatka w dokumentacji pokazującej, że konstruktor powinien wywołać 'parent :: __ construct()'. Ale mam taki sam rezultat jak ty. – Barmar

+0

której wersji PHP użyłeś z ciekawości? – user2782001

+0

Użyłem PHP 5.5.38 – Barmar

Odpowiedz

1

Wymyśliłem sposób obejścia tego w NIEKTÓRYCH okolicznościach do tej pory. DOMNodes są przechowywane w pamięci, tak długo, jak można je umieścić w dokumencie w jednym kawałku (zwanym konstruktorem), wtedy będzie to w porządku, bez względu na to, co z nim zrobisz lub jak uzyskasz dostęp do niego po tym ...

Oto przykład, gdzie możemy uzyskać konstrukt zadzwonić i nadal być w porządku z dokumentem

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

połowu:

to działa tylko wtedy, gdy wszystkie elementy tworzenia. Jeśli ładujesz znaczniki HTML za pomocą $ document-> loadHTML ($ html), twoi konstruktorzy rozszerzeń nie zostaną wywołani. Do tej pory mogę myśleć tylko o sposobach hackowania takich jak ładowanie znaczników, a następnie zapętlanie każdego węzła do replik instancji i wstawianie ich. Zdecydowanie możliwe, ale powolne ...