Laravel 7.4のコードを読む。
$ php artisan -V
Laravel Framework 7.4.0
path/public/index.php
httpリクエストのエントリーポイントのindex.phpを追う。
define('LARAVEL_START', microtime(true));
初めに LARAVEL_START
という定数が作成される。値は実行時のUnixタイムスタンプ(マイクロ秒単位/float型)。
計測で使える。(phpドキュメントによると計測用ならmicrotime()ではなくhrtime()を使った方が良いらしい)
artisanのエントリーポイントでも同様の処理がある。
require __DIR__.'/vendor/autoload.php';
続いてautoloadを読み込む。
$app = require_once __DIR__.'/bootstrap/app.php';
続いてapp.phpを読み込む。app.phpはlaravel本体の起動を行う。
autoloadはrequireで読み込んだのにappはrequire_onceになってる。本体の起動は絶対1回しかやりたくないからrequire_onceを使うのかな。
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
続いて$appのサービスコンテナの機能を使ってKernelインターフェースの実クラスを読み込む。
app.phpでバインドすることでKernelの動作を実装者サイドで決められるようにしてるのか。
$app->make
の関数は良く使うので覚えておこう。
ちなみにlaravelではインターフェースのことをConstracts(制約)と読ぶ。
C#等の言語だとインターフェースは頭にIを付けてIKernel::classになるけどlaravelではIは付けないようだ。
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
↓つまりこういうこと?
$request = Illuminate\Http\Request::capture()
$response = $kernel->handle($request);
続いてrequestとresponseの作成を行う。handle関数引数で$request宣言するのはなぜだろう。
$response->send();
続いてheaderとcontentを書き込む。
$kernel->terminate($request, $response);
最後に終了処理を行う。内部ではterminateMiddlewareの実行やapplicationのterminateを実行する。
bootstrap/app.php
index.phpで読み込まれたapp.phpを追う。
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
まずlaravel本体のApplicationクラスをインスタンス化する。
ApplicationはbasePathを引数に渡せる。環境変数APP_BASE_PATHを、無ければ dirname(__DIR__) = bootstrapフォルダの親フォルダ
が使われる。
$appはapp.phpだと思っていたのだけどApplicationインスタンスだったのね。
$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
);
続いてKernelのBindを行う。
app.phpはhttpやconsole等の異なるエントリーポイントからも呼ばれるためアプリケーション全体で最低限必要なbindのみを行うようだ。
特定のエントリーポイントのみに関連する処理は行わないよう注意したい。
ちなみにGAE/SE環境にlaravelアプリケーションをdeployする際はここに$app->useStoragePath(env('APP_STORAGE', base_path() . '/storage'));
を追加する。
Illuminate\Foundation\Application
laravelアプリケーションの本体クラスを追う。
Illuminate\Foundation名前空間に存在する。ここには他にAliasLoader.php, helper.php, Mix.php等が存在する。
約1,300行なのでそこまで量は多くない。
Applicationの継承
まずは継承を見る。
class Application extends Container implements ApplicationContract, CachesConfiguration, CachesRoutes, HttpKernelInterface
Container
Illuminate\Container\Container.php
サービスコンテナを管理するクラス。
$app->make()の処理はこれが担当している。後ほど詳しく見る。
ApplicationContract
Illuminate\Contracts\Foundation\Application.php
Applicationのインターフェース。
- version関数
- パス関連関数(basePathやbootstrapPath等)
- 環境変数関数
- provider関連関数
- boot関数
- locale関数
- terminate関数
CachesConfiguration
Illuminate\Contracts\Foundation\CachesConfiguration.php
application configurationのキャッシュを管理するインターフェースのようだけど、何をしているんだろう。
- configurationIsCached
- getCachedConfigPath
- getCachedServicesPath
CachesRoutes
Illuminate\Contracts\Foundation\CachesRoutes.php
routeファイルのキャッシュを管理する。
- routesAreCached
- getCachedRoutesPath
HttpKernelInterface
Symfony\Component\HttpKernel\HttpKernelInterface.php
これはIlluminate\ContractsではなくSymfonyのもの。
requestをresponseに変換する関数のみ定義されてる。
- handle
Applicationのコンストラクタ
*/
public function __construct($basePath = null)
{
if ($basePath) {
$this->setBasePath($basePath);
}
$this->registerBaseBindings();
$this->registerBaseServiceProviders();
$this->registerCoreContainerAliases();
}
basePathを引数に受け取る。
basePathとはプロジェクトのルートパスのこと。
basePathがnull許容なのはなぜだろう。。。
public function setBasePath($basePath)
{
$this->basePath = rtrim($basePath, '\/');
$this->bindPathsInContainer();
return $this;
}
protected function bindPathsInContainer()
{
$this->instance('path', $this->path());
$this->instance('path.base', $this->basePath());
$this->instance('path.lang', $this->langPath());
$this->instance('path.config', $this->configPath());
省略
setBasePathでサービスコンテナにpathをbindしてる。(でもこのタイミングではbasePath以外は実装者はpathをセットしていないからデフォルト値が入る)
protected function registerBaseBindings()
{
static::setInstance($this);
$this->instance('app', $this);
$this->instance(Container::class, $this);
$this->singleton(Mix::class);
$this->instance(PackageManifest::class, new PackageManifest(
new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
));
}
根幹的なクラスのbindingを行う。
Applicationクラスは $app->make('app')
でも $app->make(Container::class)
でも取得できるようだ。
PackageManifestとはなんだろう。
protected function registerBaseServiceProviders()
{
$this->register(new EventServiceProvider($this));
$this->register(new LogServiceProvider($this));
$this->register(new RoutingServiceProvider($this));
}
根幹的なサービスプロバイダーを登録する。
- EventServiceProvider : eventDispatcherをコンテナに登録
- LogServiceProvider : LogManagerをコンテナに登録
- RoutingServiceProvider : routesやurlをコンテナに登録
public function registerCoreContainerAliases()
{
foreach ([
'app' => [self::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class, \Psr\Container\ContainerInterface::class],
'auth' => [\Illuminate\Auth\AuthManager::class, \Illuminate\Contracts\Auth\Factory::class],
'auth.driver' => [\Illuminate\Contracts\Auth\Guard::class],
'blade.compiler' => [\Illuminate\View\Compilers\BladeCompiler::class],
省略
] as $key => $aliases) {
foreach ($aliases as $alias) {
$this->alias($key, $alias);
}
}
根幹的なファサードのaliasを登録する。
サービスコンテナに登録したクラスをファサード経由でアクセスするにはファサードのusingが必要だけど、aliasに登録することでファサードのusingなしに \App::version();
のように記述できるようになる。
ide_helperを使えばIDE上でコード補完できるようになる。