扩展DOMElement

时间:2013-06-23 11:13:31

标签: php xml dom

我有一个奇怪的行为扩展PHP DOMElements:

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

这里会发生什么?

如果我这样做:

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?”。

1 个答案:

答案 0 :(得分:2)

首先,你可能想知道你的第一个例子中发生了什么。为什么(地狱;))是前两个DOMElement而第三个是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不会在内存中保留大量DOMElement个对象,因为文档中包含许多元素,但内部存在其他数据结构,当您与DOMDocument交互时,PHP将创建这些对象是动态的(除非该对象已经创建并且仍然在可变内存中)。

所以这应该解释为什么前两个是DOMElement$node被重新分配,因此它们不再是PHP变量内存)而对于第三个节点,它仍然在记忆,因此没有创建。

通过这种解释,现在还应该更清楚限制的位置:即使您向文档添加了更具体的DOMElement节点(例如 JavaScript JavaScriptLibrary )它并不意味着你从DOM中取回它。因为DOM只是一个模型,可以保证给你一个DOMElement而不是“你在那里填充的东西”。

我刚才谈到的内存中的这个结构,其中没有PHP对象存在但仍然表示所有元素,它基于一个名为libxml的库。 PHP使用这些。

因此,每次PHP基于DOMElement的内部结构创建新对象时,它都使用相同名称的PHP类。

您可以通过注册基类的子类(例如DOMDocument作为基类,JavaScriptLibrary作为子类)来更改BTW中的类名,然后DOMDocument将 - 在创建新对象实例时 - 请改用该类。

因此,我们不会在上面的示例变体中重新分配给$node,而是注册一个不同的类:

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

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

输出:

JavaScriptLibrary
JavaScriptLibrary
JavaScript

Etvoilà!只要PHP从内部存储器结构中创建那些 DOMElement ,就可以在已注册的类中获取它,这里是 JavaScriptLibrary

如果这仍然太有限你想要做什么,我知道处理这个的唯一方法是扩展DOMDocument,在构造时自我引用(所以这个对象不会丢失),子类任何其他节点类型,对于任何添加,保留你在内存中添加的对象,以便它像你第三次经历的那样“按原样”返回。

如果你想快速开发,我建议你使用一个名为traits的PHP 5.4特性,因为大多数domdocument-extension-classes都是从DOMNode延伸出来的,你不能为你自己的子类扩展,他们需要从他们的基类扩展,否则这将无法工作。由于PHP没有多重继承,因此traits可以帮助您在不需要为实例/对象管理创建的所有子类之间复制写代码。

还建议您熟悉DOMDocument - 模型,以便了解如何添加和删除节点,以便管理内存。我不能说这是值得的工作,甚至是100%可能你尝试做的事情。