2024年の季節も春から夏に向かい、暑さを感じる日も増えています。秋のリリースに向けて、次のマイナーバージョンであるバージョン8.4の開発が本格化しつつあります。今回の記事では、前回に続き、PHP 8.4で導入される予定の機能の一部を紹介します。
PHP リリースサイクルが改善される見込み
現在適用されているPHP のリリースサイクルは、2010年に導入されたものです。リリースサイクルは3年間であり、リリース後、バグ修正が2年間行われた後、セキュリティ関連の修正のみが1年間行われます。リリースから3年が過ぎた時点でサポート切れとなり、End-of-Life(EOL)が宣言されます。
4/15現在、このリリースサイクルを更新する提案(RFC)の投票が行われており、4/29に投票が完了する予定です。本稿執筆時点で投票期間はまだ約2週間残っていますが、提案に関して賛成の方が圧倒的多数となっており、このままいけば、採択される見込みです。
この提案においては、以下の6項目の変更が提案されています。
- β版において小規模な変更を適用可能とする
現在のリリースサイクルでは、α版の後に新機能の導入を停止するフィーチャーフリーズが行われた後、β版がリリースされます。β版においても、RFCを必要としないような小規模な変更を可能とすることで、開発を効率化します。
- リリース候補版およびバグ修正リリースにおいて、新機能の導入を認めない
逆に、リリース候補版やバグ修正リリースにおいて、定数の追加のような小規模な変更は現在認められていますが、テストケースが動作しなくなるなどの品質低下がみられるため、変更を禁止します。
- リリース候補版のリリース回数を4回に削減する
現状、6回行われているリリース候補版の公開を4回に削減します。これにより、α版のリリースから最終リリースまでの期間が短縮されます。
- セキュリティ修正のリリース期間を1年延長する
これにより、リリースサイクルは3年(バグ修正:2年、セキュリティ修正:1年)から4年(バグ修正:2年、セキュリティ修正:2年)になります。
- セキュリティ修正期間中における再帰テストの更新を可能とする
セキュリティ修正により、再帰テストの結果が変わる可能性があるため、更新を許可します。
- リリースサイクルを年末まで延長する
リリース日から2または3年と現在定義されていますが、各バージョンで日付が異なるのはわかりにくいため、日付をサポート期間終了年の年末に固定します。
これらの変更が実際に行われるかは現時点で未定ですが、いずれもPHP開発において有意義な変更と思われます。特にセキュリティ修正の期間延長(1年間→2年間)は、現在、EOL宣言が行われた後のバージョンのPHPを使い続けるユーザーがかなりいることを考えると、セキュリティ確保の観点からは望ましいと言えます。
PHP 8.4リリースマネージャが決定!
4月16日までに開発者による投票が行われ、PHP 8.4のリリースマネージャ(ベテラン:1名、ルーキー:2名)が決定しました。ベテランは、PHP 8.3のルーキーを勤めたEric Mann氏で、筆者も訳者の一人として関わったPHPクックブック (O’Reilly)の筆者でもあります。
ルーキー2名は、Calvin Buckley氏とサキチさんが選ばれました。サキチさんは、PHP Foundationのメンバーとして今年初めに追加された4名のうちの一人で、今回、日本人として初めてリリースマネージャに選ばれたのは、素晴らしいことです。今回、リリースマネージャが選定され、PHP 8.4の開発が本格化することになります。
以下、前回に続いて、PHP 8.4で導入される予定の機能を紹介します。なお、PHP 8.4で導入される機能はまだ変更される可能性があり、紹介する内容も最終的なものと異なる可能性がることに注意してください。
Unicode書記素クラスタのサポート
前回、mbstring関連の機能強化について紹介しました。PHP 8.4では、Unicodeバージョン9.0以降で導入された書記素クラスタ (grapheme cluster) に基づく、文字列の分割ができるようになります。書記素クラスタは、Unicodeにおいて「自然な1文字」を表す単位です。Unicodeで表される文字の一部(絵文字など)は、複数のコードポイントから構成されます。マルチバイト文字を扱うmbstringエクステンションの関数(mb_str_splitなど)は現時点で書記素クラスタに未対応ですが、PHP 8.4で追加されるgraphen_str_split関数を利用することで、書記素クラスタ単位の処理が追加され、「自然な1文字」毎に文字列を分割することができるようになります。書記素クラスタにより表わされる文字には、各国の国旗、家族構成の異なる家族などがあります。
以下、簡単な例を見てみましょう。この例では、日本国旗を表す書記素クラスタ(U+1F1EF U+1F1F5)を文字単位で分割します。
<?php
$str = "\u{1F1EF}\u{1F1F5}"; // 日本国旗[JP]
var_dump(mb_str_split($str));
このスクリプトを実行すると出力は以下のようになり、mbstringエクステンションのmb_str_split()関数により4バイトの文字2つに分解されることがわかります。
array(2) {
[0]=>
string(4) "🇯" // U+1F1EF
[1]=>
string(4) "🇵" // U+1F1F5
}
Unicodeのコードポイントとしては正しく分割されていますが、書記素クラスタ単位での分割は行われていません。
では、次にPHP 8.4で導入されるgrapheme_str_split()関数を使用してみましょう。この関数は、ICUというライブラリの機能を呼び出すことで実現されており、intlエクステンションをPHP起動時に組み込むことで利用可能になります。以下のコードを実行してみましょう。
var_dump(grapheme_str_split($str)[0]); // string(8) "🇯🇵"
出力は、「string(8) “🇯🇵”」となり、書記素クラスタ単位での分割が行われていることがわかります。
同様の処理は、Unicodeに対応した正規表現関数でも実現できます。例えば、mbstringに組み込まれている正規表現ライブラリ「鬼車」を利用する正規表現マッチ関数mb_ereg()を利用する場合、マッチ対象に「\X」を指定することで、書記素クラスタ単位でのマッチが可能になります。以下のコードを実行してみましょう。
mb_ereg('(\X)', $str, $match);
var_dump($match[1]); // string(8) "🇯🇵"
マッチ対象が、配列$matchにより取得され、第2要素にマッチした書記素クラスタ単位の文字が取得されていることがわかります。
同様に、PCREエクステンションを利用する場合は、以下のコードとなります。
preg_match("/(\X)/u", $str, $match);
var_dump($match[1]); // string(8) "🇯🇵"
同じく、書記素クラスタ単位のマッチした文字が正しく出力されていることが分かります。
このように、書記素クラスタ単位の文字分割については、Unicode対応の正規表現関数を用いても実行できますが、grapheme_str_split()関数は、PHP標準の関数str_split()と同様なインターフェイスにより、より便利に利用できることが利点と思われます。
高速なAESに基づく認証付き暗号アルゴリズムが導入される
暗号処理を行うSodiumエクステンションにおいては、複数方式による認証付き暗号アルゴリズムがサポートされています。このモジュールでは、AES-GCM、CHACHA20-POLY1305のような既存のアルゴリズムと比べて大幅に実行速度が高い暗号アルゴリズムがサポートされています。
PHP 8.4では、 libsodium 1.0.19でコンパイルされた場合、新しい高速な暗号アルゴリズムである AEGIS-128L、AEGIS256 がサポートされます。
変数$messageに代入された文字列をAEGIS-128Lにより暗号化する簡単なコードを示します。
<?php
key = sodium_crypto_aead_aegis128l_keygen();
$nonce = random_bytes(SODIUM_CRYPTO_AEAD_AEGIS128L_NPUBBYTES);
$additional_data = ''; // 追加データ(空文字列または任意のデータ)
$message = 'Hello'; // 暗号対称のデータ
$ciphertext = sodium_crypto_aead_aegis128l_encrypt($message, $additional_data, $nonce, $key); // 暗号化
sodium_crypto_aead_aegis128l_encrypt() 関数に暗号化対称のメッセージ、付加データ、ノンスと秘密鍵を指定することで認証付き暗号化された暗号データが生成されます。
復号には、sodium_crypto_aead_aegis128l_decrypt() 関数を使用します。
$decryptedMessage = sodium_crypto_aead_aegis128l_decrypt($ciphertext, $additional_data, $nonce, $key); // "Hello"
従来から用いられる標準的なアルゴリズムとしてAES256GCM、CHACHA20POLY1305と実行速度を比較した結果を棒グラフに示します。128ビット暗号のAEGIS128L、256ビット暗号のAEGIS256を評価しています。暗号化入力はランダムに生成した1キロバイトのバイト列とし、100万回暗号化処理するのに要した時間を計測しています。結果は、時間あたりの処理回数(Gops/s)で示しており、AEGIS128LはAES256GCM、CHACHA20POLY1305に対して、
それぞれ、1.7倍、6.5倍に高速化されています。また、AEGIS256はAES256GCM、CHACHA20POLY1305に対して、それぞれ1.3倍、4.9倍に高速化されています。
パスワード用ハッシュ関数の標準コストが変更される
PHPでは、パスワード用ハッシュ関数password_hash()がサポートされています。この関数は、約11年前に導入されたものですが、依頼、主要なパラメータであるコストに関してデフォルト値が変更されないままになっていました。この間、攻撃側で利用されると想定される計算機能力は進歩しており、ハッシュ関数を計算するために要する時間が減ることで、パスワードが相対的に脆弱になっていると考えられます。PHP 8.4では、標準の PASSWORD_BCRYPTオプションで使用されるBCryptアルゴリズムのコストのデフォルト値が、4倍(10⇒12)に増加されます。
以下のコードで、パスワードハッシュを100回計算し、各PHPのバージョンで実行し、所要時間を比べてみましょう。
$iter = 100;
$start = microtime(true);
for ($i = 0; $i < $iter; $i++) {
$dummy = password_hash((string) $i, \PASSWORD_BCRYPT);
}
$end = microtime(true);
以下のグラフにハッシュ値を一回計算するのに要した時間を示します。PHP 8.4では以前のバージョンに対して約4倍の時間を要していることが分かります。
今回は、前回に続いて11月のリリースに向けて本格的に開発が開始されたPHP 8.4における変更点の一部を紹介しました。次回以降もPHP 8.4のその他の変更点について紹介していきます。