【问题标题】:How does PHP SimpleXML get evaluated as an expression?PHP SimpleXML 如何被评估为表达式?
【发布时间】:2015-01-25 04:50:15
【问题描述】:

考虑这个 PHP 代码:

<?php
$xmlString = 
  '<'.'?xml version="1.0" encoding="UTF-8"?'.'>' .
  '<rootEl>' .
      '<a attrA="valA">xxx</a>' .
      '<b attrB="valB"/>' .
      '<c>oink</c>' .
      '<d/>'.
      '<e>' .
          '<f>zzz</f>' .
      '</e>' .
  '</rootEl>';

$xml = simplexml_load_string( $xmlString );
foreach ( $xml->children() as $child ) {
    echo "\$CHILD [" . $child->getName() . "]: " . $child->asXML();
    echo "\n";
    $insideIf1 = false;
    if ($child) { $insideIf1 = true; }
    $insideIf2 = false;
    if (true == $child) { $insideIf2 = true; }
    $insideIf3 = false;
    if ((boolean)$child) { $insideIf3 = true; }

    echo "    - if(\$CHILD) return \"true\"; else return \"false\"; :                     " . (($insideIf1)?"true":"false")."\n";
    echo "    - if(true == \$CHILD) return \"true\"; else return \"false\"; :             " . (($insideIf2)?"true":"false")."\n";
    echo "    - if((boolean)\$CHILD) return \"true\"; else return \"false\"; :            " . (($insideIf3)?"true":"false")."\n";

    echo "    - ((\$CHILD)?\"true\":\"false\"):                                           " . (($child)?"true":"false")."\n";
    echo "    - ((true == \$CHILD)?\"true\":\"false\"):                                   " . ((true == $child)?"true":"false")."\n";
    echo "    - (((boolean)\$CHILD)?\"true\":\"false\"):                                  " . (((boolean)$child)?"true":"false")."\n";

    echo "\n";
}

