Проблема getElementsByTagName в php DomDocument

258
23 января 2018, 12:50

Есть вот такой код на php. С помощью DOMDocument ищу все теги img потом прохожу в цикле над ними и делаю изменение.

Проблема в том что в html с которым я работаю есть 5 тегов img. А цикл проходит 3 раза.

В чем может быть проблема?

$dom = new DOMDocument();
$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$imgs_t = $dom->getElementsByTagName('img');
$amp_img_src = '/images/front/what_is_dt_video.jpg';
$count_foreach = 0;
foreach( $imgs_t as $img_t ){
    $count_foreach++;
    $img_alt = '';
    $tmp_img_src = $img_t->getAttribute('src');
    $img_alt = $img_t->getAttribute('alt');
    if($tmp_img_src != $amp_img_src){                
        $tmp_amp_img = $dom->createElement('amp-img');
        $tmp_amp_img->setAttribute('width', 200);
        $tmp_amp_img->setAttribute('height', 200);
        $tmp_amp_img->setAttribute('src', $tmp_img_src);
        $tmp_amp_img->setAttribute('alt', $img_alt);
        $img_t->parentNode->replaceChild($tmp_amp_img, $img_t);
    }
}
var_dump($count_foreach);

А это именно тот html который входит в переменную $html в начале php кода.

<div class="what_is_dt drop_down_pg">
    <div class="drop_down_video drop_down_cont">
        <div class="col-sm-7 col-xs-12">
            <div class="video_wrap">
                <div id="ntx_dt_info_video_link" class="no-padding">
                    <img src="/images/front/what_is_dt_video.jpg" height="315px" width="481px" alt="Lorem ipsum">
                </div>
                <div class="what_fx_video_head">
                    <span>Lorem ipsum</span>
                </div>
            </div>
        </div>
        <div class="col-sm-5 col-xs-12 no-padding">
            <span class="background_green">
                <h2>Lorem ipsum</h2>
                <p><em>Lorem ipsum</em></p>
                <p><em>Lorem ipsum</em></p>
            </span>
            [T]CONFIRM_THEORY[/T]
        </div>
        [T]CONFIRM_THEORY_MOBILE[/T]
        <hr />
    </div>
    <h2 style="margin-top: 40px;"><strong>Lorem ipsum</strong></h2>
    <div style="padding-top: 30px;">
        <div class="signature-left col-lg-6 col-md-6 col-sm-6 col-xs-12">
            <img src="/uploads/docs/th%20Failure%20Swing.png" alt="Lorem ipsum" title="Lorem ipsum" width="200" height="200" />
            <span style="font-size: small;"> <strong>Lorem ipsum.</strong></span>
            <span style="font-size: small;">Lorem ipsum. </span>
        </div>
        <div class="signature-left col-lg-6 col-md-6 col-sm-6 col-xs-12">
            <img src="/uploads/docs/th%20Nonfailure%20Swing.png" alt="Lorem ipsum" width="200" height="200"/>
            <span style="font-size: small;"><strong>Lorem ipsum.</strong> </span>
            <span style="font-size: small;">Lorem ipsum.</span>
        </div>
    </div>
    <p style="clear: both; padding-top: 30px;" >Lorem ipsum.</p>
    <div style="padding-top: 30px;">
        <div class="signature-left col-lg-6 col-md-6 col-sm-6 col-xs-12">
            <img src="/uploads/docs/th Failure Swing Bottom.png" alt="" />
            <p><span style="font-size: small;"><strong>Failure Swing </strong>Lorem ipsum.</span></p>
        </div>
        <div class="signature-left col-lg-6 col-md-6 col-sm-6 col-xs-12">
            <img src="/uploads/docs/th Nonfailure Swing Bottom.png" alt="Lorem ipsum" width="200" height="200" />
            <p><span style="font-size: small;"><strong>Nonfailure Swing</strong>Lorem ipsum.</span></p>
        </div>
    </div>
    <p style="clear: both;"><br /></p>
    [T]OUR_LEARNING[/T]
</div>
Answer 1

в общем и целом проблема выглядит вот так, если сократить до минимального примера.

$html = <<<HTML
    <div class="what_is_dt drop_down_pg">       
        <img src="1">
        <img src="2">
        <img src="3">
        <img src="4">
        <img src="5">
    </div>
HTML;
$dom = new DOMDocument();
$dom->loadHTML($html);
$imgs_t = $dom->getElementsByTagName('img');
print_r($imgs_t);
$amp_img_src = '/images/front/what_is_dt_video.jpg';
$count_foreach = 0;
foreach( $imgs_t as $img_t ){
    $count_foreach++;
    $tmp_img_src = $img_t->getAttribute('src');
    print_r([$count_foreach, $tmp_img_src]);
    if($tmp_img_src != $amp_img_src){
        $tmp_amp_img = $dom->createElement('amp-img');
        $img_t->parentNode->replaceChild($tmp_amp_img, $img_t);
    }
}

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

В целом вы можете менять структуру если будете проходить эту коллекцию в обратном порядке. Тут в принципе такая классическая проблема удаления элементов массиве в цикле, если удаляете, обрабатывайте в обратном порядке, тогда удаление узлов никак не скажется на индексации массива еще не обработанных элементов

$len = $imgs_t->length;
for($i = $len-1; $i >=0 ; $i--){
    $count_foreach++;
    $img_t = $imgs_t->item($i);
    ...  
}

если представить, что наша коллекция была

A B C D E 

выполняем первую итерацию. индекс итератора 0, элемент А. Мы удаляем этот элемент, и коллеакция становится

B C D E

переходим к новой итерации, счетчик итератора ничего не знает, и увеличивается на 1. Извлекаем этот элемент и получаем C. В итоге получаем, что за счет удаления(замены) первого элемента мы теряем элемент B. В случае обработки коллекции в обратно порядке такой проблемы не возникает.

READ ALSO
Поиск по бинам, не выводит значение столбца найденного бина

Поиск по бинам, не выводит значение столбца найденного бина

Вместо найденного столбца выводит nullНе понимаю в чем проблема? Ведь если сделать var_dump($task_1), то бин отображается

309
Сортировка многомерного массива в PHP

Сортировка многомерного массива в PHP

Функция array_multisort возвращает false при попытке отсортировать многомерный массив и сортировки не происходитКак узнать, в чем проблема?

237
C++ socket(&hellip;) Как получить свой айпи?

C++ socket(…) Как получить свой айпи?

У меня есть программа серверУ меня получается к нему подключаться по всем 3 ip:

399