SimpleXML和foreach循环中设置值的差异

3

在回答之前的问题时,我发现了以下行为,我无法理解。以下代码展示了这个问题...

<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);

$data = <<< XML
<?xml version="1.0" standalone="yes"?>
<Base>
    <Data>
        <Value></Value>
    </Data>
</Base>
XML;

$xml = simplexml_load_string($data);
foreach ( $xml->Data->Value as $value ) {
    $value = 1;
}
echo $xml->asXML().PHP_EOL;
foreach ( $xml->Data as $value ) {
    $value->Value = 1;
}
echo $xml->asXML().PHP_EOL;

我期望每个点的输出都是相同的,但实际输出是...

<?xml version="1.0" standalone="yes"?>
<Base>
    <Data>
        <Value/>
    </Data>
</Base>

<?xml version="1.0" standalone="yes"?>
<Base>
    <Data>
        <Value>1</Value>
    </Data>
</Base>

这似乎表明直接访问<Value>元素的第一个循环没有设置值,但是间接访问它的第二个循环却可以正常工作。

这两者之间的区别在哪里?


@PatrickQ,这解释了为什么第二个循环起作用而第一个不起作用,如果可以的话,我会很高兴。 - Nigel Ren
在这两种情况下,它都会返回一个SimpleXMLElement。 - Nigel Ren
1个回答

3
这个区别与循环或引用无关,而是与每种情况下=的确切含义有关。
第一个版本可以简化为:
$value = $xml->Data->Value;
$value = 1;

这是将变量分配给一个值,然后再分配给另一个值的简单操作。旧值和新值之间没有交互,因此$xml不会改变。
第二种情况可以这样写:
$data = $xml->Data;
$data->Value = 1;
// Or just $xml->Data->Value = 1;

在这里,我们不是将值分配给普通变量,而是分配给一个对象属性。其技巧在于,对象可以截取此分配,并对其执行特殊操作。在这种情况下,它会触发SimpleXML将该值发送到内存中的XML文档的libxml表示形式中。就好像您运行了类似于$data->setValueOfChild('Value', 1);的方法调用。


请注意,如果我们写成以下形式:

$value =& $xml->Data->Value;
$value = 1;

现在,第一个赋值将$value设为引用,第二个将1分配给该引用。这已足以向实际对象属性写入该值,但并没有触发SimpleXML所需的拦截。
然而,在这种特殊情况下,我们可以使用一个额外的技巧:除了拦截属性访问之外,SimpleXMLElement类还会拦截数组访问,以便您可以编写$foo->NameThatOccursMoreThanOnce[3]$some_element ['Attribute']。因此,我们可以编写以下内容:
$value = $xml->Data->Value;
$value[0] = 1;

在这里,$value是一个SimpleXMLElement对象,可以将$value[0] = 1截取为类似于$value->setValueOfItem(0, 1)的形式。
在这种情况下,该对象保存了所有名为<Value>的元素的集合,这些元素来自于<Data>元素内部;但是方便的是,即使该对象已经被缩小到一个项目,[0]仍然指向同一元素,因此这也是有效的:
$value = $xml->Data->Value[0];
$value[0] = 1;

最后,一个快速的提示是,你自己创建的对象也可以实现这种神奇的行为!属性访问可以使用__get, __set和__unset魔术方法来实现,而数组访问可以使用ArrayAccess接口来实现。

这在某些方面(不幸地)是有道理的。我注意到你写的另一个答案,如果我在第一个循环中使用了 $value[0] = 1;,它也可以工作。这是强制触发“魔术”函数的想法。我的原始第一个循环可能只能在进行运算符重载时才能正常工作。 - Nigel Ren
@NigelRen 啊,是的,我忘记了那个技巧,我会编辑它以备将来参考。 - IMSoP

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接