PHP 8.3 の新機能を試してみよう

この記事は個人ブログと同じ内容です

www.ritolab.com


PHP 8.3 が 2024 年 11 月にリリースになります。PHP 8.3 の新しい機能をいくつかピックアップして試してみます。

クラス定数の型指定

クラスで定義する定数に型の指定が行えるようになりました。

Typed class constants
https://wiki.php.net/rfc/typed_class_constants

<?php

class Person {
    const string NAME = 'rito';
}

指定した型ではない値を代入するとエラーになります。

<?php

class Person {
    const string NAME = 777;
}

// Fatal error: Cannot use int as value for class constant Person::NAME of type string in...

クラス定数の動的参照

Dynamic class constant fetch
https://wiki.php.net/rfc/dynamic_class_constant_fetch

クラスで定義した定数を、変数で動的に参照できるようになりました。

<?php

class Person {
    const string NAME = 'rito';
}

$personNameConstant = 'NAME';

echo Person::{$personNameConstant};
// => rito

PHP 8.2 までは constant() 関数を使わないと同様のことはできませんでした。

<?php

echo constant("Person::$personNameConstant");
// => rito

8.3 でも constant() 関数は引き続き使えます。

Override attribute によるオーバーライド指定

Marking overridden methods (#[Override])
https://wiki.php.net/rfc/marking_overriden_methods

PHP は、実装されたメソッドのシグネチャが指定されたインターフェイスまたは親クラスからオーバーライドされたメソッドと互換性があることを検証しますが、メソッドが実際にインターフェイスのメソッドを実装することを目的としているのか、親メソッドをオーバーライドすることを目的としているかどうかの「意図」を確認することができません。

例として、以下の基底クラスと派生クラスがあったとします。派生クラス側で、基底クラスのメソッドをオーバーライドしています。

<?php

class Book {
    protected function setName(string $name): void { /* some processing... */}
}

class PhpReference extends Book {
    // setName() をオーバーライドしている
    public function setName(string $name): void { /* some processing... */}
}

このとき、基底クラス側のメソッド名が変更されたらどうなるでしょう。

<?php

class Book {
    // protected function setName(): void { /* some processing... */}
    // ↓ メソッド名を変更した
    protected function setTitle(string $name): void { /* some processing... */}
}

class PhpReference extends Book {
    // setName() をオーバーライドしていたはずが、基底クラス側のメソッド名が変わったために派生クラス独自のメソッドとなってしまった
    public function setName(string $name): void { /* some processing... */}
}

基底クラス側のメソッド変更によって、派生クラス側のオーバーライドが独自メソッドになってしまいます。つまり、PhpReference クラスは setTitle() 関数も、setName() 関数も動作することになります。

PHP はこういった「意図」の検出ができません。

PHP 8.3 では、Override アトリビュート(#[\Override]) をつけることによって、そのメソッドがオーバーライドであることを示せるようになりました。 これによって、基底クラス側のメソッド名変更があった時にエラーにすることができます。

<?php

class Book {
    protected function setName(): void { /* some processing... */}
}

class PhpReference extends Book {
    #[\Override]
    public function setName(): void { /* some processing... */}
}

基底クラス側でメソッドの変更が行われた場合はエラーが発生します。 

<?php

class Book {
    // protected function setName(): void { /* some processing... */}
    // ↓ メソッド名を変更した
    protected function setTitle(): void { /* some processing... */}
}

class PhpReference extends Book {
    #[\Override]
    public function setName(): void { /* some processing... */}
    // => Fatal error: PhpReference::setName() has #[\Override] attribute, but no matching parent method exists in...
}

INI ファイル環境変数設定におけるデフォルト値設定

php.ini での環境変数設定でデフォルト値が設定可能になりました。

INI ファイル内では環境変数が参照できるため、これと併用することでデフォルト値の運用が可能になります。

まずは PHP 8.2 までの挙動を見てみます。

post_max_size = ${POST_MAX_SIZE}
<?php

/*
 * 〜 PHP 8,2
 */ 

// php.ini
// post_max_size = 8M
echo ini_get('post_max_size');
// => 8M

// 環境変数 POST_MAX_SIZE が設定されている
echo ini_get('post_max_size');
// => 10M

// 環境変数 POST_MAX_SIZE が設定されていない
echo ini_get('post_max_size');
// => ''

PHP 8,2 までは、値が指定されていない場合は未設定となり、値を参照すると空文字が返ります。

PHP 8.3 では、デフォルト値を指定しておけば、環境変数の設定が無い場合もデフォルト値を適用してくれます。

post_max_size = "${POST_MAX_SIZE:-6M}"
<?php

/*
 * PHP 8,3 〜 
 */ 

// 環境変数 POST_MAX_SIZE が設定されている
echo ini_get('post_max_size');
// => 10M

// 環境変数 POST_MAX_SIZE が設定されていない
echo ini_get('post_max_size');
// => 6M

json_validate() 関数

json_validate() 関数が追加されました。

json_validate
https://wiki.php.net/rfc/json_validate

この関数は、文字列が有効な json 文字列であるかどうかを検証します。

これまでは、json_decode() 関数 の結果によってハンドリングすることが多かったはずです。

<?php

$json = '';

try {
    json_decode(json: $json, flags: JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    /* some processing... */
}

ただしこの場合、文字列の解析で ZVAL(object, arrayなど) を生成するため、メモリを消費する上、メモリ保存処理分のオーバーヘッドも発生します。

対して PHP 8.3 で追加された json_validate() 関数はこれらの処理を行わないため、リソース節約・速度向上に貢献します。

また、json_validate() 関数で使用する Parser は json_decode() 関数と共通のため、json_validate() が通れば json_decode() も成功する。ということが担保されます。

json_validate( string $json, int $Depth = 512, int $flags = 0 ) : bool
<?php

$json = '';
json_validate($json);
// => false

$json = '{"a":"aaa","b":"bbb"}';
json_validate($json);
// => true

まとめ

PHP 8.3では、クラス定数の型指定やオーバーライド指定、そして json_validate() 関数など、うれしいアップデートがありました。

今回取り上げたものは一部です。新機能は他にもあるのでチェックしてみてください。

https://wiki.php.net/rfc#php_83


現在 back check 開発チームでは一緒に働く仲間を募集中です。 herp.careers