首页 > 编程语言 >浅谈PHP框架中类成员方法的类类型形参是怎么利用ReflectionClass反射类自动实例化的(应该是全网首发)

浅谈PHP框架中类成员方法的类类型形参是怎么利用ReflectionClass反射类自动实例化的(应该是全网首发)

时间:2023-11-03 16:12:51浏览次数:26  
标签:ReflectionClass 浅谈 形参 param 实例 类型 null class

说明

1. 或许是全网首发,我翻过很多文章,从未有一个博主讲过这个东西,很多博主只讲了IOC、DI和反射机制的常见用法,因类类型形参反射的巧妙用法有相当高的难度和学习盲区,所以从未有人讲过类类型的形参它怎么就被自动实例化的。
2. 在Laravel框架,或者是其它框架中,类的成员方法中形参的类型定义为某个类,在方法体内就直接当做对象来调用,这并不是PHP本身自带的语法,而是利用了反射机制,一直很好奇是怎么实现的。然而框架源码又太繁重,所以采用原生的方式实现。
3. 反射的功能非常强大,反射可以针对类本身做很多开挂操作,因此PHP框架才会变得这么易用与强大,类类型形参实例化,仅仅是冰山一角,官方文档

代码

<?php
/**
 * @Class 封装一个可自动实例化类的类类型的成员方法形参的容器
 */
class AutoNew {
    /**
     * @function 递归实例化类构造方法中的数据
     * @param    $class string 类名
     * @return   object|null
     * @throws   ReflectionException
     */
    public function newConstructClassTypeParam($class) {
        //实例化反射类
        $reflection = new ReflectionClass($class);

        //获取类的构造函数
        $constructor = $reflection->getConstructor();
        if ($constructor === null) {
            //如果构造函数存在,实例化这个类,(此方法支持给构造函数传参,如果有参数的话)
            return $reflection->newInstanceArgs();
        }

        //获取构造方法的参数,结果返回一个数组,若有值,返回的是ReflectionParameter类
        $params = $constructor->getParameters();

        $dependencies = [];

        foreach ($params as $param) {
            //获取构造方法参数的类型,注意:之有在变量左边声明类型的才可以,例如string $p获取的是string,但$p = 'ab'获取的就是null
            $dependencyType = $param->getType();
            //isBuiltin()方法,返回bool值,参数类型修饰符为string/int/bool/float/callable/array/object/mixed的都为true,但传输的内置外置接口,类都为false。目前使用PHP8,无法声明形参为null和resource类型
            if (($dependencyType !== null) && (! $dependencyType->isBuiltin())) {
                //获取形参声明的类型,返回字符串类型,字符串就是字符串,接口就是接口,类就是类
                $dependencyClassName = $dependencyType->getName();
                //此处可以理解为如果形参是类,或者是含有构造方法的抽象类或者接口的构造方法的形参中有以上类型,就递归实例化它
                $dependencies[] = $this->newConstructClassTypeParam($dependencyClassName);
            } else {
                //检测形参是否有默认值,如果有返回默认值,如果没有,返回null
                $dependencies[] = $param->isOptional() ? $param->getDefaultValue() : null;
            }
        }

        //如果构造函数存在,实例化这个类,(此方法支持给构造函数传参,如果有参数的话)
        return $reflection->newInstanceArgs($dependencies);
    }


    /**
     * @function 自动实例化某个类中某个方法的类类型的形参
     * @param    $class      string 类名
     * @param    $method     string 方法名
     * @param    $parameters array  参数名
     * @return   void
     * @throws   ReflectionException
     */
    public function init($class, $method, $parameters = []) {
        //实例化PHP内置的反射类
        $reflection = new ReflectionClass($class);
        //检查方法是否已定义
        if ($reflection->hasMethod($method)) {
            //创建一个实例
            $instance = $this->newConstructClassTypeParam($class);
            //返回一个ReflectionMethod对象,获取方法的形参,和其它元信息,并填充到ReflectionMethod对象中
            $methodReflection = $reflection->getMethod($method);
            //返回数组,获取干净的形参数据
            $methodParams = $methodReflection->getParameters();
            $methodDependencies = [];

            foreach ($methodParams as $param) {
                //获取方法参数的类型,注意:只有在变量左边声明类型的才可以,例如string $p获取的是string,但$p = 'ab'获取的就是null
                $paramType = $param->getType();
                //isBuiltin()方法,返回bool值,参数类型修饰符为string/int/bool/float/callable/array/object/mixed的都为true,但传输的内置外置接口,类都为false。目前使用PHP8,无法将形参声明null和resource类型
                if (($paramType !== null) && (! $paramType->isBuiltin())) {
                    //获取形参声明的类型,返回字符串类型,字符串就是字符串,接口就是接口名,类就是类名
                    $dependencyClassName = $paramType->getName();
                    //返回数组,数组的值是创建出来的对象。此处的逻辑可以理解为类成员方法的形参是类的,就实例化它
                    $methodDependencies[] = $this->newConstructClassTypeParam($dependencyClassName);
                } else {
                    //判断遍历出来的形参,在不在实际传递的实参数组中,如果在把这个值返回,如果不在,判断是否有默认值,如果有则返回,如果没有默认值,赋值为null
                    if (array_key_exists($param->getName(), $parameters)) {
                        $args = $parameters[$param->getName()];
                    } elseif ($param->isOptional()) {
                        $args = $param->getDefaultValue();
                    } else {
                        $args = null;
                    }
                    $methodDependencies[] = $args;
                }
            }

            //调用创建的实例并传参
            $methodReflection->invokeArgs($instance, $methodDependencies);
        }
    }
}

