手短に言えば、一連の引数がエラーのないコンストラクターのインスタンス化を許可するかどうかを単純に判断できないということです。コメンターが上で述べたように、実際にインスタンス化を試みないとわからない実行時の考慮事項があるため、特定の引数リストを使用してクラスをインスタンス化できるかどうかを確実に知る方法はありません。
ただし、コンストラクター引数のリストからクラスをインスタンス化しようとすることには価値があります。この種の操作の最も明白な使用例は、構成可能な Dependency Injection Container (DIC) です。残念ながら、これは OP が示唆するよりもはるかに複雑な操作です。
提供された定義配列の引数ごとに、コンストラクター メソッド シグネチャから指定された型ヒントに一致するかどうかを判断する必要があります (メソッド シグネチャに実際に型ヒントがある場合)。また、デフォルトの引数値をどのように扱うかを解決する必要があります。さらに、コードを実際に使用するには、クラスをインスタンス化する前に「定義」を指定できるようにする必要があります。この問題の洗練された処理には、リフレクション オブジェクトのプール (キャッシュ) も含まれ、繰り返しリフレクションすることによるパフォーマンスへの影響を最小限に抑えます。
ReflectionParameter::getClass
もう 1 つのハードルは、メソッドを呼び出し、その後、返されたクラス名からリフレクション クラスをインスタンス化せずに、リフレクトされたメソッド パラメーターの型ヒントにアクセスする方法がないという事実です(null
が返された場合、パラメーターには型ヒントがありません)。これは、生成された反射をキャッシュすることが、実際のユースケースにとって特に重要になる場所です。
以下のコードは、私自身の文字列ベースの再帰的な依存性注入コンテナーの大幅に簡素化されたバージョンです。これは、疑似コードと実際のコードの混合物です (無料のコードをコピーして貼り付けることを望んでいた場合は、運が悪くなります)。以下のコードは、「定義」配列の連想配列キーをコンストラクター シグネチャのパラメーター名に一致させていることがわかります。
実際のコードは、関連するgithub プロジェクト ページにあります。
class Provider {
private $definitions;
public function define($class, array $definition) {
$class = strtolower($class);
$this->definitions[$class] = $definition;
}
public function make($class, array $definition = null) {
$class = strtolower($class);
if (is_null($definition) && isset($this->definitions[$class])) {
$definition = $this->definitions[$class];
}
$reflClass = new ReflectionClass($class);
$instanceArgs = $this->buildNewInstanceArgs($reflClass);
return $reflClass->newInstanceArgs($instanceArgs);
}
private function buildNewInstanceArgs(
ReflectionClass $reflClass,
array $definition
) {
$instanceArgs = array();
$reflCtor = $reflClass->getConstructor();
// IF no constructor exists we're done and should just
// return a new instance of $class:
// return $this->make($reflClass->name);
// otherwise ...
$reflCtorParams = $reflCtor->getParameters();
foreach ($reflCtorParams as $ctorParam) {
if (isset($definition[$ctorParam->name])) {
$instanceArgs[] = $this->make($definition[$ctorParam->name]);
continue;
}
$typeHint = $this->getParameterTypeHint($ctorParam);
if ($typeHint && $this->isInstantiable($typeHint)) {
// The typehint is instantiable, go ahead and make a new
// instance of it
$instanceArgs[] = $this->make($typeHint);
} elseif ($typeHint) {
// The typehint is abstract or an interface. We can't
// proceed because we already know we don't have a
// definition telling us which class to instantiate
throw Exception;
} elseif ($ctorParam->isDefaultValueAvailable()) {
// No typehint, try to use the default parameter value
$instanceArgs[] = $ctorParam->getDefaultValue();
} else {
// If all else fails, try passing in a NULL or something
$instanceArgs[] = NULL;
}
}
return $instanceArgs;
}
private function getParameterTypeHint(ReflectionParameter $param) {
// ... see the note about retrieving parameter typehints
// in the exposition ...
}
private function isInstantiable($class) {
// determine if the class typehint is abstract/interface
// RTM on reflection for how to do this
}
}