4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Laravel 7.4のコードを読む

Posted at

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上でコード補完できるようになる。

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?