FuelPHP 1.7 のSimpleAuthのEmail必須及びユニークチェックを外す

# 1. 前提
1. FuelPHP 1.7
2. SimpleAuthを利用
3. ユーザ作成をSimpleAuth にまかせている。

# 2. どう実装しよう？
1. authパッケージ内を直接編集
2. authパッケージを拡張してカスタムクラスを利用

２．の方が後々楽そう。

# 3. そもそもなぜEmailの必須を外すのか？
理由はいろいろとあると思いますが、社内システムを作るにあたり、登録予定のユーザの中に社内メールの割当がないとか、特定ユーザ郡はメールアドレスをメーリスにして登録メールアドレスが重複してしまうとかでしょうか？

# 4. 用意するファイル。
正しいかどうかわからないのが悔やまれるところですが、下記に配置する。
`packages¥customauth`
ディレクトリ構造はこんな感じ
必要なファイルをauthパッケージからコピー

```コピー元
packages¥auth¥classes¥login¥simpleauth.php
packages¥auth¥bootstrap.php
```

コピー後のディレクトリ構造

``` dir
- packages
	┗ customauth
		┗ classes
			┗ auth
				┗ login
					simpleauth.php
			bootstrap.php
```

# 5. 中身を見てみよう。
コピーしたファイルを舐めていきます。

## ユーザ作成メソッド
`packages¥customauth¥classes¥login¥simpleauth.php`内の・・・
この辺があやしい・・・というかまんまだ！

編集前

``` php

	/**
	 * Create new user
	 *
	 * @param   string
	 * @param   string
	 * @param   string  must contain valid email address
	 * @param   int     group id
	 * @param   Array
	 * @return  bool
	 */
	public function create_user($username, $password, $email, $group = 1, Array $profile_fields = array())
	{
		$password = trim($password);
		$email = filter_var(trim($email), FILTER_VALIDATE_EMAIL);

		if (empty($username) or empty($password) or empty($email))
		{
			throw new \SimpleUserUpdateException('Username, password or email address is not given, or email address is invalid', 1);
		}

		$same_users = \DB::select_array(\Config::get('simpleauth.table_columns', array('*')))
			->where('username', '=', $username)
			->or_where('email', '=', $email)
			->from(\Config::get('simpleauth.table_name'))
			->execute(\Config::get('simpleauth.db_connection'));

		if ($same_users->count() > 0)
		{
			if (in_array(strtolower($email), array_map('strtolower', $same_users->current())))
			{
				throw new \SimpleUserUpdateException('Email address already exists', 2);
			}
			else
			{
				throw new \SimpleUserUpdateException('Username already exists', 3);
			}
		}

		$user = array(
			'username'        => (string) $username,
			'password'        => $this->hash_password((string) $password),
			'email'           => $email,
			'group'           => (int) $group,
			'profile_fields'  => serialize($profile_fields),
			'last_login'      => 0,
			'login_hash'      => '',
			'created_at'      => \Date::forge()->get_timestamp(),
		);
		$result = \DB::insert(\Config::get('simpleauth.table_name'))
			->set($user)
			->execute(\Config::get('simpleauth.db_connection'));

		return ($result[1] > 0) ? $result[0] : false;
	}


```

上記ソースを下記に改定。

