PHPでびっくりしたバグの話

MoriKensukePosted by

グリフォン Advent Calendar 2018 10日目の記事を担当しました、森と申します。

運用プロジェクトでサーバーサイドエンジニアをしています。

この記事では運用中に出会ったPHPで驚いたバグについて書きます。

※この記事はGRIPHONE Advent Calendar 2018 10日目の記事です。
https://adventar.org/calendars/3147
https://qiita.com/advent-calendar/2018/griphone


バグのエラー内容

大型の改修が入ったあとにデバッグをしてもらったところ、以下のようなエラーログが出ました。

Function name must be a string

実際のコードはお見せできないのですが、例えば以下のようなコードで再現することができます。

function calc_sum($a, $b){
  return $a + $b;
}

$first_arg = 100;
$second_arg = 200;

echo sprintf(
  'result = %d',
  calc_sum(
    $first_arg
   (@$second_arg ?: NULL)
  )
);

このコードを実行すると以下のエラーが出ます。

Fatal error: Uncaught Error: Function name must be a string

自分が調査した時はおかしな関数呼び出しがないように見えるのでなんでこんなエラーになるのか不思議でした。
PHPerの方はぜひどこがおかしいのか探してみてください!

バグの原因

さて、おかしい箇所はわかりましたか?
正解は以下です。よく見るとcalc_sum関数の第一引数$first_argにカンマがついていませんね。

// 間違い
$first_arg

// 正解
$first_arg,

さて、これでなぜFunction name must be a stringエラーが出てしまうのかというと、以下の部分が関数呼び出しの構文として解釈されてしまったためです。

$first_arg
(@$second_arg ?: NULL)

$first_argが関数名、(@$second_arg ?: NULL)が引数として解釈されたのに、$first_argが100と関数名としてふさわしくなかったため、このようなエラーが出た、ということになります。

この例はプログラムも短く、ロジックも簡単なので原因がわかりやすいと思いますが、実際のプログラムは10000行を超えるファイル、ロジックもかなり複雑だったので原因判明までに1時間強かかりました。こういう構文が変わってしまうようなタイポはシンタックスエラーで落ちるだろう、という先入観があったのも良くなかったと思います。

全く心当たりのないエラーに遭遇した時、PHPでは構文が意図したものにならずにバグが起きることもあるということを覚えておくと役に立つと思います。