タイトルが長いですが、端的に言うと「特定の状況下のWordPressをアップデートしたら表示がおかしくなった」という話です。
具体的には、「シングルページで記事本文が表示されない」という状態です。
経緯
今回のシチュエーションは以下の通り。
- (テーマでシングルページのテンプレートに
index.php
を汎用的に使い回さず、single.php
等の専用テンプレートファイルを使用している) index.php
のメインループ中にテンプレートタグthe_content()
が存在しない- パーマリンク構造が投稿日時の数字のみから成る(スラッシュ
/
含む) - WordPress本体のバージョンを
4.9.13
から4.9.14
にアップデートした (4.9.14は昨日、4/29にリリースされました)- 私は実際には遭遇はしていませんが、おそらく
5.4.0
から5.4.1
にアップデートした場合も同じ現象が発生すると思われます(後述)
- 私は実際には遭遇はしていませんが、おそらく
最初の条件は大体のテーマがそうなっていると思うので、「無条件に条件として合致する」という扱いでよろしいかと。
で、たまたまですが、今回は2.の条件を満たすテーマが使用されていました。
特に注意すべきは3つ目の条件。今回のケースでは、以下のようなURLになるようにパーマリンク設定が行われていました。
https://example.com/<投稿日時の年4ケタ>/<同 月2ケタ>/<同 日2ケタ>/<同 時2ケタ><同 分2ケタ><同 秒2ケタ>/
そして、自動更新により4.9.13
から4.9.14
にアップデートされ、今回の現象に遭遇した、という経緯になります。
調査
最初は原因が分からなかったので、まずShow Current Templateをインストール。
これでどのテンプレートが適用されているか調べたところ、シングルページで使用されているテンプレートファイルはindex.php
でした。
……single.php
が存在しているのですが。
これで「何らかの理由によりテンプレート階層の判定でsingle.php
がスルーされ、index.php
が適用されている」ことが分かりました。
ここで条件2.にあるように、今回はindex.php
にthe_content()
がないので記事本文が表示されず……という現象になっていたわけです。
原因
直接の原因はWordPress本体の4.9.14
で行われた修正と思われます。該当コードは以下。
この修正内容の中で、wp-includes/class-wp-query.php
の条件分岐の1つが削除されていました。
} elseif ( ('' !== $qv['hour']) && ('' !== $qv['minute']) &&('' !== $qv['second']) && ('' != $qv['year']) && ('' != $qv['monthnum']) && ('' != $qv['day']) ) {
// If year, month, day, hour, minute, and second are set, a single
// post is being queried.
$this->is_single = true;
この部分。
$qv
はquery_vars
で、そのキー名のうち(おそらく投稿日時等の)時・分・秒・年・月・日で判定していますね。これに該当する場合、is_single
フラグがtrue
になる、と。
このコードが4.9.13
から4.9.14
の間で削除されたので、今回の現象に至ったと思われます。
5.4.0
から5.4.1
へアップデートした場合
私は実際には遭遇・確認・検証していませんが、Twitterで「5.4.1
にアップデートしたらシングルページが表示されない」というツイートを見かけました。
「もしや……」と思い先ほどと同じくGithubの5.4.0
と5.4.1
の間のコミットを追いかけてみました。
- Query: Ensure that only a single post can be returned on date/time ba… · WordPress/WordPress@a6706a1
すると、ありました。
} elseif ( ( '' !== $qv['hour'] ) && ( '' !== $qv['minute'] ) && ( '' !== $qv['second'] ) && ( '' != $qv['year'] ) && ( '' != $qv['monthnum'] ) && ( '' != $qv['day'] ) ) {
// If year, month, day, hour, minute, and second are set,
// a single post is being queried.
$this->is_single = true;
同じですね。やはり削除されています。
対処
今回はシングルページのURLに記事の投稿日時以外の文字列を混ぜることで現象を回避(=single.php
を使用する条件に引っかける)しました。
本来ならば既に運用が始まっているサイトでパーマリンク設定を変更するのは止めた方が良いと思うのですが、今回はテーマにあまり手を入れたくなかったのと、検証環境なので逆に既存のURLが無効になっても構わない、ということを承知の上でこの方法を取りました。
具体的には
https://example.com/<投稿日時の年4ケタ>/<同 月2ケタ>/<同 日2ケタ>/<同 時2ケタ><同 分2ケタ><同 秒2ケタ>-<記事ID>/
と、post_id
をパーマリンク設定に入れて、シングルページでsingle.php
が適用されるようになったことを確認しました。
備考
なお、今回の修正はパーマリンク設定を考えると個人的には妥当な修正だと思います。
当該修正のコミットメッセージにも
Query: Ensure that only a single post can be returned on date/time based queries.
とあります。
拙い訳で恐縮ですが、「日時ベースのクエリでは、1つの投稿のみが返されることを保証してください。」といったところでしょうか。
これについては、WordPressの予約投稿が分かりやすいのではないでしょうか。
予約投稿を使えば、複数の記事を全く同じ日時で公開することができます。
例として、 2020/5/1 9:00 に記事タイトル「Tipton」と「Adderley」の2つの記事を公開するように予約投稿したとしましょう。
もしパーマリンク設定を投稿日の日時のみで指定していた場合、予約投稿により1つのURLで複数の記事がヒットしてしまう可能性が考えられます。
先ほどの「 2020/5/1 9:00 」に予約投稿する例では、https://example.com/2020/05/01/090000/
に「Tipton」と「Adderley」の2つの記事が紐づいてしまう、ということになります。
これでは、どちらを表示して良いのか判断が付きません。
そのため、「日時以外のパラメータを使って記事が一意に定まる (例えば記事ID)」ようにパーマリンクの設定を行うのが一つの対処法になると考えられます。
ただし、先述でも触れましたが既に運用を開始しているサイトでパーマリンク設定を変更すると今まで投稿してきた記事に影響が及ぶのであまりお勧めはできません。
後の対処法としては、functions.php
内やプラグインとしてsingle.php
に引っかかるような判定を自前で書く、というところでしょうか。ここまで書いておいて大変申し訳ないですが、今回はこちらの方法に関しては未検証なのでパスで。