这里将看看laravel执行的流程。
一.入口文件
Laravel 应用的所有请求入口都是?public/index.php ?文件。
index.php里面其实就几行代码。
// 1.定义个laravel开始时间的常量,微秒数
define('LARAVEL_START', microtime(true));
// 2.注册自动加载程序,Composer里面的
require __DIR__.'/../vendor/autoload.php';
// 3.开始启动框架,创建一个app应用程序,并加载一些应用支持,得到$app变量返回(具体加载的东西可以打印$app看看)
$app = require_once __DIR__.'/../bootstrap/app.php';
// 4.make()是用来创建实例用的,这里创建一个http内核类的实例,这里需要处理http请求
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
// 5.处理传入的HTTP请求,返回Response (http内核的方法,更多可以查看上面的类)
$response = $kernel->handle(
// 从服务器变量创建一个新的Illuminate HTTP请求。
$request = Illuminate\Http\Request::capture()
);
// 6.send方法发送至浏览器
$response->send();
// 7.执行请求生命周期的所有最终操作
$kernel->terminate($request, $response);
1.定义个laravel开始时间的常量,微秒数 define('LARAVEL_START', microtime(true));? 2.注册自动加载程序,Composer里面的 require __DIR__.'/../vendor/autoload.php';? 3.开始启动框架,创建一个app应用程序,并加载一些应用支持,得到$app变量返回(具体加载的东西可以打印$app看看) $app = require_once __DIR__.'/../bootstrap/app.php';? 4.make()是用来创建实例用的,这里创建一个http内核类的实例,这里需要处理http请求 $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); 5.处理传入的HTTP请求,返回Response (http内核的方法,更多可以查看上面的类) $response = $kernel->handle( ? ? // 从服务器变量创建一个新的Illuminate HTTP请求。 ? ? $request = Illuminate\Http\Request::capture() ); 6.send方法发送至浏览器 $response->send(); 7.执行请求生命周期的所有最终操作 $kernel->terminate($request, $response);
完整的一次生命周期就是这7步骤。有同学就好奇了,感觉啥都没做,加载了一些东西,返回了个Response 就结束了,中间件,服务注册那些东西在哪?别急,往下看。
二、看看每步都做了啥?
1.定义个laravel开始时间的常量,微秒数 define('LARAVEL_START', microtime(true));?
作用:计算程序执行时间:截止时间 - 启动时间 = 程序执行时间
2.注册自动加载程序,Composer里面的 require __DIR__.'/../vendor/autoload.php'; 里面的内容:
// 1.加载composer真正的文件
require_once __DIR__ . '/composer/autoload_real.php';
// 2.调用getLoader()方法,返回加载的结果 $loader
return ComposerAutoloaderInitbc82a5a36625504801b4b3fcf46ffd8f::getLoader();
?(1)Composer自动加载初始(版本),看类名见名知意,详细解析请看注释。
ComposerAutoloaderInitxxxxx::getLoader();
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
// 1.返回一个单例,只实例化一次(单例模式)
if (null !== self::$loader) {
return self::$loader;
}
// 将函数注册到SPL __autoload函数队列中。如果该队列中的函数尚未激活,则激活它们。需要用到一个类的时候,就会调用 loadClass 方法。
spl_autoload_register(array('ComposerAutoloaderInitbc82a5a36625504801b4b3fcf46ffd8f', 'loadClassLoader'), true, true);
// new 一个Composer\Autoload\ClassLoader实例
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
// 注销已注册的 __autoload() 函数
spl_autoload_unregister(array('ComposerAutoloaderInitbc82a5a36625504801b4b3fcf46ffd8f', 'loadClassLoader'));
// 判断是否用静态加载(php版本id>=50600&&...)
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
//调用autoload_static.php的getInitializer($loader)方法,给$loader设置各种变量。 call_user_func(\Composer\Autoload\ComposerStaticInitbc82a5a36625504801b4b3fcf46ffd8f::getInitializer($loader));
} else {
//获取完整的命名空间和文件目录的映射,循环set给$loader
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
//获取PSR4 标准顶级命名空间映射数组,循环set给$loader
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
// 命名空间初始化,命名空间映射,add给loader
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
// spl_autoload_register(array($this, 'loadClass'), true, $prepend);将此实例注册为自动装弹器,给定函数注册为__autolload()实现,需要用到一个类的时候,就会调用 loadClass 方法。
$loader->register(true);
// 加载全局函数,把全局函数写到特定的文件,循环加载文件
if ($useStaticLoader) {
// 静态初始化文件
$includeFiles = Composer\Autoload\ComposerStaticInitbc82a5a36625504801b4b3fcf46ffd8f::$files;
} else {
// 普通初始化文件
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
//加载全局函数
composerRequirebc82a5a36625504801b4b3fcf46ffd8f($fileIdentifier, $file);
}
// 加载完成,返回结果
return $loader;
}
(2)这几个文件的内容?
1)autoload_static.php:里面塞了一堆数组,数组里面就是那些composer依赖类了
public static $files = array (
'ec07570ca5a812141189b1fa81503674' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Assert/Functions.php',
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
//...省N行
)
public static $prefixDirsPsr4 = array (
'voku\\' =>
array (
0 => __DIR__ . '/..' . '/voku/portable-ascii/src/voku',
),
//...省N行
)
public static $prefixesPsr0 = array (
'M' =>
array (
'Mockery' =>
array (
0 => __DIR__ . '/..' . '/mockery/mockery/library',
),
),
);
public static $classMap = array (
'App\\Console\\Kernel' => __DIR__ . '/../..' . '/app/Console/Kernel.php',
'App\\Exceptions\\Handler' => __DIR__ . '/../..' . '/app/Exceptions/Handler.php',
//...省N行
)
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInitbc82a5a36625504801b4b3fcf46ffd8f::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitbc82a5a36625504801b4b3fcf46ffd8f::$prefixDirsPsr4;
$loader->prefixesPsr0 = ComposerStaticInitbc82a5a36625504801b4b3fcf46ffd8f::$prefixesPsr0;
$loader->classMap = ComposerStaticInitbc82a5a36625504801b4b3fcf46ffd8f::$classMap;
}, null, ClassLoader::class);
}
2).autoload_namespaces.php,加载vendor文件夹下的一些文件
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Mockery' => array($vendorDir . '/mockery/mockery/library'),
);
?
?3).autoload_psr4.php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'voku\\' => array($vendorDir . '/voku/portable-ascii/src/voku'),
'phpDocumentor\\Reflection\\' => array($vendorDir . '/phpdocumentor/reflection-common/src', $vendorDir . '/phpdocumentor/reflection-docblock/src', $vendorDir . '/phpdocumentor/type-resolver/src'),
'Whoops\\' => array($vendorDir . '/filp/whoops/src/Whoops'),
'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'),
//...省n行
)
4).autoload_classmap.php
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'App\\Console\\Kernel' => $baseDir . '/app/Console/Kernel.php',
'App\\Exceptions\\Handler' => $baseDir . '/app/Exceptions/Handler.php',
'App\\Http\\Controllers\\API\\PostController' => $baseDir . '/app/Http/Controllers/API/PostController.php',
'App\\Http\\Controllers\\Controller' => $baseDir . '/app/Http/Controllers/Controller.php',
//...N行
)
5).autoload_files.php?
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'ec07570ca5a812141189b1fa81503674' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert/Functions.php',
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
//...n
)
?和autoload_static.php里面的内容格式差不多
(3).加载完成得到结果
$loader:长这样,就是一个对象,里面包含了composer扩展的依赖
?
?3.开始启动框架,创建一个app应用程序,并加载一些应用支持,得到$app变量返回(具体加载的东西看打印$app) $app = require_once __DIR__.'/../bootstrap/app.php';
<?php
// 创建一个application对象
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
// 单例模式,为应用载入http内核服务提供者,处理web请求
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
// 单例模式,为应用载入Console内核服务提供者,处理artisan命令
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
// 单例模式,为应用载入异常处理服务提供者,处理异常用的
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
// 返回实例
return $app;
?打印结果看里面设置了很多内容。
?1)初始化:
$app = new Illuminate\Foundation\Application( ? ? $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__) );
但是看上面结果还有很多东西,其实是在创建应用的时候初始化的。我们也可以看看初始化了哪些内容。进入这个类看看:Illuminate\Foundation\Application的构造函数
public function __construct($basePath = null)
{
//设置应用程序的基本路径。
if ($basePath) {
$this->setBasePath($basePath);
}
//将基本绑定注册到容器中
/*
static::setInstance($this);
$this->instance('app', $this);
$this->instance(Container::class, $this);
$this->singleton(Mix::class);
$this->singleton(PackageManifest::class, function () {
return new PackageManifest(
new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
);
});
*/
$this->registerBaseBindings();
//注册所有基本服务提供者。我们常看到的自定义服务提供者写在app.php文件,和这里其实一样
/*
$this->register(new EventServiceProvider($this));
$this->register(new LogServiceProvider($this));
$this->register(new RoutingServiceProvider($this));
*/
$this->registerBaseServiceProviders();
//在容器中注册核心类别名
$this->registerCoreContainerAliases();
}
初始化干了4件事:设置应用基本路径,将基本绑定注册到容器中,注册基础服务提供者,在容器中注册核心类别名。
到这里还看不到服务提供者出现了,中间件呢?
2)绑定重要接口到容器中(中间件在这里)
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
Illuminate\Contracts\Http\Kernel::class
<?php
namespace Illuminate\Contracts\Http;
interface Kernel
{
public function bootstrap();//为HTTP请求引导应用程序
public function handle($request);//处理传入的HTTP请求。
public function terminate($request, $response);//执行请求生命周期的所有最终操作。
public function getApplication();//获取Laravel应用程序实例。
}
App\Http\Kernel::class
<?php
namespace App\Http;
use App\Http\Middleware\CheckToken;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
//应用程序的全局HTTP中间件堆栈。这些中间件在对应用程序的每个请求期间运行
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\App\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
// CheckToken::class,
];
//应用程序的路由中间件组。
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
//应用程序的路由中间件,这些中间件可以分配给组,也可以单独使用。
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
// 'token' => CheckToken::class,
];
}
?
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
也类似。
3)如何使用:
如我在app.php定义了2个中间件
$app->routeMiddleware([
'auth' => App\Http\Middleware\AuthMiddleware::class,
'log' => App\Http\Middleware\LogMiddleware::class
]);
那么在route/web.php路由文件中就可以用,每次执行/day/list就好先执行log,auth中间件的handle
方法。多个中间件就类似洋葱模型,一层层进入,一层层出来。
router->group(['middleware' => ['log','auth']], function () use ($router) {
$router->group(['prefix' => '/day'], function () use ($router) {
//0101通用发车日历接口
$router->get('list', ['uses' => 'DayController@list']);
$router->post('list', ['uses' => 'DayController@list']);
});
到这里,$app就已经加载了很多东西了。
主要工作就是:注册服务提供者,执行中间件。
有前面这些东西的支持,就可以很方便得处理http请求,或执行artisan命令啦
4.make()是用来创建实例用的,这里创建一个http内核类的实例,这里需要处理http请求 $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
/**
* Resolve the given type from the container.从容器解析给定类型
*
* @param string $abstract 给定的类型,如:Illuminate\Contracts\Http\Kernel::class
* @param array $parameters 可选项参数
* @return mixed
*/
public function make($abstract, array $parameters = [])
{
//调用父类Container中的getAlias方法:如果可用,获取抽象的别名
//如果给定类型是延迟服务且实例尚未加载,则加载延迟提供程序。
$this->loadDeferredProviderIfNeeded($abstract = $this->getAlias($abstract));
//调用父类Container中的make方法:从容器解析给定类型
return parent::make($abstract, $parameters);
}
//-------------Container.php--------------------------------------/
//Container中的make方法
public function make($abstract, array $parameters = [])
{
return $this->resolve($abstract, $parameters);
}
/**
* Resolve the given type from the container.从容器解析给定类型
*
* @param string $abstract
* @param array $parameters
* @param bool $raiseEvents
* @return mixed
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
protected function resolve($abstract, $parameters = [], $raiseEvents = true)
{
$abstract = $this->getAlias($abstract);//如果可用,获取抽象的别名。
$concrete = $this->getContextualConcrete($abstract);//获取给定抽象的上下文具体绑定
$needsContextualBuild = ! empty($parameters) || ! is_null($concrete);
// If an instance of the type is currently being managed as a singleton we'll
// just return an existing instance instead of instantiating new instances
// so the developer can keep using the same objects instance every time.
//如果是单例直接返回,如果不是new一个
if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
return $this->instances[$abstract];
}
$this->with[] = $parameters;
if (is_null($concrete)) {
$concrete = $this->getConcrete($abstract);//获取给定摘要的具体类型。
}
// We're ready to instantiate an instance of the concrete type registered for
// the binding. This will instantiate the types, as well as resolve any of
// its "nested" dependencies recursively until all have gotten resolved.
// 实例化注册
if ($this->isBuildable($concrete, $abstract)) {
$object = $this->build($concrete);
} else {
$object = $this->make($concrete);
}
// If we defined any extenders for this type, we'll need to spin through them
// and apply them to the object being built. This allows for the extension
// of services, such as changing configuration or decorating the object.
// 扩展器,允许扩展服务,例如改变配置或装饰对象。
foreach ($this->getExtenders($abstract) as $extender) {
$object = $extender($object, $this);
}
// If the requested type is registered as a singleton we'll want to cache off
// the instances in "memory" so we can return it later without creating an
// entirely new instance of an object on each subsequent request for it.
// 如果是单例,缓存起来
if ($this->isShared($abstract) && ! $needsContextualBuild) {
$this->instances[$abstract] = $object;
}
if ($raiseEvents) {
$this->fireResolvingCallbacks($abstract, $object);
}
// Before returning, we will also set the resolved flag to "true" and pop off
// the parameter overrides for this build. After those two things are done
// we will be ready to return back the fully constructed class instance.
// 在返回之前,我们也将resolve标志设置为"true"并弹出
$this->resolved[$abstract] = true;
array_pop($this->with);
return $object;
}
看英文解析,一顿操作后,最终拿到实例 5.处理传入的HTTP请求,返回Response (http内核的方法,更多可以查看上面的类) $response = $kernel->handle( ? ? // 从服务器变量创建一个新的Illuminate HTTP请求。 ? ? $request = Illuminate\Http\Request::capture() );
这里注意,Kernel是个接口,只是定义了handle()有处理http请求的能力,具体实现类是在要看具体实现类里面的hander()方法.这里http内核的实现类是:app/Http/Kernel.php
/**
* Handle an incoming HTTP request.处理传入的HTTP请求。
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function handle($request)
{
try {
//启用对_method请求参数的支持以确定预期的HTTP方法。
$request->enableHttpMethodParameterOverride();
//通过中间件/路由器发送给定的请求
$response = $this->sendRequestThroughRouter($request);
} catch (Throwable $e) {
//将异常报告给异常处理程序。
$this->reportException($e);
//将异常呈现给响应。注意这里的render()方法,一般我们处理异常可以在这里处理
//return $this->app[ExceptionHandler::class]->render($request, $e);
$response = $this->renderException($request, $e);
}
$this->app['events']->dispatch(
new RequestHandled($request, $response)
);
return $response;
}
这里有个常用的方法:render()方法,一般处理异常用的。如:
在app/Exceptions/下自定义一个异常处理类ApiExceptions.php
<?php
namespace App\Exceptions;
use Throwable;
class ApiExceptions extends \Exception
{
public function __construct($message = "", $code = 0, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}
}
?那么在Handler.php的render()就可以拦截我们的异常,可以进行特定格式的结果返回。
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Throwable $exception
* @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*
* @throws \Throwable
*/
public function render($request, Throwable $exception)
{
if ($exception instanceof ModelNotFoundException) {
$exception = new NotFoundHttpException($exception->getMessage(), $exception);
}
//校验自定义返回
if ($exception instanceof ApiExceptions) {
return response()->json([
'code' => 0,
'message' => $exception->getMessage(),
], 200);
}
//校验输入字段统一返回,ValidationException框架自待的,过滤参数输入合法性
if ($exception instanceof ValidationException) {
$errors = $exception->errors();
if ($errors) {
foreach ($errors as $key => $value) {
if ($value[0]) {
return response()->json([
'code' => 0,
'message' => $value[0],
], 200);
}
}
}
}
return parent::render($request, $exception);
}
一搬我们业务逻辑在处理Service?的时候,有时想直接把结果返回给前端就可以用
ApiExceptions
$detail = findById($id);
if (!$detail) {
throw new ApiExceptions('详细不存在!');
}
//api返回的结果直接输出json
{"code":0,"message":"详细不存在!"}
6.send方法发送至浏览器 $response->send(); 7.执行请求生命周期的所有最终操作 $kernel->terminate($request, $response);
?到这里相信,对laravel执行的生命周期有个大概的了解了。
|