Как PHP SimpleXML оценивается как выражение?

Рассмотрим этот код 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, можно найти в
раздел «Преобразование в логическое значение».

Раздел «Преобразование в логическое значение» этого объяснения гласит:

Преобразование в логическое значение
При преобразовании в логическое значение следующие значения считаются ЛОЖНЫМИ:
… omissis …

Если вы выполните код выше, это результаты на моем 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 используется как условное выражение «если» (как «если», так и троичный оператор), так как это выражение оценивается по-разному от приведения к логическому.

У кого-нибудь есть представление о том, что здесь происходит?

0

Решение

Прежде всего, давайте отбросим часть вашего кода.

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 (Элементы a а также c)

Так что это не случайно, но имеет особые правила, считая его истинным или ложным.

Забыл добавить

Примечание: SimpleXML установил правило добавления итерационных свойств в большинство методов. Их нельзя просмотреть с помощью var_dump () или чего-либо еще, что может исследовать объекты.

Это «странное» поведение является следствием того, что он внедряет инераторов.

Редактировать:
Как вы указали в комментарии, что заставило вас проанализировать это поведение. Я думаю, что вы никогда не должны проверять объекты SimpleXML на if($obj) основа.
Вместо этого используйте SimpleXML::attributes() или же strval сделать все проверки. Тогда вы уверены, что вы проверяете.

Edit2:
$ xmlObject считается истинным (не пустым), если имеет свойства OR Chilren (подузлы) (это прекрасно работает)
Чтобы проверить, имеет ли объект значение, вы приводите его к строке.

В этом случае пустое значение не означает, что оно имеет значение, только если оно имеет дочерние элементы или свойства.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]