检查是否可以在没有参数的情况下实例化类

时间:2019-12-18 13:47:06

标签: php reflection constructor instantiation

说我有一个函数可以创建给定类的新实例:

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()方法并不能确定类是否可以实例化。那么什么决定了呢?

3 个答案:

答案 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来检查构造函数是否为私有。 (感谢CidMaxim 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仍然存在疑问! ?

相关问题