``` php

	/**
	 * Create new user
	 *
	 * @param   string
	 * @param   string
	 * @param   string
	 * @param   int     group id
	 * @param   Array
	 * @return  bool
	 */
	public function create_user($username, $password, $email = null, $group = 1, Array $profile_fields = array())
	{
		$password = trim($password);

		if(!empty($email))
		{
			$email = filter_var(trim($email), FILTER_VALIDATE_EMAIL);
			if(empty($email))
			{
				throw new \SimpleUserUpdateException('emailの形式が不正です。', 1);
			}
		}

		if (empty($username) or empty($password))
		{
			throw new \SimpleUserUpdateException('Username, password is not given, or email address is invalid', 1);
		}

		$same_users = \DB::select_array(\Config::get('simpleauth.table_columns', array('*')))
			->where('username', '=', $username)
			->from(\Config::get('simpleauth.table_name'))
			->execute(\Config::get('simpleauth.db_connection'));

		if ($same_users->count() > 0)
		{
			throw new \SimpleUserUpdateException('Username already exists', 3);
		}

		$user = array(
			'username'        => (string) $username,
			'password'        => $this->hash_password((string) $password),
			'email'           => $email,
			'group'           => (int) $group,
			'profile_fields'  => serialize($profile_fields),
			'last_login'      => 0,
			'login_hash'      => '',
			'created_at'      => \Date::forge()->get_timestamp(),
		);
		$result = \DB::insert(\Config::get('simpleauth.table_name'))
			->set($user)
			->execute(\Config::get('simpleauth.db_connection'));

		return ($result[1] > 0) ? $result[0] : false;
	}


```

こんな感じ。

## ログインチェック
ログイン時にユーザの存在チェックをするメソッドを発見したのでこれもemailのチェックを外しておく。

``` 修正前php
	/**
	 * Check the user exists
	 *
	 * @return  bool
	 */
	public function validate_user($username_or_email = '', $password = '')
	{
		$username_or_email = trim($username_or_email) ?: trim(\Input::post(\Config::get('simpleauth.username_post_key', 'username')));
		$password = trim($password) ?: trim(\Input::post(\Config::get('simpleauth.password_post_key', 'password')));

		if (empty($username_or_email) or empty($password))
		{
			return false;
		}

		$password = $this->hash_password($password);
		$user = \DB::select_array(\Config::get('simpleauth.table_columns', array('*')))
			->where_open()
			->where('username', '=', $username_or_email)
			->or_where('email', '=', $username_or_email)
			->where_close()
			->where('password', '=', $password)
			->from(\Config::get('simpleauth.table_name'))
			->execute(\Config::get('simpleauth.db_connection'))->current();

		return $user ?: false;
	}
```

email周りをごっそり削除

``` 修正後php
	/**
	 * Check the user exists
	 *
	 * @return  bool
	 */
	public function validate_user($username = '', $password = '')
	{
		$username = trim($username) ?: trim(\Input::post(\Config::get('simpleauth.username_post_key', 'username')));
		$password = trim($password) ?: trim(\Input::post(\Config::get('simpleauth.password_post_key', 'password')));

		if (empty($username) or empty($password))
		{
			return false;
		}

		$password = $this->hash_password($password);
		$user = \DB::select_array(\Config::get('simpleauth.table_columns', array('*')))
			->where_open()
			->where('username', '=', $username)
			->where_close()
			->where('password', '=', $password)
			->from(\Config::get('simpleauth.table_name'))
			->execute(\Config::get('simpleauth.db_connection'))->current();

		return $user ?: false;
	}
```

## ログインメソッド
重要じゃないけど変数名に違和感ができてしまうので修正しておく。

``` 修正前php
	/**
	 * Login user
	 *
	 * @param   string
	 * @param   string
	 * @return  bool
	 */
	public function login($username_or_email = '', $password = '')
	{
		if ( ! ($this->user = $this->validate_user($username_or_email, $password)))
		{
			$this->user = \Config::get('simpleauth.guest_login', true) ? static::$guest_login : false;
			\Session::delete('username');
			\Session::delete('login_hash');
			return false;
		}

		// register so Auth::logout() can find us
		Auth::_register_verified($this);

		\Session::set('username', $this->user['username']);
		\Session::set('login_hash', $this->create_login_hash());
		\Session::instance()->rotate();
		return true;
	}

```

