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 内核或控制台内核。而这两个内核是用来作为所有请求都要通过的中心位置。
对来自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内核通过应用实例Application
的make
方法创建,可以通过辅助方法app
来获取创建的实例:
可以看出,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
方法调用应用实例Application
的bootstrapWith
方法,引导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
类调用Application
的boot
引导应用实例;应用程序的所有服务提供器都在 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
中声明的providers
由RegisterProviders
类注册:
// 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
方法委托给Application
的registerConfiguredProviders
方法进行注册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\\
开头providers
和bootstrap/cache/packages.php
中定义的providers
首先,所有提供器都会调用 register 方法,接着,由 boot 方法负责调用所有被注册提供器。
Application
类的registerConfiguredProviders
方法调用ProviderRepository
的load
方法进行服务提供器的实例化:
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);
是把创建好的服务提供器类传给Application
的register
方法并在其中调用服务提供器的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
类接受请求,并调用控制器方法;