如何通过查源码的方式解决编程中遇到的问题?查源码定位问题的思路是什么?

简介: aravel的底层是如何处理HTTP请求的?Laravel的Request是如何实现的?为什么不需要配置Nginx的url解析,也不需要在Laravel的router中配置参数名称,却可以通过Request接收到参数呢?实现原理是什么?

首先抛出我的疑问:


  1. Laravel的底层是如何处理HTTP请求的?
  2. Laravel的Request是如何实现的?
  3. 为什么不需要配置Nginx的url解析,也不需要在Laravel的router中配置参数名称,却可以通过Request接收到参数呢?实现原理是什么?


下面开始进入查源码之旅:


  1. 首先调研了一下Laravel的request是基于什么实现的?
  2. 知识点如下:Laravel的很多底层组件是基于Symfony实现的,比如:请求、响应、cookie、命令行,文件等。其中HttpFoundation组件是http请求中比较重要的基础组件,它可以独立于Symfony使用的,Laravel基于此做了进一步的封装,使用示例如下


$request->input('param');
$request->cookie('cookie');
$request->file('file');


  1. 考虑到Laravel项目所有的HTTP请求都会转发到Laravel的入口文件(public/index.php),所以我们查看这个文件一探究竟:


//创建Application实例,作为服务容器
$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);
//以单例方式在服务容器中将 App\Http\Kernel 实例绑定到 Illuminate\Contracts\Http\Kernel 接口
$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);
//服务容器创建处理 HTTP 请求的内核实例(Kernel)
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
//核心方法:处理 HTTP 请求的核心代码(核心代码会在下一部分展开来讲,先从上而下理清流程)
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);
//发送响应给发送请求的客户端
$response->send();
//内核实例终止程序,基于传入的request和response做一些收尾工作
$kernel->terminate($request, $response);     


  1. 上面的注释已经比较清楚的解释了Laravel处理HTTP请求的内部实现流程。
  2. 我们再继续深入核心代码,研究一下“Laravel为什么在路由中无需指定参数是什么,在控制器方法中传入Request就可以获得参数了呢?”

比如:


//路由
Route::resource('/index', 'IndexController');
//controller中的方法
function index(Request $request)
{
      switch ($request->type) {
        .
        .
        .
    }
}


  1. 下面的非核心代码用竖着的3个.省略,避免误导


//laravel项目的入口文件 index.php
    .
    .
    .
        //重点1:handle()方法
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);
    .
    .
    .
//深入源码1 capture()
public static function capture()
{
    static::enableHttpMethodParameterOverride();
    //重点2:createFromBase() 方法
    return static::createFromBase(SymfonyRequest::createFromGlobals());
}
//我们再深入源码2 createFromBase()
public static function createFromBase(SymfonyRequest $request)
{
    if ($request instanceof static) {
        return $request;
    }
    $content = $request->content;
    //这是解决疑问的关键代码 duplicate 意为复制一份
    $request = (new static)->duplicate(
        $request->query->all(), $request->request->all(), $request->attributes->all(),
        $request->cookies->all(), $request->files->all(), $request->server->all()
    );
    $request->content = $content;
    $request->request = $request->getInputSource();
    return $request;
}


  1. 发现了一个神奇的方法:duplicate(),意为复制一份。
  2. duplicate()方法的参数,包括request−>query−>all(),request->query->all(), request>query>all(),request->request->all()....等,即底层已经实现了所有query和request的接收,我们不需要单独声明也是可以接收到所有请求参数的。


$request = (new static)->duplicate(
    $request->query->all(), $request->request->all(), $request->attributes->all(),
    $request->cookies->all(), $request->files->all(), $request->server->all()
);


  1. 上面就是我查询源码的流程:


  1. 明确自己的问题(Request的底层实现)
  2. 明确自己排查问题的边界(从入口文件入手)
  3. 一层一层的深入查询源码(源码1->源码2-->源码3)
  4. 排除非核心代码,找到最核心代码(duplicate()方法)


9.下面是另外一位博主的思考总结,个人觉得总结的更系统和专业,Laravel底层如何处理HTTP请求,而我的总结更体现出我带着问题查看源码的思路、流程、反思、总结。


10.还有一个小插曲: 源码这么写的原因是什么?不是重复操作吗?


微信图片_20221111194410.jpg


11.不认真呀,仔细看一下,2中的request是通过 (new static)->duplicate()克隆的新对象,和1中的request并不是相同的实例。


文章结尾,放两个小题供大家消遣一下。


  1. 请设计一个函数,实现以下功能:将字符串"wang_zhong_yang"转换成"WangZhongYang";


