【问题标题】:Extending DOMElement扩展 DOMElement
【发布时间】:2013-06-23 11:13:30
【问题描述】:

我在扩展 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?”。

【问题讨论】:

  • 看起来总是只有最后一个元素显示正确的类名
  • 在第一个示例中,$node 似乎将持有引用。如果你对三个节点使用 $node1、$node2 和 $node3 就可以了。
  • 但是我不知道为什么这个函数的例子不起作用。不会有参考。
  • +1 好问题。从技术上讲 DOMElement is 是正确的类名,如果你查看 PHP 手册,文档清楚地说你得到了 DOMNode 的子类型(但不是特别是你的子类型) .有关更多详细信息以及您可以/可以做些什么来改变它,请参阅下面的答案。

标签: php xml dom


【解决方案1】:

首先,您可能想知道在您的第一个示例中发生了什么。为什么(见鬼;))前两个是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 变量内存中,它就是你的类型。如果不是,并且您从父元素或文档中“返回”它(没有在您的某个变量中包含它),它会从其他一些内存中读取并返回 as DOMElement

这意味着,PHP 不会在内存中保留大量的 DOMElement 对象,只是因为文档有那么多元素,但内部还有一些其他数据结构,当您与 DOMDocument 交互时,PHP 将创建这些对象 - the-fly(除非该对象已经创建并且仍在可变内存中)。

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

有了这个解释,现在也应该更清楚限制在哪里了:即使您在文档中添加了更具体的 DOMElement 节点(例如 JavaScript@ 987654336@)它一定不意味着你从 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

等等! PHP只要从内部内存结构中创建那些DOMElements,你就可以在注册的类中得到它,这里是JavaScriptLibrary

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

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

还建议您对DOMDocument-Model 有很好的了解,以便您了解如何添加和删除节点,以便您可以管理内存。我不能说它是否值得这项工作,或者你尝试做的事情是否 100% 有可能。

【讨论】:

  • 我也试过 registerNodeClass。但似乎,这是一个全局设置。我不能即时更改课程。如果我这样做,所有存储的元素都将被最后一个定义覆盖。我认为因为你说 DOM 是“只是一个模型”。但是我能做什么呢?我想要的是——类名怎么说——用脚本标签创建一个 DOM。 JavaScriptLibrary 将始终插入到 JavaScript 元素之前。为此,我有一个特殊的文档类来管理它。
  • 是的,正如所解释的,这是(重新)创建的所有对象的一种设置。正如我进一步写的那样,您要么需要将您创建的那些对象保留在内存中(例如,拥有一个数组并在那里添加这些节点(留意您消耗的内存会发生什么)) 重新创建这些设置时更改该设置。只要您将这些具体对象保存在内存中,它们的类型就会被保留。关键是您了解为什么会发生这种情况,以便您可以构建适合您需要的东西。非常粗略的数组示例/演示:eval.in/34657
  • 只有它“工作”不代表它就是它。请注意,您可能会将 DOM 用于它不是为它设计的东西。只是说。备份以防 eval.in 出现故障:gist.github.com/hakre/5845868
猜你喜欢
  • 1970-01-01
  • 2017-06-07
  • 2010-12-23
  • 2019-02-25
  • 2020-09-10
  • 2014-09-23
  • 2020-12-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多