Замена DOMNode классами PHP DOM - PullRequest
2 голосов
/ 06 января 2011

Я учусь работать с классами DOM *, доступными в PHP, и заметил (как мне кажется,) нерегулярность в моем тестировании.

Учитывая этот документ, ZuqML_test_100.html:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:zuq="http://localhost/~/zuqml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
</head>
<body>
    <h1>
        <zuq:data name="siteHeader" />
    </h1>
    <h2>
        <zuq:data name="pageHeaderName" />
        <span>&nbsp;|&nbsp;</span>
        <zuq:data name="pageHeaderTitle" />
    </h2>
    <zuq:region name="post">
        <zuq:param name="onEmpty">
            <div class="post noposts">
                <p>There are no posts to show at this time.</p>
            </div>
        </zuq:param>
        <div class="post">
            <h3><zuq:data name="postHeader" /></h3>
            <p>
                <zuq:data name="postText">
                    <zuq:format type="trim">
                        <zuq:param name="length">300</zuq:param>
                        <zuq:param name="append">
                            <a>
                                <zuq:attr name="href">
                                    ./?action=viewpost&amp;id=<zuq:data name="postId" />
                                </zuq:attr>
                                <zuq:data name="postAuthor" />
                            </a>
                        </zuq:param>
                    </zuq:format>
                </zuq:data>
            </p>
        </div>
    </zuq:region>
</body>
</html>

Я пытаюсь заменить все узлы <zuq:data /> простым текстовым узлом со значением foo.Я делаю это со следующим фрагментом:

$root = new DOMDocument();
@$root->load('ZuqML_test_100.html');

foreach($root->getElementsByTagNameNS($root->lookupNamespaceURI('zuq'), 'data') as $node){
    $node->parentNode->replaceChild($node->ownerDocument->createTextNode('foo'), $node);
}

echo $root->saveXML();

Это вроде работает, однако мой вывод по-прежнему содержит <zuq:data /> узлов, как показано здесь:

<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:zuq="http://ichorworkstudios.no-ip.org/~/zuqml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
</head>

<body>

        <h1>
        foo
    </h1>

    <h2>
        <zuq:data name="pageHeaderName"></zuq:data>
        <span>&mdash;</span>
        foo
    </h2>

    <zuq:region name="post">
        <zuq:param name="onEmpty">
                <div class="post noposts">
                <p>There are no posts to show at this time.</p>
            </div>
        </zuq:param>
        <div class="post">
                <h3><zuq:data name="postHeader"></zuq:data></h3>
            <p>
                foo
                </p>
        </div>
    </zuq:region>

</body>
</html>

Почемунеужели некоторые <zuq:data /> узлы остались позади?

Ответы [ 2 ]

3 голосов
/ 06 января 2011

Объяснение, предлагаемое ircmaxell, что

вы изменяете список результатов при его итерации,

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

Вот что делает ваш код при запуске

В начале в NodeList будет семь узлов.

Первый из них -

<zuq:data name="siteHeader"></zuq:data>

После этого количество узлов уменьшается до шести.Следующим удаляемым узлом будет

<zuq:data name="pageHeaderTitle"></zuq:data>

Но если вы посмотрите на свою разметку, вы увидите, что следующим элементом zuq: data будет

<zuq:data name="pageHeaderName" />

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

0 siteHeader
1 pageHeaderName
2 pageHeaderTitle
n …

Когда текущая позиция равна 0 и вы удаляете этот узел из документа,вы получаете список, подобный этому

0 pageHeaderName
1 pageHeaderTitle
n …

Вы все еще находитесь в позиции 0, и, таким образом, когда вы переходите к следующему элементу в NodeList, вы пропустите узел в новой позиции 0. Вы идете прямоto pageHeaderTitle, оставляя pageHeaderName необработанным.

После удаления pageHeaderTitle количество узлов уменьшается до пяти, что делает

<zuq:data name="pageHeaderName"></zuq:data>

новым элементом в текущей позиции.Следовательно, следующий удаляемый узел - это

<zuq:data name="postText">
    <zuq:format type="trim">
    <zuq:param name="length">300</zuq:param>
        <zuq:param name="append">
        <a>
        <zuq:attr name="href">
        ./?action=viewpost&amp;id=
        <zuq:data name="postId"></zuq:data>
        </zuq:attr>
        <zuq:data name="postAuthor"></zuq:data>
        </a>
    </zuq:param>
    </zuq:format>
</zuq:data>

Как видите, в нем есть еще два элемента zuq: data.Следовательно, количество узлов уменьшится до 2 (5 - 1 текущий узел - 2 дочерних элемента).

После этого итерация по NodeList заканчивается, в результате чего вы получаете

<zuq:data name="postHeader"></zuq:data>

и

<zuq:data name="pageHeaderName"></zuq:data>

все еще в документе.

3 голосов
/ 06 января 2011

Я думаю, это связано с тем, как вы выполняете итерации. Вы изменяете список результатов, так как он повторяется, поэтому он ломается (побочные эффекты). Попробуйте изменить свой цикл на это:

$nodes = $root->getElementsByTagNameNS($root->lookupNamespaceURI('zuq'), 'data');
$i = $nodes->length - 1;
while ($i >= 0) {
    $node = $nodes->item($i);
    $node->parentNode->replaceChild(
        $node->ownerDocument->createTextNode('foo'), 
        $node
    );
    $i--;
}

По сути, он просто перебирает список узлов в обратном направлении, поэтому при удалении узлов они удаляются не с начала, а с конца ...

...