Как стать автором
Обновить

Пишем модуль uLogin для Kohana 3.2

PHPKohana
Из песочницы
Не так давно на Хабре была статья про виджет авторизации uLogin.
Что мне в нём очень понравилось, это возможность указать обязательные поля, при этом, в случае их неполучения от провайдера, пользователю предлагается врчуную их заполнить. Так возникло желания написать модуль в Kohana, который позволял бы легко осуществлять регистрацию пользователя с помощью виджета uLogin.


Немного о структуре модулей


Каждый модуль помещается в отдельную папку в папке MODPATH (по умолчанию modules)
Внутри могут находится папки classes, config, и все те же самые, что и в application. Это так называемая каскадная файловая система, подробнее здесь

Для нашего модуля понадобятся только папки classes и config.
В папке config будет лежать конфигурационный файл по умолчанию, а, при желании, его можно переопределить в application/config.
В папке classes помещается файл ulogin.php, содержащий определение класса Ulogin:
<?php defined('SYSPATH') or die('No direct script access.');

class Ulogin extends Kohana_Ulogin {}

Этот класс наследуется от Kohana_Ulogin, что позволит, при необходимости, переопределить его, поместив в файл application/classes/ulogin.php.

Так базовый класс называется Kohana_Ulogin, то должен находится в classes/kohana/ulogin.php, так как Kohana заменяет подчёркивание на слеш при поиске класса для автозагрузки.

Перед использованием модуль необходимо подключить в bootstrap.php:
Kohana::modules(array(
	......
	'ulogin'  => MODPATH.'ulogin',  // uLogin

	));


Отображение виджета

Отображение виджета авторизации осуществляется следующим образом:
echo Ulogin::factory()->render()


Что при этом происходит:

Статическая функция
	public static function factory(array $config = array())
	{
		return new Ulogin($config);
	}

Создаёт экземпляр класса Ulogin, при этом передавая эму массив с конфигурацией, это сделано для возможности перезаписывать настройки по умолчанию непосредственно при вызове.

Конструктор класса:
		public function __construct(array $config = array())
	{
		$this->config = array_merge($this->config, Kohana::$config->load('ulogin')->as_array(), $config);
		
		if ($this->config['redirect_uri'] === NULL)
			$this->config['redirect_uri'] = Request::initial()->url(true);
	}

Загружает конфиг с помощью функции Коханы Kohana::$config->load и проверяет redirect_uri на NULL. В случае положительного результат проверки просто устанавливается текущий адрес, по которому обратился пользователь, получая его с помощью вызова Request::initial()->url(true). Параметр true говорит Кохане о необходимости включить протокол в адрес. Request::initial() используется для того, что бы получить именно начальный адрес.

Функция render():
	public function render()
	{	
		$params = 	
			'display='.$this->config['type'].
			'&fields='.implode(',', array_merge($this->config['username'], $this->config['fields'])).
			'&providers='.implode(',', $this->config['providers']).
			'&hidden='.implode(',', $this->config['hidden']).
			'&redirect_uri='.$this->config['redirect_uri'].
			'&optional='.implode(',', $this->config['optional']);
			
		if (count(self::$_used_id) == 0)
		{
			$view = View::factory('ulogin/first');
			
			self::$_used_id[] = 'uLogin';
		}
		else
		{
			$view = View::factory('ulogin/second');
			
			do
			{
				$uniq_id = "uLogin_".rand();
			}
			while(in_array($uniq_id, self::$_used_id));
			
			self::$_used_id[] = $uniq_id;
			
			$view->set('uniq_id', $uniq_id);
		}
		
		return $view->set('cfg', $this->config)->set('params', $params)->render();
	}

Первым делом надо составить параметры для передачи скрипту uLogin. http_build_query() тут не подходит, так как он кодирует данные (в частностия запятые) в их шестнадцатеричные коды (%xx). Потом проверяется, первый это запуск (в массиве self::$_used_id пусто), или нет.
Если первый, то загружается View ulogin/first.php:
<?php if ($cfg['type'] == 'window') :?>
	
	<a href="#" id="uLogin">
		<img src="http://ulogin.ru/img/button.png" width=187 height=30 alt="МультиВход"/>
	</a>
	<script src="http://ulogin.ru/js/widget.js?<?php echo $params; ?>"></script>
	
<?php else: ?>
	
	<div id="uLogin"></div>
	<script src="http://ulogin.ru/js/widget.js?<?php echo $params; ?>"></script>

<?php endif; ?>

В нём, в зависимости от выбранного режима, отображается HTML-код вызова виджета.

Если это втророй запсук, то генерируем уникальный идентификатор и загруажаем View ulogin/second.php:
<div id="<?php echo $uniq_id; ?>"></div>
<script type='text/javascript'>uLogin.init('id=<?php echo $uniq_id; ?>&<?php echo $params; ?>');</script>

В нём просто вызывается виджет согласно документации, расположенной на официальном сайте

Вот и всё, что необходимо для отображения виджета авторизации

