秋といえば、WordPressを動かすサーバーサイドスクリプト言語、PHPの新バージョンがリリースされる時期です。11月21日のバージョン8.4のGA版リリース(一般提供)に向けて、PHP開発チームは8月の機能凍結以来、リリース候補を含む新たなコードベースの初期バージョンを多数発表しています。

新機能、改善点、非推奨機能に加えて、2024年のこの時期には、PHPのリリースサイクルに調整が加えられます。現在サポートされているすべてのバージョンのセキュリティリリースの終了は、PHPのGA版リリース時ではなく年末に同期されることになりそうです。

また、PHP 8.4のサポートは1年延長されるため、2028年まで安全に使うことができます(2年間はセキュリティとバグ修正、2年間はセキュリティ修正のみ)。

今回は、PHP 8.4の新機能についてご紹介します。

PHP 8.4の新機能と改善点

昨年リリースされたPHP 8.3の新機能は、今回の8.4で導入された一部機能と比較すると、控えめに感じるかもしれません。

PHP 8.4で追加された新機能は以下のとおりです。

プロパティフック

プロパティフックは、PHPのオブジェクト指向プログラミング(OOP)における、「ゲッター(getter)」と「セッター(setter)」の扱いに全く新しいアプローチを導入し、クラスファイルの構造を単純化することを可能にします。

プロパティフックで置き換えられるものの例として、以下のような$size$flavorがあります。これらのプロパティは、外部から直接アクセスされないようにするため、privateとして設定されています。このため、publicであるgetterメソッドとsetterメソッドがプロパティへのアクセスを仲介します。

class Coffee
{
    private string $size;
    private string $flavor;
    public function __construct(string $size, string $flavor) {
        $this->size   = $size;
        $this->flavor = $flavor;
    }

    // コーヒーのサイズと風味を設定(セット)
    public function setSize(string $size): void {
        $this->size = $size;
    }
    public function setFlavor(string $flavor): void {
        $this->flavor = $flavor;
    }

    // コーヒーのサイズと風味を取得(ゲット)
    public function getSize(): string {
        return $this->size;
    }
    public function getFlavor(): string {
        return $this->flavor;
    }
} // クラス終了

// コーヒーを淹れる
$coffee = new Coffee('スモール', 'パンプキンスパイス');
print $coffee->getSize() . ' ' . $coffee->getFlavor(); // 「スモール・パンプキンスパイス」を出力

// 注文を変更
$coffee->setSize('グランデ');
$coffee->setFlavor('モカ');
print $coffee->getSize() . ' ' . $coffee->getFlavor(); // 「グランデ・モカ」を出力

あるいは、クラスに多くのプロパティがあり、getterメソッドやsetterメソッドを多数書く代わりに、マジックメソッドの_get_setを使用するかもしれません。以下のように、やや乱雑なswitch文で整理することもできます。

// __setマジックメソッドの例
public function __set(string $key, $value): void 
    switch ($key) {
        case 'size':
            $this->size = $value;
            break;
        case 'flavor':
            $this->flavor = $value;
            break;
        default:
            throw new InvalidArgumentException('無効な入力です');
        }
}

// 後でコーヒーの順番をこのように変更できる
$coffee->size = 'グランデ';
$coffee->flavor = 'モカ';

どのような方法をとるにせよ、クラス内のプロパティの数が多ければ多いほど、 それらを操作するコードは、クラスファイルの先頭付近の定義から遠ざかることになります。さらに、_get_setマジックメソッドの実装によっては、公開する予定のなかったオブジェクトのprivateプロパティやprotectedプロパティへのアクセスを提供してしまうことがあります。

新たに導入されるプロパティフックは、ゲッターとセッターの機能をプロパティ自体に組み込みます。以下の例では、Coffeeクラスの$size プロパティと$flavorプロパティがpublicになっていますが、setフックには基本的なバリデーション(チェック機能)が追加され、直接の代入とは区別されています。

// Coffeeクラスのトップにあるプロパティ定義
class Coffee
{
    public string $flavor {
        set(string $value) {
            if (strlen($value) > 16) throw new InvalidArgumentException('入力が長すぎます');
                $this->flavor = $value;
        }
    }