``` 修正後php
	/**
	 * Login user
	 *
	 * @param   string
	 * @param   string
	 * @return  bool
	 */
	public function login($username = '', $password = '')
	{
		if ( ! ($this->user = $this->validate_user($username, $password)))
		{
			$this->user = \Config::get('simpleauth.guest_login', true) ? static::$guest_login : false;
			\Session::delete('username');
			\Session::delete('login_hash');
			return false;
		}

		// register so Auth::logout() can find us
		Auth::_register_verified($this);

		\Session::set('username', $this->user['username']);
		\Session::set('login_hash', $this->create_login_hash());
		\Session::instance()->rotate();
		return true;
	}

```

## ユーザ情報更新メソッド
こいつもemailの重複チェックとかしてるので修正が必要。

``` 修正前php
	/**
	 * Update a user's properties
	 * Note: Username cannot be updated, to update password the old password must be passed as old_password
	 *
	 * @param   Array  properties to be updated including profile fields
	 * @param   string
	 * @return  bool
	 */
	public function update_user($values, $username = null)
	{
		$username = $username ?: $this->user['username'];
		$current_values = \DB::select_array(\Config::get('simpleauth.table_columns', array('*')))
			->where('username', '=', $username)
			->from(\Config::get('simpleauth.table_name'))
			->execute(\Config::get('simpleauth.db_connection'));

		if (empty($current_values))
		{
			throw new \SimpleUserUpdateException('Username not found', 4);
		}

		$update = array();
		if (array_key_exists('username', $values))
		{
			throw new \SimpleUserUpdateException('Username cannot be changed.', 5);
		}
		if (array_key_exists('password', $values))
		{
			if (empty($values['old_password'])
				or $current_values->get('password') != $this->hash_password(trim($values['old_password'])))
			{
				throw new \SimpleUserWrongPassword('Old password is invalid');
			}

			$password = trim(strval($values['password']));
			if ($password === '')
			{
				throw new \SimpleUserUpdateException('Password can\'t be empty.', 6);
			}
			$update['password'] = $this->hash_password($password);
			unset($values['password']);
		}
		if (array_key_exists('old_password', $values))
		{
			unset($values['old_password']);
		}
		if (array_key_exists('email', $values))
		{
			$email = filter_var(trim($values['email']), FILTER_VALIDATE_EMAIL);
			if ( ! $email)
			{
				throw new \SimpleUserUpdateException('Email address is not valid', 7);
			}
			$matches = \DB::select()
				->where('email', '=', $email)
				->where('id', '!=', $current_values[0]['id'])
				->from(\Config::get('simpleauth.table_name'))
				->execute(\Config::get('simpleauth.db_connection'));
			if (count($matches))
			{
				throw new \SimpleUserUpdateException('Email address is already in use', 11);
			}
			$update['email'] = $email;
			unset($values['email']);
		}
		if (array_key_exists('group', $values))
		{
			if (is_numeric($values['group']))
			{
				$update['group'] = (int) $values['group'];
			}
			unset($values['group']);
		}
		if ( ! empty($values))
		{
			$profile_fields = @unserialize($current_values->get('profile_fields')) ?: array();
			foreach ($values as $key => $val)
			{
				if ($val === null)
				{
					unset($profile_fields[$key]);
				}
				else
				{
					$profile_fields[$key] = $val;
				}
			}
			$update['profile_fields'] = serialize($profile_fields);
		}

		$update['updated_at'] = \Date::forge()->get_timestamp();

		$affected_rows = \DB::update(\Config::get('simpleauth.table_name'))
			->set($update)
			->where('username', '=', $username)
			->execute(\Config::get('simpleauth.db_connection'));

		// Refresh user
		if ($this->user['username'] == $username)
		{
			$this->user = \DB::select_array(\Config::get('simpleauth.table_columns', array('*')))
				->where('username', '=', $username)
				->from(\Config::get('simpleauth.table_name'))
				->execute(\Config::get('simpleauth.db_connection'))->current();
		}

		return $affected_rows > 0;
	}

```

