laravel核心架构——请求周期

laravel核心架构——请求周期

    • 开始
    • HTTP/控制器内核
    • 服务提供器
    • 分配请求

laravel核心架构,在laravel china社区中文文档中有详细的说明。参见 《Laravel 5.5 中文文档》 | Laravel China 社区。在这里,只做主要笔记。

开始

public/index.php 文件是所有对 Laravel 应用程序的请求的入口点。

public/index.php是工程的单入口文件,这与PHP的其他框架类似。laravel工程创建成功后,可以使用http://localhost/demo/public/index.php/{router}来访问laravel工程中的路由。

而所有的请求都是经由你的 Web 服务器(Apache/Nginx)通过配置引导到这个文件。index.php 文件不包含太多的代码,却是加载框架的起点。

为了去掉url中的index.php,应该配置Web服务器的重读url规则,对于Apache服务器,laravel工程中的public/.htaccess文件已经配置好了url重读规则(要保证开启Apache服务器的modules/mod_rewrite.so模块)。对于Nginx服务器,可以如下配置:

 location / {
     
	 try_files $uri $uri/ /index.php?$query_string;
	  index index.php index.html index.htm;
	}

laravel的public是适用于公开访问的目录,为了去掉url中/public,要把虚拟主机的根目录指向laravel工程的public路径。对于Apache服务器虚拟主机的配置可能如下:

<VirtualHost *:80>
    ServerAdmin demo.test
    DocumentRoot "A:/xampp/htdocs/demo/public"
    ServerName demo.com
</VirtualHost>

Nigin服务器与此相似:

server {
     
	listen       80;
	server_name  demo.test;
	root        /usr/share/nginx/html/此处是你的项目名/public;
}

这样通过http://demo.test/{router}即可访问到laravel中的路由和public中的资源。所有的请求都经过public/index.php文件。

index.php文件加载 Composer 生成定义的自动加载器,

index.php文件中可以发现引入了Composer的自动加载器

// public/index.php

/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| our application. We just need to utilize it! We'll simply require it
| into the script here so that we don't have to worry about manual
| loading any of our classes later on. It feels great to relax.
|
*/

require __DIR__.'/../vendor/autoload.php';

然后从 bootstrap/app.php 脚本中检索 Laravel 应用程序的实例。

/bootstrap/app.php文件中,创建并返回laravel的应用的实例:

// bootstrap/app.php

/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| The first thing we will do is create a new Laravel application instance
| which serves as the "glue" for all the components of Laravel, and is
| the IoC container for the system binding all of the various parts.
|
*/

$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

...

return $app;

HTTP/控制器内核

接下来,根据进入应用程序的请求类型来将传入的请求发送到 HTTP 内核或控制台内核。而这两个内核是用来作为所有请求都要通过的中心位置。

对来自Web服务器转发的请求,都会经过public/index.php的引导发送到HTTP内核。

// public/index.php

/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

对于通过控制台php artisan的命令,会发送到控制台内核。

// artisn

/*
|--------------------------------------------------------------------------
| Run The Artisan Application
|--------------------------------------------------------------------------
|
| When we run the console application, the current CLI command will be
| executed in this console and the response sent back to a terminal
| or another output device for the developers. Here goes nothing!
|
*/

$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);

$status = $kernel->handle(
    $input = new Symfony\Component\Console\Input\ArgvInput,
    new Symfony\Component\Console\Output\ConsoleOutput
);

HTTP 内核继承了 Illuminate\Foundation\Http\Kernel 类,

HTTP内核通过应用实例Applicationmake方法创建,可以通过辅助方法app来获取创建的实例:
Http\Kernel
可以看出,HTTP内核在/App/Http/kernel中定义,且继承了Illuminate\Foundation\Http\Kernel

// App\Http\Kernel

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
     
	...
}

它定义了在执行请求之前运行的 bootstrappers 数组。这个数组负责在实际处理请求之前完成这些内容:配置错误处理、配置日志记录、检测应用程序环境 以及执行其他需要完成的任务。

// Illuminate\Foundation\Http\Kernel

/**
* The bootstrap classes for the application.
 *
 * @var array
 */
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,
];

HTTP 内核还定义了所有请求被应用程序处理之前必须经过的 HTTP 中间件的列表。这些中间件处理 HTTP 会话 的读写、确定应用程序是否处于维护模式、验证 CSRF 令牌等。

// App\Console\Kernel

/**
 * The application's global HTTP middleware stack.
 *
 * These middleware are run during every request to your application.
 *
 * @var array
 */
protected $middleware = [
    \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
    \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
    \App\Http\Middleware\TrimStrings::class,
    \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    \App\Http\Middleware\TrustProxies::class,
];

/**
 * The application's route middleware groups.
 *
 * @var array
 */
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',
        'bindings',
    ],
];

/**
 * The application's route middleware.
 *
 * These middleware may be assigned to groups or used individually.
 *
 * @var array
 */
protected $routeMiddleware = [
    'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
    'can' => \Illuminate\Auth\Middleware\Authorize::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];