    public string $size {
        set(string $value) {
            if (! in_array($value, array(‘スモール’, ‘グランデ’))) throw new InvalidArgumentException('有効なサイズではありません');
                $this->size = $value;
        }
    }

    // 残りのCoffeeクラス
}

// コーヒーを定義
$coffee = new Coffee();
$coffee->size = 'グランデ';
$coffee->flavor = 'パンプキンスパイス';

同様に、getフックを使用することで、一見ただのプロパティ参照に見えるものにも機能を追加することができます。

// 簡易のCoffeeクラス
class Coffee
{
    public string $flavor {
        get { 
            return $this->flavor . 'スパイス';
       }
    }
}

// 風味を作成
$coffee = new Coffee();
$coffee->flavor = 'パンプキン'; // 値 「パンプキン」を保存
print $coffee->flavor;       // 「パンプキンスパイス」を出力

PHPのマジックメソッドとは異なり、プロパティフックはインターフェースや抽象クラスで使用することができます。以下はインターフェースの例です。

interface Coffee
{
    public string $size { get; set; }
    public string $flavor { get; set; }
}

非対称な可視性

先ほどの一般に公開されているgetterメソッドやsetterメソッドは、クラス内のprivateプロパティやprotectedプロパティにアクセスするための従来のアプローチです。

PHP 8.4の便利な機能として、アクセスされるコンテキストによって、プロパティの可視性のレベルを変更することができます。つまり、あるプロパティが読み込まれるときにはpublicでも、設定時にはprivateまたはprotectedになるということです。

例を見てみましょう。

class Coffee
{
    public private(set) string $flavor = 'パンプキンスパイス';
}

$coffee = new Coffee();
print $coffee->flavor;     // 「パンプキンスパイス」を出力
$coffee->flavor = 'モカ';  // エラー(可視性)

この例では、クラスの$flavorプロパティは、設定コンテキスト以外ではpublicです。すでにかなりシンプルですが、非対称の可視性にはさらに便利な方法があります。

class Coffee
{
    // コンテキストが設定されていない場合はpublicを想定
    private(set) string $flavor = 'パンプキンスパイス';
}

プロパティフックと非対称の可視性を組み合わせて使うことで、さまざまな可視性のオブジェクトプロパティをかなり柔軟に扱うことができます。

括弧なしのnew連結

短縮形といえば、これまでnewや連鎖メソッドを呼び出すには以下のように括弧でくくる必要がありました。

$coffee = (new Coffee())->getFlavor()->getSize();

PHP8.4では、括弧でくくる必要がなくなります。

$coffee = new Coffee()->getFlavor()->getSize();

些細な変更に思えますが、このちょっとした括弧を削除するだけで可読性が上がり、デバッグしやすくなります。

配列の項目を探すための新関数

すでに存在していたように感じるかもしれませんが、PHP 8.4ではarray_find()という関数が導入されます。これはコールバック関数で指定された条件に一致するメンバ関数を配列要素から探すことができます。

この関数は、コールバック関数のテストに一致する最初の要素の値を返します。

  • array_find_key()array_find()と同様だが、戻り値は要素そのものの値ではなく一致した要素のキー
  • array_all():テストする配列のすべての要素がコールバックのテストに一致する場合はtrue
  • array_any():配列の要素の少なくとも1つがコールバックのテストに一致する場合はtrue

最後の2つの関数は、配列のキーや内容の代わりに真偽値を返すことに注意してください。

簡単な例を見てみましょう。

$array = [
    'a' => 'モカ',
    'b' => 'キャラメル',
    'c' => 'メープル',
    'd' => 'パンプキン'
   ];

// 5文字の最初のフレーバー名を探す
var_dump(array_find($array, function (string $value) {
    return strlen($value) == 5;
})); // 「キャラメル」を返す(「パンプキン」も同じ長さだが) 

// 5文字以上の名前を持つ最初のフレーバーの配列キーを探す
var_dump(array_find_key($array, function (string $value) {
    return strlen($value) > 5;
})); // 「b」を返す

// フレーバー名の長さが5文字以下かどうかをチェック
var_dump(array_any($array, function (string $value) {
    return strlen($value) < 5;
})); // trueを返す

// すべてのフレーバー名が8文字より短いかチェック
var_dump(array_all($array, function (string $value) {
    return strlen($value) < 8;
})); // trueを返す

