COLOPL Tech Blog

コロプラのエンジニアブログです

colopl_bc と Rector ルールを OSS として公開しました

こんにちは、 Platform Engineer の工藤です。

過去記事PHPerKaigi 2023 での登壇でたびたび触れていた社内 PHP 互換 Extension である colopl_bc 、及びそれを利用するための Rector ルールを GitHub にて OSS として公開しました。

github.com

colopl_bc について

colopl_bcPHP のバージョンアップに伴う下位互換性のない変更について、実行速度や厳密な互換性の面から 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 (ビットフラグ指定)
      • 0 : 互換性チェックを行いません。高速に動作するため、本番環境で利用する際に適しています。
      • 1 : PHP 側の比較演算と結果が同一かを検証し、異なる場合は E_DEPRECATED 相当のエラーを発生させます。
      • 2 : PHP 側の比較演算と結果が同一かを検証し、異なる場合は実行中の SAPI にログを出力します。
    • colopl_bc.php74.sort_mode (ビットフラグ指定)
      • 0 : 互換性チェックを行いません。高速に動作するため、本番環境で利用する際に適しています。
      • 1 : PHP 側のソートと結果が同一かを検証し、異なる場合は E_DEPRECATED 相当のエラーを発生させます。
      • 2 : PHP 側のソートと結果が同一かを検証し、異なる場合は実行中の SAPI にログを出力します。
  • PHP 7.0 互換機能 (\Colopl\ColoplBc\Php70\*
  • 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()

colopl_bc Extension の使い方

くどいようですが、 使わないに越したことはない です。 ValgrindLLVM 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 バージョンで動かす戦略 を取りました。ここでは以下のような手順を取ることで迅速な移行作業を行いました。

  1. PHP 7.4 の状態で colopl_bc Library を入れ、 Rector を用いて既存コードを変換
  2. 上記を本番環境にデプロイ
  3. PHP 8.1 環境を構築し、 colopl_bc Extension を導入、これにより呼び出される関数が Library -> Extension のものに
  4. コードはそのままに PHP 8.1 環境を構築

colopl_bc の Library は Packagist で公開しているので、 PHP 7.4 環境で Composer を利用してインストールすることができます。

$ composer require "colopl/colopl_bc"

より詳しい手順については PHPerKaigi 2023 にて登壇させていだいており、現在アーカイブが閲覧可能ですので、興味がありましたらぜひご覧いただけると嬉しいです。

blog.colopl.dev

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で情報発信していますので、是非メンバー登録とフォローをよろしくお願いいたします。

colopl.connpass.com

twitter.com

 

また、コロプラではゲームや基盤開発のバックエンド・インフラエンジニアを積極採用中です!
興味を持っていただいた方はぜひお気軽にご連絡ください。