下記で使われている ...
に関して
- 🔰 シンタックスは,そもそも有効でしょうか?
- 🔰 有効である場合,どういう役目を持っているでしょうか?
- 🎓 有効である場合,どの PHP バージョン以降で有効でしょうか?
ヒント: 1つだけ,エラーになるものが含まれています!
Q1
<?php
$nested = [
['foo'],
['bar', 'baz'],
['qux'],
];
// ↓
$collapsed = array_merge(...$nested);
var_dump($collapsed);
回答
引数のアンパック です。 PHP 5.6 以降で使用可能です。
PHP 7.3 以前では, array_merge()
の引数が空になることを考慮しなければなりません。
$collapsed = array_merge([], ...$nested); // PHP 7.3 以前ではこう書く
出力
ネストを一段アンラップしたものがマージされます。array(4) {
[0]=>
string(3) "foo"
[1]=>
string(3) "bar"
[2]=>
string(3) "baz"
[3]=>
string(3) "qux"
}
Q2
<?php
// ↓
function writeLine(string ...$lines): void
{
foreach ($lines as $line) {
echo "$line\n";
}
}
writeLine("foo");
writeLine("bar", "baz");
Q3
<?php
$x = ['foo', 'bar'];
$y = ['baz', 'qux'];
// ↓ ↓
var_dump([...$x, ...$y]);
回答
配列内での数値キーのアンパック です。 PHP 7.4 以降で使用可能です。
- 配列内での値のアンパック ¶
- 配列を含む任意の
iterable
に対して適用できます。
以下の記述は等価です。
[...$x, ...$y];
array_merge(
is_array($x) ? $x : iterator_to_array($x),
is_array($y) ? $y : iterator_to_array($y),
);
出力
array(4) {
[0]=>
string(3) "foo"
[1]=>
string(3) "bar"
[2]=>
string(3) "baz"
[3]=>
string(3) "qux"
}
Q4
<?php
enum Gender
{
case MALE;
case FEMALE;
case OTHER;
}
class Person
{
public static function fromArray(array $props): self
{
// ↓ ①
return new self(...$props);
}
public function __construct(
public readonly string $name,
public readonly int $age,
public readonly Gender $gender,
public readonly string $country,
) {
}
}
$common = [
'age' => 20,
'country' => 'United States',
];
$bob = [
// ↓ ②
...$common,
'name' => 'Bob',
'gender' => Gender::MALE,
];
$alice = [
// ↓ ③
...$common,
'name' => 'Alice',
'gender' => Gender::FEMALE,
'country' => 'United Kingdom',
];
var_dump(
Person::fromArray($bob),
Person::fromArray($alice),
new Person(
// ↓ ④
...$common,
name: 'Bob',
gender: Gender::MALE,
),
new Person(
// ↓ ⑤
...$common,
name: 'Alice',
gender: Gender::FEMALE,
country: 'United Kingdom',
),
);
回答
- ① 名前付き引数のアンパック です。名前付き引数が導入された PHP 8.0 以降で使用可能です。引数のアンパック自体は PHP 5.6 で対応されていましたが,名前付き引数にも対応するように拡張されました。
- ②③ 配列内での文字列キーのアンパック です。 PHP 8.1 以降で使用可能です。PHP 7.4 時点では数値キーにしか対応しておらず, 名前付き引数が導入されたタイミングの PHP 8.0 でも対応されませんでした。 PHP 8.0 以前では,文字列キーが含まれる場合は
array_merge()
を依然として使わなければなりませんでした。 - ④ 名前付き引数に前置された引数のアンパック です。 PHP 8.1 以降で使用可能です。 PHP 8.0 時点では,可変長引数と名前付き引数を同時に使うことはできませんでした。
- 引数を展開した後の名前付き引数 ¶
- 名前付き引数に引数のアンパックを後置することは, PHP 8.1 時点でも依然として不可能です。
⑤ 実行時エラーになります。配列では array_merge()
互換の上書きが許されていましたが,引数では上書きが許されません。
Named parameter $country overwrites previous argument
出力 (エラーになるものを除外)
object(Person)#3 (4) {
["name"]=>
string(3) "Bob"
["age"]=>
int(20)
["gender"]=>
enum(Gender::MALE)
["country"]=>
string(13) "United States"
}
object(Person)#4 (4) {
["name"]=>
string(5) "Alice"
["age"]=>
int(20)
["gender"]=>
enum(Gender::FEMALE)
["country"]=>
string(14) "United Kingdom"
}
object(Person)#5 (4) {
["name"]=>
string(3) "Bob"
["age"]=>
int(20)
["gender"]=>
enum(Gender::MALE)
["country"]=>
string(13) "United States"
}
Q5
<?php
// あり得る値の定義
enum Permission: string
{
case MEMBER_ADD = 'member:add';
case MEMBER_DELETE = 'member:delete';
case POST_CREATE = 'post:create';
}
// 信頼できない入力
$inputs = [
'member:add',
'post:create',
'foo:bar', // 間違った値
];
// フィルタリングした結果
$filtered = array_values(array_filter(array_map(
// ↓
Permission::tryFrom(...),
$inputs,
)));
var_dump($filtered);
回答
クロージャの生成 です。 PHP 7.1 から Closure::fromCallable() は使えましたが,静的解析と相性のいい方法として PHP 8.1 から使えるようになりました。
以下の記述はすべて等価です。
Permission::tryFrom(...);
Closure::fromCallable([Permission::class, 'tryFrom']);
Closure::fromCallable(Permission::class . '::tryFrom');
出力
array(2) {
[0]=>
enum(Permission::MEMBER_ADD)
[1]=>
enum(Permission::POST_CREATE)
}
関連記事