2013-06-23 73 views
2

我有一個奇怪的行爲擴展PHP DOMElements:擴展一個DOMElement

class JavaScript extends DOMElement { 
} 

class JavaScriptLibrary extends DOMElement { 
} 

$dom = new DOMDocument(); 

$node = new JavaScript('script'); 
$dom->appendChild($node); 

$node = new JavaScriptLibrary('script'); 
$dom->appendChild($node); 

$node = new JavaScript('script'); 
$dom->appendChild($node); 

foreach ($dom->childNodes as $childNode) { 
    echo get_class($childNode)."\n"; 
} 

預計:

JavaScript 
JavaScriptLibrary 
JavaScript 

結果:

DOMElement 
DOMElement 
JavaScript 

什麼會在這裏happend?

如果我做這種方式:

addElement($dom, new JavaScript('script')); 
addElement($dom, new JavaScriptLibrary('script')); 
addElement($dom, new JavaScript('script')); 

function addElement($dom, $node){ 
    $dom->appendChild($node); 
} 

其結果將是

DOMElement 
DOMElement 
DOMElement 

我怎麼能做到這一點,而無需使用 - >的createElement喜歡這裏 「How can I extend PHP DOMElement?」。

+0

貌似永遠只有最後一個元素顯示正確的類名 – DevZer0

+0

在它看來,第一個例子,即$節點將保持參考。如果您使用$ node1,$ node2和$ node3作爲它的三個節點。 – user2513437

+0

但我不知道爲什麼這個函數的例子不起作用。將沒有參考。 – user2513437

回答

2

首先你可能想知道你的第一個例子會發生什麼。爲什麼(地獄;))是前兩個a DOMElement和第三個a JavaScript

這是因爲$node變量到目前爲止還沒有被重新賦值或取消設置。如果未設置或重新分配它,輸出不同:

$node = 1; # re-assing to (int) 1 

foreach ($dom->childNodes as $childNode) { 
    echo get_class($childNode)."\n"; 
} 

輸出:

DOMElement 
DOMElement 
DOMElement 

正如你所看到的,第三個是現在像前兩個人。

這可能已經給你一個指針。只要你的你的DOMNode對象保存在PHP變量內存中,它就是你的類型。如果不是,並且您從父元素或文檔(不包含在您的某個變量中)中讀取它,則會從其他一些內存中讀取它,並返回作爲 a DOMElement

這意味着,PHP不會在內存中保留大量對象,因爲文檔具有很多元素,但內部有一些其他數據結構,並且當您與DOMDocument交互時,PHP將即時創建這些對象(除非該對象已經創建並且仍然在可變內存中)。

所以這應該解釋爲什麼前兩個分別爲DOMElement$node被重新分配,使他們在PHP變量內存沒有任何更長),而第三點,它仍然是在內存中,因此沒有建立。

有了這個解釋,現在還應該更清楚其中的限制是:即使你一個更具體的DOMElement節點添加到文檔(如JavaScriptJavaScriptLibrary)必須並不意味着你得到它從DOM返回。因爲DOM只是一種模式,可以保證給你一個DOMElement,而不是「你在那裏塞進什麼」。

這種結構在內存中,我剛纔講的,如果沒有PHP對象存在,但仍然是所有元素的代表,是基於一個庫調用的libxml。 PHP使用這些。

所以每次PHP創建一個基於爲DOMElement這些內部結構的新對象,它使用相同名稱的PHP類。

你可以在這裏順便說一句註冊一個基類的子類改變類的名稱(例如DOMDocument作爲基類和JavaScriptLibrary作爲子類),然後DOMDocument會 - 創建一個新的對象實例時 - 使用類代替。

所以不是重新分配在上面的例子中的變化率,以$node,我們註冊不同的類的:

$dom->registerNodeClass('DOMElement', 'JavaScriptLibrary'); 

foreach ($dom->childNodes as $childNode) { 
    echo get_class($childNode)."\n"; 
} 

輸出:

JavaScriptLibrary 
JavaScriptLibrary 
JavaScript 

的Et瞧!;只要PHP從內部存儲器結構創建那些DOMElement s,就可以在註冊類中獲得它,這裏是JavaScriptLibrary

如果這仍然是你想要做的事情太有限,我所知道的唯一的方式來處理,這是延長DOMDocument,建造時(因此這個對象不會丟失)自引用它,子類化任何其他節點類型,對於任何添加,請將您添加的對象保留在內存中,以便它像第三個那樣「按原樣」返回。

如果您想快速開發,我建議您使用稱爲traits的PHP 5.4功能,因爲即使大多數domdocument擴展類從DOMNode延伸,你不能爲自己的子類,他們需要從他們的基類延伸,否則這將無法正常工作。由於PHP沒有多重繼承,因此特性可以幫助您在不需要爲實例/對象管理創建的所有子類之間重複編寫代碼。

還有關於DOMDocument的相關知識 - 建議使用模型,以便了解如何添加和刪除節點以便管理內存。我不能說是否值得這項工作,或者甚至100%可能爲你做什麼。

+0

我也試過registerNodeClass。但看起來,這是一個全球性的環境。我無法在飛行中改變班級。如果我這樣做,所有存儲的元素將被最後一個定義覆蓋。我認爲你怎麼說DOM是「只是一個模型」。但我能做什麼呢?我想要的是 - 類名如何說 - 用腳本標記創建一個DOM。 JavaScriptLibrary將始終插入JavaScript元素之前。爲此我有一個特殊的文檔類來管理這個。 – user2513437

+0

是的,正如所解釋的那樣,它是(重新)創建的* all *對象的一個​​設置。正如我進一步寫到的,你需要保留你在內存中創建的那些對象(例如有一個數組並且在那裏添加這些節點(注意你使用的內存會發生什麼))*或*當更改該設置時那些被重新創建。只要您將這些具體對象保留在內存中,它們的類型就會保留。關鍵是你明白*爲什麼會發生這種情況,以便你可以建立適合你需要的東西。非常粗略的數組示例/演示:https://eval.in/34657 – hakre

+0

就是這樣!謝謝! – user2513437