【PHP】関数 count() にスカラー値を渡したときの挙動と、PHP7.2でのその変更点

【PHP】関数 count() にスカラー値を渡したときの挙動と、PHP7.2でのその変更点

体が全体的に左に傾いているらしいことがわかってきた kagata です。

今回は、PHP の重箱の隅をつつくような小ネタです。

サンプルコード

次のサンプルコードをご覧ください。PHP の組み込み関数 count() にさまざまな引数を渡しています。

<?php

// 要素が1個の配列
var_dump(count([0]));

// 空の配列
var_dump(count([]));

// スカラー値
var_dump(count(0));

// null
var_dump(count(null));

// 未定義の変数
var_dump(count($foo));

さて、このコードの実行結果はどうなるでしょうか?前の2つはまあわかるとして、問題は残りの3つです。結果をご存じでない方には、結果がどうなるかぜひ想像してから読み進めていただきたい。

実行結果

実行結果です。PHP7.1.14 では以下のようになりました。

int(1)
int(0)
int(1)
int(0)

Notice: Undefined variable: foo in /in/TB5Es on line 16
int(0)

いかがでしょうか。直感どおりでしょうか、それとも直感を裏切られたでしょうか。あるいはもうご存じでしたか?

関数 count() のこの挙動について、充実の PHP マニュアルでは以下のように説明されています。

もしパラメータが配列もしくは Countable インターフェイスを実装したオブジェクトではない場合、 1 が返されます。 ひとつ例外があり、array_or_countableNULL の場合、 0 が返されます。

PHP: count - Manual

こう説明されると、まあ妥当な感じもしないではありません。でも落ち着いて考えてみると、 []'' とで結果が違ったり、 [] と未定義変数とで結果が同じだったりするのは、やっぱり不都合がありそうです。

返り値によって引数のパターンを分類すると、次のようになります。

count($foo) === 1 のとき

  1. $foo は要素が1個の配列(もしくは Countable の実装)
  2. $foonull でないスカラー値( 0'' を含む)

count($foo) === 0 のとき

  1. $foo は空の配列(もしくは Countable の実装)
  2. $foonullnull が代入されているか、変数自体が未定義)

PHP7.2での仕様変更

これはよくないんじゃないか、との意見があり、PHP7.2では若干の仕様変更が加えられました。結果は次のようになります。

int(1)
int(0)

Warning: count(): Parameter must be an array or an object that implements Countable in /in/TB5Es on line 10
int(1)

Warning: count(): Parameter must be an array or an object that implements Countable in /in/TB5Es on line 13
int(0)

Notice: Undefined variable: foo in /in/TB5Es on line 16

Warning: count(): Parameter must be an array or an object that implements Countable in /in/TB5Es on line 16
int(0)

返り値は変わらないのですが、 count() にスカラー値を渡すと警告が発生するようになりました。

返り値が変わるでもなく、例外を投げたり致命的なエラーを発したりするわけでもない、比較的マイルドな変更だと思います。

PHP: 下位互換性のない変更点 - Manual

WordPress での事例

PHP7.2のこの仕様変更で、WordPress が影響を受けています。

WordPress4.9.2のコードがこう。

            $this->found_posts = count( $this->posts ); 

で、先ごろリリースされた WordPress4.9.3のコードがこう。

            if ( is_array( $this->posts ) ) { 
                $this->found_posts = count( $this->posts ); 
            } else { 
                if ( null === $this->posts ) {   
                    $this->found_posts = 0; 
                } else { 
                    $this->found_posts = 1; 
                } 
            } 

先のコードだと PHP7.2の環境で警告を発するようになったため、最新のコードでは count() に渡す前に is_array() で配列かどうか判定しています。配列でないときは、 null なら 0 、それ以外では 1 にする…という、count() の挙動が if 文で再現された格好になっているあたりに、歴史の積み重ねを感じずにいられません。

#42860 (PHP 7.2 warning - Parameter must be an array or an object that implements Countable in /wp-includes/class-wp-query.php on line 3035) - WordPress Trac

むすび

関数 count() にスカラー値を渡したときの挙動と、その挙動が PHP7.2で若干変更されたことをご紹介しました。

PHP には型に厳しくないイメージがあります。しかし、最近の PHP の仕様変更の中には、今回紹介したような型の扱いがあいまいだったところを見直すものがいくつか見られます。手堅いコーディングを心がけたいものです。

  • このエントリーをはてなブックマークに追加

この記事を読んだ人にオススメ