毎度おなじみ、意図的に重箱の隅をつついてみたよって話です。あるPHPプログラムを実行したら次のようなエラーに遭遇しました。
$ php over-2g-lines.php int(0) PHP Fatal error: Uncaught Error: Call to undefined function var____dump() in /Users/hnw/over-2g-lines.php:2150000004 Stack trace: #0 {main} thrown in /Users/hnw/over-2g-lines.php on line -2144967292
21億5千万4行目で致命的エラーが発生したよ!という表示のあとでスタックトレースが表示されているんですが、スタックトレースの方ではマイナス21.4億行目あたりでエラーが出ていることになっています。行数がマイナスというのは不思議ですね。
種明かしするまでもないと思いますが、原因は32bit整数のオーバーフローです。PHPのVM命令を管理するzend_op構造体のlinenoメンバは32bit符号無し整数で管理されているため、PHPは43億行目あたりまでしか正確にプログラムの行数をカウントできません。
さらに、printfの修飾子として%dなどとsignedの指定をしている場所があると上のように21.5億あたりでオーバーフローして負数になってしまいます。これはもちろんバグですが、修正したところで誰得な気がします。気が向いたらバグレポを出そうかなと思いつつ、今日はブログ記事にして寝ることにします。
ちなみに上記結果はPHP 7.0.9のものですが、PHP 5系(5.3.0以降)でも大差ないエラー表示になります。
手元でも再現したい方のために、実験に使ったPHPプログラムをgistに上げておきました。次のようにすれば元のプログラムを手元に復元できます。
$ curl 'https://gist.githubusercontent.com/hnw/128439edf806daadbdf548b730d67627/raw/over-2g-lines.php.bz2.base64' | base64 -D | bzip2 -cd > over-2g-lines.php
gistに上がっているファイルは2KBほどですが、展開後のPHPファイルは約2GBになりますので、テキストエディタなどで開くと確実に大惨事です。ご注意ください。
ちなみにPHPファイルの内容は以下の通りです。
<?php /* (21億5千万行の空行) */ $x=0; var_dump($x); var____dump($x);