PHP7.2のcountにハマった話


最近、開発環境のPHPのバージョンを5.6→7.2に変更しました。
PHP5.6 + CakePHP3 + Apache2.2のECサービスからAMP並のTTFBを実現するまで

こちらの記事にあるように、速度向上の施策の1つとして最近インターン先での本番環境がPHP7.2にバージョンアップしました。
いまさらcountの話かと思うかもしれませんが、後から自分が見返した時にこんなこと悩んだ時期があったなと思うためのメモのようなものです。
ここからはバージョンアップによって、ここ数日で苦しめられたPHPの関数countについての話をしたいと思います。

それは突然始まった

本番環境が、PHP7.2に変わってすぐにエラーログにWarningが出るようになり、原因調査と改修を行うように指令が出たので早速調査を行いました。

ログを確認してみた

確認してみるとそこには

Warning (2): count(): Parameter must be an array or an object that implements Countable in file名 line 〇〇

というエラーが出ていた。

英語が苦手な私が、理解するのに少しかかったが、どうやらcountの引数には配列、もしくはカウントすることができるオブジェクトでなければならないというエラーのようだった。

PHP7.2の変更点

PHPの公式マニュアルを確認すると以下の記述を発見
PHP:count

In PHP 7.2.0
count(NULL) returns a warning:
Warning: count(): Parameter must be an array or an object that implements Countable

In previous versions:
count(NULL) returned 0 without any warnings.

なるほど・・・
前のバージョンではcount(NULL)に対して0を返していたが、PHP7.2からwarningを出しているとのこと。
つまり、countを使いたい、でも変数の中身が配列またはオブジェクトの場合もあるし、NULLを返す場合もあるよという時には

if (is_array($hoge)) {
    count($hoge);
}

という判定をさせると回避できそう。

また、単なるNULLチェックをするだけなのにcountで判定している場合は

isset($hoge)

もしくは

!empty($hoge)

に変更してあげれば万事解決できそうということがわかった。

countの挙動

さらによく調べるとこんな記述があった。
countable ではない型をカウントしたときの警告

var_dump(
    count(1), // integer はカウントできません
    count('abc'), // string はカウントできません
    count(new stdclass), // Countable インターフェイスを実装していないオブジェクトはカウントできません
    count([1,2]) // array はカウントできます
);

出力例

Warning: count(): Parameter must be an array or an object that implements Countable in %s on line %d

Warning: count(): Parameter must be an array or an object that implements Countable in %s on line %d

Warning: count(): Parameter must be an array or an object that implements Countable in %s on line %d
int(1)
int(1)
int(1)
int(2)

苦しんだこと

解決法はわかるんだけど、影響範囲が広いということ

問題の解決法としては難しくないが、意外とcountにcountableではない値が入ってしまうことがあるため色々な箇所でWarningが出てしまった。そのため、いく先々でcountを見たら、中身をチェックして必要があれば修正しなければならない。
過去の遺物でNULLチェックをcountでやってしまっているところがあるので、そこでWarningが発生する。

PHPという言語についての超個人的なイメージ

PHPという言語でプログラムを書く時にあまり型を意識したことがない。
型を意識しなくてもとりあえず変数宣言をする時に$といえば良い。

$hoge = 0;
$fuga = 'aaa';
$foo = [];

という感じ。
また、PHPをやって驚いたことは

$hoge = 1; // int型
$fuga = '1'; // string型

if ($hoge == $fuga) // true

となり、もはや型とは何かと意識する必要はなかった。

この意識を変えていかないとまた、バグを生み出すことがありそう。

まとめ

PHP7.2からcountにarrayもしくはcountableなobjectを引数として与えないとWarningが出るようになった。
回避策として、is_arrayで判定するようにすれば良い。

何か、不備があればコメントいただけると幸いです。

最後に

現在、エンジニア用Twitterアカウント@gawa2101を開設しました。
今後、自身のアウトプットを積極的にしていこうと考えているのでフォローしていただけると幸いです。