Кто-нибудь знает не-TeX эквивалент для TikZ?

Причина, по которой удаляется только первый div, пожалуй, проще всего объясняется так:

Вы перебираете все дочерние узлы. Эта итерация начинается с установки текущего узла на первый ребенок ( DOMNode::$firstChild ). Затем вы обрабатываете этого дочернего элемента, а когда закончите, вы продолжаете следующий дочерний элемент (то есть DOMNode::$nextSibling ).

Но если теперь удалить текущий узел из родительского

$object->parentNode->removeChild($object);

У текущего узла в итерации больше нет следующего брака (поскольку он был удален из его родителя). Поэтому итерация foreach заканчивается сразу же после удаления первого элемента div .

Существуют различные способы решения этой проблемы. С чистым PHP и не используя любой xpath, вы можете сохранить все узлы для удаления в массиве сначала, а затем удалить их. Функция iterator_to_array весьма удобна в таких ситуациях:

$divs = iterator_to_array($doc->getElementsByTagName('div'));
foreach ($divs as $div) {
    $div->parentNode->removeChild($div);
}

Эти четыре строки кода заменяют всю логику итерации и рекурсии вашей (не работающей) функции ( !).

Вы также можете исправить свою функцию, используя CachingIterator , который уже внутренне уже является следующим элементом, в то время как вы итерации текущего (текущий элемент кэшируется). Он не будет признан недействительным, потому что, как только вы удалите текущий узел из родителя, следующий узел уже был извлечен.

Примерно для вашего кода, который изменил бы следующие строки:

foreach($object->childNodes as $child) {            
    iterate_children($child);
}

:

$children = $object->childNodes;
$children = new IteratorIterator($children);
$children = new CachingIterator($children, CachingIterator::TOSTRING_USE_KEY);
foreach ($children as $child) {
    iterate_children($child);
}

, но обратите внимание, что этот код предназначен только для демонстрации. Если вы скопируете & amp; вставьте это в свой пример, это сработает, потому что у вас есть некоторые другие проблемы в вашем коде, которые стали бы серьезными с такими изменениями.

Этот код по-прежнему будет иметь рекурсию, которая на самом деле не нужна, поскольку вы могли бы выполнять итерацию узлы в порядке документа. Для этого у меня есть DOMNodeIterator в Iterator Garden . Эта библиотека также имеет простой DOMElementFilter в ветке разработки . Поскольку проблема с соседним братом здесь одинакова, использование этих двух требует также CachingITerator:

$divs = new CachingIterator(new DOMElementFilter(new DOMNodeIterator($doc), 'div'), CachingIterator::TOSTRING_USE_KEY);
foreach ($divs as $div) {
    $div->parentNode->removeChild($div);
}

Этот код снова очень похож на пример iterator_to_array. Как часто итераторы позволяют вам создавать больше повторно используемого кода благодаря их декоративному характеру.

Надеюсь, это помогло вам понять, почему это произошло, а также продемонстрировало некоторые способы борьбы с этим.


По соображениям полноты здесь ваш код с лучшей логикой обработки ошибок и обхода:

function iterate_children(DOMNode $node)
{
    if ($node instanceof DOMElement and $node->tagName == "div") {
        $parent = $node->parentNode;
        $parent->removeChild($node);
        return;
    }

    $children = $node->childNodes;
    if (!$children) {
        return;
    }

    $children = new IteratorIterator($children);
    $children = new CachingIterator($children, CachingIterator::TOSTRING_USE_KEY);
    foreach ($children as $child) {
        iterate_children_old($child);
    }
}

И здесь реализация без рекурсии и с массивом:

 Great 
dont want this

some more

more crap here
'; $doc = new DOMDocument(); $doc->recover = true; $saved = libxml_use_internal_errors(true); $doc->loadHTML($html); libxml_use_internal_errors($saved); $divs = iterator_to_array($doc->getElementsByTagName('div')); foreach ($divs as $div) { $div->parentNode->removeChild($div); } echo $doc->saveHTML();

19
задан Community 23 May 2017 в 10:29
поделиться