本文Laravel版本是8
如果请求的是一个http请求,那么所有的请求都先经过入口文件public/index.php
<?php
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Http\Request;
define('LARAVEL_START', microtime(true));
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
require $maintenance;
}
require __DIR__.'/../vendor/autoload.php';
$app = require_once __DIR__.'/../bootstrap/app.php';
$kernel = $app->make(Kernel::class);
$response = $kernel->handle(
$request = Request::capture()
)->send();
$kernel->terminate($request, $response);
require __DIR__.'/../vendor/autoload.php';
1. 引入 Composer 自动加载器
当执行 require __DIR__.'/../vendor/autoload.php'; 时,PHP 会包含并执行 vendor/autoload.php 文件。这个文件是由 Composer 生成的,位于项目根目录下的 vendor 目录中。
2. 初始化 Composer 自动加载
vendor/autoload.php 文件主要做了以下几件事:
2.1 引入 Composer 生成的类加载器
vendor/autoload.php 文件会引入由 Composer 生成的 vendor/composer/autoload_real.php 文件,这个文件包含了实际的自动加载逻辑:
require_once __DIR__ . '/composer/autoload_real.php';
2.2 加载 PSR-4 和 Classmap 配置
autoload_real.php 文件中的 ComposerAutoloaderInit 类负责初始化自动加载器,并根据 composer.json 文件中的 autoload 部分配置 PSR-4 和 Classmap 规则。
2.3 返回自动加载器实例
ComposerAutoloaderInit::getLoader() 方法返回一个自动加载器实例。这个实例会被存储在 $loader 变量中,以便后续使用:
$loader = ComposerAutoloaderInit::getLoader();
3. 注册自动加载器
返回的 $loader 实例会被配置为 PHP 的自动加载器之一,这意味着当尝试使用未定义的类时,PHP 将调用这个自动加载器来查找和加载相应的类文件。
4. 自动加载文件
根据 composer.json 文件中的 files 部分,Composer 会自动加载一些指定的文件。这些文件通常包含一些全局函数或工具类。
5. 自动加载命名空间
根据 composer.json 文件中的 psr-4 部分,Composer 会设置多个命名空间前缀和对应的目录路径。这样,当需要加载某个命名空间下的类时,Composer 可以正确地找到并加载相应的类文件。
总结
require __DIR__.'/../vendor/autoload.php'; 这一行代码其实执行了以下几个关键步骤:
- 引入 Composer 自动加载器:引入并执行
vendor/autoload.php文件。 - 初始化自动加载器:通过
autoload_real.php文件初始化 Composer 的自动加载器实例。 - 注册自动加载器:将 Composer 的自动加载器注册为 PHP 的自动加载器之一。
- 加载预定义文件:根据
composer.json文件中的配置信息,自动加载一些指定的文件和类。
$app = require_once __DIR__.'/../bootstrap/app.php';
$app = require_once __DIR__.'/../bootstrap/app.php';会创建一个应用实例,
<?php
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
return $app;
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
这句点进去看,可以看到是注册信息比如服务提供者(config/app.php中的providers数组里面的服务提供者不在这里注册的 具体看下面)等
public function __construct($basePath = null)
{
if ($basePath) {
$this->setBasePath($basePath);
}
$this->registerBaseBindings();
$this->registerBaseServiceProviders();
$this->registerCoreContainerAliases();
}
然后是
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
是注册HTTP和Console的内核类,http内核类包含了应用程序的全局中间件和路由中间件定义
具体可以看App\Http\Kernel::class的代码
然后会进行路由分发,根据Route的配置【RouteServiceProvider提供者已经在之前加载了】会将请求映射到相应的控制器方法或闭包函数。
我们回到index.php里面,还有几段代码:
$kernel = $app->make(Kernel::class);
$response = $kernel->handle(
$request = Request::capture()
)->send();
$kernel = $app->make(Kernel::class);是实例化对象,但是Kernel::class是个接口【Illuminate\Contracts\Http下的Kernel】,所以这边是实例化他的实现类【App\Http\Kernel】类,为什么是这个类呢,前面的bootstrap\app.php里面绑定了:
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
也就是说$kernel是App\Http\Kernel类的实例,然后调用handle方法,其实App\Http\Kernel类没有handle方法,但是它继承了Illuminate\Foundation\Http\Kernel类,这个类有handle方法:
public function handle($request)
{
try {
$request->enableHttpMethodParameterOverride();
$response = $this->sendRequestThroughRouter($request);
} catch (Throwable $e) {
$this->reportException($e);
$response = $this->renderException($request, $e);
}
$this->app['events']->dispatch(
new RequestHandled($request, $response)
);
return $response;
}
点击$response = $this->sendRequestThroughRouter($request);进去看:
protected function sendRequestThroughRouter($request)
{
$this->app->instance('request', $request);
Facade::clearResolvedInstance('request');
$this->bootstrap();
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());
}
有一个$this->bootstrap();代码行,点进去看:
public function bootstrap()
{
if (! $this->app->hasBeenBootstrapped()) {
$this->app->bootstrapWith($this->bootstrappers());
}
}
$this->bootstrappers()点进去看,就是一个数组:
protected $bootstrappers = [
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
\Illuminate\Foundation\Bootstrap\RegisterProviders::class, //注意看这里
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
$this->app->bootstrapWith这个$this->app其实是Illuminate\Foundation\Application类,bootstrapWith方法如下:
public function bootstrapWith(array $bootstrappers)
{
$this->hasBeenBootstrapped = true;
foreach ($bootstrappers as $bootstrapper) {
$this['events']->dispatch('bootstrapping: '.$bootstrapper, [$this]);
$this->make($bootstrapper)->bootstrap($this);
$this['events']->dispatch('bootstrapped: '.$bootstrapper, [$this]);
}
}
通过make方法把bootstrappers数组里的类一个个实例化,并且调用bootstrap()方法注意看bootstrappers数组的\Illuminate\Foundation\Bootstrap\RegisterProviders::class
点进去:
<?php
namespace Illuminate\Foundation\Bootstrap;
use Illuminate\Contracts\Foundation\Application;
class RegisterProviders
{
/**
* Bootstrap the given application.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function bootstrap(Application $app)
{
$app->registerConfiguredProviders();
}
}
bootstrap()方法调用registerConfiguredProviders(),这里的$app也是Illuminate\Foundation\Application 类,所以调用的是Illuminate\Foundation\Application的registerConfiguredProviders()方法:
public function registerConfiguredProviders()
{
$providers = Collection::make($this->make('config')->get('app.providers'))
->partition(function ($provider) {
return strpos($provider, 'Illuminate\\') === 0;
});
$providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);
(new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
->load($providers->collapse()->toArray());
}
看到了没$this->make('config')->get('app.providers'),这就是config/app.php里面providers数组的服务提供者注册代码。
如果使用的是php artisan命令的话:
php artisan 的原理和流程
-
入口文件: 当你在命令行中运行
php artisan时,PHP 会执行 Laravel 项目根目录下的artisan文件。 -
引导框架: 这个
artisan文件实际上是一个 PHP 脚本,它首先加载自动加载文件vendor/autoload.php,然后引导 Laravel 框架的启动过程。 -
创建应用实例: 在
artisan文件中,会创建一个 Laravel 应用实例:$app = require_once __DIR__.'/bootstrap/app.php'; -
绑定服务容器: 在
bootstrap/app.php文件中,会初始化 Laravel 服务容器(Service Container),并注册核心的服务提供者。 -
创建 Artisan Console 实例: 然后,
artisan脚本会创建一个 Artisan Console 实例,并将应用实例传递给它:$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class); -
处理命令: 接下来,
artisan脚本会调用$kernel->handle()方法,处理传入的命令行参数并执行相应的命令:$status = $kernel->handle( $input = new Symfony\Component\Console\Input\ArgvInput, //获取php artisan参数 new Symfony\Component\Console\Output\ConsoleOutput //返回不同参数的不同对象命令 ); -
命令注册: Laravel 的 Console Kernel 会在
app/Console/Kernel.php文件中注册所有可用的 Artisan 命令。这些命令可以是在commands属性中显式声明的,也可以是在routes/console.php文件中定义的命令。protected $commands = [ // 注册自定义命令 Commands\YourCustomCommand::class, ]; -
命令发现: Laravel 还支持通过自动发现命令,利用命名空间扫描来发现命令。
-
执行命令: 当用户输入一个具体的命令时,例如
php artisan migrate,Artisan Console 会找到相应的命令类并执行其handle方法,从而完成特定的任务。