//调用端-------------------------------------------------------------------------------------
//相当于框架业务逻辑层的代码
class StudentService {
    /**
     * @function 通过班级id查询一个班有多少人
     * @param    $class_id
     * @return   int
     */
    public function getStudentNum($class_id) {
        //sql ...
        return 123;
    }
}


//相当于框架的控制器
class StudentController {
    /**
     * @function 此方法根据逻辑可要可不要
     * @return   void
     */
    public function __construct() {
        echo '构造方法被调用了' . PHP_EOL;
    }


    /**
     * @function 模拟获取学生人数的方法
     * @param    $usersService StudentService 学生服务类
     * @param    $class_id     int            班级id
     * @param    $unit         string         单位
     * @return   void
     */
    public function getStudentNum(StudentService $usersService, $class_id, $unit = '人') {
        echo $class_id . '班有' . $usersService->getStudentNum($class_id) . $unit . PHP_EOL;
    }
}


//初始化Language类并调用getStudentNum方法且传参,传参的过程相当与前端请求接口携带的参数。整体相当于框架的路由
(new AutoNew())->init(StudentController::class, 'getStudentNum', ['class_id' => '7', 'unit' => '名学生']);

//结果如下:
构造方法被调用了
7班有123名学生

标签:ReflectionClass,浅谈,形参,param,实例,类型,null,class
From: https://www.cnblogs.com/phpphp/p/17807799.html

相关文章

  • 模拟退火(浅谈)
    写在前面放眼历史,喧嚣过后终归无声,热寂才是最终归宿。算法思想世界上的万事万物总是比较容易稳定在低能量的状态,当物体温度较高时,内能较大,固体内部粒子处于快速无规则运动,在固体温度慢慢降低的过程中,固体内能减小,粒子慢慢趋于有序。举个现实生活的小栗子:在冶金工业中,通常会把......
  • 带形参的main函数
    上面第二个参数为啥又有星号又有方框?这其实是一个指针数组,包含若干个指针,每个指针指向一维字符数组另一篇文章:命令行......
  • ThreadPoolExecutor使用浅谈
    1.基础介绍ThreadPoolExecutor是Python标准库concurrent.futures模块中的一个类,用于实现线程池的功能。ThreadPoolExecutor模块相比于threading等模块,通过submit方法返回的是一个Future对象,它代表了一个未来可期的结果。通过Future对象,我们可以在主线程(或主进程)中获取某个线程(......
  • 浅谈搜索展现层场景化技术-tanGo实践 算子化
    浅谈搜索展现层场景化技术-tanGo实践https://mp.weixin.qq.com/s/nVy9SYRIaaqZWgOHKTMQ4Qintroduction本文为搜索展现层相关技术,主线会先通过介绍搜索阿拉丁的产品形态,让读者初步了解什么是阿拉丁,及相关展现概念。之后会聚焦场景化产品,场景化是搜索构建沉浸式完美体验(重新组合整......
  • 浅谈筛法——埃氏筛
    前置芝士:见浅谈筛法——普通筛例·1素数个数:运用上一篇的知识,可得出以下代码:#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintmaxn=1e8+5;intn,ans;boolprime(intx){ if(x==1){ return0; } if(x==2){ return1; } for(inti=2......
  • 浅谈筛法——普通筛
    前置知识-因数和倍数(六年级及以上自行跳过):\(n\divm=k\),我们就说\(n\)是\(m\)和\(k\)的倍数,\(m\)和\(k\)是\(n\)的倍数。简单来说就是这样的:\(\text{被除数}\div\text{除数,余数为0}\),那我们就说除数是被除数的因数,被除数是除数的倍数。前置知识-素数和合数(六年级及以上自......
  • 浅谈UI自动化测试
    随着软件行业的不断发展,建立一个完善的自动化测试体系变得至关重要。目前,自动化测试主要涵盖接口自动化测试和UI自动化测试两个主要领域。就目前而言,企业在UI自动化测试方面的覆盖率仍然相对较低。接口自动化测试可以模拟和执行应用程序接口的各种操作,以验证接口的功能、性能和稳定......
  • 浅谈UI自动化测试
    随着软件行业的不断发展,建立一个完善的自动化测试体系变得至关重要。目前,自动化测试主要涵盖接口自动化测试和UI自动化测试两个主要领域。就目前而言,企业在UI自动化测试方面的覆盖率仍然相对较低。接口自动化测试可以模拟和执行应用程序接口的各种操作,以验证接口的功能、性能和稳......
  • 浅谈城市综合管廊运维的系统集成方案
    安科瑞电气股份有限公司:罗轩志摘要:从网络拓扑结构、开放式实时以太网协议、控制层系统配置方面介绍了综合管廊的系统网络架构设计,分析了无线网络特性,阐述了基于HTML5架构所能实现的功能的初步构想,以便于综合管廊运维人员巡检,确保管廊本体安全。0引言综合管廊的控制部分是保证综合......
  • 浅谈关于羚通智能分析平台通道管理和告警查询的使用功能
    ​上期我们知道了如何使用羚通智能分析平台,今天我们来详细了解一下羚通智能分析平台的通道管理和告警查询的使用功能。因为羚通智能分析平台主要是做视频算法分析的,所以我们今天来看一看羚通智能分析平台的通道管理和告警查询两个部分的使用情况如何。上一期,我们了解了一下......