Laravel
1$user = auth()->user();
これでUserインスタンスが取得できるという結果はわかるのですが、過程がわかりません。
auth()という関数は、helpers.phpに以下あるように綿々と処理が連鎖していく様子はわかるのですが、
Laravel
1function auth($guard = null) 2 { 3 if (is_null($guard)) { 4 return app(AuthFactory::class); 5 } 6 7 return app(AuthFactory::class)->guard($guard); 8 } 9 10↓ 更にappメソッドが呼ばれてる 11 12function app($abstract = null, array $parameters = []) 13 { 14 if (is_null($abstract)) { 15 return Container::getInstance(); 16 } 17 18 return Container::getInstance()->make($abstract, $parameters); 19 } 20 21・・・・・ 22
auth()にチェインしている user() という関数はいったいどこで定義されているものなのでしょうか。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答2件
0
デバッガ等で調べればなんのクラスかは簡単に調べられますが、過程を知りたいと言うことなので追いかけてみます。かなり道のりは遠いので頑張って読んでください。
まずapp()
関数についてマニュアルを見ましょう。
app()
app関数は、サービスコンテナのインスタンスを返します。
$container = app();
コンテナにより依存解決する、クラス名かインターフェイス名を渡すこともできます。
$api = app('HelpSpot\API');
この場合はパラメタAuthFactory::class
を与えてますので、このクラス名またはインタフェース名をサービスコンテナで依存解決します。「サービスコンテナによる依存解決」をざっくりいうと、あらかじめあるクラスのインスタンスを生成する方法に名前を付けて登録して管理するのがサービスコンテナ、サービスコンテナに名前を与えてそのクラスのインスタンスを作ってもらうのが依存解決です。名前はなんでもいいのですが、普通はクラス名かインタフェース名を使います。また特によく使われる物には短い名前が特別に用意されていることもあります。
サービスコンテナについて詳しくはマニュアルの「サービスコンテナ」を参照してください。
AuthFactory
はhelper.php
の冒頭で以下のようにuseされていますので実際にはIlluminate\Contracts\Auth\Factory
を表します。
php
1use Illuminate\Contracts\Auth\Factory as AuthFactory;
さて、Illuminate\Contracts\Auth\Factory::class
は'Illuminate\Contracts\Auth\Factory'
という文字列に展開されますが、この名前で登録されているクラスはなにかというのが問題になります。
検索してみると\Illuminate\Foundation\Application::registerCoreContainerAliases
という関数内に見つかります。
php
1 'auth' => [\Illuminate\Auth\AuthManager::class, \Illuminate\Contracts\Auth\Factory::class],
registerCoreContainerAliases
はその名の通り登録名に別名を用意する関数で、'Illuminate\Contracts\Auth\Factory'
は'auth'
という名前の別名であるということになります。
では'auth'
は何になるのでしょうか。これは先ほど言ったよく使われるので短い名前で用意しているものの一つで、マニュアルの「ファサード」の「ファサードクラス一覧」に一覧表があります。この表の「サービスコンテナ結合」カラムが用意されている短い名前で、ここからauthを探すと\Illuminate\Auth\AuthManager
クラスであることがわかります。
さて、\Illuminate\Auth\AuthManager
からuser
関数を探すと...ありません。でもよくみると__call
関数は用意されています。
php
1 /** 2 * Dynamically call the default driver instance. 3 * 4 * @param string $method 5 * @param array $parameters 6 * @return mixed 7 */ 8 public function __call($method, $parameters) 9 { 10 return $this->guard()->{$method}(...$parameters); 11 }
つまり$this->guard()
が返したなにかのクラスのインスタンスに対してuser()
を呼ぶわけです。
ということで$this->guard()
を見るとこうなっています。
php
1 /** 2 * Attempt to get the guard from the local cache. 3 * 4 * @param string $name 5 * @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard 6 */ 7 public function guard($name = null) 8 { 9 $name = $name ?: $this->getDefaultDriver(); 10 11 return $this->guards[$name] ?? $this->guards[$name] = $this->resolve($name); 12 }
引数を省略した場合は$this->getDefaultDriver
が返した値を$name
として`$this->resolve($name)の値を返します。
getDefaultDriverは簡単で
php
1 public function getDefaultDriver() 2 { 3 return $this->app['config']['auth.defaults.guard']; 4 }
ですからconfig/app.php
の中を見ると(独自に書き換えていなければ)'web'
であることがわかります。
さて、パラメタweb
でresolve
を呼ぶとどうなるでしょうか。
php
1 /** 2 * Resolve the given guard. 3 * 4 * @param string $name 5 * @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard 6 * 7 * @throws \InvalidArgumentException 8 */ 9 protected function resolve($name) 10 { 11 $config = $this->getConfig($name); 12 13 if (is_null($config)) { 14 throw new InvalidArgumentException("Auth guard [{$name}] is not defined."); 15 } 16 17 if (isset($this->customCreators[$config['driver']])) { 18 return $this->callCustomCreator($name, $config); 19 } 20 21 $driverMethod = 'create'.ucfirst($config['driver']).'Driver'; 22 23 if (method_exists($this, $driverMethod)) { 24 return $this->{$driverMethod}($name, $config); 25 } 26 27 throw new InvalidArgumentException("Auth driver [{$config['driver']}] for guard [{$name}] is not defined."); 28 } 29
getConfigは簡単です。auth.guards.web
はさっきのconfig/app.php
をみて[ 'driver' => 'session', 'provider' => 'users', ]
です。
php
1 protected function getConfig($name) 2 { 3 return $this->app['config']["auth.guards.{$name}"]; 4 }
カスタムドライバの登録はしてないとして結局呼ばれるのは$driverMethod = 'create'.ucfirst($config['driver']).'Driver';
で求められた名前の関数です。driverの値はsessionなのでcreateSessionDriver
関数ということになります。
ではcreateSessionDriver
関数を見てみましょう。
php
1 /** 2 * Create a session based authentication guard. 3 * 4 * @param string $name 5 * @param array $config 6 * @return \Illuminate\Auth\SessionGuard 7 */ 8 public function createSessionDriver($name, $config) 9 { 10 $provider = $this->createUserProvider($config['provider'] ?? null); 11 12 $guard = new SessionGuard($name, $provider, $this->app['session.store']); 13 14 // When using the remember me functionality of the authentication services we 15 // will need to be set the encryption instance of the guard, which allows 16 // secure, encrypted cookie values to get generated for those cookies. 17 if (method_exists($guard, 'setCookieJar')) { 18 $guard->setCookieJar($this->app['cookie']); 19 } 20 21 if (method_exists($guard, 'setDispatcher')) { 22 $guard->setDispatcher($this->app['events']); 23 } 24 25 if (method_exists($guard, 'setRequest')) { 26 $guard->setRequest($this->app->refresh('request', $guard, 'setRequest')); 27 } 28 29 return $guard; 30 }
作成したあとの初期化処理が入っていてちょっと長いですが、返されるのは\Illuminate\Auth\SessionGuard
クラスのインスタンスです。このクラスのuser()
関数が探していたものです。お疲れ様でした。
投稿2019/01/31 03:31
総合スコア1177
0
処理を追いながら解説します。
サービスコンテナが絡むので、その辺りの理解があると理解しやすいと思います。
参考: サービスコンテナ
※ 以下、Laravel5.5のソースを参考にしています。最新版だと少し違う可能性がありますが、流れは概ね同じはずです。
サービスコンテナ
auth()->user()を呼び出すと、ヘルパ関数app()の下の分岐に入ります。
return Container::getInstance()->make($abstract, $parameters); // ここで、$abstract = AuthFactory::class // AuthFactoryはhelpers.phpファイル上部のuseで定義されているエイリアス // クラスの実体は Illuminate\Contracts\Auth\Factory
Laravelのサービスコンテナ、\Illuminate\Container\Containerクラスのmake()というメソッドが呼ばれています。
このメソッドは、サービスコンテナに事前に登録されている規則に従ってオブジェクトを生成します。
\Illuminate\Container\Container::make()の第一引数は、コンテナに登録されているオブジェクトを呼び出すためのキーとなる文字列です。
インターフェース名やクラス名、識別用の文字列(ex. 'auth', 'config'など)のいずれかですね。
コンテナから生成されるオブジェクト
今回の場合は、\Illuminate\Contracts\Auth\Factoryというインターフェース名が引数に与えられていますので、\Illuminate\Contracts\Auth\Factoryというキーに紐つけられたクラスのインスタンスが生成されます。
Laravelのデフォルトの状態では、\Illuminate\Contracts\Auth\Factoryインターフェースを実装したIlluminate\Auth\AuthManagerクラスが紐つけられています。
この紐付けはServiceProviderの仕組みで行われています。詳細処理の解説は省略しますが、\Illuminate\Auth\AuthServiceProviderや\Illuminate\Foundation\Application::registerCoreContainerAliases()あたりですかね。
マジックメソッドによる処理の移譲
ここまででauth()は\Illuminate\Auth\AuthManagerのインスタンスを返すことがわかりました。
つまり、\Illuminate\Auth\AuthManager::user()が呼ばれています。
このメソッドは直接定義されていないので、マジックメソッドの__call()が呼ばれます。
参考: PHP: オーバーロード
__call()の中で、Guardクラスのインスタンスの同名メソッドに処理が移譲されています。
public function __call($method, $parameters) { return $this->guard()->{$method}(...$parameters); }
guard()メソッドの実装を見ると、Illuminate\Contracts\Auth\Guardインターフェースの実装クラスのうちデフォルトのものが呼ばれる形になります。
public function guard($name = null) { $name = $name ?: $this->getDefaultDriver(); return $this->guards[$name] ?? $this->guards[$name] = $this->resolve($name); }
初期設定では認証のドライバーは'session'になっているので、
処理を追うと
AuthManager::guard() → AuthManger::resolve() → AuthManager::createSessionDriver()
の流れで\Illuminate\Auth\SessionGuardが生成されていることがわかります。
以上から、ご質問への答えとしては
- user()という関数の実体は\Illuminate\Auth\SessionGuard::user()である
- ただし設定によってはGuardのインターフェースを実装した別のクラスの関数になる
ということなります。
まとめ
- サービスコンテナの設定通りに、\Illuminate\Contracts\Auth\Factoryに対して\Illuminate\Auth\AuthManagerが取り出される
- \Illuminate\Auth\AuthManager::user()はデフォルトのGuardにマジックメソッドにより委譲される
- Guard::user()が呼び出される
サービスコンテナによるオブジェクトの解決とマジックメソッドの多用は双方Laravelの特徴です。
この2つの合わせ技は慣れないとかなり処理を追いにくいですね。
投稿2019/01/31 02:05
編集2019/01/31 02:11総合スコア40
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/02/04 01:13