根据 PHP 文档(在线,请参阅 http://php.net/manual/en/control-structures.if.php):

正如关于表达式的部分所述,表达式被计算 为其布尔值。如果表达式的计算结果为 TRUE,PHP 将 执行语句,如果它的计算结果为 FALSE - 它会忽略它。 有关哪些值评估为 FALSE 的更多信息,请参见 “转换为布尔值”部分。

此解释的“转换为布尔值”部分指出:

转换为布尔值 转换为布尔值时,以下值被视为 FALSE: ...遗漏...

从空标签创建的 SimpleXML 对象

如果你执行上面的代码,这些是我的 PHP 5.5.9-1ubuntu4.5 (cli) 上的结果:

$CHILD [a]: <a attrA="valA">xxx</a>
- if($CHILD) return "true"; else return "false"; :                     true
- if(true == $CHILD) return "true"; else return "false"; :             true
- if((boolean)$CHILD) return "true"; else return "false"; :            true
- (($CHILD)?"true":"false"):                                           true
- ((true == $CHILD)?"true":"false"):                                   true
- (((boolean)$CHILD)?"true":"false"):                                  true

$CHILD [b]: <b attrB="valB"/>
- if($CHILD) return "true"; else return "false"; :                     true
- if(true == $CHILD) return "true"; else return "false"; :             false
- if((boolean)$CHILD) return "true"; else return "false"; :            true
- (($CHILD)?"true":"false"):                                           true
- ((true == $CHILD)?"true":"false"):                                   false
- (((boolean)$CHILD)?"true":"false"):                                  true

$CHILD [c]: <c>oink</c>
- if($CHILD) return "true"; else return "false"; :                     false
- if(true == $CHILD) return "true"; else return "false"; :             true
- if((boolean)$CHILD) return "true"; else return "false"; :            false
- (($CHILD)?"true":"false"):                                           false
- ((true == $CHILD)?"true":"false"):                                   true
- (((boolean)$CHILD)?"true":"false"):                                  false

$CHILD [d]: <d/>
- if($CHILD) return "true"; else return "false"; :                     false
- if(true == $CHILD) return "true"; else return "false"; :             false
- if((boolean)$CHILD) return "true"; else return "false"; :            false
- (($CHILD)?"true":"false"):                                           false
- ((true == $CHILD)?"true":"false"):                                   false
- (((boolean)$CHILD)?"true":"false"):                                  false

$CHILD [e]: <e><f>zzz</f></e>
- if($CHILD) return "true"; else return "false"; :                     true
- if(true == $CHILD) return "true"; else return "false"; :             false
- if((boolean)$CHILD) return "true"; else return "false"; :            true
- (($CHILD)?"true":"false"):                                           true
- ((true == $CHILD)?"true":"false"):                                   false
- (((boolean)$CHILD)?"true":"false"):                                  true

子元素“b”和“d”是空的,因此我期待“false”,而“a”、“c”、“e”不是空的,所以我期待“true”。

事实证明,PHP 文档中的声明绝对不正确,而且行为甚至看起来也不一致:隐式或显式转换为布尔值的行为方式不同(参见元素“c”的情况) ,并且如果将裸 SimpleXML 对象用作“if”条件表达式(“if”和三元运算符),则该表达式的计算结果会以某种方式从强制转换为布尔值。

有人知道这里发生了什么吗?

【问题讨论】:

  • 刚刚在 PHP 中将此作为 BUG 归档:错误 #68507:SimpleXML 对象转换为布尔值不一致bugs.php.net/bug.php?id=68507
  • 不太确定。你检查过对象的结构吗?

标签: php xml casting expression simplexml


【解决方案1】:

首先,让我们丢弃一些代码。

foreach ( $xml->children() as $child ) {
    echo "\$CHILD [" . htmlspecialchars($child->getName()) . "]: " . htmlspecialchars($child->asXML());
    echo "\n";
    $insideIf1 = false;
    if ($child) { $insideIf1 = true; }
    $insideIf2 = false;
    if (true == $child) { $insideIf2 = true; }
    $insideIf3 = false;
    if ((boolean)$child) { $insideIf3 = true; }

    var_dump($child);
  //  echo intval($child)."\n";
    echo 'VALUE EVALUATES TO: '. $child."\n";
    echo "    - if(\$CHILD) return \"true\"; else return \"false\"; :                     " . (($insideIf1)?"true":"false")."\n";
    echo "    - if(true == \$CHILD) return \"true\"; else return \"false\"; :             " . (($insideIf2)?"true":"false")."\n";
    echo "    - if((boolean)\$CHILD) return \"true\"; else return \"false\"; :            " . (($insideIf3)?"true":"false")."\n";
// This part evaluated exactly same as above. So no need to make confusion
    echo "\n";
}

下一步:

第一次和第三次测试
这些基本相同。对象被认为是真的,如果它有属性。所以基本上没有属性的 xml 标签没有属性(只是 var_dump 它们)。 没有属性的对象(没有属性但请注意它们可以有值 - 由迭代器返回)被认为是错误的:c, d

第二次测试
另一方面,此测试不测试对象本身。但是它从转换为返回的所有值:int、bool 和 string。 其中一项测试需要__toString 魔术方法。哪个返回值。然后它评估为true(元素ac

所以它不是随机的,而是在考虑它是真是假时有特殊的规则。

忘记添加

注意:SimpleXML 制定了向大多数方法添加迭代属性的规则。无法使用 var_dump() 或其他任何可以检查对象的方法查看它们。

这种“奇怪”的行为是它实现了迭代器的结果。

编辑:
正如您在评论中所说,是什么让您分析了这种行为。我认为您永远不应该选择在if($obj) 基础上检查 SimpleXML 对象。
相反,请使用 SimpleXML::attributes()strval 进行所有检查。然后你就确定你在检查什么。

Edit2:
$xmlObject 如果具有属性ORChilren(子节点),则被认为是真的(不为空)(可以正常工作) 要检查对象是否具有值,请将其转换为字符串。

在这种情况下,空并不意味着它是否具有价值,只有当它具有子或属性时。

【讨论】:

  • 感谢加塞克。我不认为这是随机行为,它有一个模式。但是这种行为绝对不是 PHP 文档中所述的行为,并且可能是基于 PHP/SimpleXML 的软件中出现错误的原因,就像我刚刚在 Magento(管理控制器)中调试的那样。 Magento 依赖“if ($xmlElement)”来表示“if (''===((string)$xmlElement))”。该问题似乎出现在 PHP 5.5 和 5.4 上,但不在 5.3 上
  • 顺便说一句。我不知道使用 if($xmlElement) 会检查什么。如果它有子、属性或值?
  • 嗯,根据 PHP 官方文档,它应该告诉您元素是否为空...参见 php.net/manual/en/language.types.boolean.php(“转换为布尔值”,与 SimpleXML 相关的部分)和 @987654322 @:正如关于表达式的部分所述,表达式被评估为其布尔值。如果表达式计算结果为 TRUE,PHP 将执行语句,如果计算结果为 FALSE - 它会忽略它。有关哪些值评估为 FALSE 的更多信息,请参阅“转换为布尔值”部分。
  • 我认为这个Edit2,应该可以消除你的疑虑。 PHP Docs 是正确的。我认为他们在这种情况下被误读了。或者不清楚“空对象”是什么意思。
  • PHP Doc 没有声明“空对象”,我同意这很令人困惑,它声明了“空标签”,这在 XML 中具有明确定义的含义: ,基本上是任何没有内容或子级的 xml 标签。
猜你喜欢
  • 2016-12-04
  • 1970-01-01
  • 2013-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多