😵‍💫

PHPのDOM操作でタグの入れ子が変なとき

2024/11/21に公開

環境

WordPress内での出来事ですが、どうやらWPのタグ補完が原因ではなさそうだったのでPHPを主題にしています。

事象

<p>テキスト</p>をaタグで囲みたかったのですが、下記のようになりました。

【理想】

<a href="https://~">
  <p>テキスト</p>
</a>

【現実】

<a href="https://~"></p>
  <p>テキスト</p>
<p></a></p>

なにこれ(ゴロリ)。

【補足】
デベロッパーツールから確認するとタグがブラウザに修正された状態で表示されるので、CTRL + Uでソースを表示すると元のコードが見れます。

DOM操作に使用したphpのコードは以下。

// DOMDocumentを使用してHTMLを解析
$dom = new DOMDocument('1.0','UTF-8');
$dom->loadHTML(mb_encode_numericentity(trim($content),[0x80, 0x10ffff, 0, 0x1fffff]),LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD);

$element = $dom->getElementsByTagName('p');
foreach ($element as $node) {
  $anchor = $dom->createElement('a');
  $anchor->setAttribute('href','https://~');
  // <a> 要素に <p> 要素を追加 
  $anchor->appendChild($node);

  echo $dom->saveHTML($anchor);
}

stackoverflowで「インライン要素を外側に配置しようとすると閉じタグが無視される」という投稿を見かけたので、たぶんそれに類する仕様なんだと思う。
https://stackoverflow.com/questions/45964229/is-this-a-bug-in-phps-domdocument-library

対応

というわけで、p要素の中にaタグを仕込むことにした。

<p>
  <a href="https://~">テキスト</a>
</p>

コードは以下。

$anchor = $dom->createElement('a');
$anchor->setAttribute('href','https://~');

// <p> 要素の内容を <a> 要素に移動 
while ($node->hasChildNodes()) { 
  $anchor->appendChild($node->firstChild); 
}

// <p> 要素に <a> 要素を追加 
$node->appendChild($anchor);

echo $dom->saveHTML($node);

これで正しく表示されるようになった。めでたし。

Discussion