haschildren()
Узнайте, как PHP-метод SimpleXMLIterator::hasChildren() определяет наличие дочерних узлов у текущего XML-элемента итератора, с примерами и нюансами.
Что делает hasChildren()
hasChildren() — это метод класса SimpleXMLIterator. Он возвращает true, если элемент, на котором в данный момент позиционирован итератор, имеет хотя бы один дочерний элемент, и false, если этот элемент является листовым (содержащим только текст или пустым) узлом.
Метод унаследован из интерфейса PHP RecursiveIterator, который реализуют и SimpleXMLIterator, и SimpleXMLElement. Его задача — сообщить механизму рекурсивного обхода: «следует ли спускаться в этот узел?». Это ключевая идея и источник большинства недоразумений:
hasChildren() не спрашивает «есть ли у $this дочерние элементы?». Он спрашивает «есть ли дочерние элементы у элемента в текущей позиции итератора?». Как правило, его вызывают внутри цикла с rewind() / valid() / next(), или позволяют RecursiveIteratorIterator вызывать его автоматически — но не обращаются к нему напрямую для произвольного элемента.
public SimpleXMLIterator::hasChildren(): boolМетод не принимает параметров и возвращает bool. Чтобы прочитать дочерние элементы после того, как метод вернул true, используйте его вместе с getChildren().
Создание SimpleXMLIterator
hasChildren() существует только в SimpleXMLIterator, поэтому создайте экземпляр из XML-строки с помощью new SimpleXMLIterator() или загрузите документ и приведите его к нужному типу. Вот небольшой каталог, где один элемент имеет дочерние элементы, а другой — нет:
<?php
$xml = new SimpleXMLIterator(<<<XML
<store>
<book>
<title>Modern PHP</title>
<author>Josh Lockhart</author>
</book>
<note>Closed on holidays</note>
</store>
XML);
for ($xml->rewind(); $xml->valid(); $xml->next()) {
if ($xml->hasChildren()) {
echo $xml->key() . " has children:\n";
foreach ($xml->getChildren() as $name => $value) {
echo " {$name}: {$value}\n";
}
} else {
echo $xml->key() . " (leaf): " . $xml->current() . "\n";
}
}Вывод:
book has children:
title: Modern PHP
author: Josh Lockhart
note (leaf): Closed on holidaysОбратите внимание: цикл обходит итератор вручную с помощью rewind(), valid(), next(), key() и current(). hasChildren() сообщает о том элементе, на котором в данный момент находится курсор.
Рекурсивный обход всего дерева
Настоящая польза от hasChildren() проявляется, когда вы передаёте итератор в RecursiveIteratorIterator. Эта обёртка сама вызывает hasChildren() и getChildren(), автоматически спускаясь вглубь, — так можно «распрямить» вложенный документ любой глубины:
<?php
$xml = new SimpleXMLIterator(<<<XML
<library>
<shelf>
<book>
<title>PHP Basics</title>
</book>
</shelf>
<desk>Front entrance</desk>
</library>
XML);
$tree = new RecursiveIteratorIterator(
$xml,
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($tree as $name => $node) {
echo str_repeat(' ', $tree->getDepth()) . $name . "\n";
}Вывод:
shelf
book
title
deskЗдесь вы вообще не вызываете hasChildren() вручную — RecursiveIteratorIterator использует его внутри себя, чтобы решить, когда нужно рекурсировать.
Когда применять
- Вы обходите XML-структуру неизвестного формата и хотите знать, нужно ли углубляться перед чтением значений.
- Вы строите древовидное представление, цепочку хлебных крошек или плоский список из вложенного XML.
- Вы хотите, чтобы механизм итераторов PHP (
RecursiveIteratorIterator, фильтры) обходил XML за вас, не прибегая к вложенным цикламforeach.
Если вам нужно просто получить дочерние элементы известного элемента, hasChildren() обычно не нужен — вызовите children() у SimpleXMLElement и проверьте результат с помощью count().
Распространённые ошибки
- Вызов на
SimpleXMLElement. ОбычныйSimpleXMLElement, созданный черезsimplexml_load_file()илиsimplexml_load_string(), реализуетRecursiveIterator, однако удобная семантикаhasChildren()относится именно кSimpleXMLIterator. Используйте этот класс, когда нужен данный метод. - Ожидание обнаружения атрибутов или текста.
hasChildren()учитывает только дочерние элементы. Элемент, содержащий только текст или только атрибуты, вернётfalse. - Вызов до позиционирования курсора. Всегда сначала вызывайте
rewind()(или выполняйте итерацию); результат отражает текущую позицию, которая не определена до первого элемента.
Заключение
SimpleXMLIterator::hasChildren() — это привратник рекурсивного обхода XML: он сообщает, есть ли у текущего элемента итератора дочерние элементы, чтобы ваш код — или RecursiveIteratorIterator — знал, когда следует спускаться вглубь. Используйте его вместе с getChildren() для чтения этих дочерних элементов, а children() или полное руководство по SimpleXML — когда нужно получить содержимое узла напрямую.