laravel框架学习(04):生命周期

这里将看看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执行的生命周期有个大概的了解了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值