8

「ノードを削除」して、その内部コンテンツを親ノードに移動するという恐ろしいアルゴリズムがあります(以下を参照)...しかし、 DOMDocumentFragmentを使用して(saveXML/loadXMLを使用せずに)、より良いアルゴリズムを開発することは可能だと思います

以下のアルゴリズムはrenameNode()に触発されたものです。

 /**
  * Move the content of the $from node to its parent node.
  * Conditions: parent not a document root, $from not a text node.  
  * @param DOMElement $from to be removed, preserving its contents.
  * @return true if changed, false if not.
  */
 function moveInner($from) {
     $to = $from->parentNode;
     if ($from->nodeType==1 && $to->parentNode->nodeType==1) {     
        // Scans $from, and record information:
        $lst = array(); // to avoid "scan bugs" of DomNodeList iterator
        foreach ($to->childNodes as $e)
           $lst[] =  array($e);
        for($i=0; $i<count($lst); $i++)
          if ($lst[$i][0]->nodeType==1 && $from->isSameNode($lst[$i][0])) {  
            $lst[$i][1] = array();
            foreach ($lst[$i][0]->childNodes as $e)
                $lst[$i][1][] = $e;
          }

        // Build $newTo (rebuilds the parent node):
        $newTo = $from->ownerDocument->createElement($to->nodeName);
        foreach ($to->attributes as $a) {
        $newTo->setAttribute($a->nodeName, $a->nodeValue);
        }
        foreach ($lst as $r) {
        if (count($r)==1)
            $newTo->appendChild($r[0]);
        else foreach ($r[1] as $e)
            $newTo->appendChild($e);
        }

        // Replaces it:
        $to->parentNode->replaceChild($newTo, $to);
        return true;

    } else
        return false;
 }

入力

<html id="root">
<p id="p1"><i>Title</i></p>
<p id="p2"><b id="b1">Rosangela<sup>1</sup>, Maria<sup>2</sup></b>, 
           <b>Eduardo<sup>4</sup></b>
</p>
</html>

の出力moveInner($dom->getElementById('p1'))

... <p id="p1">Title</p> ...

の出力moveInner($dom->getElementById('b1'))

... <p id="p2">Rosangela<sup>1</sup>, Maria<sup>2</sup>, 
        <b>Eduardo<sup>4</sup></b>
    </p> ...

最初の使用後moveInner($dom->getElementById('root'))、またはに変化はありません。moveInner($dom->getElementById('p1'))

PS: 「TRIM TAG」機能のようなものです。

4

2 に答える 2

8

同じドキュメント内を移動するとき、これは実際にはそれほど面倒ではありません。あなたが単独で投稿したコードには、それ自体で最適化できる多くの場所が既にありました。たとえば、 achildNodes NodeListを単に使用して配列に変換するにはiterator_to_array:

$children = iterator_to_array($from->childNodes);

また、より話しやすい変数名を使用する必要があります。長い名前を使用しても問題はありません。コードが読みやすくなり、より重要なものをより速く表示する余地が残されます。

/**
 * Move the content of the $from node to its parent node.
 *
 * @param DOMElement $from to be removed, preserving its contents.
 * @return DOMElement the element removed (w/o it's former children)
 * @throws InvalidArgumentException in case there is no parent element
 */
function moveInner(DOMElement $from)
{
    if (!$from->parentNode instanceof DOMElement) {
        throw new InvalidArgumentException(
            'DOMElement does not have a parent DOMElement node.'
        );
    }

    /** @var DOMNode[] $children */
    $children = iterator_to_array($from->childNodes);
    foreach ($children as $child) {
        $from->parentNode->insertBefore($child, $from);
    }

    return $from->parentNode->removeChild($from);
}

それだけで機能します。同じ要素を DOMDocument の別の場所に挿入すると、要素は複製されずに移動されます。

複製する場合 (子を移動するのではなく保持するため)、子ノードをプロトタイプとして使用し、それらを複製することができます。この関数は削除された要素を返すため、コピーが含まれています。

最初にクローンなしの例、上記の関数のみ:

$removed = moveInner($doc->getElementById('b1'));

echo $doc->saveHTML(), "\nRemoved: ", $doc->saveHTML($removed);

出力:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html id="root"><body><p id="p1"><i>Title <b>2</b></i></p>
<p id="p2">Rosangela<sup>1</sup>, Maria<sup>2</sup>,
           <b>Eduardo<sup>4</sup></b>
</p>
</body></html>

Removed: <b id="b1"></b>

次に、変更された関数の 2 番目の変更はclone、次の行に追加するだけです。

        $from->parentNode->insertBefore(clone $child, $from);
                                        #####

出力:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html id="root"><body><p id="p1"><i>Title <b>2</b></i></p>
<p id="p2">Rosangela<sup>1</sup>, Maria<sup>2</sup>,
           <b>Eduardo<sup>4</sup></b>
</p>
</body></html>

Removed: <b id="b1">Rosangela<sup>1</sup>, Maria<sup>2</sup></b>

これが役に立ち、ニーズに合っていることを願っています。そこには本当に非常に多くのコードがありましたが、おそらくノードの置換シナリオが異なるため、少し誤解を招きました。また、そのシナリオでは、常に適切なコードに変更するのに最適なベースとは限らない、いくつかの異なるエラー コードにパッチを当てていました。

このところで。今日答えたばかりのクローンも非常に役立つという質問を思い出しました。

于 2013-09-27T21:16:26.640 に答える
-3

このタスクには phpQuery の使用を試すことができます。jquery に似た構文を持っています。このphpQuerypq("#b1")->html(pq("#b1")->text()); へのリンクのようなもの

于 2013-09-25T20:09:31.480 に答える