说我有一个函数可以创建给定类的新实例:
function createInstance($class) {
return new $class();
}
$dateTime = createInstance('DateTime');
如果该类不存在或无法用无参数构造函数实例化,如何更改此方法的实现以使其“具有防御性”并且不产生错误?
检查类是否存在很容易:
function createInstance($class) {
classExists($class)
return new $class();
}
function classExists($class) {
if (empty($class) || !class_exists($class))
throw new InvalidArgumentException("\$class argument invalid - no such class '$class'");
}
但是,如何检查可实例化的类?
function createInstance($class) {
classExists($class)
if (canCreateInstance($class))
return new $class();
else
throw new InvalidArgumentException("Cannot create instance of $class");
}
function classExists($class) {
if (empty($class) || !class_exists($class))
throw new InvalidArgumentException("\$class argument invalid - no such class '$class'");
}
function canCreateInstance($class) {
// ???
}
我尝试使用method_exists($class, '__construct')
,但是像REPL demonstrates一样,这是不可靠的,因为是否存在__construct()
方法并不能确定类是否可以实例化。那么什么决定了呢?
答案 0 :(得分:4)
您应该使用反射来获取此信息,例如:
class Foo
{
public function __construct(string $a, string $b = '')
{
// some implementation
}
}
class Bar
{
private function __construct()
{}
}
您可以使用ReflectionClass获取有关已定义类的所有信息。例如,您可以使用帮助isInstantiable方法检查它是否可以实例化
$reflection = new ReflectionClass(Foo::class);
$isInstantiable = $reflection->isInstantiable(); // get true
$reflection = new ReflectionClass(Bar::class);
$isInstantiable = $reflection->isInstantiable(); // get false
此外,您还可以使用帮助getConstructor方法获取有关构造函数的信息
$reflection = new ReflectionClass(Foo::class);
$constructor = $reflection->getConstructor();
之后,您的变量将包含ReflectionMethod对象,您可以使用它来获取有关它的所有必要信息。例如
$constructor->getNumberOfParameters(); // return 2
$constructor->getNumberOfRequiredParameters(); // return 1
答案 1 :(得分:1)
一个类不能实例化的唯一原因是它是abstract
还是具有私有构造函数。您可以使用ReflectionClass::isAbstract
函数进行检查。然后,您可以使用函数ReflectionMethod::isPrivate
来检查构造函数是否为私有。 (感谢Cid和Maxim Fedorov指出这一点。)
function createInstance($class) {
classExists($class)
if (canCreateInstance($class))
return new $class();
else
throw new InvalidArgumentException("Cannot create instance of $class");
}
function classExists($class) {
if (empty($class) || !class_exists($class))
throw new InvalidArgumentException("\$class argument invalid - no such class '$class'");
}
function canCreateInstance($class) {
$reflectionClass = new ReflectionClass($class);
if($reflectionClass->isAbstract() || $reflectionClass->isPrivate('__construct'))
throw new InvalidArgumentException("\$class argument invalid - cannot instanciate class '$class'");
}
答案 2 :(得分:0)
如Maxim所建议的,ReflectionClass::isInstantiable
方法可用于防止大多数情况:
function canCreateInstance($class) {
return (new ReflectionClass($class))->isInstantiable();
}
但是,这不能捕获类没有无参数构造函数的实例。幸运的是,所产生的错误是可捕获的,因此我将createInstance
方法设为如下:
function createInstance($class) {
classExists($class)
if (canCreateInstance($class)) {
try {
return new $class();
} catch (Error $er) {
throw new Exception("Error instantiating $class", $er->getCode() $er);
}
} else {
throw new InvalidArgumentException("\$class argument invalid - Objects of type $class cannot be instantiated");
}
}
但是,我猜想这仅适用于PHP 7或更高版本,因为添加了Error
类,所以对于PHP 5仍然存在疑问! ?