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

时间:2018-01-03 16:30:17

标签: php simplexml

在回答上一个问题时,我发现了以下无法理解的行为。以下代码显示了问题...

<?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>元素的第一个循环没有设置值,但是间接访问它的第二个循环工作正常。

有什么区别?

1 个答案:

答案 0 :(得分:2)

差异与循环或引用无关,但与=在每种情况下的含义无关。

第一个版本可以简化为:

$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]和{{1} }。所以事实证明我们可以这样写:

$some_element['Attribute']

此处$value = $xml->Data->Value; $value[0] = 1; 是一个$value对象,它可以截取SimpleXMLElement $value[0] = 1之类的内容。

在这种情况下,该对象包含$value->setValueOfItem(0, 1)元素内部所有名为<Value>的元素的集合;但方便的是,即使对象已经缩小到一个项目,<Data>只是引用相同的元素,所以这也适用:

[0]

最后,快速说明您自己的对象也可以实现这种神奇的行为!可以使用the __get, __set, and __unset magic methods实现属性访问,并且可以使用the ArrayAccess interface实现数组访问。