1年間に渡り開発が行われてきたPHP8の最新版であるPHP 8.1.0が11月25日に正式リリースされました.本稿では,PHP リリース関連情報および今後のPHPのリリース予定について紹介します.また,PHP開発コミュニティを支えるための財政的基盤としてPHP Foundationが11月22日に設立されましたので,併せて紹介いたします.
PHP Foundation設立
PHPが誕生してから26年間になりますが,一貫して特定の企業に依存せずにボランティアベースの開発が行われてきました.もちろん,Zend社がPHPの心臓部であるスクリプトエンジンやキャッシュ機能,JIT機能といった主要な機能を提供したり,Facebook社が主要な貢献をしたりといったことはありましたが,重要な意思決定はPHPグループと呼ばれる主要な開発者が主体となり,開発者のコミュニティによる議論,投票により行われています.この運営方針は,特定の企業に支配されることなく,PHPコミュニティを運営するために望ましい方法ですが,他方,PHPの開発に携わる開発者が全く報酬を得られないため,安定して開発の品質を保つことが難しく,特定の開発者の高いモチベーションに支えられる状態となっていることが指摘されてきました.最近では,主要な若手開発者であるNikita Popov氏がLLVM関連の開発に注力するためPHPの開発者としての貢献度を下げることを発表しています.こうした経緯から,PHPの開発に関わるエンジニアに財政的支援を行う非営利財団(NPO)としてPHP Foundationの設立が11月22日に発表されました.この財団には,Zend社,WordPress関連で有名なAutomattic社を含め,主要な9団体が支持を行っています.運営は,Open Collectiveを用いたクラウドファンディング形式(https://opencollective.com/phpfoundation)で行われ,すでに支援の受付が始まっています.財団の運営は,当面,スポンサー代表とPHPグループにより行われ,運営方針・規則等を決めた後,持続的な運営組織に引き継がれる予定です.PHP Foundationの支援を受けるための申し込みもすぐに告知される予定です.
なお,PHPの開発プロセス自体には変更はなく,従来通り,PHP開発コミュニティにより意思決定が行われることになっています.PHPの開発に関わるエンジニアの生活基盤が継続的に安定することで,PHPの機能の進化や拡充,品質の安定化につながることが期待されます.
PHP 8.1の開発スケジュール
PHP 8.1の開発では,7月20にフィーチャーフリーズが行われた後,7~8月にベータ版,9~11月にリリース候補版のリリースが計画通り実施されました.リリース候補版は2週間毎に合計6回リリースされ,リリースに向けた最終的な確認が行われています.PHP8.1のリリース判断は,リリースマネージャであるPatrick Allaert氏が行っています.
11月25日にPHP 8.1.0がリリースされた後,PHP 8.1系については,ほぼ月に1回のペースでバグ修正を行ったマイナーバージョン(PHP 8.1.1, …)がリリースされる予定です.
本連載では,前回までにPHP 8.1で導入される新機能について紹介してきました.今回は,残る機能および後方互換性に影響を与える変更点について紹介します.
DNS-over-HTTPS (DoH)のサポート
PHP 8.1からはCurlエクステンションによりWebページを取得する際のDNSクエリにおいて,セキュアな接続を行うHTTPSを利用する DNS-over-HTTPS (DoH)を指定できるようになります. 以下にYahoo!のページを取得する簡単な例を示します.
$ch = curl_init(‘https://www.yahoo.co.jp’);
curl_setopt($ch, CURLOPT_DOH_URL, ‘https://dns.google/dns-query’);
curl_exec($ch);
DoHを利用するには,curl_exec()関数によりWebページを取得する前に,curl_setopt()関数によりCURLOPT_DOH_URLオプションを指定し,DoHによりDNSクエリを行うURLを指定します.ここでは,GoogleのPublic DNSのURL(https://dns.google/dns-query)を指定しています.
ファイルアップロード時のfull_path追加
PHPでは,HTMLフォームによるファイルアップロードをサポートしています.最近のWebブラウザではwebkitdirectory属性を指定することにより,ディレクトリツリー全体をアップロードすることが可能です.
ディレクトリ毎のファイルアップロードに対応したHTMLフォームの例を以下に示します.
<form action=”” method=”post” enctype=”multipart/form-data”>
<input name=”v[]” type=”file” webkitdirectory multiple />
<input type=”submit” />
</form>
PHPでは,ファイル名などの属性をグローバル配列変数$_FILESにより取得することができます.しかし,従来のバージョンのPHPでは,$_FILESにはディレクトリ構造が保存されない仕様であったため,例えば,複数のディレクトリに同じ名前のファイルがあると区別することが難しくなってしまっていました.
例えば,以下のような同じ名前(nama.txt)の2つのファイルを含むフォルダfruitをアップロードする場合を考えてみましょう.
fruit/orange/name.txt
fruit/banana/name.txt
ファイル名は,HTMLフォームに指定した配列変数名(v[])を用いて$_FILES[“v”][“name”]のように取得できます.この例では,2つのファイルをアップロードしているため,以下のような配列として取得されます.
$_FILES[“v”][“name”][0] = “name.txt”
$_FILES[“v”][“name”][1] = “name.txt”
また,それぞれのファイルの実体は以下のようにテンポラリファイルへのパスとして取得できます.なお,テンポラリファイルの保存先は,PHP設定オプションupload_tmp_dirで指定できます.
$_FILES[“v”][“tmp_name”][0] = “/tmp/phpxxxx”
$_FILES[“v”][“tmp_name”][1] = “/tmp/phpyyyy”
通常,元のファイル名を用いてサーバー上のファイルを作成し,move_uploaded_file関数によりテンポラリファイルをサーバー上のファイルにコピーします.しかし,この例では,元の2つのファイルの名前が同じであるため,同じファイルに上書きしてしまう可能性があります.
PHP 8.1では,以下のようなfull_path属性が追加され,元のディレクトリ構造の取得が可能となります.
$_FILES[“v”][“full_path”][0] = “fruit/orange/name.txt”
$_FILES[“v”][“full_path”][1] = “fruit/banana/name.txt”
これにより,ディレクトリ毎アップロードを行った場合でも,サーバー上で元のディレクトリ構造を復元したり,相対パスで保存することができるようになります.
メソッド継承時のstatic変数の共有化
PHPでは,クラス内でスタティック変数を定義することができます.以下の例のようにクラスAでスタティック変数$cntを定義し,クラスAを継承するクラスBを定義してみましょう.
class A {
private static $cnt = 0;
public static function counter() {
return ++static::$cnt;
}
}
class B extends A {}
counterメソッドにより,スタティック変数をカウントアップすることを考えます.以下のようなコードを実行すると,
var_dump(A::counter());
var_dump(A::counter());
var_dump(B::counter());
var_dump(B::counter());
以下のようにクラスAのcounterメソッドを実行した場合でも,クラスBのcounterメソッドを実行した場合でも,1つずつスタティック変数の値がカウントアップされていきます.
int(1)
int(2)
int(3)
int(4)
これは,スタティック変数の実体が継承したクラスにおいても共有されているためです.
次に,以下の例を見てみましょう.上の例とほぼ同じですが,スタティック変数$cntがcounterメソッドの内部で定義されています.
class A {
public static function counter() {
static $cnt = 0;
return ++$cnt;
}
}
class B extends A {}
PHP 8.1より前のバージョンのPHPで同様に以下のコードを実行してみましょう.
var_dump(A::counter());
var_dump(A::counter());
var_dump(B::counter());
var_dump(B::counter());
結果は以下のように前記の例とは異なる出力となります.
int(1)
int(2)
int(1)
int(2)
この場合,スタティック変数は継承されたクラスBでは共有されていないのです.この動作は,スタティック変数の特徴から想定される通常の動作と異なっており,わかりにくいという課題がありました.PHP 8.1以降ではこの動作が修正され,出力は以下のようにクラス変数として定義した場合と同じになります.
int(1)
int(2)
int(3)
int(4)
この変更は下位互換性に影響するため,PHPの従来の動作に依存したコードは変更する必要があります.ただし,PHPの開発者が既存のコードを調査した結果では,こうしたPHPの特殊な挙動に基づくコードの事例は極めてまれであり,ほとんどのコードは影響を受けないといわれています.
リソース型からクラス型への移行
PHPにおいては,ユーザが直接操作できないリソースオブジェクトと呼ばれる特殊な型が存在します.これは,例えば,データベースのクエリ結果やイメージ画像のハンドラとかを保持する変数で使用されます.このリソース型は初期のバージョンのPHPから存在し,広く利用されていますが,通常のクラスオブジェクトと異なり,型チェックやプロパティに対応できず,また,ユーザによる直接の操作ができないという問題がありました.このため,リソースオブジェクト型の通常のクラスオブジェクト型への移行が段階的に行われており,将来的にはクラスオブジェクト型に統合される予定です.
従来のリソースオブジェクト型との後方互換性を維持するため, クラス定義においてfinalが宣言されており,継承することはできません.また,「new クラス名」のようにインスタンスを作成することも許可されていません.
移行は段階的に行われており,PHP 8.0では以下のリソースオブジェクトがクラスオブジェクトに変換されています.
cURL, enchant, GD, OpenSSL, shmop, sockets, sysvmsg/sysvsem/sysvshm,
XML, XML-RPC, XMLWriter, zip, zlib
これにより,例えば,前述の以下のコードの動作が変わっています.
$ch = curl_init(‘https://www.yahoo.co.jp’);
PHP 8.0より前のバージョンでは,$ch はCurl型のリソースオブジェクトでしたが,PHP 8.0以降ではCurlHandleオブジェクトとなっています.このため,is_resource($ch) のようにリースかどうかを判定する関数によりハンドルが正常に取得できたかどうか確認するコードの動作は意図しない結果となる可能性があります.PHP 8.0以降で正しくハンドルが取得できたかどうかを確認するには,$ch === false のように論理値との比較を行うように変更する必要があります.
PHP 8.1では,以下のリソースオブジェクト型がクラスオブジェクト型に変更されています.
Fileinfo, FTP, PSpell, IMAP, LDAP, PgSQL
今後,その他のリソースオブジェクト型に関してもクラスオブジェクト型に変更される予定です.
今回は,ついにリリースされたPHP 8.1における変更点について,互換性のない変更点を含めて紹介しました.今後,次のリリースにあたるPHP 8.2の開発が本格化する予定ですので,関連する新機能・変更点について随時紹介していきたいと藻います.また,PHP開発コミュニティを支える活動として,PHP Foundationが設立された話題を紹介しました.全世界の多くのWebサーバーを支えているPHPの開発が継続的に安定して行われていくために,こうした活動は重要と言えるでしょう.