PHPコア開発者になって半年経ったので、php-srcでの活動を振り返る

こんにちは！PHPとCを主に書いています、Sakiです。日中はBASE株式会社さんでお仕事させていただいています。早朝と夜間にphp-srcでの活動をしています。PHP8.4のリリースマネージャーもやってます。

2024年からありがたいことにPHPコア開発者として採用していただき、半年とちょっとになりました。ちょうど一区切りということで、これまでの活動を簡単にまとめたいと思います。細かいものや、最終的に成果が出なかった（議論がまとまらなかったなど）ものは除外しています。
# 2023年、コア開発者になる前
実は私はphp-srcどころかOSSの経歴がかなり短いです。
## はじめてのコントリビュート
PHP8.1から、PDO MySQLでmysqlndをドライバとして使用し、`PDO::ATTR_EMULATE_PREPARES`が`true`の場合、PHPのネイティブタイプで値が返される、というように仕様が変更されました。従来の動作を望むなら`PDO::ATTR_STRINGIFY_FETCHES`を`true`にしてね、とドキュメントには記載されています。

ところが、（すでにMySQLで非推奨ですが）`FLOAT(M,D)`のように桁数まで指定した浮動小数型カラムを使用している場合、従来では例えば`'3.00'`が返ってきていたところが`'3'`になってしまう、というBC Breakがあることがわかりました。

これのissueをまずOpenし、修正できそうだからやってしまおう、ということで`PDO::ATTR_EMULATE_PREPARES`と`PDO::ATTR_STRINGIFY_FETCHES`がどちらも`true`である場合は完全に従来の動作になるように修正するPRをOpenし、無事にマージされました。

php-srcどころか、これがはじめてのOSSマージ経験でもありました。2023年の7月17日のことです。
## 思っていたより自分に向いていたので、活動を続ける
OSS活動というのは、どこか雲の上というか、自分とは関係ない世界の話、というイメージが無意識にあったことを否めません。きっと圧倒的に技術力が高い人たちばかりで、コンテキストにも詳しい人たちばかりで...と考えていました。

ところがPRがいざマージされてみて、「あれ、思っていたよりもみなさん優しいし（もっと辛辣なレビューが来るかと身構えていた）、すんなりマージされたなぁ」という感じで、心の中にあった"ハードル"がふわっと消えてなくなる感覚がありました。

私はバグの調査と原因発見が好きなのもあり、これは自分に向いていそうだ、と活動を続けることにしました。
## PDOのメインメンテナーらしい人がどうやらいない
少し前まではcmb氏が主にPDO関連を見ていたようなのですが、この時点では非アクティブ。1つ目のPRがPDO関連だった縁から、PDOと可能であれば個別のDBドライバについて頑張ってみよう、と思い立ちました。

### バンドルされてるDBの開発環境を全て揃える
PDOは汎用DBドライバですので、MySQL以外にも色々あります。PECLまで含めると、メンテされていないのも含めて15個くらいあります。

PECLまで見るのは無理ですし多分見る必要もないので、バンドルされているドライバに絞って環境を整えました。ソケット通信の特性の問題で、同一ホスト内にDBがある場合とリモートにDBがある場合で挙動が異なるケースが僅かにあるため、環境はdockerとリモートホストの両方に揃えました。

### PDOのissueを中心に見て回る
DBのissueって実はあまり頻繁には来なくて、古いissueを掘り返したりもして、Calvin（もう1人のPHP8.4のリリースマネージャールーキー）がOpenしたissueを見つけてIBM関連の調査や実装を行ったりもしました。その頃に書いた記事がこちら
[IBMi db2へ、PHP ODBCから接続する](https://qiita.com/SakiTakamachi/items/34ffb9722f9c6aa135be)

あまりissueの数が多くないのもあり、PDO関連はほぼ全て捌いていたと思います。

## pdo_firebirdのコードを見て改修を決意する
FirebirdというDB用のPDOなのですが、これが少し厄介なことになっているのを発見しました。

他のDBと違いFirebirdはフルトランザクションDBで、常にトランザクションが開始された状態になっています。つまり、トランザクション分離レベルや書き込み/読み込み専用の切り替えがクエリではできず（できないというより、PHPでそれをするべきではない）、ライブラリのAPIを使用して制御する必要があります。

ところがちょうどそのあたりの記述は中途半端な状態になっていて、おそらく開発途中で断念したように見えました。~~このあたりが大いに関係し「1度でもコミットすると2度とコミットできない」みたいなよくわからないバグがあったりもしました~~。

訂正) 1度でもトランザクションを手動で開始したら最後、コミット、あるいはロールバックした後に2度とトランザクションを開始できない、でした

これらを全てどうにかするため、トランザクション分離レベルと書き込み/読み込みの切り替え機能を実装しました。

## pdo_sqlsrvが壊れた
はじめてのコントリビュートの変更がトリガーになり、pdo_sqlsrvが壊れました。これはMicrosoftのSQL Server用のPDOで、php-src本体にはバンドルされていません（Microsoftのリポジトリにあります）。

