PHP 7.4->8.0にバージョンアップしました~Swoole 4.8.2を添えて~ - TORANA TECH BLOG¶
PHP 7.4->8.0にバージョンアップしました~Swoole 4.8.2を添えて~ - TORANA TECH BLOG クラシマです。 社内向け管理画面のMadrasのPHPバージョンを7.4から8.0へメジャーバージョンアップしました。 苦労話を共有させてください。
PHP7.4->8.0移行計画¶
まず、ドキュメントを書きました。 これにより、なんでこれをやってるんだっけ、ということを明確にします。 https://gyazo.com/a32e28d68a96ab83bee3e14f47a721e6
公式ドキュメントを熟読します。 下位互換性のない変更点
↓こんな移行作戦を立てました。
- ローカル開発用のDocker環境がPHP8.0で動くようにする
- ローカルでのテストが全部通るようにする
- CI用のDockerイメージをPHP8.0でも作って、手動でCIを回してテストが全部通るようにする
- 通常のCIはPHP7.4で動作させておき、本番deploy後にPHP8.0イメージでCIを回す
- stg環境にPHP8.0を導入して動作検証する
- 本番移行手順を作る
- 本番移行する
ポイント¶
PHP7.4とPHP8.0でcomposer.jsonを共通化する¶
```(json) "require": { "php": "7.4||8.0",
毎回composer.jsonを修正して検証用ブランチにpushしてGitHub Actionsのworkflow_dispatchで手動実行していたのです。(修正しないとcomposer validateで落ちる)
上記の修正後は検証用の専用ブランチではなくdefaultブランチで検証できるようになったので、本番へのdeploy後にGitHub Actionsのworkflow_run機能でPHP8.0用のCIを回すところが自動化できました。
## @ 演算子は、致命的なエラー を隠さなくなりました
急にテストでHTTPステータス400エラーを期待している箇所で500エラーが出るようになりました。
ログを見ると、`array_keys(): Argument #1 ($array) must be of type array, null given` というエラーが出ています。
```(diff)
$hasX = @count(array_keys($hash['x'])) !== 1;
```(diff) - \(hasX = @count(array_keys(\)hash['x'])) !== 1; + \(hasX = count(array_keys(\)hash['x'] ?? [])) !== 1;
## \Symfony\Component\Process\Processは生きてるのに\Swoole\Coroutineは死んでる
これが一番のハマりどころで、移行が1週間遅れました...。
バッチ処理の高速化のために[\Symfony\Component\Process\Process](https://symfony.com/doc/current/components/process.html)を使ってマルチプロセスで実行している箇所があるのですが、`$process->isRunning()`がtrueを返すのにSwooleのCoroutineが`WARNING swoole_signalfd_event_callback(): read from signalfd failed, Error: Resource temporarily unavailable[11](11)`って警告を吐いて死んでしまっていました。
(余談ですが、signalfdってsignal用のファイルディスクリプタなんですね。初めて知りました [Man page of SIGNALFD](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/signalfd.2.html))
原因として推測したのは、PHP8.0ではexit()は例外と同じ挙動になるようで、この辺りはSwooleでもIssueがいくつか上がっていました。
移行を始めた時点はSwoole4.8.1を使用していたのですが、バグにハマっている間に4.8.2がでており、[ReleaseNote](https://github.com/swoole/swoole-src/releases/tag/v4.8.2)をみると、`Fixed cannot exit directly when a fatal error occurs in PHP8 environment`との記述が!
しめしめとバージョンアップしてみたものの、残念ながら同じ警告がでました...。
SwooleのソースもPHPのソースも読んでも何もわからん(CとC++を読んで原因までたどり着ける力量がない)ので、ワークアラウンドとしてtimeoutを設定するようにしました。
これで、異常時は検知できるので、拾ってリトライすれば良さそうです。
```(diff)
$process = new Process(['ls', '-lsa']);
+ $process->setTimeout(3600);
$process->start();
while ($process->isRunning()) {
// waiting for process to finish
+ // check if the timeout is reached
+ $process->checkTimeout();
usleep(200000);
}
echo $process->getOutput();
まとめ¶
2021/10/01から開始したPHP8.0移行プロジェクトは、本日11/26の動作検証を以て完了となります。 PHP8.0に起因していない障害(cronの記述ミス...)こそあったものの、日次のバッチ処理は正常に稼働しており、まずは成功と言えるのではないでしょうか。
さて、昨日11/25に待望のPHP8.1が来ました! Fibersにより、いよいよPHPで非同期処理が公式サポートされるようになります。 PHP8.0移行プロジェクトの終了と同時に、PHP8.1移行プロジェクトの幕開けとなりました。 (その前にLaravel6->8移行プロジェクトもあるのですが...) では、次のプロジェクト完了報告でお会いしましょう...!