概要
\Illuminate\Foundation\Bootstrap\HandleExceptions
で error_reporting
が上書きされて php.ini
での設定が無視されるので上書きし返した話
本文
PHP 8.0がリリースされて4ヶ月が経ちました。Laravel本体はPHP 8.0に対応しているそうなので、もう少し様子見をして問題なさそうであれば新規プロジェクトから入れていきたいですね。 match
式が待ち遠しいです。
ところで、本記事は PHP 7.4 とLaravelの error_reporting に関する挙動についてになります。
PHP 7.4 で null や数値への配列アクセスが E_NOTICE
レベルの警告が出るようになりました。Laravelでは hasOne リレーションの先のモデルに対してうっかり配列アクセスしたりすると出たりします(とくに、この場合はDB内容に依存して出たり出なかったり……)
ある環境でPHPのバージョンを7.3から7.4に上げるアップデートを行い、マネージャーに確認して本番環境のみ php.ini の error_reporting
設定で E_NOTICE
を上げないように設定しました。このときは一旦エラーを出さないことを優先し、開発環境側で順次修正する計画としました。
(本来ならテスト自動化と静的解析とを用いて発見、修正するのが正しいアプローチ、だと思います。特にこの警告に関しては PHP 8.0 で E_WARNING
レベルに上がることが分かっているので、次回もこのアプローチを使って警告を握りつぶす範囲が広がるのは避けたいところです)
ところが、ブラウザからWebアプリケーションを開いてみると消したはずの Trying to access array offset on value of type null
警告が出ます。コードの該当箇所では E_NOTICE
をエラーとして上げているようです。
(error_reporting() & E_NOTICE) !== 0
調査すると、 \Illuminate\Foundation\Bootstrap\HandleExceptions
の function bootstrap($app)
で
error_reporting(-1);
と設定されていることが分かりました。
さいわい、\App\{Http,Console}\Kernel
の $bootstrappers
を変更することで HandleExceptions
を差し替えられそうだったので、
protected $bootstrappers = [
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
\App\HandleExceptions::class, // ここで差し替え
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
<?php
namespace App;
use Illuminate\Contracts\Foundation\Application;
/**
* Class HandleExceptions
* @package App
*
* php.ini で error_reporting = ... & ~E_NOTICE と設定されていた場合に、
* Laravelによって上書きされた error_reporting を再度上書きして ... & ~E_NOTICE する
*/
class HandleExceptions extends \Illuminate\Foundation\Bootstrap\HandleExceptions {
public function bootstrap(Application $app)
{
$php_error_reporting = error_reporting();
parent::bootstrap($app);
$laravel_error_reporting = error_reporting();
// php.ini で ~E_NOTICE されていた場合
if (!($php_error_reporting & E_NOTICE)) {
// parent::bootstrap($app) 内で error_reporting(-1); されるので、
// E_NOTICE を再度除外
error_reporting($laravel_error_reporting & ~E_NOTICE);
}
}
}
と変更し、E_NOTICE
がエラーにならないようになりました。
(このコードだとLaravelインスタンスごとに変更できず php.ini の変更でまとめて変わってしまうので、実際にはconfigも読むようにしました)