WordPressの wptexturize フィルターがインラインJavaScriptを密かに破壊し、論理演算子の && を HTMLエンティティの && に変換して、ショートコード内で実行されるスクリプトを壊しています。

この問題は、一連の計算機ショートコードを管理している開発者のもとで表面化しました。数週間にわたり完璧に動作していましたが、送信ボタンが反応しなくなりました。PHPの警告もコンソールのエラーもなく、HTMLのインスペクトも一見すると綺麗でした。しかし、レンダリングされたソースを確認すると if (!isNaN(bf) && bf > 0) となっていました。この単一の構文エラーにより、スクリプトブロック全体が実行されなくなっていました。

なぜこのフィルターが問題になるのか

WordPressは、コンテンツがブラウザに届く前に、一連のフィルターを通して投稿内容を処理します。wptexturize はそれらのフィルターの最初の一つです。これは、直線的な引用符をタイポグラフィ用の曲線的な引用符に変換し、複数のハイフンをエムダッシュに置き換え、アンパサンドをサニタイズします。このフィルターは、対象がコードではなく散文(文章)であることを前提としています。ショートコードがインラインの <script> タグを注入する場合でも、フィルターは実行され、JavaScriptを通常のテキストとして扱います。そのため、&& に含まれるアンパサンドは &#038; にエスケープされ、ブラウザはそれを論理的なAND演算子ではなく、リテラルな文字列として解釈してしまいます。

計算機、フォームバリデーター、インタラクティブなウィジェットなど、投稿コンテンツ内に直接JavaScriptを埋め込んでいる開発者は、この影響を受けやすくなります。この問題は計算機に限ったことではありません。&&&、またはそれに類する文字を使用するインラインスクリプトはすべて破損する可能性があります。この変換はサーバーサイドで行われるため、ブラウザは元のコードを一切見ることができず、エラーは典型的なJavaScript例外として表面化しません。

影響を受ける対象と失われるもの

  • サイトオーナー: 計算機やフォームの動作不良は、訪問者の不満を招き、直帰率を上昇させ、信頼を損なう可能性があります。
  • 開発者: ログやコンソール出力に痕跡を残さない「ゴースト」バグの調査に、何時間も費やすことになります。
  • コンテンツエディター: ショートコードを含むページを編集している際に、意図せず機能を壊してしまう可能性があります。

そのコストは単なる時間だけではありません。特に、価格設定、ローンの見積もり、健康診断などのためにカスタム計算機に依存しているサイトでは、コンバージョン率の低下に直結する可能性があります。

フィルターが何を行っているのか(簡潔な説明)

  1. 直線的な引用符を検出'" を「スマート」なタイポグラフィ用のバージョンに置き換えます。
  2. アンパサンドをクリーンアップ – すでに有効なHTMLエンティティを形成していない限り、&&amp; に変換します。
  3. コンテンツ文字列全体に適用 – ショートコードによって生成される <script> タグ内のものも含みます。

フィルターが && に遭遇すると、既存のHTMLエンティティの一部ではない2つのアンパサンドとして認識するため、それぞれを個別にエスケープし、結果として &#038;&#038; になります。

破損を防ぐ方法

1. コードを含むページでフィルターを無効にする

add_action( 'template_redirect', function () {
    if ( is_page() ) {
        remove_filter( 'the_content', 'wptexturize' );
        remove_filter( 'widget_text_content', 'wptexturize' );
    }
} );

このスニペットは、ページテンプレート上でのみ wptexturize を無効にするもので、投稿やその他のコンテンツタイプにおけるタイポグラフィの改善は維持します。また、インラインスクリプトの二次的な発生源となり得るウィジェットテキストからもフィルターを削除します。

2. && を避けるようにロジックを書き換える

フィルターの削除が望ましくない場合は、論理AND演算子が必要にならないようにJavaScriptをリファクタリングします。

// Original
if (a && b) { … }

// Refactored
var ok = a;
if (ok) { ok = b; }
if (ok) { … }

これにより数行増えますが、フィルターをトリガーするアンパサンドを排除できます。このアプローチは単純な条件には有効ですが、複雑な式では扱いにくくなる可能性があります。

3. すべてのスクリプトを外部化する

最も堅牢な解決策は、コードをインラインで埋め込むのではなく、JavaScriptファイルをエンキュー(enqueue)することです。

wp_enqueue_script( 'my-calculator', get_template_directory_uri() . '/js/calculator.js', [], null, true );

エンキューされたスクリプトは、the_content フィルターを完全にバイパスします。また、ブラウザのキャッシュの恩恵を受けられるほか、他のアセットと一緒にミニファイやバンドルを行うことも可能です。

発生している問題を特定する方法

スクリプトが明らかなエラーなしに停止した場合は、DOMインスペクターではなく「ページのソースを表示」して、&#038; を検索してください。もし <script> ブロック内でそれを見つけた場合、原因はフィルターです。ブラウザは解析可能な構文的に正しいスクリプトを一切受け取っていないため、コンソールには問題が表示されません。

反論:なぜ wptexturize を残すべきなのか?

wptexturize はフロントエンドの可読性を向上させます。曲線的な引用符や適切なダッシュ文字は、文章に洗練された印象を与え、多くのサイトオーナーにとってそれは譲れない美的な機能とみなされています。フィルターをグローバルに削除すると、テキストはタイポグラフィ的にプレーンな、生のままの状態に戻ってしまいます。

折衷案は、選択的な無効化です。コードが存在する箇所でのみフィルターをオフにするか、出力が texturizing の対象外であることを明示的にマークするカスタムショートコードを使用します。WordPress にはすでに wp_kses_post などのサニタイズ用ヘルパーが用意されています。開発者はこれらを remove_filter の呼び出しと組み合わせることで、両方の利点を最大限に活用できます。

今後の注意点

WordPress コアは、<script> タグを自動的に除外するような wptexturize の変更をまだ発表していません。そのような変更が導入されるまでは、開発者はインラインコードを手動で保護しなければなりません。フィルターをコンテキスト依存(文脈を考慮したもの)にするための提案がないか、コアの開発トラッカーを注視してください。それまでの間は、JavaScript を注入するショートコードやページビルダーの要素を監査し、上記の3つの修正方法のいずれかを適用してください。

結論: WordPress サイトでインライン JavaScript を実行している場合は、wptexturize がそれを知らぬ間に書き換えていないか確認してください。たった一つのエスケープされたアンパサンドが機能全体を動作不能にさせる可能性があり、その修正は通常、数行の PHP を記述するか、外部スクリプトへ移行することで解決できます。