秋が終わり,今年も残りわずかになってきました.次のマイナーバージョンであるPHP 8.2の開発は,引き続き順調に進んでいます.本稿では,前回に続いて正式リリースが間近に迫っているPHP 8.2関連の残りの話題として,下位互換性に関係する変更点や実行速度の改善などについて紹介します.
PHP 8.2の開発状況
今年の11月24日に計画されている正式リリースに向けて,PHP 8.2の開発が順調に進んでいます. 10月27日に5回目のリリース候補版(RC5)のリリースが行われ,11月10日に予定されているリリース候補版(RC6)が正式リリース前の最後のテスト版となります.リリース候補版をテストしていただき,安定した正式版のリリースに向けてご支援いただければと思います.
Traitで定数が利用可能に
PHPではtraitと呼ばれるコードの再利用を簡易的に行う仕組みがサポートされています.このtraitはC言語のプリプロセッサがサポートするマクロ置換に相当する機能であり,コンパイラが動作する前にコピーペーストにより置換が行われます.従来,実装上の制約によりtraitの中でPHP定数を宣言することができませんでした.PHP 8.2以降では,traitの内部でPHP定数の宣言が可能になります.早速,例を見てみましょう.以下のコードでは,簡単なtraitとしてFooを定義しています.
trait Foo {
public const C = 1;
public function bar(): int {
return self::C;
}
}
Fooの内部では,PHP定数Cが定義され,値として1が代入されています(2行目).この定数Cは,関数barの内部でself::Cとして呼ばれています.
Traitはクラスのようにインスタンスを作成するのではなく,以下のようにクラスからuse文で呼び出します.これにより,この部分にtrait内で定義されたコードがコピーされたかのように機能します.
class Moo {
use Foo;
}
PHP 8.1までのバージョンでは,上記のようにtraitの内部でPHP定数を定義すると致命的なエラーを発生していました.PHP 8.2以降ではPHP定数をtraitの内部で定義できるようになります.
PHPコードで,以下のように呼び出し側のクラスの定数として参照することができます.
print(Moo::C); // 1
しかし,traitの利用上の制約により,以下のようにtraitの定数としてコールすることはできないことに注意してください.この場合はエラーとなります.
print(Foo::C); // NG
互換性に関する変更点
PHP 8.2は,PHP 8.1までのバージョンに対して上位互換となるよう設計されていますが,本連載記事でも紹介したように一部の機能についてはコードの明確さの改善などのために,廃止または動作が変更されます.PHP 8.2において下位互換性に影響を与える可能性がある主な変更点を示します.
- 動的プロパティ定義の廃止
クラス内で未定義のプロパティを参照した場合,従来のPHPでは自動的に定義されていましたが,typo等でも意図せずに作成されてしまうため,PHP 8.2で警告,PHP 9.0で廃止されます.本連載の22回および24回で紹介しています. - 大文字・小文字変換がロケール非依存に
大文字小文字変換を行うPHP関数strtolower(),strtoupper()などの関数は従来ロケールに依存して動作が変わっていましたが,PHP 8.2以降ではロケールを変更しても結果は変わらなくなります.本連載の22回で紹介しています. - 一部のcallableの廃止
call_user_func(“self:sayHello”) のようなコール方法が廃止され,PHP 8.2で警告,PHP 9.0でエラーとなります.本連載の22回で紹介しています. - str_split()の空文字列に対する戻り値
str_split()関数の引数に空文字列を指定した際,従来は空文字列を要素とする配列が返されていましたが,空の配列が返されるようになります.なお,mb_str_split()は従来から変更後の動作となっているため,変更ありません. - mbstringの一部エンコーディングの廃止
QPrint,Base64,UuencodeエンコーディングおよびHTMLエンティティによるテキストエンコーディングが廃止されます. - mysqliにおけるlibmysqlサポートの廃止
mysqliエクステンションをlibmysqlとリンクして構築することができなくなり,libmysql固有の機能も廃止されます. - Windows環境におけるエラーメッセージのローカル化廃止
Windows環境におけるエラーメッセージがローカル化されなくなり,英語表示に統一されます. - 連想配列のキーに基づくソートを行う関数ksort,krsortにおけるソートの優先順位が変更されます.以下に具体例を示します.
ksort(krsort)に関するサンプルを以下に示します.ここでは,数値および文字列をキーとする連想配列をキーによりソートしています
$x = array("1"=>"taro","a"=>"jiro","b"=>"hanako","2"=>"shiro");
ksort($x);
var_dump($x);
このコードの結果は,PHP 8.1までのバージョンでは, $x=
[“a”=>”jiro”,”b”=>”hanako”,”1″=>”taro”,”2″=>”shiro”]
となり,アルファベットのキーが数字のキーよりも前に配置されます.
一方,PHP 8.2以降では,PHP 8の標準的な規則に基づき数値を優先したソートとなり,結果は$x=[“1″=>”taro”,”2″=>”shiro”,”a”=>”jiro”,”b”=>”hanako”]となります.
上記の変更が影響を与える可能性もあるため,PHP 8.2以降へのバージョン更新においては,十分な互換性テストを行うことが推奨されます.
過去のバージョンとの実行速度比較
PHPでは,スクリプトエンジンの改良を継続して行っており,バージョン更新と共にスクリプトの実行速度を改善させてきました. ここでは,PHPに付属する標準ベンチマークツールZend/bench.phpおよびZend/micro_bench.phpを用いて,実行速度を比較します.Zend/bench.phpは主にソートなどのアルゴリズムに関するコードのベンチマークです.一方,Zend/micro_bench.phpは変数の代入などの言語としての基本機能に関するベンチマークです.なお,本結果は,スクリプトエンジンの実行速度を評価することを目的としており,Webアプリケーションの実行速度の比較そのものではないことに注意してください.Webアプリケーションの実行速度は,PHPスクリプトの実行速度以外にも,データベースなどの外部ツールやネットワークの速度,使用メモリなどにも大きく依存しており,一般に,スクリプトの実行速度が少し向上してもWebアプリケーションの速度はほとんど変わらないことが多いです.
図1に,Zend/micro_bench.phpの結果を示します.左の4列はキャッシュやJITなどを用いた状態での結果を示します.図の縦軸が小さい程,高速であることを示します.PHP 7から8に更新された際,約10%高速化されています.一方,PHP 8.0から8.2にかけては,ほとんど変化していないといえます.
図1の右5列は,PHP 8で導入されたJITの効果を示しています.JITはオペコードキャッシュの機能の一部として実行されるため,比較対象としてキャッシュを有効にした場合の結果を示しています.JITを利用することで,実行速度が約2倍に高速化されることがわかります.ただし,PHP 8.0~8.2におけるJIT適用時の実行速度の改善効果は比較的小幅(10%以下)にとどまっています.
次に図2にZend/bench.phpの結果を示します.同じく左4列はキャッシュ・JITを使用しない場合の結果を示します.PHP 7.4からPHP 8.2までの間,着実に実行速度が改善されていることがわかります.PHP 8.1とPHP 8.2の間でも約9%改善しています.JITの効果(右5列)も同様で,キャッシュ利用時とくらべてJITを利用した場合の改善効果は約3倍と大きいですが,PHP 8系におけるバージョン間での差異は比較的小さく,PHP 8.1からPHP 8.2への改善は約5%となっています.
PHP 8.1からPHP 8.2にかけては地道に改善されている印象を受けますが,改善の程度は約10%以下となっており,実際のWebアプリケーションで改善を体感できるレベルではないと思われます.
今回は,PHP 8.2における新機能としてTraitにおける定数の利用,また,下位互換性に関わる変更と実行速度のベンチマークを示しました.もうすぐリリースされるPHP 8.2をぜひお試しいただければと思います.