Laravel マルチログイン Lifecycle
マルチログイン関連ファイル
model、controller、view、route
providers/RouteServiceProvider.php
ログイン後のURL
config/auth.php
Guard設定 ログイン方法、どのテーブルを使うか
middleware/Authenticate.php
未認証時のリダイレクト処理
middleware/RedirectIfAuthenticated.php
ログイン済みユーザーのリダイレクト
ブラウザに表示されるまでの流れ
laravelで作成されてページへアクセスするとまず、index.phpにすべて集まる
WEBサーバがpublic/index.phpにリダイレクト
→ページが読み込まれたタイミングでサービスコンテナ、サービスプロバイダ、ミドルウェア、環境設定などを読み込んでリクエスト、レスポンスが帰ってくる。
- autoload読み込み→requireなしで別ファイルのクラスを利用可能
- Applicationインスタンス作成(サービスコンテナ)
- HttpKernelインスタンス作成
- Requestインスタンス作成
- HttpKernelがリクエストを処理してResponse取得
- レスポンス送信
- terminate()
1. autoload読み込み、2. Applicationインスタンス作成(サービスコンテナ)
autoload読み込み→bootstrap/app.php
読み込み→サービスコンテナを読み込んでる
<?php /* |-------------------------------------------------------------------------- | Register The Auto Loader |-------------------------------------------------------------------------- | | Composer provides a convenient, automatically generated class loader for | this application. We just need to utilize it! We'll simply require it | into the script here so we don't need to manually load our classes. | requireなしで別ファイルクラスを利用可能→namespaceやuse文が使える */ require __DIR__.'/../vendor/autoload.php'; /* |-------------------------------------------------------------------------- | Run The Application |-------------------------------------------------------------------------- | | Once we have the application, we can handle the incoming request using | the application's HTTP kernel. Then, we will send the response back | to this client's browser, allowing them to enjoy our application. | */ $app = require_once __DIR__.'/../bootstrap/app.php'; $kernel = $app->make(Kernel::class); $response = $kernel->handle( $request = Request::capture() )->send(); $kernel->terminate($request, $response);
Illuminate\Foundation\Application
をインスタンス化し、
$appへ代入したものがサービスコンテナ
- NULL合体演算子
式(expr1)??(expr2) expr1がnullならexpr2と評価され、それ以外の場合expr1と評価されます。
pathがnullだったらdirそうでなければpathが実行
singleton
はサービスコンテナに紐付ける仕組みで一度だけインスタンス化するメソッド。→カーネルクラスをサービスコンテナに登録する
→3. HttpKernelインスタンス作成
<?php $app = new Illuminate\Foundation\Application( $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__) ); $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 ); );
サービスコンテナを使ってみる
ルーティングの定義
<?php use Illuminate\Support\Facades\Route; use App\Http\Controllers\ComponentTestController; use App\Http\Controllers\LifeCycleTestController; Route::get('/', function () { //prefix設定 return view('user.welcome'); }); Route::get('/dashboard', function () { //prefix設定 return view('user.dashboard'); //guard権限:を付与、→usersの権限を持ってたらダッシュボードへ })->middleware(['auth:users'])->name('dashboard'); Route::get('/component-test1', [ComponentTestController::class, 'showComponent1']); Route::get('/component-test2', [ComponentTestController::class, 'showComponent2']); Route::get('/servicecontainer-test', [LifeCycleTestController::class, 'showServiceControllerTest']); Route::get('/serviceprovider-test', [LifeCycleTestController::class, 'showServiceProviderTest']); require __DIR__.'/auth.php';
コントローラ作成
php aritsan make:controller LifeCycleTestController
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class LifeCycleTestController extends Controller { // public function showServiceProviderTest() { $encrypt = app()->make('encrypter'); $password = $encrypt->encrypt('password'); $sample = app()->make('serviceProviderTest'); dd($sample, $password, $encrypt->decrypt($password)); } public function showServiceControllerTest() { //bindでサービスコンテナ登録 app()->bind('lifeCycleTest', function(){ return 'ライフサイクルテスト'; }); //makeでサービスコンテナから取り出す $test = app()->make('lifeCycleTest'); //サービスコンテナなしのパターン // $message = new Message(); // $sample = new Sample($message); // $sample->run(); //サービスコンテナapp()ありのパターン app()->bind('sample', Sample::class); $sample = app()->make('sample'); $sample->run(); dd($test, app()); } } class Sample { public $message; public function __construct(Message $message){ $this->message = $message; } public function run(){ $this->message->send(); } } class Message { public function send(){ echo('メッセージ表示'); } }
bind
でサービスコンテナに登録したあとにDDでみると71→72にふえていてlifeCycleTestが追加されてる。
http://localhost:8005/servicecontainer-testへアクセスするとlifeCycleTestを確認でき、サービスコンテナに登録できていることがわかる。
サービスコンテナから取り出す場合はmake
を使用する。
再び確認すると、ライフサイクルテストと表示されサービスコンテナから取り出しができていることがわかる。
サービスプロバイダ
register()
メソッドでサービスコンテナにサービスを登録
boot()
メソッドですべてのサービスを登録した後に実行したい処理を書く
サービスプロバイダの読み込み箇所→bootstrap/app.php
内
→Illuminate\Foundation\Application
サービスコンテナの元であるcontainerクラスを継承しこの中にbindやsingletonなどはいってる
<?php class Application extends Container implements ApplicationContract, CachesConfiguration, CachesRoutes, HttpKernelInterface { /** * Register all of the configured providers. * * @return void */ public function registerConfiguredProviders() { $providers = Collection::make($this->make('config')->get('app.providers')) ->partition(function ($provider) { return strpos($provider, 'Illuminate\\') === 0; }); $providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]); (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath())) ->load($providers->collapse()->toArray()); }
->get('app.providers'))
でconfig/app.php
のなかのproviders
を読み込んでいる
<?php '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,
ためしに暗号機能のサービスプロバイダIlluminate\Encryption\EncryptionServiceProvider::class,
の中をみてくと、registerメソッド内にサービスコンテナに登録する処理が書かれてる
$this->app->singleton('encrypter',
でencrypterという名前でサービスコンテナに登録している
<?php namespace Illuminate\Encryption; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Str; use Laravel\SerializableClosure\SerializableClosure; use Opis\Closure\SerializableClosure as OpisSerializableClosure; class EncryptionServiceProvider extends ServiceProvider { /** * Register the service provider. * * @return void */ public function register() { $this->registerEncrypter(); $this->registerOpisSecurityKey(); $this->registerSerializableClosureSecurityKey(); } /** * Register the encrypter. * * @return void */ protected function registerEncrypter() { $this->app->singleton('encrypter', function ($app) { $config = $app->make('config')->get('app'); return new Encrypter($this->parseKey($config), $config['cipher']); }); }
サービスプロバイダを使ってみる
サービスプロバイダのファイルを作成して、config/app.php
に書いてあげるとサービスコンテナの中に登録されて、
それをコントローラーからmakeでいつでも使えるようにする流れ
registerのほうにサービスコンテナに登録する処理を書く→bind
で登録
$ php artisan make:provider SampleServiceProvider
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; class SampleServiceProvider extends ServiceProvider { /** * Register services. * * @return void */ public function register() { app()->bind('serviceProviderTest', function(){ return 'サービスプロバイダテスト'; }); } /** * Bootstrap services. * * @return void */ public function boot() { // } }
SampleServiceProviderを起動するときに読み込むためにapp/config.php
のproviders
に記述する
<?php /* * Application Service Providers... */ App\Providers\AppServiceProvider::class, App\Providers\AuthServiceProvider::class, // App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, App\Providers\SampleServiceProvider::class,
サービスコンテナに登録したものを実際に表示する。
Controllers/LifeCycleTestController.php
でのapp()->make('serviceProviderTest');
でサービスコンテナから取り出しDDで表示させる
<?php class LifeCycleTestController extends Controller { // public function showServiceProviderTest() { $encrypt = app()->make('encrypter'); $password = $encrypt->encrypt('password'); $sample = app()->make('serviceProviderTest'); dd($sample, $password, $encrypt->decrypt($password)); }
3. HttpKernelインスタンス作成
→Karnelクラスをみていくと$middleware
,$middlewareGroup
,$routeMiddleware
の3つが設定されてる
$routeMiddleware
にアクセスした際に認証されてるか確認する仕組み→AUTHが設定されてる
マルチログインだと$routeMiddleware
の
'auth' => \App\Http\Middleware\Authenticate::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
辺りが絡んでくる。
<?php namespace App\Http; use Illuminate\Foundation\Http\Kernel as HttpKernel; class Kernel extends HttpKernel { /** * The application's global HTTP middleware stack. * * These middleware are run during every request to your application. * * @var array<int, class-string|string> */ protected $middleware = [ // \App\Http\Middleware\TrustHosts::class, \App\Http\Middleware\TrustProxies::class, \Fruitcake\Cors\HandleCors::class, \App\Http\Middleware\PreventRequestsDuringMaintenance::class, \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, \App\Http\Middleware\TrimStrings::class, \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, ]; /** * The application's route middleware groups. * * @var array<string, array<int, class-string|string>> */ 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' => [ // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, 'throttle:api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], ]; /** * The application's route middleware. * * These middleware may be assigned to groups or used individually. * * @var array<string, class-string|string> */ protected $routeMiddleware = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, ]; }
use Illuminate\Foundation\Http\Kernel as HttpKernel;
のIlluminate\Foundation\Http\Kernel
を見ていく。
HTTPカーネルのhandle
メソッドはリクエストを受け取ってレスポンスを帰す→カーネルをアプリケーション全体で表す大きなブラックボックスと考える
HTTPリクエストを取り込み、HTTPレスポンスを返す。
4.Requestインスタンス作成、5. HttpKernelがリクエストを処理してResponse取得
<?php /** * 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 (Throwable $e) { $this->reportException($e); $response = $this->renderException($request, $e); } $this->app['events']->dispatch( new RequestHandled($request, $response) ); return $response; }
sendRequestThroughRouter
メソッドへジャンプ
<?php /** * 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()); }
$this->app->instance('request', $request);
でサービスコンテナを経由して必要なクラスをもってきて、returnでRouterに渡す。
dispatchToRouter
メソッドへジャンプ
6.レスポンス送信
findRouteでルーティングを探してきてコントローラへ渡す
<?php /** * Dispatch the request to a route and return the response. * * @param \Illuminate\Http\Request $request * @return \Symfony\Component\HttpFoundation\Response */ public function dispatchToRoute(Request $request) { return $this->runRoute($request, $this->findRoute($request)); }
7. terminate()
再びindex.php
に戻ると
サービスコンテナに登録したKernelをmakeでとってきて変数$Kernelにいれて、
$responseとして$kernelのなかの$handleメソッドでリクエストを設定し、レスポンスが帰ってきたらterminateでどちらとも削除する
<?php require __DIR__.'/../vendor/autoload.php'; $app = require_once __DIR__.'/../bootstrap/app.php'; $kernel = $app->make(Kernel::class); $response = $kernel->handle( $request = Request::capture() )->send(); $kernel->terminate($request, $response);