こんにちは、 Platform Engineer の工藤です。
過去記事や PHPerKaigi 2023 での登壇でたびたび触れていた社内 PHP 互換 Extension である colopl_bc
、及びそれを利用するための Rector ルールを GitHub にて OSS として公開しました。
colopl_bc
について
colopl_bc
は PHP のバージョンアップに伴う下位互換性のない変更について、実行速度や厳密な互換性の面から PHP だけで対処することが難しいものを PHP に提供する拡張機能 (Extension) です。
ini による設定で実行時に互換性を維持できているかを確認することができるので、本番環境では高速に動作するよう互換性チェックを無効にしつつ、 canary 環境でのみ非互換を検出した場合にログを記録する、という運用ができるため、段階的に PHP のバージョンアップ対応を行うことができます。
ただこれ、やっぱり バッドノウハウ なのです。本来はしっかりとテストを書いて全パターンを網羅し、非互換を許容する形で対応していくのが理想です。そのため公開するかどうかは社内でも議論が続いていました。
とはいえ、外部発表させていただいてcolopl_bcを公開してほしいとの声もいただいていたので、話し合いの末、公開することとしました。
ライセンスは PHP 本体と同様 PHP License 3.01 です。コントリビュートも歓迎しています。
colopl_bc
の機能
colopl_bc
は以下の機能を持っています (version 10.0.1
時点)
- PHP 本体の実装と非互換があるかどうかを実行時に検出し、
E_DEPRECATED
エラーや SAPI へのログを出力する機能colopl_bc.php74.compare_mode
(ビットフラグ指定)colopl_bc.php74.sort_mode
(ビットフラグ指定)
- PHP 7.0 互換機能 (
\Colopl\ColoplBc\Php70\*
srand()
/rand()
互換関数 (グローバルスコープな GNU libc 相当の乱数生成器)mt_srand()
/mt_rand()
互換関数 (壊れたメルセンヌ・ツイスタ)- 内部で乱数を利用する関数
shuffle()
str_shuffle()
array_rand()
- PHP 7.4 互換機能 (
\Colopl\ColoplBc\Php74\*
)- PHP 8.0 で挙動の変わった曖昧比較演算子のドロップイン置換関数
eq()
(==
)neq()
(!=
)lt()
(<
)lte()
(<=
)gt()
(>
)gte()
(>=
)spaceship()
(<=>
)
- 内部で曖昧比較を暗黙的に呼び出している関数のドロップイン置換関数
in_array()
array_search()
array_keys()
- PHP 8.0 で安定ソートになったソート関数のドロップイン置換関数
ksort()
krsort()
asort()
arsort()
sort()
rsort()
usort()
uasort()
uksort()
array_multisort()
- PHP 8.0 で挙動の変わった曖昧比較演算子のドロップイン置換関数
colopl_bc
Extension の使い方
くどいようですが、 使わないに越したことはない です。 Valgrind や LLVM Sanitizer を用いることで可能な限り安全性は担保しようとしていますが、バグが無い保証は一切ありません。これらの PHP Extension を書く際のあれこれや安全担保の方法については、また別途記事を書きたいと考えています。
PHP Extension は PHP スクリプトと違い、実行される環境にてネイティブに (つまり機械語で) 動きます。リクエストの度に PHP VM (仮想マシン) が初期化される PHP スクリプトと違い、プロセスが動作している限り常にメモリ上に読み込まれており、意図しないメモリアクセス違反が発生した場合は 問答無用でプロセスがクラッシュします。 これらのリスクを深く理解した上で利用してください。 でもコロプラでは使っています。 (そして徐々に廃止中)
現状、 PECL では公開していないため、インストールには独自でのコンパイルが必要です。が、 PHP には便利なビルドツールチェーンが入っているので簡単にコンパイルできます。以下では DockerHub にホスティングされている PHP イメージでビルド、インストールする手順を紹介します。
$ docker run --rm -it php:8.2-cli /bin/bash $ apt-get update && apt-get install -y "git" $ cd "/root" $ git clone --branch="10.0.1" --depth=1 "https://github.com/colopl/php-colopl_bc" "colopl_bc" $ cd "colopl_bc/ext" $ phpize $ ./configure --with-php-config="$(which php-config)" $ make -j$(nproc) $ TEST_PHP_ARGS="-q" make test $ make install $ docker-php-ext-enable "colopl_bc" $ php --re "colopl_bc"
これで ext-colopl_bc
が入ります。
colopl_bc
Library について
colopl_bc
には Extension だけではなく、 PHP で書かれた Library も存在しています。これは PHP 7.4 -> 8.x への移行を同一のコードベースで行うためのものです。
コロプラでは、『白猫プロジェクト NEW WORLD's』 (以下: 白猫) の移行の際に 完全に同一のコードベースを異なる PHP バージョンで動かす戦略 を取りました。ここでは以下のような手順を取ることで迅速な移行作業を行いました。
- PHP 7.4 の状態で
colopl_bc
Library を入れ、 Rector を用いて既存コードを変換 - 上記を本番環境にデプロイ
- PHP 8.1 環境を構築し、
colopl_bc
Extension を導入、これにより呼び出される関数が Library -> Extension のものに - コードはそのままに PHP 8.1 環境を構築
colopl_bc
の Library は Packagist で公開しているので、 PHP 7.4 環境で Composer を利用してインストールすることができます。
$ composer require "colopl/colopl_bc"
より詳しい手順については PHPerKaigi 2023 にて登壇させていだいており、現在アーカイブが閲覧可能ですので、興味がありましたらぜひご覧いただけると嬉しいです。
Rector ルールについて
colopl/colopl_bc
パッケージには、上記のコード変換に使える Rector ルールが同梱されています。基本的には演算子を関数に置き換えたりするものです。利用することでコード変換の手間を削減し、漏れもなくすことができます。
https://github.com/colopl/php-colopl_bc/tree/main/src/Rector
小話: colopl_bc
の歴史
colopl_bc
は元々 colopl_php70bc
という名前でした。元々 2017 年に新卒で入社した時に作ったものです。
当時私は PHP 5.6 で運用されているプロジェクトに携わっていました。まだサポート期間中とはいえ、 PHP 7.0 での高速化は著しく、なんとか自分の運用しているプロジェクトでも利用できないかと考えていました。 いやぶっちゃけ PHP 7.0 が使いたかっただけです。
それもあって当時、業務中に時間を見つけては PHP のソースコードを読んでいたところ、 Extension を書けば互換性の担保ができるのでは?とふと思いつきました。そこからの流れは早く、実証試験的な拡張機能を作ってからとあるタイトルで導入し、瞬く間に各タイトルに広がっていきました。
思えばこの時の体験があったから PHP 8.2 に Random 拡張を提案 しようと思ったんだろうと思います。当時、部長に PHP の comitter になっちゃえよ、と冗談交じりに言われたのですが、気がついたら なっちゃって ました。
ColoplTechについて
コロプラでは、勉強会やブログを通じてエンジニアの方々に役立つ技術や取り組みを幅広く発信していきます。
connpassおよびTwitterで情報発信していますので、是非メンバー登録とフォローをよろしくお願いいたします。
また、コロプラではゲームや基盤開発のバックエンド・インフラエンジニアを積極採用中です!
興味を持っていただいた方はぜひお気軽にご連絡ください。