PHP重鎮の廣川類氏のコラム第32回「PHPの最新状況:PHP 8.3の改善点」

PHP

廣川類

PHP 8.3開発盛り上がる!新たにjson_validate関数やmb_str_pad関数を追加、スタティック変数の改善等、多数の新機能を実装。開発終盤のRC5公開し、正式版は11月予定。#PHP新機能 #開発者注目

秋も深まり,肌寒い日が続いています.引き続き,PHP 8.3の開発は順調に推移し,リリース候補版が公開されています.今回は,前回に続きPHP 8.3に向けて開発中の新機能の一部について紹介します.

PHP 8.3の開発状況

 PHP 8.3の開発はいよいよ終盤に差し掛かっており,2週間に1回のペースでリリース候補版が公開されています.本稿執筆時点では,10月26日にリリースされたリリース候補5版(RC5)が最新となっています.計画では,11月9日に最後(6回目)のリリース候補版がリリースされ,問題がないことが確認されれば,11月23日に正式公開となります.

以下,前回に続いて,PHP 8.3向けに実装済みの機能を順番に紹介していきます.

json_validate()関数追加

構造化されたデータを取得するためにJSON形式が多くの場面で利用されています.JSON形式のデータが正しくフォーマットされているかどうかを確認する手段として,従来は,json_decode() 関数が利用されていました.この関数は,JSO形式のデータをデコードするための関数ですが,正しくデコードできるかを検証する際にも利用することができます.著名なフレームワークの内部でJSON形式のデータを検証する際にも json_decode() 関数が利用されています.ただし,この関数はJSON形式のデータの検証を行った後,パースしてデコードし,PHP変数の生成などを行いますので,メモリを消費し,また,実行時間が余計にかかる欠点があります.このため,JSON形式のデータの検証のみを行う json_validate() 関数が新規に作成されました.この検証の処理は,json_decode() と同じであるため,一度,検証を通過すれば,json_decode() でデコードできるレベルで正しいJSON形式であることが保証されます.

 では,さっそく簡単な例を見てみましょう.

<?php
var_dump(json_validate('[1, 2, 3]')); // bool(true)
var_dump(json_validate('{1, 2, 3]')); // bool(false)

最初の行は,正しいJSON形式のデータであるため,真値(true)が出力されます.一方,次の行は,JSON形式として正しくないデータであるため,偽値(false)が出力されます.

別の例として,検証結果を取得する例を紹介します.

<?php
$_GET['json'] = '{"name":"山田花子"]';
if (json_validate($_GET['json'],
flags: JSON_INVALID_UTF8_IGNORE)===false) {
    print(json_last_error());
    print(json_last_error_msg());
}

この例では,ユーザ入力を模擬して$_GET配列の変数を検証します.Json_validate()関数の第二引数にオプションとして JSON_INVALID_UTF8_IGNORE フラグを指定しています.このフラグを指定すると,UTF8エンコードされたデータとして正しくないデータは無視されます.json_validate()で正しくないJSON形式のデータとして判定された際,json_last_error()関数およびjson_last_error_msg()関数によりそれぞれエラーコードおよびエラーメッセージを取得できます.この場合は,以下が出力されます.

2
State mismatch (invalid or malformed JSON)

mb_str_pad() 関数追加

 従来,文字列のパディング処理(特定の文字で文字列を埋める)においては,str_pad()関数が使用されてきました.ただし,この関数は,マルチバイト文字に対応していないため,問題を発生するケースがありました.以下の簡単な例を見てみましょう.

var_dump(str_pad('日本語', 10, '_', STR_PAD_RIGHT));

str_pad()関数は対象とする文字列に文字数を指定して余った部分を指定した文字で埋める処理を行います.埋める文字を配置する位置を STR_PAD_RIGHT (右側)のように指定することも可能です.上の例では,「日本語」に対して,10文字となるように’_’を右側に埋める処理のため,「日本語_______」のようになることが期待されます.しかし,実際には,UTF-8環境でこのコードを実行すると,出力が「日本語_」のようになってしまいます.これは,str_pad()関数がデータを文字単位ではなく,バイト単位で扱っており,日本語の文字がUTF-8エンコーディングでは1文字あたり3バイトで表現されるためです.

PHP 8.3では,マルチバイト文字に対応した新しい関数 mb_str_pad()が導入されます.

