バージョンはよく確認したほうがいい


## 導入

なし崩し的に導入した laravel framework 4.2 で管理画面を作りたかったのでコピペしつつ書いてみた。 

```php:app/controllers/LoginController.php
  public function postLogin()
  {
    $inputs = Input::only('name', 'password');
    $result = $this->validateLogin($inputs);
    if ($result->fails()) {
      return Redirect::back()
        ->withErrors($result)
        ->withInput();
    }   

    $credentials = array(
      'name' => Input::get('name'),
      'password' => Input::get('password'),
    );  
    if (Auth::attempt($credentials)) {
      return 'loginned';
    }   

    $data = Input::only('name');
    $data['flash'] = 'Invalid name or password';
    return View::make('admin.login', $data);
  }
```

### 結果

![569f88286c5e7aaea379f827a3ed64bc.png](https://qiita-image-store.s3.amazonaws.com/0/36244/c33b9611-4306-abd9-099c-aacc8dbdbd25.png)

ファッ！？
何故かログインに失敗する。

## laravelのソースコードを読んでみる

[larave/laravel GitHub](https://github.com/laravel/laravel)
[laravel/framework GitHub](https://github.com/laravel/framework)

`Auth::attempt()`の実体を求めて`function attempt`で検索すると`Illuminate/Auth/Guard.php`が引っかかる。

```php:Illuminate/Auth/Guard.php
	public function attempt(array $credentials = array(), $remember = false, $login = true)
	{
		$this->fireAttemptEvent($credentials, $remember, $login);
		$this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
		// If an implementation of UserInterface was returned, we'll ask the provider
		// to validate the user against the given credentials, and if they are in
		// fact valid we'll log the users into the application and return true.
		if ($this->hasValidCredentials($user, $credentials))
		{
			if ($login) $this->login($user, $remember);
			return true;
		}
		return false;
	}
```

あっヤバイ・・・わかんない。
そもそも呼び出したのは`Auth::attempt()`なのに`Guard`クラスになってる。
こいつどうやって実体化してるの・・苦しい・・。

## laravelのソースコードを最初から読んでみる

困った時の最初から読み。
まあ手続き型のプログラムだし最初から読めば分かるでしょ。

 - `public/.htaccss`
  - 基本。他のフレームワークと同じ。
 - `public/index.php`
  - `bootstrap/autoload.php`を呼んで`bootstrap/start.php`で`$app`を生成して `$app->run()`
 - `bootstrap/autoload.php`
  - ああなんかちょっともう吐きそう・・。
  - `vendor/autoload.php`
      - `vendor/composer/autoroad_real.php`
          - うわぁ・・・。知らんけどautoloadしてるんやろはいはいわかったわかった次いこ。
   - `Illuminate\Support\ClassLoader::register();`
     - なんか重要そうなので探してみる。
     - `Illuminate/Support/ClassLoader.php`
         - 知らんけどクラス呼ぶときのrequireの手間とかあるあるエラーとか防いでそう。
         - この時点で色んなクラスが登録されてるのか良くわかんない。
 - `bootstrap/start.php`
   - `$app`は`new Illuminate\Foundation\Application`だ！
   - あと`Illuminate/Foundation/start.php`も呼ばれてる。

という感じでframework側へ

## frameworkのソースコードを読んでみる

「クソっ俺は管理画面を作りたいんであってframeworkはどうでもいいんだよ！」という心の声を押し殺しながら・・・

- `Illuminate/Foundation/Application.php`
  - `new`から呼ばれているので普通に`__construct()`が呼ばれるんでしょう。phpの仕様わからないけど。
  - `extends Container` (`Illuminate/Container/Container.php`) はなんとなくクラスを配列っぽくアクセスしたり管理してるっぽい気がした。難しい。

- `Illuminate/Foundation/start.php`
  - `$app->instance()`がやたら呼ばれてる。このあたりでいよいよ基礎的な要素が実体化されつつあるようなないような。
  - `$app->registerCoreContainerAliases();`
    - クラス呼び出しのエイリアスを貼ってるっぽい。
    - `Auth::attempt()`の`Auth`もすごくこれっぽい。結局`AuthManager::attempt()`が呼ばれてるっぽい。

## 魔境へ

- `Illuminate/Auth/AuthManager.php`
  - `attempt()`が無いので仕方なく`extends Manager`へ
- `Illuminate/Support/Manager.php`

```php:Illuminate/Support/Manager.php
public function __call($method, $parameters)
	{
		return call_user_func_array(array($this->driver(), $method), $parameters);
	}
```
出たか`__call`！
`Auth::attempt($credentials)`が`AuthManager->driver()->attempt($credentials)`に変換されている。

```php:Illuminate/Support/Manager.php
public function driver($driver = null)
	{
		$driver = $driver ?: $this->getDefaultDriver();
		// If the given driver has not been created before, we will create the instances
		// here and cache it so we can return it next time very quickly. If there is
		// already a driver created by this name, we'll just return that instance.
		if ( ! isset($this->drivers[$driver]))
		{
			$this->drivers[$driver] = $this->createDriver($driver);
		}
		return $this->drivers[$driver];
	}
```

```php:Illuminate/Auth/AuthManager.php
public function getDefaultDriver()
	{
		return $this->app['config']['auth.driver'];
	}
```

```php:app/config/auth.php
  'driver' => 'eloquent',
```

```php:Illuminate/Support/Manager.php
protected function createDriver($driver)
	{
		$method = 'create'.ucfirst($driver).'Driver';
		// We'll check to see if a creator method exists for the given driver. If not we
		// will check for a custom driver creator, which allows developers to create
		// drivers using their own customized driver creator Closure to create it.
		if (isset($this->customCreators[$driver]))
		{
			return $this->callCustomCreator($driver);
		}
		elseif (method_exists($this, $method))
		{
			return $this->$method();
		}
		throw new \InvalidArgumentException("Driver [$driver] not supported.");
	}
```

`createDriver("eloquent")`から`createEloquentDriver()`が呼ばれている。魔法使い過ぎヤバイ。

```php:Illuminate/Auth/AuthManager.php
	public function createEloquentDriver()
	{
		$provider = $this->createEloquentProvider();
		return new Guard($provider, $this->app['session.store']);
	}
```
`function attempt`の検索で出てきた`Guard`さんここで出番。
こういう繋がりで`Auth::attempt()`から`Guard::attempt()`が呼ばれている。
もう一度見直してみる。

## Guardさんのprovider

```php:Illuminate/Auth/Guard.php
	public function attempt(array $credentials = array(), $remember = false, $login = true)
	{
		$this->fireAttemptEvent($credentials, $remember, $login);
		$this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
		// If an implementation of UserInterface was returned, we'll ask the provider
		// to validate the user against the given credentials, and if they are in
		// fact valid we'll log the users into the application and return true.
		if ($this->hasValidCredentials($user, $credentials))
		{
			if ($login) $this->login($user, $remember);
			return true;
		}
		return false;
	}
```

```php:Illuminate/Auth/Guard.php
protected function hasValidCredentials($user, $credentials)
	{
		return ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
	}
```

DBアクセスが発生するとしたら`provider`に頼んでるあたりだ。

```php:Illuminate/Auth/Guard.php
public function __construct(UserProviderInterface $provider,
								SessionStore $session,
								Request $request = null)
	{
		$this->session = $session;
		$this->request = $request;
		$this->provider = $provider;
	}
```
Guardさんの`provider`はnew時の第1引数らしい。
確かにさっき`AuthManager.php`で生成してた！うっ頭が・・・もう・・。

```php:Illuminate/Auth/Guard.php
protected function createEloquentProvider()
	{
		$model = $this->app['config']['auth.model'];
		return new EloquentUserProvider($this->app['hash'], $model);
	}
```
ここだ！
整理すると`AuthManager->driver()->attempt($credentials)`の中のDB照合してそうな処理である

`$this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);`
`$this->provider->validateCredentials($user, $credentials);`

の`provider`は`EloquentUserProvider`だった！

## 魔境は脱したか・・？

```php:Illuminate/Auth/EloquentUserProvider.php
    public function retrieveByCredentials(array $credentials)
	{
		// First we will add each credential element to the query as a where clause.
		// Then we can execute the query and, if we found a user, return it in a
		// Eloquent User "model" that will be utilized by the Guard instances.
		$query = $this->createModel()->newQuery();
		foreach ($credentials as $key => $value)
		{
			if ( ! str_contains($key, 'password')) $query->where($key, $value);
		}
		return $query->first();
	}
```

```php:Illuminate/Auth/EloquentUserProvider.php
public function createModel()
	{
		$class = '\\'.ltrim($this->model, '\\');
		return new $class;
	}
```

```php:Illuminate/Auth/EloquentUserProvider.php
public function validateCredentials(UserInterface $user, array $credentials)
	{
		$plain = $credentials['password'];
		return $this->hasher->check($plain, $user->getAuthPassword());
	}
```

- `createModel()`は`$this->model`で与えられたクラスをnewしている。
- `validateCredentials()`の方は`$this->hasher->check()`を呼び出している。
- 両者ともに次のようにnew時の引数で与えられている。

```php:Illuminate/Auth/EloquentUserProvider.php
	public function __construct(HasherInterface $hasher, $model)
	{
		$this->model = $model;
		$this->hasher = $hasher;
	}
```

戻って見直すと

```php:Illuminate/Auth/Guard.php
protected function createEloquentProvider()
	{
		$model = $this->app['config']['auth.model'];
		return new EloquentUserProvider($this->app['hash'], $model);
	}
```

さて、

```php:app/config/auth.php
  'model' => 'User',
```

`$model`は"User"になるので`new User`になるっぽい。
クエリ生成して`first()`のあたりはぱっと見は問題なさそう。

`$this->hasher->check()`は`Guard->app['hash']->check()`になるっぽい？

```php:Illuminate/Foundation/Application.php
'hash'           => 'Illuminate\Hashing\HasherInterface',
```

アレ？Interfaceで実体が無い・・。
仕方ないから`check()`で検索してみようか。

```php:Illuminate/Hashing/BcryptHasher.php
public function check($value, $hashedValue, array $options = array())
	{
		return password_verify($value, $hashedValue);
	}
```

実体ぽいのが居た。
`BcryptHasher`がどうやって実体化しているのかはさっぱり分からないのだけれども、`HashServiceProvider->register()`がどっかから呼ばれているらしい。
`HashServiceProvider`が直接呼ばれているソースは無いので、`Workbench`の`{{name}}ServiceProvider`あたりから呼ばれているのだろうか・・さっぱりわからない。

### 検証

```php:Illuminate/Hashing/BcryptHasher.php
	/**
	 * Check the given plain value against a hash.
	 *
	 * @param  string  $value
	 * @param  string  $hashedValue
	 * @param  array   $options
	 * @return bool
	 */
	public function check($value, $hashedValue, array $options = array())
	{
		return password_verify($value, $hashedValue);
	}
```

さてこいつがtrueを返せばきっとログインに成功するだろう。
試しに`return true;`を書いてログインしてみた。

![5ad41dd745c7ba4cd30e2d48ee3237fb.png](https://qiita-image-store.s3.amazonaws.com/0/36244/0f231431-1cc0-199b-88b0-2e3d848c300f.png)

行けた！ここか！こいつか！
対話式で確認してみる

```shell
% php -a                                                                                               (git)-[develop]
Interactive shell

php > password_verify('password', 'XXXXXXXXXXXXXXXXXXXXXXX');
PHP Fatal error:  Call to undefined function password_verify() in php shell code on line 1
php > exit
```

undefined function！？
ググってみる。

[password_verify - php.net](http://php.net/manual/ja/function.password-verify.php)

```text
password_verify
(PHP 5 >= 5.5.0)
password_verify — パスワードがハッシュにマッチするかどうかを調べる
```

```shell
% php -v
PHP 5.4.35 (cli) (built: Nov 21 2014 00:43:27) 
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2014 Zend Technologies
```

あ・・・はい。

### 教訓

バージョンと名のつくものは依存性に気をつけましょう。（おしまい）