HTTP 内核的 handle 方法的方法签名非常简单:接收 Request 并返回 Response。可以把内核当作是代表整个应用程序的大黑盒,给它 HTTP 请求,它就返回 HTTP 响应。

// Illuminate\Foundation\Http\Kernel

/**
 * Handle an incoming HTTP request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Http\Response
 */
public function handle($request)
{
     
    try {
     
        $request->enableHttpMethodParameterOverride();

        $response = $this->sendRequestThroughRouter($request);
    } catch (Exception $e) {
     
        $this->reportException($e);

        $response = $this->renderException($request, $e);
    } catch (Throwable $e) {
     
        $this->reportException($e = new FatalThrowableError($e));

        $response = $this->renderException($request, $e);
    }

    $this->app['events']->dispatch(
        new Events\RequestHandled($request, $response)
    );

    return $response;
}

服务提供器

最重要的内核引导操作之一是加载应用程序的 服务提供器。

Kernel类的handle方法调用sendRequestThroughRouter方法处理请求。sendRequestThroughRouter中调用bootstrap方法进行引导操作

// Illuminate\Foundation\Http\Kernel

    /**
     * Send the given request through the middleware / router.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    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());
    }

bootstrap方法调用应用实例ApplicationbootstrapWith方法,引导Kernel类中定义的引导项:

// Illuminate\Foundation\Http\Kernel

    /**
     * The bootstrap classes for the application.
     *
     * @var array
     */
    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,
    ];
    
    /**
     * Bootstrap the application for HTTP requests.
     *
     * @return void
     */
    public function bootstrap()
    {
     
        if (! $this->app->hasBeenBootstrapped()) {
     
            $this->app->bootstrapWith($this->bootstrappers());
        }
    }

Application类的bootstrapWith方法中调用引导类的bootstrap方法:

// Illuminate\Foundation\Application 

class Application extends Container implements ApplicationContract, HttpKernelInterface
{
     
	// ...
	
    /**
     * Run the given array of bootstrap classes.
     *
     * @param  array  $bootstrappers
     * @return void
     */
    public function bootstrapWith(array $bootstrappers)
    {
     
        $this->hasBeenBootstrapped = true;

        foreach ($bootstrappers as $bootstrapper) {
     
            $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);

            $this->make($bootstrapper)->bootstrap($this);

            $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
        }
    }
	
	// ...
}

各个引导类功能:

  • LoadEnvironmentVariables引导类加载.env文件中的环境变量;
  • LoadConfiguration引导类加载config/*.php文件中的配置项;
  • HandleExceptions引导类配置异常处理;
  • RegisterFacades引导类注册Facades类;
  • RegisterProviders引导类注册服务提供器providers;
  • BootProviders类调用Applicationboot引导应用实例;

应用程序的所有服务提供器都在 config/app.php 配置文件的 providers 数组中配置。


// config/app.php

return [
	...
	
    'providers' => [

        /*
         * Laravel Framework Service Providers...
         */
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,
        Illuminate\Bus\BusServiceProvider::class,
        Illuminate\Cache\CacheServiceProvider::class,
        Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
        Illuminate\Cookie\CookieServiceProvider::class,
        Illuminate\Database\DatabaseServiceProvider::class,
        Illuminate\Encryption\EncryptionServiceProvider::class,
        Illuminate\Filesystem\FilesystemServiceProvider::class,
        Illuminate\Foundation\Providers\FoundationServiceProvider::class,
        Illuminate\Hashing\HashServiceProvider::class,
        Illuminate\Mail\MailServiceProvider::class,
        Illuminate\Notifications\NotificationServiceProvider::class,
        Illuminate\Pagination\PaginationServiceProvider::class,
        Illuminate\Pipeline\PipelineServiceProvider::class,
        Illuminate\Queue\QueueServiceProvider::class,
        Illuminate\Redis\RedisServiceProvider::class,
        Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
        Illuminate\Session\SessionServiceProvider::class,
        Illuminate\Translation\TranslationServiceProvider::class,
        Illuminate\Validation\ValidationServiceProvider::class,
        Illuminate\View\ViewServiceProvider::class,

        /*
         * Package Service Providers...
         */

        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,

		...
		]

	...
]

config/*.php文件在Kernel引导数组中的LoadConfiguration引导项来加载、解析:

// Illuminate\Foundation\Bootstrap\LoadConfiguration

class LoadConfiguration
{
     
	...
	
    /**
     * Load the configuration items from all of the files.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @param  \Illuminate\Contracts\Config\Repository  $repository
     * @return void
     * @throws \Exception
     */
    protected function loadConfigurationFiles(Application $app, RepositoryContract $repository)
    {
     
        $files = $this->getConfigurationFiles($app);

        if (! isset($files['app'])) {
     
            throw new Exception('Unable to load the "app" configuration file.');
        }

        foreach ($files as $key => $path) {
     
            $repository->set($key, require $path);
        }
    }

    /**
     * Get all of the configuration files for the application.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return array
     */
    protected function getConfigurationFiles(Application $app)
    {
     
        $files = [];

        $configPath = realpath($app->configPath());

        foreach (Finder::create()->files()->name('*.php')->in($configPath) as $file) {
     
            $directory = $this->getNestedDirectory($file, $configPath);

            $files[$directory.basename($file->getRealPath(), '.php')] = $file->getRealPath();
        }

        ksort($files, SORT_NATURAL);

        return $files;
    }

	...
}

