为什么php simplexml允许addChild在其中一个例子中而不是另一个?

时间:2016-07-13 04:56:08

标签: php namespaces simplexml xml-namespaces

我想知道是否有人能够清楚地解释为什么这段代码不起作用:

<?php
$base = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" .
    '<ns:RootNode xmlns:ns="http://www.bogus.com/bogus/">' .
    '<ns:RequestHeader/>' .
    '<ns:ContractInfo/>' .
    '</ns:RootNode>' . "\n";
$xml = simplexml_load_string($base);
$header = $xml->RequestHeader;

echo get_class($header) . "\n";

$header->addChild('SourceID', '123456');
echo $xml->asXML() . "\n\n";

...导致此输出:

SimpleXMLElement
PHP Warning:  SimpleXMLElement::addChild(): Cannot add child. Parent is not
a permanent member of the XML tree in 
C:\yadayada\test.php on line 12

Warning: SimpleXMLElement::addChild(): Cannot add child. Parent is not a permanent member of the XML tree in 
C:\yadayada\test.php on line 12
<?xml version="1.0" encoding="UTF-8"?>
<ns:RootNode xmlns:ns="http://www.bogus.com/bogus/"><ns:RequestHeader/><ns:ContractInfo/></ns:RootNode>

...但是这段代码确实如此:

$base = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" .
    '<ns:RootNode xmlns:ns="http://www.bogus.com/bogus/">' .
    '<ns:ContractInfo/>' .
    '</ns:RootNode>' . "\n";
$xml = simplexml_load_string($base);
$header = $xml->addChild('RequestHeader');

echo get_class($header) . "\n";

$header->addChild('SourceID', '123456');
echo $xml->asXML() . "\n\n";

...导致此输出:

SimpleXMLElement
<?xml version="1.0" encoding="UTF-8"?>
<ns:RootNode xmlns:ns="http://www.bogus.com/bogus/"><ns:ContractInfo/><ns:RequestHeader><ns:SourceID>123456</ns:SourceID></ns:RequestHeader></ns:RootNode>

基本上我能看到的唯一区别是,在第二种情况下,我将节点添加到根元素,在第一个示例中,我将它们添加到子元素。

我已经阅读了PHP.net上的相关SimpleXML文档,它使用的示例更像第二个,但我没有看到任何明确禁止第一种方法的内容。我还发现了一些SO文章谈论类似的问题,但没有一个真正解释为什么我不能使用SimpleXML直接将子节点添加到现有节点。

我觉得奇怪的是,在这两种情况下,代码试图修改的对象都是相同的类型,SimpleXMLElement,但它们显然没有共享相同的属性,因为一个允许addChild和另一个没有。

在这种情况下,我已经找到了让我的代码工作的方法,但我真的很想理解为什么它的工作原理。

修改

感谢BeetleJuice指出PHPs SimpleXML处理器没有自动处理名称空间。 Kevin Yank建议对代码稍作修改,使其正常工作。这条线

$header = $xml->RequestHeader;

变为

$header = $xml->children('http://www.bogus.com/bogus/')->RequestHeader;

问题解决了,孩子节点被添加到正确的命名空间而没有额外的工作:

SimpleXMLElement
<?xml version="1.0" encoding="UTF-8"?>
<ns:RootNode xmlns:ns="http://www.bogus.com/bogus/"><ns:RequestHeader><ns:Source
ID>123456</ns:SourceID></ns:RequestHeader><ns:ContractInfo/></ns:RootNode>

1 个答案:

答案 0 :(得分:2)

在您的第一个实例中,您认为$xml->RequestHeader引用了源中的<ns:RequestHeader/>节点,但它没有。您可以使用任何虚假名称而不更改$xml->asXML()的输出;因此,当您尝试添加子项时会收到警告,因为$header实际上并不是$xml树的成员。

在第二个实例中,您将孩子直接添加到已解析的$xml树中,因此您不会收到警告:

$header = $xml->addChild('RequestHeader');

似乎SimpleXML ->运算符不是为了整理ns:标志而设计的。如果您从$base

中删除它们,则第一个代码块有效
$base = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" .
    '<RootNode xmlns="http://www.bogus.com/bogus/">' .
    '<RequestHeader/>' .
    '<ContractInfo/>' .
    '</RootNode>' . "\n";
$xml = simplexml_load_string($base);
$header = $xml->RequestHeader;

$header->addChild('SourceID', '123456');
echo $xml->asXML() . "\n\n";

这不会引发警告