SimpleXML 和 foreach 循环中设置值的差异
Differences in setting value in SimpleXML and foreach loop
在回答上一个问题时,我发现了以下我无法理解的行为。以下代码显示了问题...
<?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>
元素的第一个循环没有设置值,但间接访问它的第二个循环工作正常。
有什么区别?
区别与循环或引用无关,而与 =
在每种情况下的确切含义有关。
第一个版本可以简化为:
$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
class 拦截数组访问以便您可以编写$foo->NameThatOccursMoreThanOnce[3]
和 $some_element['Attribute']
。所以事实证明我们可以这样写:
$value = $xml->Data->Value;
$value[0] = 1;
这里,$value
是一个SimpleXMLElement
对象,它可以拦截$value[0] = 1
像$value->setValueOfItem(0, 1)
.
在这种情况下,该对象包含 <Data>
元素内部称为 <Value>
的所有元素的集合;但方便的是,即使对象已经缩小到一个项目,[0]
也只是指回相同的元素,所以这也有效:
$value = $xml->Data->Value[0];
$value[0] = 1;
最后,请注意您自己的对象也可以实现这种神奇的行为! 属性 访问可以使用 the __get
, __set
, and __unset
magic methods, and the array access can be implemented using the ArrayAccess interface.
实现
在回答上一个问题时,我发现了以下我无法理解的行为。以下代码显示了问题...
<?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>
元素的第一个循环没有设置值,但间接访问它的第二个循环工作正常。
有什么区别?
区别与循环或引用无关,而与 =
在每种情况下的确切含义有关。
第一个版本可以简化为:
$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
class 拦截数组访问以便您可以编写$foo->NameThatOccursMoreThanOnce[3]
和 $some_element['Attribute']
。所以事实证明我们可以这样写:
$value = $xml->Data->Value;
$value[0] = 1;
这里,$value
是一个SimpleXMLElement
对象,它可以拦截$value[0] = 1
像$value->setValueOfItem(0, 1)
.
在这种情况下,该对象包含 <Data>
元素内部称为 <Value>
的所有元素的集合;但方便的是,即使对象已经缩小到一个项目,[0]
也只是指回相同的元素,所以这也有效:
$value = $xml->Data->Value[0];
$value[0] = 1;
最后,请注意您自己的对象也可以实现这种神奇的行为! 属性 访问可以使用 the __get
, __set
, and __unset
magic methods, and the array access can be implemented using the ArrayAccess interface.