php - 快速检查对象是否会在 PHP 中成功实例化?

标签 php reflection static-code-analysis

如何在不实际创建实例的情况下检查对象是否将使用给定参数成功实例化?

实际上我只是检查(没有测试这段代码,但应该可以正常工作...)必需参数的数量,忽略类型:

// Filter definition and arguments as per configuration
$filter = $container->getDefinition($serviceId);
$args   = $activeFilters[$filterName];

// Check number of required arguments vs arguments in config
$constructor = $reflector->getConstructor();

$numRequired  = $constructor->getNumberOfRequiredParameters();
$numSpecified = is_array($args) ? count($args) : 1;

if($numRequired < $numSpecified) {
    throw new InvalidFilterDefinitionException(
        $serviceId,
        $numRequired,
        $numSpecified
    );
}

编辑:$constructor 可以是null...

最佳答案

简短的回答是,您根本无法确定一组参数是否允许构造函数的无错误实例化。正如评论者在上面提到的,没有办法确定一个类是否可以用给定的参数列表实例化,因为如果不实际尝试就无法知道运行时的注意事项 实例化。

但是,尝试从构造函数参数列表中实例化一个类是有值(value)的。这种操作最明显的用例是可配置的依赖注入(inject)容器 (DIC)。不幸的是,这比 OP 建议的操作复杂得多。

我们需要为提供的定义数组中的每个参数确定它是否与构造方法签名中指定的类型提示匹配(如果方法签名实际上有类型提示)。此外,我们需要解决如何处理默认参数值的问题。此外,为了使我们的代码具有任何实际用途,我们需要允许提前指定“定义”以实例化一个类。对该问题的复杂处理还将涉及反射对象池(缓存),以最大限度地减少重复反射事物对性能的影响。

另一个障碍是,如果不调用其 ReflectionParameter::getClass 方法并随后从返回的类名实例化反射类,则无法访问反射方法参数的类型提示 (如果返回 null,则参数没有类型提示)。这就是缓存生成的反射对于任何现实世界的用例变得特别重要的地方。

下面的代码是我自己的基于字符串的递归依赖注入(inject)容器的精简版。它是伪代码和真实代码的混合体(如果您希望获得免费代码来复制/粘贴,那您就不走运了)。您会看到下面的代码将“定义”数组的关联数组键与构造函数签名中的参数名称相匹配。

真正的代码可以在相关的github project page找到.

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
    }
}

关于php - 快速检查对象是否会在 PHP 中成功实例化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13517192/

相关文章:

javascript - 我正确使用 htmlspecialchars 吗?

php - 我可以在 PHP 中混合使用 MySQL API 吗?

php - 正则表达式:如何捕获以匹配的字符集开头的组

java - 如何从 Java 程序动态创建新的 .java 文件?

c# - 在 C# 中制作泛型表达式生成方法

code-analysis - 静态和动态代码分析

javascript - 类型错误 : unable to create data property ~ React ~ ESLint

PHP代码注入(inject)。我们有安全风险吗?

c# - 在 .NET 中获取泛型类型的基本名称的正确方法是通过 Substring?

java - 多模块项目中,sonar分析绑定(bind)maven生命周期,如何让SonarQube模块只分析一次项目?