この記事について
PHPのバージョン8.3が先日2023年11月23日にリリースされました。このバージョンアップではどのような点が変更されたのか、個人的に気になったところをピックアップして紹介していきます。
変更点ピックアップ
新機能
クラス定数の型付け
クラス定数に型を書けるようになりました。
class HogeClass {
const string A = 'hoge';
}
interface HogeInterface {
const string A = 'hoge';
}
trait HogeTrait {
const string A = 'hoge';
}
enum HogeEnum {
const string A = 'hoge';
}
以前は型が書けなかったので、extendsやimplimentsした際に異なる型の値で初期化してしまうことができました。型を明示してそういった事態を防ぐことができるので、ありがたい変更です。
interface I {
const PHP = 'PHP 8.2';
}
class Foo implements I {
const PHP = []; // 😨
}
Override アトリビュート
PHP8からアトリビュートが実装されましたが、今回そこに#[\Override]
が追加されました。これにより、メソッドがオーバーライドしたものであることを明示することができます。
class C1 {
public function hoge(): void {}
}
class C2 extends C1 {
#[\Override]
public function hoge(): void {}
}
これにより、親クラスやインターフェースに同じメソッドが定義されていない場合はエラーが発生します。例えば、メソッド名をtypoしてしまったり、元のメソッドが変更された場合に気がつくことができそうです。
class C1 {
public function hoge(): void {}
}
class C3 extends C1 {
#[\Override]
public function hogee(): void {}
}
// Fatal error: C3::hogee() has #[\Override] attribute,
// but no matching parent method exists
クラス定数への動的なアクセス構文
クラス定数に対して動的にアクセスできるようになりました。
class Sample {
const HOGE = 'hoge';
}
$value = 'HOGE';
$getValue = fn() => 'HOGE';
var_dump(Sample::{$value}); // hoge
var_dump(Sample::{$getValue()}); // hoge
合わせて列挙型/Enumの値にも動的にアクセスできるようになりました。以前はシンプルにアクセスする方法がなかったので、これは嬉しい変更です。
enum Suit: string
{
case Hearts = 'H';
case Diamonds = 'D';
case Clubs = 'C';
case Spades = 'S';
}
$param = 'Hearts';
var_dump(Suit::{$param}->value); // H
新しく追加された関数
json_validate
文字列が有効なJSONかどうかを調べるjson_validateが追加されました。検証自体はjson_decodeでもできるのですが、検証だけでデコードされた使わない場合はこちらのほうがパフォーマンスに優れるようです。例えば、単体テストで検証をたくさん実行するケースに使えそうですね。
var_dump(json_validate('{ "test": { "foo": "bar" } }')); // true
var_dump(json_validate('{ "": "": "" } }')); // false
mb_str_pad
文字列に対して特定の文字列で埋めて固定長にするstr_padのマルチバイト版としてmb_str_padが追加されました。
var_dump(mb_str_pad('▶▶', 6, '❤❓❇', STR_PAD_RIGHT)); // string(18) "▶▶❤❓❇❤"
var_dump(mb_str_pad('▶▶', 6, '❤❓❇', STR_PAD_LEFT)); // string(18) "❤❓❇❤▶▶"
var_dump(mb_str_pad('▶▶', 6, '❤❓❇', STR_PAD_BOTH)); // string(18) "❤❓▶▶❤❓"
var_dump(mb_str_pad("🎉", 3, "祝", STR_PAD_LEFT)); // string(10) "祝祝🎉"
Random\Randomizer::getBytesFromString
PHP8.2から乱数生成のためのrandom拡張モジュールが追加されましたが、今回はそこに、指定した文字列からランダムな文字列を生成するgetBytesFromStringが追加されました。
$randomizer = new \Random\Randomizer();
echo $randomizer->getBytesFromString(
'abcdefghijklmnopqrstuvwxyz0123456789',
10,
);
// z0cshw5me7
これだけで暗号的にも安全なランダム文字列が生成できるのは便利ですね。
下位互換性のない変更点
空の配列に負のインデックスを割り当てた場合の挙動
空の配列に負のインデックスを割り当てた場合に次のインデックスは必ずそこから+1されていく、とのことです。具体的には次のようになります。
$arr = [];
$arr[-10] = 'a';
$arr[] = 'b';
$arr[] = 'c';
var_dump($arr);
// [
// -10 => "a",
// -9 => "b",
// -8 => "c"
// ]
ちなみに以前はどうだったかというと、負のインデックスを割り当てても次は0になり、そこから+1されていきます。
range関数に対するさまざまな変更
ある範囲の要素を含む配列を作成するrange関数ですが、変更を一言で表すと「より自然な挙動をするようになった」という感じでしょうか。
大まかに2つに分けると、1つは、より自然な配列が生成されるようになった点があります。
// PHP < 8.3
range('9', 'A'); // [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
// PHP 8.3
range('9', 'A'); // ["9", ":", ";", "<", "=", ">", "?", "@", "A"]
もう1つは、一般的でない引数が指定された時にエラーが発生するようになりました。
// PHP 8.3
range(1, []); // TypeError
range(1, 10, 0); // ValueError
range(1, 10, -1); // ValueError
推奨されなくなる機能
mb_strimwidth関数に負のwidth
を渡すこと
文字列を指定した幅に丸めるmb_strimwidthですが、その幅を指定する引数width
には負の値を指定することができます。今回の変更により、負の値を指定するとDeprecatedエラーが発生するようになりました。
echo mb_strimwidth('abcde', 0, -1);
// Deprecated: mb_strimwidth(): passing a negative integer to argument #3 ($width) is deprecated
// abcd
「指定した幅に丸める」というのが目的なので、負の値を指定するのは目的に合わないですね。
まとめ
先日(2023年11月23日)リリースされたPHPのバージョン8.3について、いくつか変更点をピックアップしてみました。型システムの強化や不自然な挙動の調整が実施され、引き続き安全性が高まっている印象です。他にも変更点はいろいろあるので、移行ガイドから一読してみてはいかがでしょうか。
さて、新しいバージョンがリリースされたということはつまり、古いバージョンのサポート終了が迫っているということです。1 8.0は終わりを迎え、8.1はあと1年、8.2はあと2年を切りました。慌てることのないよう、余裕を持って新しいバージョンへ移行しましょう(戒め)。
謝辞
今回PHP8.3を試すためにDockerイメージを待っていたのですが、プルリクエストでこのような発言がありました。
あるいは、少し我慢して待つという手もある。~中略~ 例えば、家族や友人と過ごしたり、自由な時間を楽しんだりするために、メンテナはコンピュータから離れる時間を作ることができます。(機械翻訳)2
メンテナーに感謝と敬意を払う姿勢は忘れたくないものです。PHPとDockerイメージのメンテナーの皆様ありがとうございます。