導入
なし崩し的に導入した laravel framework 4.2 で管理画面を作りたかったのでコピペしつつ書いてみた。
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);
}
結果
ファッ!?
何故かログインに失敗する。
laravelのソースコードを読んでみる
larave/laravel GitHub
laravel/framework GitHub
Auth::attempt()
の実体を求めてfunction attempt
で検索すると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
public function __call($method, $parameters)
{
return call_user_func_array(array($this->driver(), $method), $parameters);
}
出たか__call
!
Auth::attempt($credentials)
がAuthManager->driver()->attempt($credentials)
に変換されている。
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];
}
public function getDefaultDriver()
{
return $this->app['config']['auth.driver'];
}
'driver' => 'eloquent',
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()
が呼ばれている。魔法使い過ぎヤバイ。
public function createEloquentDriver()
{
$provider = $this->createEloquentProvider();
return new Guard($provider, $this->app['session.store']);
}
function attempt
の検索で出てきたGuard
さんここで出番。
こういう繋がりでAuth::attempt()
からGuard::attempt()
が呼ばれている。
もう一度見直してみる。
Guardさんのprovider
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;
}
protected function hasValidCredentials($user, $credentials)
{
return ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
}
DBアクセスが発生するとしたらprovider
に頼んでるあたりだ。
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
で生成してた!うっ頭が・・・もう・・。
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
だった!
魔境は脱したか・・?
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();
}
public function createModel()
{
$class = '\\'.ltrim($this->model, '\\');
return new $class;
}
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時の引数で与えられている。
public function __construct(HasherInterface $hasher, $model)
{
$this->model = $model;
$this->hasher = $hasher;
}
戻って見直すと
protected function createEloquentProvider()
{
$model = $this->app['config']['auth.model'];
return new EloquentUserProvider($this->app['hash'], $model);
}
さて、
'model' => 'User',
$model
は"User"になるのでnew User
になるっぽい。
クエリ生成してfirst()
のあたりはぱっと見は問題なさそう。
$this->hasher->check()
はGuard->app['hash']->check()
になるっぽい?
'hash' => 'Illuminate\Hashing\HasherInterface',
アレ?Interfaceで実体が無い・・。
仕方ないからcheck()
で検索してみようか。
public function check($value, $hashedValue, array $options = array())
{
return password_verify($value, $hashedValue);
}
実体ぽいのが居た。
BcryptHasher
がどうやって実体化しているのかはさっぱり分からないのだけれども、HashServiceProvider->register()
がどっかから呼ばれているらしい。
HashServiceProvider
が直接呼ばれているソースは無いので、Workbench
の{{name}}ServiceProvider
あたりから呼ばれているのだろうか・・さっぱりわからない。
検証
/**
* 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;
を書いてログインしてみた。
行けた!ここか!こいつか!
対話式で確認してみる
% 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 5 >= 5.5.0)
password_verify — パスワードがハッシュにマッチするかどうかを調べる
% 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
あ・・・はい。
教訓
バージョンと名のつくものは依存性に気をつけましょう。(おしまい)