PHP8.0は2020年11月26日にリリース予定のPHPの新バージョンです。この記事ではPHP8.0で導入されるであろう機能を紹介します。
PHP: rfc:nullsafe_operator[RFC] Nullsafe operator - Externals
Nullsafe operator は null を参照しても問題なく動作を続行するための演算子です。具体的に何をするかというと次です。
// @see https://wiki.php.net/rfc/nullsafe_operator
/**
* Nullsafe operator なしの素朴な記述
*/
$country = null;
// 参照途中の各要素(主にプロパティ)が null でないか都度検査する
if ($session !== null) {
$user = $session->user;
if ($user !== null) {
$address = $user->getAddress();
if ($address !== null) {
// 全て not null ならば代入
$country = $address->country;
}
}
}
// 最奥の if 文内までは入れていないならば $country === null
/**
* Nullsafe operator あり
*/
// 途中で null を参照するとその時点で処理を打ち切って $country に null を代入
$country = $session?->user?->getAddress()?->country;
これはJavaScriptの Optional chaining とほぼ同じ( JavaScript 側は値 undefined の処理が絡む)で、Laravelユーザ的にはヘルパ関数の optional がわかりやすいでしょう。
Nullsafe operator を使うことで長大な null 検査を記述する必要がなくなります。
[RFC] Named arguments - Externals
【PHP】名前付き引数が使える様になるかも – 株式会社シーポイントラボ | 浜松のシステム・RTK-GNSS開発
以前記事に起こしたのでざっくり紹介します。
// @see https://wiki.php.net/rfc/named_params
// 名前付き引数なし
array_fill(0, 100, 50);
// 名前付き引数あり
array_fill(start_index: 0, num: 100, value: 50);
array_fill(value: 50, start_index: 0, num: 100);
引数を名前で指定することができます。引数の数が多くても順番に気を配る必要がなくなります。
PHP: rfc:throw_expression[RFC] throw expression - Externals
throw が式として書けます。具体的には次です。
// throw式なし
if(! isset($nullableValue)){
throw new InvalidArgumentException();
}
$value = $nullableValue;
// throw式あり
$value = $nullableValue ?? throw new InvalidArgumentException();
文でなく式になることで今まで記述できなかった部分でも throw を書けるようになります。値の検査に合格したら代入、失敗したら例外、という処理は頻出します。その度に冗長に見える if 文を書くことに微妙に不満のあった自分としては割とうれしい変更です。
[RFC][DISCUSSION] Match expression v2 - Externals
match式はswitch文的な動作をする新たな式です。具体的に何をするかというと次です。
// @see https://wiki.php.net/rfc/match_expression_v2
// switch文による記述
switch ($this->lexer->lookahead['type']) {
case Lexer::T_SELECT:
$statement = $this->SelectStatement();
break;
case Lexer::T_UPDATE:
$statement = $this->UpdateStatement();
break;
case Lexer::T_DELETE:
$statement = $this->DeleteStatement();
break;
default:
$this->syntaxError('SELECT, UPDATE or DELETE');
break;
}
// match 式による記述
$statement = match ($this->lexer->lookahead['type']) {
Lexer::T_SELECT => $this->SelectStatement(),
Lexer::T_UPDATE => $this->UpdateStatement(),
Lexer::T_DELETE => $this->DeleteStatement(),
default => $this->syntaxError('SELECT, UPDATE or DELETE'),
};
ぱっと見は短縮構文です。1ケースに1式しか用いられない代わりに 1/3 程に圧縮されました。これだけでも各caseの中に長々と処理を書いて switch 文を含む関数が恐ろしい長さになるという失敗を防げるうえ記述が楽になります。match 式が switch 文と異なる最大の点は条件式の比較が厳密な比較であることです。具体的には次です。
// @see https://wiki.php.net/rfc/match_expression_v2
/**
* switch 文による期待しない動作の例
*/
switch ('foo') {
// switch 文での比較は緩やかな比較で暗黙の型変換が起きます。
// 'foo' を int 型に変換すると数字に換算できない文字列なので 0 になります。
case 0:
// 0 == 'foo' なのでこのケースに引っかかります
$result = "Oh no!\n";
break;
case 'foo':
$result = "This is what I expected\n";
break;
}
echo $result; // Oh no!
/**
* match 式による期待通りの動作の例
*/
echo match ('foo') {
0 => "Oh no!\n",
'foo' => "This is what I expected\n",
}; // This is what I expected
// match 式では厳密な比較をします。
// 'foo' === 0 は false と評価され"Oh no!\n"は echo されません
// 'foo' === 'foo' は true と評価されるので"This is what I expected\n"が echo されます。
ありがたいですね。これのおかげで今まで switch 文を避けて大量の if, if else にしていた記述をより適した記述にできます。