HTML5のパース

HTML5は、現代のウェブページの構造における事実上の標準ですが、PHPのドキュメントオブジェクトモデル(DOM)解析技術は、HTML 4.01でHTML 4.01で停滞していました。

PHP 8.4では、古いHTML標準で動作する既存のDOMDocumentクラスをアップグレードするのではなく、 HTML5に対応したDomHTMLDocumentクラスが新たに導入されています。

以下のように、HTML5ページの内容をインポートすることができます。

$document = DomHTMLDocument::createFromString($html)

上記のcreateFromString($html)コンストラクタに加えて、createFromFile($path)createEmpty()もサポートしています。

パーサーは、mainarticlesectionのようなセマンティックなHTML5タグを認識します。

マルチバイトのtrim関数

また、多くのユーザーが待望していた機能として、trim関数におけるマルチバイトのサポートがあります。

  • mb_trim()
  • mb_ltrim()
  • mb_rtrim()

長年使用されているtrim()関数と同様、mb_trimはマルチバイト文字を含む文字列の両端から、空白文字や改行文字などの特殊文字を取り除きます。他の関数は、文字列の左端か右端のどちらかを切り取ります。

PHP 8.4の非推奨機能

PHPの各リリースには、最終的にプラットフォームから削除される可能性のある機能や関数(中にはかなり曖昧なものも)が多数含まれています。PHP 8.4で非推奨となる機能の一つに、Cookieレスセッション追跡があります。

GET/POSTセッション

一般に、ユーザーセッションの追跡にはCookieの使用が推奨されますが、PHPではセッションIDのデータをGETやPOSTパラメーターに固定することもサポートされていました。URL のパラメータによるセッション追跡を有効にするには、 PHPの設定session.use_only_cookiesを無効にし、session.use_trans_sid設定を有効にします。

PHP 8.4では、これらの設定のいずれの状態でも非推奨の警告が表示され、 ウェブサイトのログに表示される可能性があります。PHP 9のリリース時には使用できなくなります。

その他の非推奨(および削除済み)機能

最後に、PHP 8.4の開発チームが非推奨とした機能一覧をご紹介します。

  • 段階的非推奨であったDOMDocumentおよびDOMEntityプロパティが正式に非推奨に
  • DOMImplementation::getFeature($feature, $version)(削除)
  • DOM_PHP_ERR定数
  • unserialize()で使用される「S」タグ
  • session.sid_lengthおよびsession.sid_bits_per_character
  • SplFixedArray::__wakeup()の非推奨
  • xml_set_object()およびxml_set_*_handler()の文字列メソッド名
  • dba_key_split()にnullとfalseを渡す
  • ext/hash関数にオプションとして不正なデータ型を渡す
  • 定数SUNFUNCS_RET_STRINGSUNFUNCS_RET_DOUBLESUNFUNCS_RET_TIMESTAMP
  • 独自のCSVエスケープメカニズム
  • E_STRICT定数
  • strtok()定数
  • ユーザーレベルの出力ハンドラから文字列以外の値を返す
  • ユーザーレベルの出力ハンドラでの出力の生成
  • file_put_contents()$dataを配列として使用する
  • mysqli_ping()およびmysqli::ping()
  • mysqli_refresh()
  • mysqli_kill()
  • mysqli_store_result()の2番目のパラメータ
  • lcg_value()
  • uniqid()
  • md5()sha1()md5_file()sha1_file()
  • trigger_error()E_USER_ERRORを渡す
  • アンダースコア(_)をクラス名として使用する
  • SOAP_FUNCTIONS_ALL定数、およびこの定数をSoapServer::addFunction()に渡す

まとめ

PHP 8.4には、注目したい変更点がいくつか含まれています。Kinstaでは、この最新バージョンを自社のサーバーに導入し、毎年恒例のPHPベンチマークPHPベースのコンテンツ管理システム(CMS)でのテストを実施する予定です。

また、開発者の皆さんがPHP 8.4の新機能、特にプロパティフックをいつプロジェクトに取り入れるのかも気になるところです。

Steve Bonisteel Kinsta

Kinstaのテクニカルエディター。救急車や消防車を追いかける記者としてキャリアをスタート。1990年代後半からインターネット関連の技術情報を担当している。