config/app.php中声明的providersRegisterProviders类注册:

// Illuminate\Foundation\Bootstrap\RegisterProviders

class RegisterProviders
{
     
    /**
     * Bootstrap the given application.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public function bootstrap(Application $app)
    {
     
        $app->registerConfiguredProviders();
    }
}

RegisterProviders类的bootstrap方法委托给ApplicationregisterConfiguredProviders方法进行注册config/app.php配置的服务提供器

// Illuminate\Foundation\Application

    /**
     * Register all of the configured providers.
     *
     * @return void
     */
    public function registerConfiguredProviders()
    {
     
        $providers = Collection::make($this->config['app.providers'])
                        ->partition(function ($provider) {
     
                            return Str::startsWith($provider, 'Illuminate\\');
                        });

        $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);

        (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
                    ->load($providers->collapse()->toArray());
    }

registerConfiguredProviders方法中会加载config/app.php文件定义的数组中的以Illuminate\\开头providersbootstrap/cache/packages.php中定义的providers

首先,所有提供器都会调用 register 方法,接着,由 boot 方法负责调用所有被注册提供器。

Application类的registerConfiguredProviders方法调用ProviderRepositoryload方法进行服务提供器的实例化:


class ProviderRepository
{
     
	// ...
	
    /**
     * Register the application service providers.
     *
     * @param  array  $providers
     * @return void
     */
    public function load(array $providers)
    {
     
        $manifest = $this->loadManifest();

        // First we will load the service manifest, which contains information on all
        // service providers registered with the application and which services it
        // provides. This is used to know which services are "deferred" loaders.
        if ($this->shouldRecompile($manifest, $providers)) {
     
            $manifest = $this->compileManifest($providers);
        }

        // Next, we will register events to load the providers for each of the events
        // that it has requested. This allows the service provider to defer itself
        // while still getting automatically loaded when a certain event occurs.
        foreach ($manifest['when'] as $provider => $events) {
     
            $this->registerLoadEvents($provider, $events);
        }

        // We will go ahead and register all of the eagerly loaded providers with the
        // application so their services can be registered with the application as
        // a provided service. Then we will set the deferred service list on it.
        foreach ($manifest['eager'] as $provider) {
     
            $this->app->register($provider);
        }

        $this->app->addDeferredServices($manifest['deferred']);
    }

    /**
     * Create a new provider instance.
     *
     * @param  string  $provider
     * @return \Illuminate\Support\ServiceProvider
     */
    public function createProvider($provider)
    {
     
        return new $provider($this->app);
    }

	// ...
}	

ProviderRepository类中$this->app->register($provider);是把创建好的服务提供器类传给Applicationregister方法并在其中调用服务提供器的register方法和boot方法:

// Illuminate\Foundation\Application

    /**
     * Register a service provider with the application.
     *
     * @param  \Illuminate\Support\ServiceProvider|string  $provider
     * @param  array  $options
     * @param  bool   $force
     * @return \Illuminate\Support\ServiceProvider
     */
    public function register($provider, $options = [], $force = false)
    {
     
        if (($registered = $this->getProvider($provider)) && ! $force) {
     
            return $registered;
        }

        // If the given "provider" is a string, we will resolve it, passing in the
        // application instance automatically for the developer. This is simply
        // a more convenient way of specifying your service provider classes.
        if (is_string($provider)) {
     
            $provider = $this->resolveProvider($provider);
        }

        if (method_exists($provider, 'register')) {
     
            $provider->register();
        }

        $this->markAsRegistered($provider);

        // If the application has already booted, we will call this boot method on
        // the provider class so it has an opportunity to do its boot logic and
        // will be ready for any usage by this developer's application logic.
        if ($this->booted) {
     
            $this->bootProvider($provider);
        }

        return $provider;
    }

    /**
     * Boot the given service provider.
     *
     * @param  \Illuminate\Support\ServiceProvider  $provider
     * @return mixed
     */
    protected function bootProvider(ServiceProvider $provider)
    {
     
        if (method_exists($provider, 'boot')) {
     
            return $this->call([$provider, 'boot']);
        }
    }

分配请求

一旦引导了应用程序且注册所有服务提供器,Request 请求就会被转交给路由器来进行调度。路由器将请求发送到路由或控制器或任何运行于路由的特定中间件。

Kernel类的sendRequestThroughRouter方法中,以管道的模式发送请求,经过要执行的中间件Middleware,然后分发给路由:

// Illuminate\Foundation\Http\Kernel

    /**
     * Send the given request through the middleware / router.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    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());
    }

Illuminate\Http\Request类从$_SERVER$_GET$_POST$_COOKIE等全局数组中解析请求参数;
Illuminate\Routing\Router类匹配路由规则发现路由,并分发请求到指定路由;
Illuminate\Routing\Route类接受请求,并调用控制器方法;

你可能感兴趣的:(laravel,laravel,php)