见这个例子:
此类扩展DOMElement。每个DOMElement都必须是其中的一部分 任何DOMDocuement都可以修改。
class Xmlrize extends DOMElement
{
private static $dom;
public function __construct(/*. string .*/ $name,$value =null) {
parent::__construct($name,$value,null);
if(!self::$dom) {
self::$dom = new DOMDocument('1.0','UTF-8');
}
self::$dom->appendChild($this);
}
}
用于将数组添加到DOM中。如果数组的值不是Xmlrize,那么我忽略它。
class Attributes extends Xmlrize {
public function __construct(array $attributes)
{
parent::__construct('Attributes');
foreach($attributes as $name => $value) {
$element = new Xmlrize($name);
if($value instanceof Xmlrize)
$element->appendChild($value);
$this->appendChild($element);
}
}
}
您可以在构造函数
中添加属性实例class Html extends Xmlrize {
public function __construct(Attributes $obj) {
parent::__construct('html');
$this->addAttributes($obj);
}
此方法显示if属性是否为Xmlrize的实例。
protected function addAttributes(Attributes $attributes)
{
foreach($attributes->childNodes as $attribut) {
if($attribut->childNodes->length == 1) {
$item = $attribut->childNodes->item(0);
echo $attribut->nodeName.': ';
echo $item->nodeName.' has value '.$item->nodeValue.'. ';
echo 'Is it DomElement? ';
echo ($item instanceof DomElement) ? 'true' : 'false';
echo '. Is it Xmlrize? ';
echo ($item instanceof Xmlrize) ? 'true' : 'false';
echo '.'.PHP_EOL;
}
}
return null;
}
}
按阵列启动属性的示例。看到差异!
/* works as expected */
$like = new Html(new Attributes(
$xx = array('XXX'=> new Xmlrize('foo', 'baar'))
));
/* do not work as exptected */
$like = new Html(new Attributes(
array('XXX'=> new Xmlrize('foo', 'baar'))
));
?>
上面的示例返回:
XXX: foo has value baar. Is it DomElement? true. Is it Xmlrize? true.
XXX: foo has value baar. Is it DomElement? true. Is it Xmlrize? false.
为什么我添加'$ xx ='该元素被视为Xmlrize,如果不是,则不是?
答案 0 :(得分:1)
恭喜,您在PHP的DOM实现中发现了一个错误。我花了一些时间处理你的代码,我能够将错误的行为缩小到这个简单的测试用例:
class MyElement extends DOMElement { }
// #1 - okay
$dom = new DOMDocument();
$e = new MyElement("e");
$dom->appendChild($e);
echo get_class($dom->childNodes->item(0)) . "\n";
// #2 - wrong
$dom = new DOMDocument();
$dom->appendChild(new MyElement("e"));
echo get_class($dom->childNodes->item(0)) . "\n";
// #3 - wrong
$dom = new DOMDocument();
$e = new MyElement("e");
$dom->appendChild($e);
$e = null;
echo get_class($dom->childNodes->item(0)) . "\n";
这显然是PHP中的一个错误,但这就是为什么会发生这种情况。 PHP中的每个对象都有一个引用计数(在任何地方有多少引用此对象)。当你调用$dom->appendChild(...)
时,这个函数应该在内部增加对象的引用计数,但事实并非如此。当您运行示例#1时,一切似乎都很好,但在示例#2中,对象是“临时”创建的(仅用于调用),引用计数为1.当appendChild
完成时,PHP将引用减少为0因此从内存中删除对象。
现在出现了棘手的部分:你现在应该想“哇,这意味着有一些指向解除分配(死机)内存的指针,程序应该严重崩溃”。但这并不仅仅是因为PHP的内存管理器并没有真正解除分配未使用的内存,而只是将其标记为已释放。所以你仍然可以从中读取,虽然内存的内容可能已经被另一个分配改变了。这才是真正发生的事情。我用调试器检查了对象是否真的被释放了(显然不应该,它应该有来自DOM层次结构的引用)。
现在你应该做的是1)向PHP的网站提交一个错误报告,2)要么停止使用DOMElement类,要么确保你“追加”到DOM层次结构的每个对象都是其他地方的引用。 / p>
此外,如果还不清楚,这就是为什么您在foo(array(...))
和foo($x = array(...))
之间遇到不同的行为:第二个将数组分配给全局(或本地,取决于您调用它的位置) )变量,它将保存数组,在数组中,有一个对象的引用。因此不会被释放,并且DOM中的层次结构是可以的。
如果您要提交错误报告,请告诉我。如果你愿意,我会这样做,但是,嘿,这是你的错误:)
答案 1 :(得分:0)
foo(array(…))
和foo($x=array(…))
之间的区别仅在于:
foo(array(…))
只将数组作为参数传递给foo方法。
foo($x=array(…))
将数组保存到$ x变量并将相同的变量$ x传递给foo方法。