W3docs

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 — когда нужно получить содержимое узла напрямую.

Практика

Практика
Что возвращает SimpleXMLIterator::hasChildren()?
Что возвращает SimpleXMLIterator::hasChildren()?
Was this page helpful?