すぐに修正PRをpdo_sqlsrv側に出してマージされたのですが、なかなかそれがリリースされず、度重なる交渉の末、予定を数ヶ月繰り上げてhotfixとしてリリースしてもらうことができました。

ちなみに、問題が起きてから解決するまでにそれなりに時間がかかったこともあり、Laravel用にこの問題を回避するためのパッケージを出したりもしています。 
[laravel-sqlsrv-err-avoid](https://github.com/SakiTakamachi/laravel-sqlsrv-err-avoid)

hotfixがリリースされたら消すよ〜と書いてあるのですが、未だにPackagistではinstallされているようで、累計25000ほどのinstall数です。~~早くみなさんアプデした方がいいと思う....~~

### SQLiteが何かおかしい
Alpineの32bit版で、なぜかSQLiteに記録される浮動小数の値がおかしくなる、というissueが報告されました。

これはFPU制御レジスタ起因だったのですが、問題は、PHPのスタンスとSQLiteのスタンスが異なってしまっていることでした。どちらかにバグがあるのではなく、ポリシーの不一致。

SQLiteのコミュニティに参加して、再現コードやいくつかの根拠を提示するなどして交渉し、PHP側の動作を壊さないように修正してもらうことができました。

後になって知ったのですが、この時やりとりしていただいていたお相手はSQLiteの生みの親のRichard Hippさんでした...恐れ多い...

## `round()`のissueに混ざってみた
ある日、[RFC: Add 4 new rounding modes to round() function](https://wiki.php.net/rfc/new_rounding_modes_to_round_function)から派生したであろうissueのスレッドに、動作検証などで何かお手伝いできたらな〜くらいの軽い気持ちで参加してみました。

詳細は省きますが、最終的に「`0.285`は実際には`0.284999...`だけれど、これは`round()`のエッジケースと見なされるべきか否か？」という問題に発展し、コア開発者同士でも意見が割れ、[RFC: Change the edge case of round()](https://wiki.php.net/rfc/change_the_edge_case_of_round)を作成するに至りました。

このRFCは否決され、従来通りの挙動を維持することとなりました。

ただ、結局のところ浮動小数は正確ではないので、BCMathに`round()`のようなものがあれば...というところから同時並行で[RFC: Adding bcround, bcfloor and bcceil to BCMath](https://wiki.php.net/rfc/adding_bcround_bcfloor_bcceil_to_bcmath)も進めていて、こちらはめでたく可決されました。

メーリングリストでの議論のフローとRFCのフローはこの時に覚えることができました。

# 2024年、コア開発者になる
ありがたいことに、採用していただけました。1月は主にドキュメントを読んだり開発環境をコミッター用に整えたりするなどに時間を使っていたと思います。

## BCMathはなぜクラスがないのだろう？とふと思いつく
GMPはあるのに。

というところから、[RFC: Support object type in BCMath](https://wiki.php.net/rfc/support_object_type_in_bcmath)を作成しました。

### 「BCMath is very slow.」
前の時からそうでしたが、BCMathの話題をメーリングリストで出す度に必ずBCMathの遅さを指摘され、「これに手間をかけるのは...まぁおすすめはしませんよ」というような温度感だったり。

ちょっとこれがどうにも悔しくて、2024 2Qの私の中期目標は「BCMathのパフォーマンス改善」となりました。

## BCMathを遅い子から速い子にする
BCMathのソースコードはファイル数こそ多いものの分割が細かくされているだけなので、コード量はそこまで多くなく、複雑でもありません。

なので、とにかくコードを読み込み、改善できそうなポイントを洗い出しました。

### Niels参戦
パフォーマンス改善第一弾のPRをOpenしたところ、パフォーマンスチューニングが好物（自称もしてる）のNielsが参戦し、SIMDやバイトトリックを駆使するなどして、私の考えていたのとは全く別の方向性でもBCMathのパフォーマンスアップが開始されました。

### 計算アルゴリズムを変更した
Nielsの最適化とは別方向で、私は計算アルゴリズムの最適化を行いました。本日時点までに加算、減算、乗算はマージ済みで、今除算をレビューしてもらっています。

従来のBCMathは「1桁ずつ」の計算、つまり人間が筆算で計算するような計算ロジックを使用していました。

これを、64bitの場合は8桁一気に計算するようにするなどして高速化を実現しました（乗算にKaratsuba法を使用するPRも作成しましたが、効果が顕著に得られるのが数千桁以上の掛け算の場合であり、ほとんどユースケースがなさそうなこと、その割にコードが著しく複雑であることからマージは見送りました）。

### どのくらい速くなったのか

まだ速くできるアイデアはいくつかありますが、現実的な範囲でそれなりに大きな桁数の場合、現時点では加算と減算は大体倍速、乗算は3倍速、除算は10倍速くらいになっています。

数千桁にもなると、除算は50倍速くらいになります。

# まとめると
PDOを中心に見る予定が、（もちろんPDOのメインメンテナとしてちゃんとお仕事してますが）それ以上にBCMathへ手間暇をかけていた印象です。

PDOとBCMathをメインで見つつ、他の拡張機能も見れるものを増やしたいと思います。

お読みいただき、ありがとうございました。