NGINXのerror.logにて、PHPのcount()関数で引数にCountableでない変数を設定していたため、エラーが発生していました。自分のテーマではなくwordpressのファイルで発生していましたが、どうにか対処することができましたよ。

事象

NGINXのerror.logに下記のようなエラーログがたくさん出力されてました。このエラーは当ブログの古い記事ページへのアクセス時に発生していました。

2018/08/01 19:02:23 [error] 14031#14031: *12154 FastCGI sent in stderr: "PHP message: PHP Warning: count(): Parameter must be an array or an object that implements Countable in 【wordpressインストールディレクトリ】/wp-includes/post-template.php on line 284" while reading response header from upstream, client: 【IPアドレス】, server: 【サーバー名】, request: "GET 【アクセス先のスラッグ(例:/hello-world/)】 HTTP/1.1", upstream: "fastcgi://unix:/var/run/php-fpm/php-fpm.sock:", host: 【ホスト名】

このブログは昔はFC2ブログで書いていて、その後wordpressに引っ越しして、記事をwordpressにインポートしています。wordpressに移行してから新しく投稿した記事ではこのエラーはあまり見当たらなかったです(少しは発生していた)。

ブログの表示自体には特に問題はなかったので、放置していても良かったのですが、エラーログが多すぎでどうしても気になります。

原因

PHP7.2からcount関数の引数にNULLやカウントできない型の変数を設定した場合に、上記のようなエラーが発生するようになったそうです。PHP7.1までは発生しないみたいです。

また、当ブログで上記エラーが発生したときの各ソフトウェアのバージョンですが、PHP 7.2.8、nginx 1.15.2、wordpress 4.9.7になります。

エラーログに表示されているように、wordpressのファイル「/wp-includes/post-template.php」の284行目でエラー発生しています。

function get_the_content( $more_link_text = null, $strip_teaser = false ) {
  global $page, $more, $preview, $pages, $multipage;



    if ( $page > count( $pages ) ) // if the requested page doesn't exist ←284行目

ここに登場する$pagesというグローバル変数ですが、下記のソースコメントより、一つの記事を複数ページに分けたとき(「続きを読む」的なやつ)の記事内容の配列みたいですね。
$pages Array of all pages in post/page. Each array element contains part of the content separated by the <!--nextpage--> tag.

でも、私は今まで、一つの記事を「続きを読む」で複数ページに分けたことないんですよね。。。謎です。

対策

古い記事でこのエラーが発生していることをヒントにして解析していきました。昔はアイキャッチ画像を設定していなかったり、抜粋(description)を書いていなかったり、記事にh2見出しを設定していなかったりしていました。

ですが、このエラーが発生している記事に共通していることは、head部のmetaタグで抜粋(description)を設定していないことでした。アイキャッチ画像やh2見出しの有無は関係ないようです。

当ブログのテーマファイルでは、抜粋を取得するための関数として、get_the_excerpt()を使っていたのですが、試しに呼ばないようにしたら、本事象のエラーログが出力されなくなりました。get_the_excerptの中まではソースを読んでないですが、そこが原因なのでしょうね。

ということで、対策としては下記のどちらかかなと思います。

  1. 抜粋(description)をちゃんと書く
  2. 抜粋(description)が無い記事ではget_the_excerpt()関数を使わないようにする

本来は抜粋を書いたほうがSEO的な意味でもいいと思うのですが、昔の記事を一つずつリライトしていくのはとても面倒なので、テーマファイルでget_the_excerpt()を使っていることころを修正しました。has_excerpt()で抜粋の有無を判定するようにしましたよ。

if(is_single()) {
  //抜粋を設定している場合
  if(has_excerpt()){
    //get_the_excerptで抜粋を取得する
    $description = get_the_excerpt();
  //抜粋を設定していない場合
  } else {
    //先頭80文字を抜粋とする
    $description = mb_substr( strip_tags( $post->post_content ), 0, 80 );
  }
}
?>
<meta id="description" name="description" itemprop="description" content="<?php echo $description; ?>">

おわりに

wordpressのPHPのファイルで発生しているエラーなので、本当はwordpressのバージョンアップで直して欲しいと思いますが、とりあえずテーマファイルの修正で対応してみました。気が向いた時に過去記事のリライトもしていこうかと思います。