前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >laravel源码解读学习(一)Application的实例化

laravel源码解读学习(一)Application的实例化

原创
作者头像
ccccs0v0
发布2024-06-26 18:13:26
1020
发布2024-06-26 18:13:26
举报

前言

心血来潮发现laravel有php artisan serve监听端口的启动方式,突然想仔细看看laravel源码,本系列随时可能鸽,laravel版本8.83.27。为什么是这版本呢,因为在写这篇文章时直接执行laravel的composer方式安装默认就是这版本

Application的构造方法

切入口就基于根目录的artisan文件

图1 artisan
图1 artisan

常见的框架初始化入口,接下来去到/bootstrap/app.php

图2 /bootstrap/app.php
图2 /bootstrap/app.php

读注释可以理解到14~16行代码new了一个Illuminate\Foundation\Application实例,该实例作用是粘合laravel各组件和作为系统的依赖注入容器。

先关注Illuminate\Foundation\Application类的定义

图3 vendor/laravel/framework/src/Illuminate/Foundation/Application.php
图3 vendor/laravel/framework/src/Illuminate/Foundation/Application.php

Illuminate\Foundation\Application继承了Illuminate\Container\Container,作为依赖注入容器需要的功能基本都在Container里,其余实现的接口我们在后面用到时再讨论

来到Illuminate\Foundation\Application的构造方法

图3 vendor/laravel/framework/src/Illuminate/Foundation/Application.php
图3 vendor/laravel/framework/src/Illuminate/Foundation/Application.php

从这里开始我们把构造方法里的每一个调用拆分开解读

1.setBasePath($basePath)

2.registerBaseBindings()

3.registerBaseServiceProviders()

4.registerCoreContainerAliases()


1.setBasePath($basePath)

这一步的主要作用是注册项目的目录结构,包括

base、lang、config、public、storage、database、resources、bootstrap

图4 vendor/laravel/framework/src/Illuminate/Foundation/Application.php
图4 vendor/laravel/framework/src/Illuminate/Foundation/Application.php

xxxPath()方法的内容都大致一样,我们随便挑一个$this->configPath()看看内容

图5 vendor/laravel/framework/src/Illuminate/Foundation/Application.php
图5 vendor/laravel/framework/src/Illuminate/Foundation/Application.php

该方法返回了项目根目录路径+文件分隔符+config+$path,假设项目根目录路径是/data/web/project,则返回结果就是

/data/web/project/config/$path

注意在构造方法流程里$path值为空,所以实际结果为 /data/web/project/config

接下来我们来看$this->instance()做了什么事,这里需要稍微注意下图5的调用里传的参数值格式 'path.xxx'

vendor/laravel/framework/src/Illuminate/Container/Container.php
vendor/laravel/framework/src/Illuminate/Container/Container.php

此时方法调用来到了Illuminate/Container/Container,由此方法注释可知这方法是Laravel的IOC容器对外开放的注册实例的入口(之一)。

instance()方法逻辑大致如下

1.清除已注册过的组件对应实例关系

2.注册实例

3.如果发现在本次注册实例之前已注册过,则重新执行注册回调函数

vendor/laravel/framework/src/Illuminate/Container/Container.php
vendor/laravel/framework/src/Illuminate/Container/Container.php

reboundClassbacks可以由Illuminate/Container/Container::rebinding()写入)

要注意的是在setBasePath()这一步中并没有实例化任何类,只是在容器中注册了'path.xxx',与其对应的值是各目录路径字符串

至此setBasePath()的逻辑结束。


2.registerBaseBindings()

这一步的逻辑是

Illuminate\Foundation\Application自身注册为Laravel的IOC容器实例

把自身注册为'app'的实例把自身注册为Illuminate\Container\Container的实例

注册Illuminate\Foundation\Mix单例到容器

注册Illuminate\Foundation\PackageManifest单例到容器

vendor/laravel/framework/src/Illuminate/Foundation/Application.php
vendor/laravel/framework/src/Illuminate/Foundation/Application.php

Mix是关于前端资源管理的类,PackageManifest是关于项目的包发现类(感兴趣的可以去看vendor/laravel/framework/src/Illuminate/Foundation/PackageManifest.phpbind()方法),此处不做这两个类的分析。

至此registerBaseBindings()的逻辑结束。


3.registerBaseServiceProviders()

这一步就如方法名字面上的意思,注册基础Provider

vendor/laravel/framework/src/Illuminate/Foundation/Application.php
vendor/laravel/framework/src/Illuminate/Foundation/Application.php

注意到这几个Provider都继承了抽象类Illuminate\Support\ServiceProvider,这个类允许子类重写register()方法,而这些Provider的构造函数都是基于Illuminate\Support\ServiceProvider的构造函数

vendor/laravel/framework/src/Illuminate/Support/ServiceProvider.php
vendor/laravel/framework/src/Illuminate/Support/ServiceProvider.php

先来看Illuminate/Foundation/Applicationregister()方法

vendor/laravel/framework/src/Illuminate/Foundation/Application.php
vendor/laravel/framework/src/Illuminate/Foundation/Application.php

$this->getProvider($provider) 的逻辑是读取 $this->serviceProviders$provider::class ,返回值是null或具体的Provider实例,当Provider已注册过且$forcefalse时不重复注册

