1

看这个例子:

这个类扩展了 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);
    }

此方法显示属性是否是 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,如果不是,则不是?

4

2 回答 2

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 层次结构的每个对象也是在其他地方引用。

另外,如果尚不清楚,这就是为什么您在foo(array(...))and之间遇到不同行为的原因foo($x = array(...)):第二个将数组分配给全局(或本地,取决于您调用它的位置)变量,该变量将保存数组和数组,有对您的对象的引用。因此不会被释放,并且 DOM 中的层次结构是可以的。

让我知道您是否要提交错误报告。如果你愿意,我会这样做,但是,这是你的错误 :)

于 2012-04-20T15:32:03.583 回答
0

foo(array(…))和之间的唯一区别foo($x=array(…))是:

foo(array(…))仅将数组作为参数传递给 foo 方法。

foo($x=array(…))将数组保存到 $x 变量并将相同的变量 $x 传递给 foo 方法。

于 2012-04-20T13:35:43.603 回答