Ага, лучше всего создать класс, который будет обрабатывать данные в суперглобальных массивах, и назови его Request
Это всё настраивается. Я, к примеру, отключил предупреждение о суперглобальных массивах, и о скобочках после if/
А где, как? Я так сразу не нашел хм.. Хотел сюда код вставить, но скопировав его к себе он у меня просто не запустился
Обычно всякие сервисы такие имена не принимают. Не нравится тем, что для него отдельный обработчик писать, а так по идее любое имя ок, даже с кавычками. mkramer, спасибо нашел подсказки). Вроде там и искал, но с первой попытки не тот язык смотрел.
Ну в ТЗ это у суриката оговорено не было, так что не стоит. Вообще, при добавлении в БД - mysqli_escape_string, при выводе - htmlspecialchars.
Вот код в общем, вроде работает всё. Потратил часов 10 на него. Кошмар. Хотя для первого за последние 7 лет php приложения использующего объектную модель может и неплохо. Просьба дать конструктивную критику: Код (Text): <?php $db_host='127.0.0.1'; //хост где находится mysql $db_login='root'; //логин пользователя бд $db_pass=''; //пароль пользователя бд $db_name='counter734'; //имя бд $min_user_age=16; // минимальный возраст юзера $max_user_age=60; // максимальный возраст юзера $firstrun=true; //при первом запуске создает таблички в бд // Начало скрипта $dblink=DbAndSession::init($db_host, $db_login, $db_pass, $db_name); if ($firstrun) DbAndSession::create_tables(); // создает таблички в бд switch (@$_GET['act']) { case '': // главная страничка c формой логин/регистрация Outputs::print_login_page(); break; case 'regpage': // страничка с формой регистрации Outputs::print_register_page(); break; case 'registeruser': //обработка регистрации UserAuth::init($dblink); $check_age=Userdata::checkuserage($_POST['b_day'],$_POST['b_month'],$_POST['b_year'],$min_user_age,$max_user_age); if ($check_age>0) Outputs::print_error_page('Too old'); if ($check_age<0) Outputs::print_error_page('Too young'); if ($check_age==0) { $res=UserAuth::register_user($_POST['login'], $_POST['password']); if ($res) { $userdata=new Userdata($dblink,$_POST['login']); $userdata->addnewuser($_POST['b_day'],$_POST['b_month'],$_POST['b_year']); $innerpage=true; } else { Outputs::print_error_page("Can't register. Maybe login is used " . "my someone else, or maybe you used incompatible characters in your login name"); } } break; case 'login': // обработка залогинивания UserAuth::init($dblink); $login=UserAuth::login($_POST['login'], $_POST['password']); if ($login) { $innerpage=true; } else { Outputs::print_error_page("Can't log in. Maybe something wrong with your login or password"); } break; case 'plusone': //счетчик плюс один UserAuth::init($dblink); $userdata=new Userdata($dblink, UserAuth::getlogin()); $userdata->counter_plus_one(); $innerpage=true; break; case 'logout': // разлогинивание DbAndSession::killsession(); // добавлено благодаря mkramer Outputs::print_login_page(); break; } if (isset($innerpage)) // работа с внутренней страницей { if (!isset($userdata)) $userdata=new Userdata($dblink, UserAuth::getlogin()); $counter=$userdata->getcounter(); Outputs::print_inner_page($counter); } // Конец скрипта class DbAndSession // Статичный класс, отвечает за коннект с бд и сессию. { private static $link; private function __construct() {} public static function init($mysql_host, $mysql_login, $mysql_password, $mysql_dbname) //старт сессии, подключение к бд, возвращает линк на бд { if (!(isset(self::$link))) { session_start(); self::$link=@mysqli_connect($mysql_host,$mysql_login,$mysql_password,$mysql_dbname); if (!self::$link) throw new Exception ("Can't connect to db"); } return self::$link; } public static function killsession() { session_destroy(); } public static function create_tables() { mysqli_query(self::$link, 'Create table if not exists user_auth ' . '(`login` char(50) not null, `passhash` char(50) not null, ' . 'primary key (`login`) ) engine=InnoDB default charset=utf8'); mysqli_query(self::$link, 'Create table if not exists user_data ' . '(`login` char(50) not null, `birthday` char(50) not null, counter char(50) not null, ' . 'primary key (`login`) ) engine=InnoDB default charset=utf8'); } public static function get_db_link() //возвращает линк на бд { return self::$link; } } class UserAuth //Статичный класс, отвечает за авторизационные данные пользователя { private static $link; private function __constuct() {} public static function init(mysqli $mysql_link) // инициализация { self::$link=$mysql_link; } public static function register_user($user_login, $user_password) { //заведение в базу нового юзера, при неудаче возвращает false if (($user_login!=htmlspecialchars($user_login)) or (strlen($user_login)<1)) return false; $passhash=md5($user_password.$user_login); $res=mysqli_query(self::$link, "Insert into user_auth set `login` = '$user_login', `passhash` = '$passhash'"); $_SESSION['login']=$user_login; return $res; } public static function login($user_login, $user_password) { // проверяет пользователя в бд. если найдет возвращает true, иначе false if (($user_login!=htmlspecialchars($user_login)) or (strlen($user_login)<1)) return false; $passhash=md5($user_password.$user_login); $res=mysqli_query(self::$link, "Select login from user_auth where `login` = '$user_login' and `passhash` = '$passhash'"); if (!($row=mysqli_fetch_row($res))) return false; $_SESSION['login']=$user_login; return $user_login; } public static function getlogin() { //возвращает логин юзера из сессии return $_SESSION['login']; } } class Userdata { //Класс отвечает за пользовательские данные, в частности счетчик private $login; private static $link; public function __construct(mysqli $mysql_link, $user_login) { $this->login=$user_login; self::$link=$mysql_link; } public function addnewuser($birth_day, $birth_month, $birth_year) { // добавить пользовательские данные для нового пользователя $birthdate=$birth_day.'.'.$birth_month.'.'.$birth_year; mysqli_query(self::$link, "Insert into user_data set `login` = '$this->login', `birthday` = '$birthdate', `counter` = '0'"); } public static function checkuserage($birth_day, $birth_month, $birth_year, $minage, $maxage) { //проверяет возраст пользователя. //если старше $maxage, то возвращает 1, если моложе $minage, то -1 //если возраст в пределах $minage, $maxage возвращает 0 $birthdate=$birth_day.'.'.$birth_month.'.'.$birth_year; $c_time=time(); $c_day=@date('j',$c_time); $c_month=@date('n',$c_time); $c_year=@date('Y',$c_time); $age360=($c_year-$birth_year)*360+($c_month-$birth_month)*30+($c_day-$birth_day); if ($age360>$maxage*360) return 1; if ($age360<$minage*360) return -1; return 0; } public function counter_plus_one() { $counter=$this->getcounter()+1; $res=mysqli_query(self::$link,"Update user_data set `counter` = '$counter' where `login` = '$this->login'"); } public function getcounter() { $res=mysqli_query(self::$link,"Select counter from user_data where `login` = '$this->login'"); if($row=mysqli_fetch_row($res)) return $row[0]; else return false; } } class Outputs { public static function print_login_page() // печать главной странички { echo ' <html><body><h1><br><br></h1><center> <form action="?act=login" method="post"> <p>Логин: <input type="text" name="login" /></p> <p>Пароль: <input type="password" name="password" /></p> <p><input type="submit" value="Войти"></p> <p><button formaction="?act=regpage">Регистрация</button></p> </form> </center></body></html> '; } public static function print_register_page() // печать странички регистрации { $months = array ('1' => 'январь', '2' => 'февраль', '3' => 'март', '4' => 'апрель', '5' => 'май', '6' => 'июнь', '7' => 'июль', '8' => 'август', '9' => 'сентябрь', '10' => 'октябрь', '11' => 'ноябрь', '12' => 'декабрь'); echo ' <h1><br><br></h1><center> <form action="?act=registeruser" method="post"> <p>Логин: <input type="text" name="login" /></p> <p>Пароль: <input type="password" name="password" /></p> <p> Дата рождения: <select name="b_day">'; for ($i=1 ; $i<=31 ; $i++) echo '<option value="'.$i.'">'.$i.'</option>'; echo ' </select> <select name="b_month">'; for ($i=1 ; $i<=12 ; $i++) echo '<option value="'.$i.'">'.$months[$i].'</option>'; echo ' </select> <select name="b_year">'; for ($i=1900 ; $i<=2015 ; $i++) echo '<option value="'.$i.'">'.$i.'</option>'; echo ' </select> </p> <p><input type="submit" value="Зарегистрировать"></p> </form> </center> '; } public static function print_error_page($message) // печать страницу с ошибкой { echo '<h3><br><br>'.$message.'</h3>'; } public static function print_inner_page($counter) // печать внутреннюю страницу { echo '<center><p style="font-size:70px">'.$counter.'</p> ' . '<form method=post>' . '<p><button formaction="?act=plusone">+1</button></p>' . '<p><button formaction="?act=logout">Выход</button></p>' . '</form>' . '</center>'; } public static function print_exit_page() // печать страницу выхода { echo 'You have been exited. Have a good time.'; } } Добавлено спустя 3 минуты 7 секунд: Тоже так подумал но смутило две вещи: 1. mysqli_real_escape_string ( mysqli $link , string $escapestr ) Зачем обрезальщику символов ссылка на объект типа mysqli ? 2. В документации написано: "Предостережение. Безопасность: набор символов по умолчанию. Набор символов должен быть задан либо на стороне сервера, либо с помощью API функции mysqli_set_charset(). В противном случае mysqli_real_escape_string() работать не будет."
1. Чтобы учесть кодировку, установленную для соединения. Иначе она может пропустить опасный символ. Она ничего не обрезает, она экранирует, чтоб mysql воспринимал эту строку как данные, а не как команду. И получится, что ничего страшного, если пользователь у вас будет иметь имя Код (Text): '; delete from users; Пока все запросы к БД с его учаситем идут через mysqli_escape_string или через плейсхолдеры 2. Ну собственно, в 1 есть ответ. Ну mysqli_set_charset() вы же не развалитесь вызвать? Добавлено спустя 2 минуты 31 секунду: Это не разлогинивание, это вывод страницы логина. Разлогинивание - это удаление из сессии сведений о том, что пользователь залогинен
Упс.. метод создал, а не использовал. Спасибо. Не поленюсь, если разберусь с полными списоком что туда ставить.
Да, выложите на бесплатный хостинг какой-нибудь, есть же такие. А то так непонятно. К тому же у Fell-x27 было там кое-что про вёрстку в ТЗ, посмотреть как выглядит. Не поднимать же у себя. Интересно, что никто из выполнявших задание не использовал фреймворки. Я бы взял какой-нибудь мини или микро-фреймворк, чтоб нормальная маршрутизация была.
Фрейморки я еще не изучал)). С маршрутизацией я чего-то недогоняю...(( Добавлено спустя 45 минут 38 секунд: Домен: mirosasphp.tk IP: 31.170.166.23
У вас если просто переходить по ссылке http://mirosasphp.tk/?act=plusone, не отправляя форму, тоже счётчик увеличивается.
А ведь вопрос не стоял делать на фреймворке, поэтому я не стал делать на фреймворке. Так уж и быть. Я собирался делать на laravel теперь выложу вариант на symfony 2
код говно неплох, есть к чему стремиться Комбо: собачка + прямая работа с $_GET. Никогда не знаешь, что ждет тебя в этих переменных, и есть ли они вообще. Поэтому принято сначала проверять, потом класть данные в собственные переменные и уже с ними работать. Тогда и собачка не нужна. Ожидал бы Код (Text): Outputs::printLoginPage(); ведь имена классов именно "верблюжьи" Полезность $dblink в этом месте чуть ниже нуля. Ведь синглтон DB это просто must have в поделке. А ведь вместо if - else отлично бы сработало исключение с указанием конкретной ошибки. В пределах одного блока можно было бы уместить и проверку возраста, и попытку регистрации. Кстати, почему длина имени проверяется там, а возраст - тут? вообще ты можешь написать свой sessions handler оу щит, у тебя же есть класс с static $link, так зачем его передавать в функции? Тебя от if спасет mysqli_report(MYSQLI_REPORT_STRICT);, и от собачки тоже. не, ну это не серьезно в плане защиты от инъекций. Когда-то многие функции переставали работать на null-byte. Что-то подобное может повториться. Лучше вообще не знать про md5(). Её время ушло, как md4 десять лет назад. Запрещенные года можно сразу запретить выбирать.
Дожили, новое направление кодинга - бессмысленные и беспощадные оверхеды ради того, чтобы нетбинс не ругался. Автор, поставь себе уже phpStorm. Он создан для веба как ничто другое. Добавлено спустя 1 минуту 33 секунды: В логине должно быть возможным использовать любые символы юникода, mkramer прав. Если бы нужны были ограничения, они были бы прописаны. Добавлено спустя 2 минуты 57 секунд: Пожалей воробушков, пошто по ним с пушек-то? И меня пожалей. Если все начнут фреймворки юзать для такой фигни, мне, для того, чтобы понять, что тут накодено, и какого оно качества, придется только ради этого учить %тот_фреймворк_который_попал_под_руку_человеку%? Добавлено спустя 3 минуты 1 секунду: Зашел на http://mirosasphp.tk/?act=plusone Увидел счетчик, пусть и не работающий. И кнопку "выход". Не будучи зареганным. Не будучи залогиненным. Остальное пока не проверяю - это таки косяк, потому как для незалогиненного пользователя это показываться не должно вовсе. Сайт должен определить, что я не залогинен и вывести мне главную страницу с логинилкой/регалкой. Это, конечно, прямо не прописано, но, с точки зрения здравого смысла, это вполне ожидаемое поведение приложения.
Начни с ларки. Конечно, задание Суриката уже как минимум наполовину реализовано в любом фреймворке, но если говорить о качестве кода, в духе фреймворка и со всеми общепринятыми нормами, то тут не все так очевидно как кажется на первый взгляд, особенно в симфони. Добавлено спустя 27 минут 5 секунд: На самом деле всё неплохо, не том плане что код хорош, а в том что есть стремление не говнокодить ) 1. Не надо все писать в одном файле: - Конфиг лучше выносить отдельно - это даст возможность обновить скрипт не боясь перезаписи настроек, а при использовании GIT его вообще можно будет отправить в игнор, что спасет от публикации паролей на весь мир, что довольно таки распространенная ошибка ) - Один класс - один файл. Вообще, если в файле лежит класс, то там больше ничего не должно быть. В PHP появилась автозагрузка (spl_autoload_register), классы можно подключать на лету по мере необходимости. Что будет, если в подключенном файле само по себе выполняется лишнее действие? 2. Один стиль. Либо camelCase либо snake_case, не надо делать оба варианта. В идеале UpperCamelCase для классов и неймспейсов и lowerCamelCase для функций и методов. 3. "static", не то что бы зло, местами оно полезно, но сейчас по сути это самообман. Были функции, мы их засунули в класс, объявили как static и сказали что ООП. На самом же деле не изменилось ничего, кроме способа вызова этих функций. p.s. это чисто по оформлению кода, на логику пусть Сурикат смотрит ))
Поправлю код, позже. Не сейчас. Меня такой момент интересует. А что если файлы подключаемые выпадут из дискового кэша (ну там между чтением файлов кто-то скопировал файл большой например)? А диск у нас не твердотельный, а с пластинками.. А там 100-1000 классов, каждый в своем файле. Ничего хорошего, но можно и другими средствами этого избежать. Посомтрю, учту. Так и есть. Но по классам раскидано как-то красивше. нет?)