wpautopフィルターがかかる前にショートコードを実行させるもっそい汚いやり方

なんだかWordPressとPHPのことばっかり書いてるなあ…ホントはPHP書けないのに…という丁稚さんです。

例によってWordPressのこと。

WordPressはデフォルトで「本文に <p><br> をよしなに追加する」という機能があります(仮に wpautop と呼ぶ)。

ちょっとショートコードやプラグインを書いたことのある人にとって、これは鬼門です。なぜかというと「投稿者が入力したとおりのテキストではなく、wpautop が加工したテキストを相手にしなくてはならない」からです。

図にするとこういう感じで、

本文(the_content)に対して、priorityつまり優先順位が 10wpautopが実行されてから 11 のショートコード群が呼ばれます。

具体的にはどうなるか。たとえば [markdown]...ほげほげ...[/markdown] とかいって「囲った内容をMarkdown化するショートコード」を書くと、ほげほげの部分に勝手に <p><br> が入ってきてしまいます。Markdownはいわゆる「HTMLブロック要素」内のマークアップを無視する使用なので、うまく動かなくなります。

まじめな対策としてはいくつかあります。下のページさんなんか細かく書いてありますが。

  1. wpautop を無効にする
    → 無効でもいいんだけど。サイト全体に影響が出ますね^^;
  2. wpautop の優先順位を本来の 10 より下げる(12 とかにする)
    → ほかのショートコードの実行に影響が出るかもしれない
  3. 上掲サイトのような方法
    → 現状ではこれがいちばんよいのだけれど、正直見通しがよくない気がする
  4. 優先順位 9 に自分のショートコード用フィルタを登録し、自前パースする
    → ショートコードの結果に wpautop がかかるのが気に入らない

ということでいまひとつ決定打がない…。

しかたないので、こんなふうにしてみました。

  • 優先順位 9 で自分のショートコード要素が内包するテキストをぜんぶ base64 エンコードする(改行もなにもなくなる)
  • 自分のショートコードの処理が呼び出されたら、まず受け渡された内包テキストを base64 デコードする
  • あとはふつうにショートコードの実装をする

図にするとこうです。

むっちゃくちゃ汚い実装ですね ^^; でも、丁稚さんみたいに実力のないプログラマが他人のパーサを乗っ取ってなにかするとき、わりと重宝する手法です(前科がたくさんあるらしい…)。

ちなみに、一部のサンプルコードを載せておきます。

preg_replace_callback を使ってすごいことになってますね。
単に正規表現でパースするだけならまだしも [\s\S]*? とか使ってるので…。
不特定第三者の入力を受け付けるショートコードだと RDoS 攻撃の対象になるかもしれません。
サイト運営者しか使わないショートコード向けならだいじょうぶでしょう。
本気で使うときは元コンテンツの md5 をキーにキャッシュ取ったほうがいいのかも。

まあ、どっちにしてもいちばん正しいアプローチは こんなことしなくてもすむようなパーサをちゃんと書く ですね…。