Обработка результата


Обработка с помощью модуля примерно выглядит так:
		$ulogin = Ulogin::factory();
		if (!$ulogin->mode())
			$this->template->content = $ulogin->render();
		else
		{
			try
			{
				$ulogin->login();
			}
			catch(ORM_Validation_Exception $e)
			{
				$this->template->errors = $e->errors('');
			}
		}


То есть проверяем режим с помощью вызова mode(), после чего либо отображаем виджет, либо авторизуем/регистрируем пользователя. try/catch нужен для проверки правильности регистрации пользователя, например совпадающий e-mail/

Функция mode() просто проверят наличие $_POST['token']:
	public function mode()
	{
		return !empty($_POST['token']);
	}


C login() посложнее:
	public function login()
	{
		if (empty($_POST['token']))
			throw new Kohana_Exception('Empty token.');
			
		if (!($domain = parse_url(URL::base(), PHP_URL_HOST)))
		{
			$domain = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'];
		}
		
		$s = Request::factory('http://ulogin.ru/token.php?token=' . $_POST['token'] . '&host=' . $domain)->execute()->body();
		$user = json_decode($s, true);
		
		$orm_user = ORM::factory('user', array('identity' => $user['identity']));
		
		if (!$orm_user->loaded())
		{
			$data['username'] = '';
			foreach($this->config['username'] as $part_of_name)
				$data['username'] .= (empty($user[$part_of_name]) ? '' : (' '.$user[$part_of_name]));
			
			$data['username'] = trim($data['username']);
			
			if (!$data['username'])
				throw new Kohana_Exception('Username fields not set in config/ulogin.php');
				
			$data['password'] = 'ulogin_autogenerated_password';
			
			$data['identity'] = $user['identity'];
			$data['network'] = $user['network'];
			
			$cfg_fields = array_merge($this->config['fields'], $this->config['optional']);
			foreach($cfg_fields as $field)
			{
				if (!empty($user[$field]))
					$data[$field] = $user[$field];
			}
			
			$orm_user->values($data);
			$orm_user->create();
			$orm_user->add('roles', ORM::factory('role', array('name' => 'login')));
			
			Auth::instance()->force_login($orm_user);
		}
		else
		{
			Auth::instance()->force_login($orm_user);
		}
	}


Что здесь происходит:
Повторно проверяем токен (вдруг кто-то вызвал просто login()), после чего пытаемся получить хост для передачи uLogin'у: либо из базового адреса (указывается в bootstrap.php в Kohana::init()), либо через $_SERVER['HTTP_HOST'] или $_SERVER['SERVER_NAME'].

После чего делаем запрос на uLogin, для получения данных пользователя:

		$s = Request::factory('http://ulogin.ru/token.php?token=' . $_POST['token'] . '&host=' . $domain)->execute()->body();
		$user = json_decode($s, true);

В результате получаем нечто такое (пример для Google):
array(6) (
 	"network" => string(6) "google"
	"identity" => string(50) "Уникальный УРЛ пользователя, например https://plus.google.com/u/0/google+ идентификатор/"
	"uid" => string(21) "google+ идентификатор"
	"email" => string(21) "e-mail"
	"first_name" => string(10) "Имя"
	"last_name" => string(14) "Фамилия"
)


Далее пытаемся найти его в базе данных, используй его уникальный урл: $user['identity']:
$orm_user = ORM::factory('user', array('identity' => $user['identity']));

Если найден ($orm_user->loaded() == true), просто авторизуем его:
Auth::instance()->force_login($orm_user);

В обратном случае его надо зарегистрировать:

  1. Составляем имя пользователя:
    $data['username'] = '';
    foreach($this->config['username'] as $part_of_name)
    	$data['username'] .= (empty($user[$part_of_name]) ? '' : (' '.$user[$part_of_name]));
    			
    $data['username'] = trim($data['username']);


  2. Заполняем поля модели:
    $data['password'] = 'ulogin_autogenerated_password';
    			
    $data['identity'] = $user['identity'];
    $data['network'] = $user['network'];
    $cfg_fields = array_merge($this->config['fields'], $this->config['optional']);
    foreach($cfg_fields as $field)
    {
    	if (!empty($user[$field]))
    		$data[$field] = $user[$field];
    }
    
    $orm_user->values($data);
    

  3. Создаём пользователя, добавляем ему роль login, после чего авторизуем его:
    $orm_user->create();
    $orm_user->add('roles', ORM::factory('role', array('name' => 'login')));
    Auth::instance()->force_login($orm_user);



Исходные коды доступны на github

Как видите, и использование uLogin, и написание модулей к Kohana, не представляют никаких сложностей.
Удачи!

UPD: Добавил View'ы
UPD2: Переписал немного код и статью, спасибо dohlik
Теги:uloginkohanakohana 3.2
Хабы: PHP Kohana
Всего голосов 22: ↑20 и ↓2+18
Просмотры8.6K

Похожие публикации

Лучшие публикации за сутки