``` 修正後php
	/**
	 * Update a user's properties
	 * Note: Username cannot be updated, to update password the old password must be passed as old_password
	 *
	 * @param   Array  properties to be updated including profile fields
	 * @param   string
	 * @return  bool
	 */
	public function update_user($values, $username = null)
	{
		$username = $username ?: $this->user['username'];
		$current_values = \DB::select_array(\Config::get('simpleauth.table_columns', array('*')))
			->where('username', '=', $username)
			->from(\Config::get('simpleauth.table_name'))
			->execute(\Config::get('simpleauth.db_connection'));

		if (empty($current_values))
		{
			throw new \SimpleUserUpdateException('Username not found', 4);
		}

		$update = array();
		if (array_key_exists('username', $values))
		{
			throw new \SimpleUserUpdateException('Username cannot be changed.', 5);
		}
		if (array_key_exists('password', $values))
		{
			if (empty($values['old_password'])
				or $current_values->get('password') != $this->hash_password(trim($values['old_password'])))
			{
				throw new \SimpleUserWrongPassword('Old password is invalid');
			}

			$password = trim(strval($values['password']));
			if ($password === '')
			{
				throw new \SimpleUserUpdateException('Password can\'t be empty.', 6);
			}
			$update['password'] = $this->hash_password($password);
			unset($values['password']);
		}
		if (array_key_exists('old_password', $values))
		{
			unset($values['old_password']);
		}
		if (array_key_exists('email', $values))
		{
			$email = filter_var(trim($values['email']), FILTER_VALIDATE_EMAIL);
			if ( ! $email)
			{
				throw new \SimpleUserUpdateException('Email address is not valid', 7);
			}
			$update['email'] = $email;
			unset($values['email']);
		}
		if (array_key_exists('group', $values))
		{
			if (is_numeric($values['group']))
			{
				$update['group'] = (int) $values['group'];
			}
			unset($values['group']);
		}
		if ( ! empty($values))
		{
			$profile_fields = @unserialize($current_values->get('profile_fields')) ?: array();
			foreach ($values as $key => $val)
			{
				if ($val === null)
				{
					unset($profile_fields[$key]);
				}
				else
				{
					$profile_fields[$key] = $val;
				}
			}
			$update['profile_fields'] = serialize($profile_fields);
		}

		$update['updated_at'] = \Date::forge()->get_timestamp();

		$affected_rows = \DB::update(\Config::get('simpleauth.table_name'))
			->set($update)
			->where('username', '=', $username)
			->execute(\Config::get('simpleauth.db_connection'));

		// Refresh user
		if ($this->user['username'] == $username)
		{
			$this->user = \DB::select_array(\Config::get('simpleauth.table_columns', array('*')))
				->where('username', '=', $username)
				->from(\Config::get('simpleauth.table_name'))
				->execute(\Config::get('simpleauth.db_connection'))->current();
		}

		return $affected_rows > 0;
	}

```

## namespaceの決定


``` 修正前php
namespace Auth;
```

``` 修正後php
namespace CustomAuth;
```

## class の継承元変更

``` 修正前php
class Auth_Login_Simpleauth extends \Auth_Login_Driver
```

``` 修正後php
class Auth_Login_Simpleauth extends \Auth\Auth_Login_Simpleauth
```

## bootstrap.phpの修正
下記ファイルを修正する。
`packages¥auth¥bootstrap.php`

