PHPには名前付きパラメータという機能があります。これは次のように各引数を名前で指定することによってその順序に関わらず引数を設定する機能です。
PHP: 関数の引数 – Manual#名前付き引数
これは次のように引数の名前の後にコロンをつけることで指定できます。
function test($hoge, $fuga, $piyo)
{
var_dump($hoge);
var_dump($fuga);
var_dump($piyo);
}
$x = 'xxxx';
$y = 'yyyy';
$z = 'zzzz';
test(fuga: $y, hoge: $x, piyo: $z);
// string(4) "xxxx"
// string(4) "yyyy"
// string(4) "zzzz"
PHPには配列の展開機能もあります。これは次のようにして配列を展開して展開した場所に合わせた使い方をする機能です。
$params = ['hoge' => 'xxxx', 'fuga' => 'yyyy', 'piyo' => 'zzzz']; $params2 = [...$params, 'foo' => 'aaa']; // hoge, fuga, piyo, foo が揃った配列になります。
この二つ機能を組み合わせると次のように配列内の要素を関数の名前付き引数として直接渡すことができるようになります。
function test($hoge, $fuga, $piyo)
{
var_dump($hoge);
var_dump($fuga);
var_dump($piyo);
}
$params = ['hoge' => 'xxxx', 'piyo' => 'zzzz', 'fuga' => 'yyyy', ];
// 引数の名前と連想配列のキー名が合っていれば順不同
test(...$params);
// string(4) "xxxx"
// string(4) "yyyy"
// string(4) "zzzz"
そしてこの挙動を利用すると次のように完全コンストラクタをビルダーパターンで扱うようなクラスも作れます。
<?php
/**
* クラスのインスタンスを動的に生成するファクトリクラス
*/
class InstanceFactory
{
/**
* インスタンス生成時に使用する引数
* @var array
*/
protected array $args = [];
/**
* コンストラクタ
*
* @param string $className インスタンスを生成するクラス名
*/
public function __construct(private readonly string $className)
{
}
/**
* インスタンスを生成して返す
*
* @return object 生成されたクラスのインスタンス
*/
public function make(): object
{
return new $this->className(...$this->args);
}
/**
* マジックメソッド__callを使用して、任意のメソッド名で引数を設定
*
* @param string $name メソッド名として解釈されるパラメータ名
* @param array $arguments 設定したい値のリスト
* @return $this 自身のインスタンスを返す(メソッドチェーン可能)
*/
public function __call(string $name, array $arguments)
{
$this->args[$name] = $arguments[0];
return $this;
}
}
class User
{
public function __construct(public string $name, public int $age)
{
}
}
// ファクトリの初期化
$factory = new InstanceFactory(User::class);
// プロパティ名のメソッドをsetterとして呼べます
$user = $factory->name('浜松 太郎')->age(15)->make();
var_dump($user);
// object(User)#2 (2) {
// ["name"]=>
// string(13) "浜松 太郎"
// ["age"]=>
// int(15)
//}
// ファクトリインスタンスを使いまわせば、同じクラスの似た引数のインスタンスを簡単に生成できます
$user2 = $factory->age(25)->make();
var_dump($user2);
//object(User)#3 (2) {
// ["name"]=>
// string(13) "浜松 太郎"
// ["age"]=>
// int(25)
//}
// 最終的にコンストラクタをそのまま使っているため、半端なパラメータしか指定していない場合はエラーになります
(new InstanceFactory(User::class))->name('浜松 太郎')->make();
// PHP Fatal error: Uncaught ArgumentCountError: Too few arguments to function User::__construct(), 1 passed in /xxx/tmp.2.php on line 30 and exactly 2 expected in /xxx/tmp.2.php:50
//Stack trace:
//#0 /xxx/tmp.2.php(30): User->__construct()
//#1 /xxx/tmp.2.php(65): InstanceFactory->make()
//#2 {main}
// thrown in /xxx/tmp.2.php on line 50
この機能はAPIのラッパーを作成する際や関数に多数のオプションを柔軟に渡したい場合に便利です。また関数の引数が極端に多い場合、コードの可読性を保ちながら効率的に処理を行うことが可能になります。