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

Laravel框架
474
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