揭秘框架的控制器中的控制器__construct()参数怎么实例化的

背景描述

一个成熟的框架对与每个请求的处理其实大致都是相同的,大致流程(主要突出解析路由到控制器的步骤):

简化流程图

  • 疑问一:我理解路由只能解析出对应的控制器和方法,如果实例化控制器需要参数改怎么处理呢?
  • 疑问二:如果控制器实例化需要的参数是个对象或者是类怎么办?(确实存在这种情况)

问题解析:

带着疑问我们取啃源码,找到根源所在。当你看到这断代码ReflectionClass,就是找到问题的根源了。它叫反射

new ReflectionClass($className);
  1. 何为反射?有什么作用?

官方文档没有写太多,我就说说我自己的理解:反射包含很多方法,在框架中使用广泛的是 ReflectionClass,它可以帮我们取实例化一个类,类似new 但是 new一个对象,我们需要自己传参数,用 ReflectionClass 它会告诉我们模板对象需要哪些参数,再循环去处理需要的参数

反射的理解和使用:

创建一个测试项目,跟我一起来探秘一下ReflectionClass的使用

| Test
| - Controller
| - - TestController.php
| - Model
| - - TestModel.php
| - Service
| - - TestService.php
| - inde,php
  1. TestController.php
<?php
namespace Test\Controller;
use Test\Service\TestService;

class TestController {
    private $model;
    public function __construct(TestService $model)
    {
        $this->model = $model;
    }
    
    public function test(){
        echo $this->model->test();
    }
}
  1. TestService.php
<?php
namespace Test\Service;

use Test\Model\TestModel;

class TestService {
    private $test;
    public function __construct(TestModel $test)
    {
        $this->test = $test;
    }
    
    public function getTest(){
        return $this->test->test();
    }
}
  1. TestModel.php
<?php
namespace Test\Model;
class TestModel  {
    public function test(){
        return 'implements TestInterface';
    }
}
  1. 正常调用TestController (index.php) 自动加载相关请看
function  test_autoload($class){
    $classPath = dirname(__DIR__).'\\'.$class.'.php';
    if(file_exists($classPath)){
        require_once ($classPath);
    }else{
        echo $classPath."不存在";
    }
}
spl_autoload_register('test_autoload');

$model = new \AotoLoad1\Model\TestModel();
$server = new \AotoLoad1\Service\TestService($model);
$c = new \AotoLoad1\Controller\TestController($server);
$c->test();//输出:implements TestInterface
  1. 利用反射实例化TestController(index.php) 自动加载相关请看
//自动加载(简化代码,代替incloud_once)
function  test_autoload($class){
    $classPath = dirname(__DIR__).'\\'.$class.'.php';
    if(file_exists($classPath)){
        require_once ($classPath);
    }else{
        echo $classPath."不存在";
    }
}
spl_autoload_register('test_autoload');
/**
 * 反射加载
 * @param $className
 *
 * @return object
 * @throws ReflectionException
 */
function classReload($className){
    try{
    	//实例化需要的类
        $class = new ReflectionClass($className);
        //是否有__contruct()
        $constructor = $class->getConstructor();
        $p = [];//定义需要实例化的类的构造函数都有哪些参数
        if($constructor!==null){//有_contruct()构造函数
            foreach ($constructor->getParameters() as $k=>$parameter){//遍历构造函数的参数
                $parameterClass = $parameter->getClass();//构造函数的参数是不是类
                if ($parameterClass === null) {//不是类
                    $resolveName = $parameter->getName();//获取参数的名字
                } else {
                    $resolveName = $parameterClass->name;//或者参数类的className
                }
                try {
                    $p[] = classReload($resolveName);//递归处理构造函数的参数
                } catch (ReflectionException $e) {//如果不能加载
                    if ($parameter->isDefaultValueAvailable()) {//验证是否有可用默认值 有
                        $p[] = $parameter->getDefaultValue(); //直接获取默认值
                    } else if ($parameterClass !== null) {//如果参数是类 且不能加载
                        $resolveName = $parameter->getName();//获取类名
                        $p[] = classReload($resolveName);//递归处理类名
                    } else {//如果依然不能加载  抛错
                        throw $e;
                    }
                }
            }
        }
        return $class->newInstanceArgs($p);//传参执行实例化
    }catch (ReflectionException $exception){
        throw $exception;
    }
}
$c = classReload('AotoLoad1\Controller\TestController');//去实例化加载TestController
$c->test();//执行test方法
  1. 输出结果:
implements TestInterface
  1. 代码执行流程
    反射流程使用

总结

通过例子可以看出 其实框架就是通过反射ReflectionClass方法去实例化控制器,如果有在构造函数内的对象,就通过递归再次用ReflectionClass去实例化,知道把所有都全部实例化了,再去调用控制器内的方法,使代码更加简洁

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值