fbpx

メニュー

WordPressでthe_contentをDOMりたい

高橋文樹 高橋文樹

この投稿は 9年半 前に公開されました。いまではもう無効になった内容を含んでいるかもしれないことをご了承ください。

タイトルそのままですが、現在破滅派では、ePub書き出し機能を製作中で、ePub書き出し機能自体はできたのですが、また別の問題が発生しました。

たとえば、僕は方舟謝肉祭という長編小説を発表しているのですが……

  • 連載作品だった場合、ePubで売っているのにWebで全部読めてしまうのは困るので、途中で隠したい。
  • その際、全部統一したやり方で隠したい。抜粋( excerpt ) めんどくさいから入れたくない。

破滅派では何も考えずに書くとpタグが連続する感じになるので、タグの数を数えてその4分の1だけ表示するという方針でいきます。n話までは無料で読めて、あとは途中までしか読めないというパターンですね。

さて、DOMるときに使うライブラリはHTML5-PHPです。HTML5だとbrなどの空タグにスラッシュがあってもなくてもよいからパースエラーになったりとか、DOMDocumentだと日本語が全部実体参照になるとか、そういうどうしようもないエラーが回避できます。

このライブラリ、単に読み込みと書き出しがHTML5対応になっているだけっぽいので、あとはDOMDocumentと同じです。

それでは、the_contentフィルターをかけてみます。ポイントは次の通りですね。

  1. the_contentでわたってくるタグは親ノードがないので、htmlタグを擬似的に作成する。
  2. 書き出しのとき、bodyタグの中身だけ返す
  3. ショートコードとかwpautopとかに影響を受けないように、一番最後の方にフィルター。
add_filter('the_content', function($content){
    if( sold_in_amazon() ){ // 独自関数なのでコピペしないでね!
         // パーサーを用意
         $html5 = new Masterminds\HTML5();
         // HTMLを作る
         $html = <<<HTML
<DOCTYPE html>
<html>
<head><meta charset="utf-8" /></head>
<body>{$content}</body>
</html>
HTML;
         // DOMの一部を切り出す
         $dom = $html5->loadHTML($content);
         // bodyタグを取得
         $body = $dom->getElementsByTagName('body')->item(0);
         // bodyタグの子ノードを数える
         $dom_count = $body->childNodes->length;
         // その4分の1の数を取得
         $limit = floor( $dom_count / 4 );
         // 1/4より大きいノードは削除
         for( $i = $dom_count - 1; $i >= 0; $i-- ){
             if( $i > $limit ){
                  $body->removeChild($body->childNodes->item($i));
             }
         }
         // bodyタグの中身だけ取得して$contentに設定
         preg_match('/<body>(.*)<\/body>/s', $html5->saveHTML($dom), $match);
         $content = $match[1];
    }
    return $content;
}, 9999);

というわけで、このお尻にメッセージボックスを足したりすると、こんな感じになります。ぱっと見わからないので、デザインに工夫の余地ありかもですね……透明のフィルターっぽいのかぶせるとか。

本文が途中までしか表示されなくなる
本文が途中までしか表示されなくなる

DOMの何がいいかというと、堅牢性です。「すぐパースエラーになるのに堅牢もクソもあるか」という向きもあるでしょうが、属性値を変えたりとか、そういう操作は正規表現や文字列置換でやるとぶっ壊れる可能性があるので、DOMだと安心です。

以前、WordBenchで「h3タグの1つ目の前にAdsenseタグ入れる」というプラグインを作っている方もいましたが、そういう場合なんかはDOMると楽ですね。

XPathとも一緒に使えるので、フィルタリングも可能なはず。ただし、XPathを覚えないといけないという別の罠があるんですけどね。

注意点としては、DOMるときのコストが結構高いこと。シングルページだと一回だけだからいいですけど、ループの中で使ったりするとかなり計算負荷が高いです。目に見えて遅くなるでしょうね。

DOMにもいろいろあって、BehatとかのおまけについてくるDOMパーサーは軽量らしいですね。

というわけで、よく考えたらWordPressじゃなくてPHP全般の話だったんですけど、終わります。

[429] [429] Client error: `POST https://webservices.amazon.co.jp/paapi5/getitems` resulted in a `429 Too Many Requests` response: {"__type":"com.amazon.paapi5#TooManyRequestsException","Errors":[{"Code":"TooManyRequests","Message":"The request was de (truncated...)

すべての投稿を見る

高橋文樹ニュースレター

高橋文樹が最近の活動報告、サイトでパブリックにできない情報などをお伝えするメーリングリストです。 滅多に送りませんので、ぜひご登録お願いいたします。 お得なダウンロードコンテンツなども計画中です。