
Как и в большинстве фреймворков, Laravel использует единую «точку входа» - файл public/index.php. Все запросы к серверу перенаправляются в этот файл настройками вашего веб-сервера (Apache/Nginx), который далее подключает необходимые ресурсы для работы приложения и перенаправляет вызов.
public/index.php
В начале файла устанавливается константа LARAVEL_START:define('LARAVEL_START', microtime(true));которая сохраняет метку времени с миллисекундами при старте приложения. Это используется при отладке для определения времени которое прошло до момента замера. Например проверим сколько времени выполняется приложение на сервере, для этого разместите код:
$time_end = microtime(true); $time = $time_end - LARAVEL_START; echo "Выполнено за $time секунд";в самом конце данного файла. После открытия любой страницы сайта в самом низу появится строка с соответствующим значением.
Далее подключается автозагрузчик Composer который используется фреймворком для загрузки необходимых классов:
require __DIR__.'/../vendor/autoload.php';
Ниже подключается файл bootstrap/app.php который рассмотрим подробнее.
В файле
bootstrap/app.php
создается экземпляр приложения:$app = new Illuminate\Foundation\Application( realpath(__DIR__.'/../') );Класс приложения наследуется от сервис-контейнера:
class Application extends Container implements ApplicationContract, HttpKernelInterfaceтаким образом из объекта приложения ($app) можно получить доступ ко всем сервисам, которые регистрируются в сервис-контейнере. Например выполнив:
app('router')получим объект Illuminate\Routing\Router.
Далее в bootstrap/app.php как раз регистрируются в сервис-контейнере первые сервисы (интерфейсы и классы их реализующие). А именно классы семейства Kernel (для WEB и консольного приложения) и класс для обработки ошибок:
$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 );
Например первый вызов метода singleton() регистрирует интерфейс Illuminate\Contracts\Http\Kernel и класс App\Http\Kernel. Данный класс наследует от класса Illuminate\Foundation\Http\Kernel, который определяет массив загрузчиков 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, ];
Эти загрузчики настраивают обработку ошибок, ведение журналов, определяют среду приложения и выполняют другие задачи, которые надо выполнить перед самой обработкой запроса.
Сам же класс App\Http\Kernel дополнительно определяет посредников, через которые должны пройти все запросы.
public/index.php
Выполнение кода возвращается к файлу public/index.php, сохранив в переменную $app объект приложения с ядрами зарегистрированными в сервис-контейнере.Следующей строкой кода:
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);в переменную $kernel сохраняется объект класса Illuminate\Contracts\Http\Kernel который будет обрабатывать входящий запрос в соответствии с массивом классов содержащихся в свойстве $bootstrappers и установленным посредникам, которые были зарегистрированы при создании объекта приложения в файле bootstrap/app.php.
Выведем на экран свойства данного объекта дописав следующей строкой:
dd($kernel);
Kernel {#29 ▼ #middleware: array:5 [▼ 0 => "Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode" 1 => "Illuminate\Foundation\Http\Middleware\ValidatePostSize" 2 => "App\Http\Middleware\TrimStrings" 3 => "Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull" 4 => "App\Http\Middleware\TrustProxies" ] #middlewareGroups: array:2 [▼ "web" => array:6 [▶] "api" => array:2 [▶] ] #routeMiddleware: array:6 [▶] #app: Application {#2 ▶} #router: Router {#25 ▶} #bootstrappers: array:6 [▼ 0 => "Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables" 1 => "Illuminate\Foundation\Bootstrap\LoadConfiguration" 2 => "Illuminate\Foundation\Bootstrap\HandleExceptions" 3 => "Illuminate\Foundation\Bootstrap\RegisterFacades" 4 => "Illuminate\Foundation\Bootstrap\RegisterProviders" 5 => "Illuminate\Foundation\Bootstrap\BootProviders" ] #middlewarePriority: array:6 [▶] }тут я развернул только 2 его свойства.
Таким образом свойства данного объекта содержат необходимые данные для обработки входящего запроса.
И следующей строкой:
$response = $kernel->handle( $request = Illuminate\Http\Request::capture() );вызывается метод handle() класса Illuminate\Foundation\Http\Kernel из файла vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php, который занимается обработкой входящего запроса:
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; }
В качестве аргумента ему передается объект класса Illuminate\Http\Request.
Строка
$request->enableHttpMethodParameterOverride();выполнит метод enableHttpMethodParameterOverride() из файла vendor/symfony/http-foundation/Request.php который включает поддержку параметра _method request для определения предполагаемого метода HTTP.
Строка
$response = $this->sendRequestThroughRouter($request);отправляет запрос через маршрутизатор и посредники.
Код данного метода из файла vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:
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()); } }вызывающий метод hasBeenBootstrapped() класса Illuminate\Foundation\Application файла vendor/laravel/framework/src/Illuminate/Foundation/Application.php
приводящий к запуску классов начальной загрузки, таких как:
"Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables"
"Illuminate\Foundation\Bootstrap\LoadConfiguration"
"Illuminate\Foundation\Bootstrap\HandleExceptions"
"Illuminate\Foundation\Bootstrap\RegisterFacades"
"Illuminate\Foundation\Bootstrap\RegisterProviders"
"Illuminate\Foundation\Bootstrap\BootProviders"
а так же событий привязанных к ним.
В конце метода видим:
return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter());возвращается результат выполнения методов объекта Pipeline. Данный объект используется для отлова любых исключений. Исключения преобразуются в HTTP-ответы для правильной обработки промежуточного программного обеспечения.
->send($request)устанавливает объект который будет отправляться по конвееру.
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)определяется было ли отключено промежуточное програмное обеспечение для приложения и если да, то устанавливается массив посредников через которые будет пропущен запрос.
->then($this->dispatchToRouter());Метод then() пропускает результат выполнения $this->dispatchToRouter() через посредники. Скромный, на первый взгляд, метод dispatchToRouter(), передающий результат своей работы в качестве аргумента методу then(), по факту запускает глобальный процесс формирования ответа сервера. Вот его код:
protected function dispatchToRouter() { return function ($request) { $this->app->instance('request', $request); return $this->router->dispatch($request); }; }Как видим, метод возвращает анонимную функцию, которая формирует объект Illuminate\Routing\Router методу then() для корректировки данного объекта в соответствии с определенными в приложении посредниками. Т.е. строка:
$this->router->dispatch($request);отправляет запрос в приложении, где вступают в действие такие компоненты как контроллер, модель и вид.
В результате работы многих методов различных классов нам возвращается уже объект Illuminate\Routing\Router наполненный нужными для вывода данными. Вызов метода dispatch($request) у объекта Router возвращает объект Illuminate\Http\Response содержащий заголовки для браузера и html код страницы.
Выполнение возвращается в метод handle() класса vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php в котором перед возвратом результата работы этого метода (объекта Illuminate\Http\Response) выполняется
$this->app['events']->dispatch( new Events\RequestHandled($request, $response) );запуск событий и вызов их слушателей.
public/index.php
В результате выполнение кода возвращается в файл index.php и в переменную $response сохраняется объект Illuminate\Http\Response с данными готовыми для отправки сервером клиенту.Следующая же строка:
$response->send();собственно и отправляет HTTP-заголовки и контент.
Последняя строка:
$kernel->terminate($request, $response);завершает работу приложения. Выполняются одноименные методы у зарегистрированных посредников, например у lluminate\Session\Middleware\StartSession или у пользовательских, если переопределите в своем посреднике метод terminate($request, $response).
Так же этот метод вызывает зарегистрированные в свойстве приложения $terminatingCallbacks функции обратного вызова. Можно создать и привязать свои функции обратного вызова, чтобы они выполнились после отправки ответа браузеру. Это делается так:
app()->terminating(function(){ // });