Вижу, это актуальная тема. По многочисленным просьбам выкладываю на всеобщее обозрение свой подход в авторизации и регистрации пользователей. Следует учесть, что у меня это есть часть одной большой системы, потому чтобы заработало у вас - надо будет кое что поменять. Что? Читайте комменты по ходу, которые я, специально для php.ru, написал на русском. Сразу скажу, что я знаю, что такое phpDoc и не использую его специально. Субъективно мне он кажется неудобным. Если вы хотите - можете отредактировать эти классы с комментариями в стиле phpDoc. Класс User стоит немного отредактировать, если вы хотите его использовать - он создан для такой структуры проекта, какая используется у меня. У вас он в таком виде, скорее всего, будет работать некоректно. Подробнее - читайте комментарии внутри класса. Также предупреждаю - данный код еще не прошел боевой проверки. Его использование я планировал начать через несколько дней. Потому - возможны мелкие недоработки. По ходу буду править и сообщать об изменениях в этой теме. Пока - отвечаю на вопросы Лицензия: GPL v3. PHP: <?php /** * Класс Session v. 0.1.0.0 * Удобная оберка для стандартных сессий * Примечание: * Класс статичный, потому любой метод надо вызывать через двойное двоеточие, например: * Session::start(); * $user = Session::get('user'); * Session::set('var', $variable); * Метод isCreated () * Проверяет, установленны ли Cookie сессии и правильные ли они * @ Return boolean * Метод start () * Инициирует сессию. Перед любой попыткой вызвать метод ниже надо обязательно стартовать сессию! * Если вы не уверены, была ли сессия начата - можете вызвать этот метод снова. * Если сессия не была начата, метод ее начнет. Иначе - вызов будет проинорирован * Метод set (String $name, Mixed $value) * Добавляет в сессию переменную с именем $name и значением $value * Метод get (String $name) * Возвращает значение переменной $name или null * @ Return Mixed * Метод del (String $name) * Удаляет переменную с именем $name * Метод clear () * Очищает все значения, переданные сессии, но сессию не уничтожает * Метод destroy () * Уничтожает сессию, при этом стираются Cookie, и все значения, переданные в сессию * Метод restart () * Уничтожает, а потом стартует сессию * Метод getArray () * Возвращает массив всех переменных, переданных в сессию. * Рекомендуется использовать только для отладки, а все значения * получать при помощи метода get * @ Return Array * Метод commit () * Сохраняет все значения и закрывает сессию. После вызова этого * метода работа с сессией невозможна, пока сессия не будет снова * запущенна методом start() */ class Session { private static $lifetime = 1200000; // 14 дней private static $cookieName = "cid"; private static $started = false; public static function isCreated () { return (!empty($_COOKIE[self::$cookieName]) and ctype_alnum($_COOKIE[self::$cookieName])) ? true : false; } public static function start () { if(!self::$started) { // Если содержи if(!empty($_COOKIE[self::$cookieName]) and !ctype_alnum($_COOKIE[self::$cookieName])) { unset($_COOKIE[self::$cookieName]); } session_set_cookie_params (self::$lifetime, '/'); session_name (self::$cookieName); session_start (); self::$started = true; } } public static function set ($name, $value) { if(self::$started) { $_SESSION[$name] = $value; } else { trigger_error('You should start Session first', E_USER_WARNING); } } public static function get ($name) { if(self::$started) { return isset($_SESSION[$name]) ? $_SESSION[$name] : null; } else { trigger_error('You should start Session first', E_USER_WARNING); } } public static function del ($name) { if(self::$started) { unset($_SESSION[$name]); } else { trigger_error('You should start Session first', E_USER_WARNING); } } public static function clear () { if(self::$started) { unset($_SESSION); } else { trigger_error('You should start Session first', E_USER_WARNING); } } public static function destroy () { if(self::$started) { self::$started = false; unset($_COOKIE[self::$cookieName]); setcookie(self::$cookieName, '', 1, '/'); session_destroy(); } else { trigger_error('Session is not started!', E_USER_WARNING); } } public static function restart () { self::destroy(); self::start(); } public static function getArray () { if(self::$started) { return $_SESSION; } else { trigger_error('You should start Session first', E_USER_WARNING); } } public static function commit () { if(self::$started) { session_write_close(); self::$started = false; } else { trigger_error('You should start Session first', E_USER_WARNING); } } } ?> PHP: <?php /** * Класс User v 0.1.0.0 * Класс авторизации и регистрации пользователя. * Примечание: * Класс статичный, потому любой метод надо вызывать через двойное двоеточие, например: * if ( User::isLogined() ) { * Метод check () * Проверяет, авторизировался ли пользователь, этот метод следует вызвать до вызова любого другого метода класса * Метод isLogged () * Возвращает значение, залогинен ли пользователь в этом сеансе * @ Return boolean * Метод register (String $nick, String $pass) * Регистрирует пользователя с ником $nick (если не занят) и паролем $pass * @ Return TRUE или "NickBusy" * ВНИМАНИЕ: Данный метод нуждается в доработке, если вы хотите его использовать у себя на сайте * подробнее - в комментариях метода * Метод login (String $nick, String $pass) * Авторизует пользователя с ником $nick (если существует) и паролем $pass (если правильный) * @ Return TRUE или "WrongPass" или "WrongNick" * ВНИМАНИЕ: Данный метод нуждается в доработке, если вы хотите его использовать у себя на сайте * подробнее - в комментариях метода * Метод logout () * Делает пользователя неавторизированным на сайте и запоминает это состояние * ВНИМАНИЕ: Данный метод нуждается в доработке, если вы хотите его использовать у себя на сайте * подробнее - в комментариях метода * Метод getInstance () * Возвращает объект пользователя. В этом объекте могут быть данные о имени, дате регистрации, * количестве сообщений и так далее. Вы можете отредактировать класс так, чтобы возвращался * ассоциативный массив вместо объекта. Смотрите код и комментарии методов register, login и logout * @ Return Object */ class User { protected static $instance = null; protected static $logged = false; public static function check () { Session::start(); if ($data = Session::get('User')) { $data = unserialize($data); self::$logined = $data['logined']; self::$instance = $data['instance']; } else { // Не может быть сессии без переменной User. Должно быть, это ошибка Session::restart(); // Пользователь будет незалогиненым self::logout(); } } public static function isLogged () { return self::$logged; } public static function register ($nick, $pass) { // В этом блоке if надо проверить, нету ли уже пользователя с ником $nick // Если есть - вернуть ошибку. Отредактируйте так, чтобы подходило под ваш код if (Db_Get_User::byNick($nick)) { return 'NickBusy'; } else { // Стоит создать пользователя с таким ником в базе $user = new Object_User(); $user->setNick($nick); $user->setPass($pass, true); self::$instance = Db_Put_User::create($user); // Пользователь уже создан и в свойство класса добавлена его сущность // Например - объект, или ассоциативный массив self::$logined = true; self::addToSession(); return true; } } public static function login ($nick, $pass) { // Из базы надо попробовать получить пользователя с таким НикНеймом. $user = Db_Get_User::byNick($nick); if ($user) { // Если такой пользователь его - сравниваем хеш пароля, который ввел // пользователь со значением в базе if ($user->getPass() === Make::hash($pass)) { // Если совпало - значит вход совершен self::$instance = $user; self::$logined = true; self::addToSession(); return true; } else { return "WrongPass"; } } else { return "WrongNick"; } } public static function logout () { // Надо указать, что сущность пользователя теперь - гость и // записать это значение в сессию self::$instance = new Object_Guest; self::$logined = false; self::addToSession(); } public static function getInstance () { return self::$instance; } private static function addToSession () { Session::set("User", serialize(array( "instance" => self::$instance, "logined" => self::$logined ))); } } ?>
kostyl, пиши, кто ж тебе мешает то? а движок - это одна совокупная система. в данном случае надо изменить пару строчек, чтобы соединить эти классы с вашим движком. и они будут единым целым с вашим движком, а не будет создаватся впечатление, что их прилепили как что-то левое. Глупо вызывать функцию mysql_query в классе, если у движка есть свой класс абстракции БД, или , более того, ORM Глупо заставлять всех использовать чистый md5. Каждый должен использовать ту хеш-функцию, которую он хочет. Ну и тому подобное Хотя, если можешь написать что-то достойное и при этом полностью абстрагироватся от всех остальных классов - давай. будет интересно почитать.
PHP: <? User::check(); if(User::isLogined()) { echo "You are logined"; } else { printLoginForm(); } PHP: <? $nick = !empty($_POST['nick']) ? $_POST['nick'] : null; $pass = !empty($_POST['pass']) ? $_POST['pass'] : null; $registration = !empty($_POST['reg']) ? true : false; if ($nick and $pass) { if($registration) { // Если регистрация $result = User::register($nick, $pass); if($result === 'NickBusy') { echo "Nickname '$nick' is busy"; } else { header('location:/index.php?reg=true'); } } else { // Если вход $result = User::login($nick, $pass); if ($result === 'WrongNick') { echo "There are no such nickname!"; } else if ($result === 'WrongPass') { echo "Password is wrong!"; } else { header('location:/index.php?login=true'); } } } else { echo "Nickname or password is empty!"; } printLoginForm(); Ну это на коленке, конечно
Мне понравилось Последний пример хорошо иллюстрирует сам принцип работы системы. Мой небольшой "проектик" работает на фукциях, но принцип практически тот же. Значит я был недалеко от истины =) у меня дурацкий вопрос. вы пишите: Db_Get_User::byNick($nick) т.е. я понимаю, что Db_Get_User это другой класс, а byNick это метод этого класса? и этот класс тоже статичный? вопрос может показаться идиотским, но я все свое недолгое время писал функциями, а сейчас хочу понять смысл этих конструкций.
Elkaz, ну для новичков там и комментарии есть и понятные названия и учится на чем-то надо да и профессионалы друг у друга идеи могут хорошие почерпнуть. любой профессионал чего-то, да не знает. вдруг именно то, что я реализовал в одном из этих классов ) > Значит я был недалеко от истины =) Мое решение считают непоколебимой истиной: очень приятно > т.е. я понимаю, что Db_Get_User это другой класс, а byNick это метод этого класса? Да. Фактически, это не совсем ООП, тут скорее применился NameSpace для функций. В чем смысл? В строгой иерархии. "Db_Get_User" я ищу ENGINE_PATH/Classes/Db/Get/User.php, а там есть несколько методов: byNick, byId и т.п, которые возвращают объект, или массив объектов Object_User. Отличается от функций в первую очередь строгостью. Вот у вас, например, есть функция getUserById. Где мне, стороннему человеку, ее искать? Та даже со своим кодом я намучался, когда больше тридцати функций - уже фиг найдешь нужную. А так - есть четкое правило размещения. Ну и, например, в классе Db_Get_User у меня есть приватный метод by($field, $value), а остальные методы только реализуют нужное экранирование (byId - приведение к числовому типу, byCid - проверку на то, есть ли значение hex'ом и т.п.) и вызывают self::by('ID', $id). Это тоже помогает избежать дублицирования кода. Плюс, возможность использовать приватные свойства - тоже хороша. Можно увидеть это в статических классах User и Session выше. PHP: <? class Db_Get_User { public static function byId ($id) { if(!is_int($id)) { trigger_error('`ID` should be int', E_USER_ERROR); return false; } $users = self::by('ID', $id, 1); return empty($users) ? null : $users[0]; } public static function byNick ($nick) { $value = Db_Engine::escape($nick); $users = self::by('Nick', $value, 1); return empty($users) ? null : $users[0]; } public static function byRegDate ($unixTime, $limitCount = 10, $limitFrom = 0) { if(!is_int($unixTime)) { trigger_error('`RegDate` should be int', E_USER_ERROR); return false; } return self::by('RegDate', $unixTime, $limitCount, $limitFrom); } public static function byLevel ($level, $limitCount = 10, $limitFrom = 0) { if(!is_int($level) and $level >= Object_User::GUEST and $level <= Object_User::ADMIN) { trigger_error('`Level` should be int beetwen 1 and 4', E_USER_ERROR); return false; } return self::by('Level', $level, $limitCount, $limitFrom); } private static function by ($field, $value, $limitCount = 10, $limitFrom = 0) { $limit = $limitCount ? "LIMIT $limitFrom, $limitCount" : ""; $result = Db_Engine::select(" SELECT * FROM `Users` WHERE `$field` = '$value' $limit "); $users = array(); foreach ($result as $row) { $users[] = new Object_User($row); } return $users; } }
Ой, простите меня дамы и господа, но я не могу удержаться: Что такое Logined? Во-вторых расхождения в принципе ООП. Почему многие члены объявлены как private? А если я захочу наследовать класс и дополнить его нужным функционалом? Приватными должны быть те члены класса, которые принадлежат только к определенному объекту и не могут быть затронуты в наследуемых, в данном же случае мне необходимо обращаться к свойству $logined (ой, не могу это слово).
Рад, что я тебя повеселил, Apple Вижу, ты очень хорошо разбираешься в ООП и наследовании Очень по-ООПешному. Я думал, что Estonia extends Country, а тут она, оказывается, наследует World Согласен, isLogged было бы лучше. Тем не менее, это не уменьшило читабельности и интуитивной понятности, что данный метод делает. Не понимаю, вообще, что тебя веселит. Смех без причины ... ?
Думайте. Мы не переходили на "ты" - это раз. Меня бы ничего не веселило, если бы в глаза не бросилась эта НУ ПРОСТО ДУШЕРАЗДИРАЮЩАЯ фраза: Знаток английского, оказывается, пишет logined (мало того, что неправильно само по себе, ибо нет такого слова, так ещё и грамматически некорректно оно введено - без удвоения суфикса n). Когда кто-то что-то говорит, то, соответственно, он готов это отстаивать, иначе бы, наверное, не сказал. Если я в чем-то сомневаюсь, я или не напишу вовсе, или сначала узнаю как надо. А если уже так случилось, что написал, то я с удовольствием признаю свою ошибку (если она конечно есть) и исправлю.
Разве на форуме нельзя делать замечания по коду и обсуждать его корректность? Даже оффтопа не было, всё по теме.
не указывай что мне делать. и не надо учить меня жить Переход на "ты" вполне допустим по правилам сетевого этикета - это раз Я говорю "Вы" только тому человеку, которому хочу говорить "Вы". А если человек начинает смеятся мне "в лицо" и тыкать в банальные ошибки, которые случаются у всех и мусолить эту тему вокруг одного, малозначимого слова, то я думаю, что этот человек - неадекватен. К таким я на "Вы" не обращаюсь. Да и фраза "А мы на вы не переходили" чаще всего звучит от девочек-подростков, которые хотят поколотить понты во время знакомства. Тебя, Apple, я на свиданку не приглашаю, потому особо напрягатся с этими выдумаными статусами "ты"-"Вы" я не собираюсь. Ну и сам никогда никому не делал замечание по этому поводу. Предпочитаю, чтобы ко мне говорили на "ты". Это показывает, что я не ставлю себя выше этого человека, а общаюсь с ним наравных. Где я указал, что знаю английский. Может я в школе французкий учил, а английский знаю только по примерам кода на php ? Укажи мне на место, где я указал, что я знаток английского? Или то, что я специально для того чтобы выложить на форуме код его прокомментировал на русском языке уже что-то негативное значит? Раз уж начал разговор - читай не через строчку. [vs], я в третьем посте объяснил, почему именно он не работает самостоятельно. Можно было заменить, например, абстрактное Db_Get_User::byNick($nick); на MySQL запрос, но вдруг окажется, что человек использует другую базу данных. И у него другая структура таблиц. И хеширует пароли он функцией sha256. Ну много вопросов. Потому я оставил абстрактные строчки с комментариями. Ну а на счет обертки - мне не нравится способ именования некоторых многих функций в пхп, ну и нужна дополнительная функциональность. Хотя тут, конечно, чисто субъективное мнение
Столько слов и всё впустую. Я указал на ошибки (в коде), вы отказались их принять и раскапризничались, словно девчонка. Ну что же, ваше право. Оффтоп окончен.
Можно вопрос!? (ко всем) Вот смотрю на всякие вариации этой темы и везде программисты стараются сделать код с использованием сессий. А вот при включенной session.use_trans_sid и выключенных куках код перестает делать свое дело. Так в чем тогда смысл использовать session?
Я не понимаю людей, которые выключают куки. Если же они считают, что куки им не нужны, то я не имею права им перечить и искать способ из залогинить в обход. Это решение пользователя.
TheShock на аватарках могут быть только личные фотки с лицом. Терминал не канает А смысл писать Session::set('foo', bar); если можно написать $_SESSION['foo'] = 'bar'; ? не нужно никаких лишних вызовов. Это так же как Шевчук дополнил свой класс работы со строками методом add (конкатенация)?
Это называется абстракция. Вот надо будет тебе переписать сессии со стандартных на что-то другое. По любым причинам. Что, будешь бегать по всему коду и сторонним модулям исправлять эти присваивания?
я просто сомневаюсь в том, что когда либо нужно будет переписать стандартные сессии на что-то другое.
А где эти ваши правила? Чтото я не нашел на главной ссылки. Ну даже если так: морда у меня слишком широкая: на мини-аву 80*80 никак не влазит. Хотябы 120*120 Потому или терминал, или вообще без авы - меня вполне устроит. Или могу в качестве альтернативы выложить свои настоящие фотги в большом размере + оставить аву-терминал В Джава крайне не рекомендуется конкатенировать строки. Если надо добавить в конец строки еще одну строку, то делается так: String str = "Some str"; str = new StringBuilder.append(str).append("something else").toString(); Правда, это обусловленно более быстрой производительностью, но тем не менее.
ну и представь цепочку вызовов: $str->replace('x', 'y')->split('a')->shuffle()->join('b')->add('c')->substr(5); а без возможность конкатенации методом пришлось бы разрывать на три строки пример, конечно, надуманный, но вполне даже ничего. кстати. а есть ли где-то действительно качественная обертки для массива и строк? очень хочу такую