$this-> resolveProvider($provider) 的逻辑是简单的new一个对应Provider实例

接着是调用Providerregister()方法,后续我们会分别来看这三个Providerregister()方法

687~697行为Provider提供了方便的依赖注入方式,在前面的内容中可以了解到Provider在构造函数中传入了Illuminate/Foundation/Application,意味着开发者可以在Provider内通过$this->app去获取bindingssingletons中的实例

后续可能会单开个篇幅来看bind()singleton()的实现,目前只需要了解它会关联到IOC容器注册且实例化类的核心,且在第一次显示获取实例前不会实例化对应类,即是注册时并不会实例化类或执行注册的回调函数,不了解反射的请先学习一下

$this->markAsRegistered($provider)的逻辑很简单,存入Provider实例到$this->serviceProviders,标记Provider已注册

由于此时$this->isBooted()的值默认为false ,先不关注bootProvider()的逻辑

至此register()方法的逻辑结束,来看看三个Providerregister()方法有什么逻辑

1)Illuminate\Events\EventServiceProvider

vendor/laravel/framework/src/Illuminate/Events/EventServiceProvider.php
vendor/laravel/framework/src/Illuminate/Events/EventServiceProvider.php

注册events单例组件Illuminate\Events\Dispatcher,Illuminate\Events\Dispatcher内又引用了Illuminate\Contracts\Queue\Factory接口

我们能在Illuminate/Foundation/Application的构造方法第四步registerCoreContainerAliases()中得知Illuminate\Contracts\Queue\Factory接口绑定的具体实现是Illuminate\Queue\QueueManager

2)Illuminate\Log\LogServiceProvider

vendor/laravel/framework/src/Illuminate/Log/LogServiceProvider.php
vendor/laravel/framework/src/Illuminate/Log/LogServiceProvider.php

注册log组件Illuminate\Log\LogManager

3)Illuminate\Routing\RoutingServiceProvider

vendor/laravel/framework/src/Illuminate/Routing/RoutingServiceProvider.php
vendor/laravel/framework/src/Illuminate/Routing/RoutingServiceProvider.php

这里就不一一截图代码出来了,简述一下各个方法的内容

$this->registerRouter() 注册 router 单例组件 Illuminate\Routing\Router

路由核心组件,功能包括注册路由、重定向、匹配请求路由(dispatch)、响应请求(toResponse)等

$this->registerUrlGenerator() 注册 url 单例组件 Illuminate\Routing\UrlGenerator

可以视作 Illuminate\Routing\Router 的装饰器,功能包括多种格式化路由Url的方式和router部分API

$this->registerRedirector() 注册 redirect 单例组件 Illuminate\Routing\Redirector

负责请求重定向,注入了Illuminate\Routing\UrlGenerator

$this->registerPsrRequest() 注册 Psr\Http\Message\ServerRequestInterface 组件 Nyholm\Psr7\Factory\Psr17Factory

这个包默认没有下载,需要手动执行composer require symfony/psr-http-message-bridge,symfony的基于psr7的请求工厂类,详情请看文档

$this->registerPsrResponse() 注册 Psr\Http\Message\ResponseInterface 组件 Nyholm\Psr7\Response

这个包默认没有下载,需要手动执行composer require nyholm/psr7,轻量级psr7实现,详情请看github

$this->registerResponseFactory() 注册 Illuminate\Contracts\Routing\ResponseFactory 单例组件 Illuminate\Routing\ResponseFactory

响应工厂类,负责返回多种格式的响应类,如 Illuminate\Http\JsonResponse、Symfony\Component\HttpFoundation\StreamedResponse 等

$this->registerControllerDispatcher() 注册 Illuminate\Routing\Contracts\ControllerDispatcher 单例组件 Illuminate\Routing\ControllerDispatcher

控制器调度类,实现了dispatch()和getMiddleware()方法供router发起调度执行对应逻辑

至此registerBaseServiceProviders()的逻辑结束


4.registerCoreContainerAliases()

这个方法内注册了大量组件alias,大部分组件在这个时候还没有被实例化,仅注册了类似于实现类与interface的“别名”关系

vendor/laravel/framework/src/Illuminate/Foundation/Application.php
vendor/laravel/framework/src/Illuminate/Foundation/Application.php

具体的alias()方法实现会和bind()singleton()一起看,到时候可能会基于这里挑个例子来讲流程并验证结果

至此registerCoreContainerAliases()的逻辑结束


总结

这次内容只看了Application的实例化,做的事情不多,可以概括为基于IoC容器,组织项目目录结构,注册Application作为IoC容器实例,注册路由、Event、日志三大模块的Provider,注册基础组件(如db、redis、queue)的接口及实现关系。

最后放个简单的图

Application实例化逻辑
Application实例化逻辑

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • Application的构造方法
    • 1.setBasePath($basePath)
      • 2.registerBaseBindings()
        • 3.registerBaseServiceProviders()
          • 1)Illuminate\Events\EventServiceProvider
          • 2)Illuminate\Log\LogServiceProvider
          • 3)Illuminate\Routing\RoutingServiceProvider
        • 4.registerCoreContainerAliases()
        • 总结
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档