var_dump(mb_str_pad('日本語', 10, '_', STR_PAD_RIGHT));

この出力は,「日本語_______」と意図したものになります.

次に,埋める文字がマルチバイト文字である場合について確認してみましょう.

var_dump(str_pad('日本', 10, '_', STR_PAD_RIGHT));

この場合の出力は,「日本________」のように合計10文字になることが期待されますが,実際には,「日本_�」のように一部文字化けしてしまいます.これは,UTF-8文字列をバイト単位で10バイト分取り出したことで,一部の文字が破壊されたためです.mb_str_pad()を利用することで,意図した出力が得られます.

var_dump(mb_str_pad('日本', 10, '_', STR_PAD_RIGHT));

スタティック変数の変数による初期化

従来,PHP では,static変数の初期化は, 定数などの固定した値でしか行うことができませんでした.PHP 8.3では,変数や関数の戻り値などによる初期化が可能となります.以下の簡単な例を見てみましょう.

<?php
function hello() {
    echo "hello() called.";
    return 1;
}
function foo() {
    static $i = hello();
    echo $i++;
}
foo();  // "hello() called." 1
foo();  // 2

このコードでは,foo()関数の中でスタティック変数$iが宣言され,hello()関数の戻り値で初期化されています.13行目でfoo()を1回目にコールすると,static変数の初期化が行われて,hello()関数がコールされます.出力「hello() called.」が表示されると共に,戻り値 1でstatic 変数$iが初期化されます.foo()関数の出力として$iの値が1として出力されます.次の14行目で再度foo()関数をコールすると,static変数$iは初期化されているため,前回のコールでインクリメントされた値(2)が出力されます.このコードは,PHP 8.3では正常に動作しますが,PHP 8.2までのバージョンで実行すると,以下のように致命的なエラーが発生します.

Fatal error: Constant expression contains invalid operations

static変数を変数や関数の戻り値で初期化するユースケースでは,便利に使えると思われます.

SQLite3エクステンションにおける例外

軽量なデータベースであるSQLiteとの接続を行うために SQLite3クラスがサポートされています.SQLite3クラスでは,標準で例外処理が無効になっており,通常のPHP警告のみが発生します.例外処理を行うためには,enableExceptions()メソッドにtrueを指定します.以下に簡単な例を示します.

<?php
$sqlite = new SQLite3(':memory:');
try {
    $sqlite->enableExceptions(true);
    $sqlite->exec('create table bar');
} catch (Exception $e) {
    echo $e::class . ': ' . $e->getMessage();
}

従来のPHPでは,一般的な例外として定義されていましたが,PHP 8.3では専用の例外クラスSQLite3Exceptionが定義されます.

<?php
$sqlite = new SQLite3(':memory:');
try {
    $sqlite->enableExceptions(true);
    $sqlite->exec('create table bar');
} catch (SQLite3Exception $e) {
    echo $e::class . ': ' . $e->getMessage();
}

PHP 8.3では,SQLite3クラスの例外処理は標準で無効のままですが,有効にすることが前提になっています.このため,例外を無効にするために,enableExceptions()メソッドの引数にfalseを指定して実行すると,以下のような廃止対象の機能に関する警告(E_DEPRECATED)が発生します.

Deprecated: SQLite3::enableExceptions(): Use of warnings for SQLite3 is deprecated

今後,時期メジャーバージョン(PHP 9)では,SQLite3Exceptionが標準で有効になる予定です.

 今回は,前回に続いて,PHP 8.3で導入される予定の新機能の一部を紹介しました.次回以降もPHP 8.3の新機能を始め,PHP関連の話題を紹介いたします.

<< PHP重鎮の廣川類氏のコラム第31回「PHPの最新状況:PHP 8.3リリース候補版が公開」PHP重鎮の廣川類氏のコラム第33回「PHP 8.3ついにリリース」 >>

関連記事

Webサイト運用の課題解決事例100選 プレゼント

Webサイト運用の課題を弊社プロダクトで解決したお客様にインタビュー取材を行い、100の事例を108ページに及ぶ事例集としてまとめました。

・100事例のWebサイト運用の課題と解決手法、解決後の直接、間接的効果がわかる

・情報通信、 IT、金融、メディア、官公庁、学校などの業種ごとに事例を確認できる

・特集では1社の事例を3ページに渡り背景からシステム構成まで詳解