質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Laravel 5

Laravel 5は、PHPフレームワークLaravelの最新バージョンで、2014年11月に発表予定です。ディレクトリ構造がが現行版より大幅に変更されるほか、メソッドインジェクションやFormRequestの利用が可能になります。

Q&A

2回答

15088閲覧

【Laravel】auth()->user() のuser()はどこからきているのか

RogerFederer

総合スコア19

Laravel 5

Laravel 5は、PHPフレームワークLaravelの最新バージョンで、2014年11月に発表予定です。ディレクトリ構造がが現行版より大幅に変更されるほか、メソッドインジェクションやFormRequestの利用が可能になります。

0グッド

1クリップ

投稿2019/01/29 10:50

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ページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

デバッガ等で調べればなんのクラスかは簡単に調べられますが、過程を知りたいと言うことなので追いかけてみます。かなり道のりは遠いので頑張って読んでください。

まずapp()関数についてマニュアルを見ましょう。

app()

app関数は、サービスコンテナのインスタンスを返します。

$container = app();

コンテナにより依存解決する、クラス名かインターフェイス名を渡すこともできます。

$api = app('HelpSpot\API');

この場合はパラメタAuthFactory::classを与えてますので、このクラス名またはインタフェース名をサービスコンテナで依存解決します。「サービスコンテナによる依存解決」をざっくりいうと、あらかじめあるクラスのインスタンスを生成する方法に名前を付けて登録して管理するのがサービスコンテナ、サービスコンテナに名前を与えてそのクラスのインスタンスを作ってもらうのが依存解決です。名前はなんでもいいのですが、普通はクラス名かインタフェース名を使います。また特によく使われる物には短い名前が特別に用意されていることもあります。

サービスコンテナについて詳しくはマニュアルの「サービスコンテナ」を参照してください。

AuthFactoryhelper.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'であることがわかります。

さて、パラメタwebresolveを呼ぶとどうなるでしょうか。

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

crhg

総合スコア1175

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

RogerFederer

2019/02/04 01:13

詳細なご回答ありがとうございました! 重ねて質問なのですが、こういった中身の実装というのは使っているうちに自然と理解できてくるものなのでしょうか? Laravelを使って簡単な掲示板を作れるようにはなったのですが、コードを書いて取り出せる結果は分かっても過程がよくわからないので、全く「身についている」感覚になりません。 なので、Laravelソースコードを読み込まなきゃいけないのか?そもそもPHPの知識が足りない(=Laravelに手を出してはいけない)のか?それとも、ガンガンLaravelを使ってコードを書きまくれば感覚として分かってくるものなのか?結局何をすればいいのか?というのがわかりません。。
guest

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のインターフェースを実装した別のクラスの関数になる

ということなります。

まとめ

  1. サービスコンテナの設定通りに、\Illuminate\Contracts\Auth\Factoryに対して\Illuminate\Auth\AuthManagerが取り出される
  2. \Illuminate\Auth\AuthManager::user()はデフォルトのGuardにマジックメソッドにより委譲される
  3. Guard::user()が呼び出される

サービスコンテナによるオブジェクトの解決とマジックメソッドの多用は双方Laravelの特徴です。
この2つの合わせ技は慣れないとかなり処理を追いにくいですね。

投稿2019/01/31 02:05

編集2019/01/31 02:11
n_1215

総合スコア40

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問