1.1 我的解题思路:


public function actionCreate()
    {
        $str1 = "wang_zhong_yang";
        $arr1 = explode("_",$str1);
        foreach ($arr1 as $value){
            $str2 .=ucfirst($value);
        }
        var_dump($str2);
    }


  1. 请再设计一个函数,将字符串"wang_zhong_yang"转换成"wAngzHongyAng";


2.2 我的解题思路:


public function actionCreate()
    {
        $str1 = "wang_zhong_yang";
        $arr1 = explode("_",$str1);
        foreach ($arr1 as $value){
            //拆分
            $first = substr($value,0,1);
            $sec = substr($value,1,1);
            $sec = strtoupper($sec);
            $third = substr($value,2,strlen($value));
            $final = $first . $sec.$third;
            $str2 .= $final;
        }
        var_dump($str2);
    }


我上面2道题的解法思路肯定不是最优解,欢迎大家不吝赐教。

相关文章
|
存储 监控 小程序
《优化接口设计的思路》系列:第三篇—留下用户调用接口的痕迹
大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。 作为一名从业已达六年的老码农,我的工作主要是开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接过许多开放平台,也搞过消息中心这类较为复杂的应用,但幸运的是,我至今还没有遇到过线上系统由于代码崩溃导致资损的情况。这其中的原因有三点:一是业务系统本身并不复杂;二是我一直遵循某大厂代码规约,在开发过程中尽可能按规约编写代码;三是经过多年的开发经验积累,我成为了一名熟练工,掌握了一些实用的技巧。
110 0
《优化接口设计的思路》系列:第三篇—留下用户调用接口的痕迹
|
6月前
|
JSON 前端开发 Java
前后端数据交互-----表单数据获取不到,出错的原因,在编写接口时,没有考虑数据如何返回,解决问题的思路,找到自己出错的地方,围绕着出错的地方进行考虑(很重要),找对解决问题的视频,理清返回数据的思路
前后端数据交互-----表单数据获取不到,出错的原因,在编写接口时,没有考虑数据如何返回,解决问题的思路,找到自己出错的地方,围绕着出错的地方进行考虑(很重要),找对解决问题的视频,理清返回数据的思路
|
8月前
|
消息中间件 前端开发 关系型数据库
🤔️测试问我:为啥阅读量计数这么简单的功能你都能写出bug?
🤔️测试问我:为啥阅读量计数这么简单的功能你都能写出bug?
|
8月前
|
存储 移动开发 安全
【Linux技术专题】「必备基础知识」带你仔细梳理一下平时排查问题查询日志的基本操作和指令
【Linux技术专题】「必备基础知识」带你仔细梳理一下平时排查问题查询日志的基本操作和指令
190 0
|
8月前
|
监控 数据库 索引
面试题21:如何优化查询命令?
面试题21:如何优化查询命令?
|
8月前
|
分布式计算 JavaScript 前端开发
给大家讲讲过滤查询的思路(一点就通)
给大家讲讲过滤查询的思路(一点就通)
65 1
|
Java 应用服务中间件 Android开发
开发踩坑记录之四:Tomcat内存溢出问题分析
系统平台运行一段时间后,平台出现无法访问的问题,重启对应的服务后平台恢复正常。查看日志发现在凌晨两点零四分之后没有对应的日志输出,直到重启服务后才有日志的正常输出。同时发现在Tomcat的目录下存在hprof文件,即java进程的内存镜像文件。初步猜测Tomcat发生了内存溢出导致服务出现假死现象,即在任务管理器中虽然为运行状态,但是实际已不能正常对外提供服务。   对于hprof文件的分析需要借助于内存分析工具Eclipse Memory Analyzer,通过它寻找到平台发生内存泄露的根源,再根据发生内存泄露的地方以及相关的日志信息定位什么样的业务场景下导致该异常情况的发生。
开发踩坑记录之四:Tomcat内存溢出问题分析
|
设计模式 缓存 Java
面试题 | 怎么写一个又好又快的日志库?(一)(下)
面试题 | 怎么写一个又好又快的日志库?(一)
137 0
|
Java Android开发 数据安全/隐私保护
面试题 | 怎么写一个又好又快的日志库?(一)
面试题 | 怎么写一个又好又快的日志库?(一)
144 0
|
数据库
【高效编码】查询日志的命令老是记不住?没关系,这篇文章帮你记
您好,我是码农飞哥,一直想飞暂时在跑个那个老哥。
270 0
【高效编码】查询日志的命令老是记不住?没关系,这篇文章帮你记