laravel6 Util::getParameterClassName 获得类方法参数类型的类名源码分析

Laravel框架
419
0
0
2022-11-15

在 laravel 的 Controller 中可以见到

class DemoController {
    public function test(Request $request) {}
}

laravel 帮我们自动把 $request 注入进来,是怎么办到的呢?

首先 test(Request $request) 这是类型提示(或类型约束)的标准PHP语法,换个写法就不觉的陌生了 test(int $number)

$request 为参数,Request 即为参数类型,那么通过获得参数 $request 的参数类型的类名 Request,就可以实例化了。

一共列举了 class interface abstract int(内置类型之一)self parent 作为类型提示,以及两种没有类型提示的参数(有默认值和无默认值)。

目的:获得 Main 类的 abcd 方法的参数类型的类名。

class ClassParam {}
interface InterfaceParam {}
abstract class AbstractClassParam {}

class ParentClass {}
class Main extends ParentClass {
  function abcd(
    ClassParam $class_param,
    InterfaceParam $interface_param,
    AbstractClassParam $abstract_class_param,
    int $int_param,
    self $self_param,
    parent $parent_param,
    $has_default_param = 'default',
    $no_default_param
  ) {
    // ..
  }
}

反射方法,返回 ReflectionMethod 对象

$method_reflecter = new ReflectionMethod(Main::class, 'abcd');
/**class ReflectionMethod#1 (2) {
  public $name =>
  string(4) "abcd"
  public $class =>
  string(4) "Main"
}*/

获取参数,返回 ReflectionParameter 对象数组

$parameters = $method_reflecter->getParameters();
/**array(8) {
  [0] =>
  class ReflectionParameter#2 (1) {
    public $name =>
    string(11) "class_param"
  }
  ..
  [7] =>
  class ReflectionParameter#9 (1) {
    public $name =>
    string(16) "no_default_param"
  }
}*/

获得参数类型

foreach ($parameters as $parameter) {
    $type = $parameter->getType();
}
/**
class_param的类型是:
class ReflectionNamedType#10 (0) {
}
// 省略.. 除下面没有类型提示的参数类型为 NULL 外,皆是 ReflectionNamedType 对象
----
has_default_param的类型是:
NULL
----
no_default_param的类型是:
NULL
*/

排除没有类型提示的参数(类型为 NULL)

if (! $type instanceof  ReflectionNamedType) {
    // $has_default_param , $no_default_param
}

排除内置类型 int string

if ($type->isBuiltin()) { //Builtin 翻译:内置
    // int $int_param,
}

获得参数类型的名称

foreach ($parameters as $parameter) {
    $type = $parameter->getType();
    if (! $type instanceof  ReflectionNamedType  || $type->isBuiltin()) {
         continue;
    }
    $name = $type->getName();
}
/**ClassParam
InterfaceParam
AbstractClassParam
self
parent*/

上一步就已经获得了class interface abstract 三种类型的名称了,还有 self parent 需要特殊处理,self 是当前类, parent 是父类。 laravel 用了 ReflectionParameter::getDeclaringClass 获得当前类。

//获取声明类,即参数声明所在的类
//返回 ReflectionClass 类反射对象
$class = $parameter->getDeclaringClass(); 
/**
class ReflectionClass#11 (1) {
  public $name =>
  string(4) "Main"
}
*/
$class->getName();
// Main

在这个例子中,所有参数声明所在的类都是 Main,那么 self 就是 Main, parent 就是 ParentClass 类。

以下就是 laravel (大于等于6版本) 的源码

public static function getParameterClassName($parameter)
{
    // 这里 $has_default_param、$no_default_param 为 NULL,而其他都是 ReflectionNamedType 对象 
    $type = $parameter->getType();

    // 排除 $has_default_param、$no_default_param、内置类型int等 
    if (! $type instanceof ReflectionNamedType || $type->isBuiltin()) {
        return;
    }

    //ClassParam、InterfaceParam、AbstractClassParam、self、parent 
    $name = $type->getName();

    // getDeclaringClass 获取参数声明所在的类,函数(非类方法)则返回 null 
    // $class 为 ReflectionClass 类反射对象, 
    if (! is_null($class = $parameter->getDeclaringClass())) {
        if ($name === 'self') {
            return $class->getName(); // Main
        }

        // ReflectionClass::getParentClass 获取父类的反射对象 
        if ($name === 'parent' && $parent = $class->getParentClass()) {
            return $parent->getName();
        }
    }

    return $name;
}

注:在 laravel6 之前版本,获取参数类型相对简单,直接 $parameter->getClass()->name (获取类型提示类名),但该方法已弃用。

$parameter->getClass()->name;
/**ClassParam
InterfaceParam
AbstractClassParam
Main
ParentClass*/

php.net 时而打不开,这个文档不错 https://www.w3cschool.cn/doc_php/php-class-reflection.html?lang=en