``` 修正前php
<?php
/**
 * Fuel
 *
 * Fuel is a fast, lightweight, community driven PHP5 framework.
 *
 * @package    Fuel
 * @version    1.7
 * @author     Fuel Development Team
 * @license    MIT License
 * @copyright  2010 - 2015 Fuel Development Team
 * @link       http://fuelphp.com
 */

\Autoloader::add_core_namespace('Auth');

\Autoloader::add_classes(array(
	'Auth\\Auth'                        => __DIR__.'/classes/auth.php',
	'Auth\\AuthException'               => __DIR__.'/classes/auth.php',

	'Auth\\Auth_Driver'                 => __DIR__.'/classes/auth/driver.php',

	'Auth\\Auth_Opauth'                 => __DIR__.'/classes/auth/opauth.php',

	'Auth\\Auth_Acl_Driver'             => __DIR__.'/classes/auth/acl/driver.php',
	'Auth\\Auth_Acl_Simpleacl'          => __DIR__.'/classes/auth/acl/simpleacl.php',
	'Auth\\Auth_Acl_Ormacl'             => __DIR__.'/classes/auth/acl/ormacl.php',

	'Auth\\Auth_Group_Driver'           => __DIR__.'/classes/auth/group/driver.php',
	'Auth\\Auth_Group_Simplegroup'      => __DIR__.'/classes/auth/group/simplegroup.php',
	'Auth\\Auth_Group_Ormgroup'         => __DIR__.'/classes/auth/group/ormgroup.php',

	'Auth\\Auth_Login_Driver'           => __DIR__.'/classes/auth/login/driver.php',
	'Auth\\Auth_Login_Simpleauth'       => __DIR__.'/classes/auth/login/simpleauth.php',
	'Auth\\Auth_Login_Ormauth'          => __DIR__.'/classes/auth/login/ormauth.php',

	'Auth\\SimpleUserUpdateException'   => __DIR__.'/classes/auth/exceptions.php',
	'Auth\\SimpleUserWrongPassword'     => __DIR__.'/classes/auth/exceptions.php',
	'Auth\\OpauthException'             => __DIR__.'/classes/auth/exceptions.php',

	'Auth\\Model\\Auth_User'            => __DIR__.'/classes/model/auth/user.php',
	'Auth\\Model\\Auth_Userpermission'  => __DIR__.'/classes/model/auth/userpermission.php',
	'Auth\\Model\\Auth_Metadata'        => __DIR__.'/classes/model/auth/metadata.php',
	'Auth\\Model\\Auth_Group'           => __DIR__.'/classes/model/auth/group.php',
	'Auth\\Model\\Auth_Grouppermission' => __DIR__.'/classes/model/auth/grouppermission.php',
	'Auth\\Model\\Auth_Role'            => __DIR__.'/classes/model/auth/role.php',
	'Auth\\Model\\Auth_Rolepermission'  => __DIR__.'/classes/model/auth/rolepermission.php',
	'Auth\\Model\\Auth_Permission'      => __DIR__.'/classes/model/auth/permission.php',
	'Auth\\Model\\Auth_Provider'        => __DIR__.'/classes/model/auth/provider.php',
));

```

``` 修正後php
<?php

\Autoloader::add_core_namespace('CustomAuth');

\Autoloader::add_classes(array(
	'CustomAuth\\Auth_Login_Simpleauth'       => __DIR__.'/classes/auth/login/simpleauth.php',
));

```

# 結構いろいろ直したなぁ・・・
上記で動くようになると思います。

# *注意*
上記修正は一貫してソース周りだけです。
migrationファイルでDB作成を行っている場合、emailにUK,NN制約が付いているので外してあげましょう。

# GitHub Link
~~https://github.com/unagiya/FuelPHP_1_7_CustomAuth~~
https://github.com/bikun-bikun/FuelPHP_1_7_CustomAuth

# 2015-12-14追記
パッケージに追加したので、configから該当パッケージを読めるようにしないとダメでした。

## config.phpから作成したパッケージを読み込めるように修正
修正するファイル
`fuel\app\config\config.php`

``` config.php修正前
		'packages'  => array(
			'auth',
			'parser',
		 	'orm',
			'less',
		),

```

``` config.php修正後
		'packages'  => array(
			'auth',
			'parser',
		 	'orm',
			'less',
            'customauth',
		),

```