За последние 24 часа нас посетили 15911 программистов и 1546 роботов. Сейчас ищет 1001 программист ...

Самообучение. Нужны задания на отработку навыков.

Тема в разделе "PHP для новичков", создана пользователем mirosas, 20 июл 2015.

  1. mahmuzar

    mahmuzar Старожил

    С нами с:
    6 апр 2012
    Сообщения:
    4.631
    Симпатии:
    425
    Адрес:
    РД, г. Махачкала.
    Ага, лучше всего создать класс, который будет обрабатывать данные в суперглобальных массивах, и назови его Request
     
  2. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.600
    Симпатии:
    1.764
    Это всё настраивается. Я, к примеру, отключил предупреждение о суперглобальных массивах, и о скобочках после if/
     
  3. mirosas

    mirosas Активный пользователь

    С нами с:
    17 июл 2015
    Сообщения:
    236
    Симпатии:
    5
    А где, как? Я так сразу не нашел

    хм.. Хотел сюда код вставить, но скопировав его к себе он у меня просто не запустился
     
  4. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.600
    Симпатии:
    1.764
  5. mirosas

    mirosas Активный пользователь

    С нами с:
    17 июл 2015
    Сообщения:
    236
    Симпатии:
    5
    Обычно всякие сервисы такие имена не принимают. Не нравится тем, что для него отдельный обработчик писать, а так по идее любое имя ок, даже с кавычками.

    mkramer, спасибо нашел подсказки). Вроде там и искал, но с первой попытки не тот язык смотрел.
     
  6. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.600
    Симпатии:
    1.764
    Ну в ТЗ это у суриката оговорено не было, так что не стоит. Вообще, при добавлении в БД - mysqli_escape_string, при выводе - htmlspecialchars.
     
  7. mirosas

    mirosas Активный пользователь

    С нами с:
    17 июл 2015
    Сообщения:
    236
    Симпатии:
    5
    Вот код в общем, вроде работает всё. Потратил часов 10 на него. Кошмар. Хотя для первого за последние 7 лет php приложения использующего объектную модель может и неплохо.

    Просьба дать конструктивную критику:

    Код (Text):
    1.  
    2. <?php
    3.  
    4. $db_host='127.0.0.1'; //хост где находится mysql
    5. $db_login='root'; //логин пользователя бд
    6. $db_pass=''; //пароль пользователя бд
    7. $db_name='counter734'; //имя бд
    8. $min_user_age=16; // минимальный возраст юзера
    9. $max_user_age=60; // максимальный возраст юзера
    10. $firstrun=true; //при первом запуске создает таблички в бд
    11.  
    12.  
    13. // Начало скрипта
    14. $dblink=DbAndSession::init($db_host, $db_login, $db_pass, $db_name);
    15. if ($firstrun) DbAndSession::create_tables(); // создает таблички в бд
    16.  
    17. switch (@$_GET['act'])
    18. {
    19.     case '':  // главная страничка c формой логин/регистрация
    20.         Outputs::print_login_page();
    21.         break;
    22.     case 'regpage': // страничка с формой регистрации
    23.         Outputs::print_register_page();
    24.         break;
    25.     case 'registeruser': //обработка регистрации
    26.         UserAuth::init($dblink);
    27.         $check_age=Userdata::checkuserage($_POST['b_day'],$_POST['b_month'],$_POST['b_year'],$min_user_age,$max_user_age);
    28.         if ($check_age>0) Outputs::print_error_page('Too old');
    29.         if ($check_age<0) Outputs::print_error_page('Too young');
    30.         if ($check_age==0)
    31.         {
    32.             $res=UserAuth::register_user($_POST['login'], $_POST['password']);
    33.             if ($res)
    34.             {
    35.                 $userdata=new Userdata($dblink,$_POST['login']);
    36.                 $userdata->addnewuser($_POST['b_day'],$_POST['b_month'],$_POST['b_year']);
    37.                 $innerpage=true;
    38.             } else
    39.             {
    40.                 Outputs::print_error_page("Can't register. Maybe login is used "
    41.                 . "my someone else, or maybe you used incompatible characters in your login name");
    42.             }
    43.         }
    44.         break;
    45.     case 'login': // обработка залогинивания
    46.         UserAuth::init($dblink);
    47.         $login=UserAuth::login($_POST['login'], $_POST['password']);
    48.         if ($login)
    49.         {
    50.             $innerpage=true;
    51.         } else
    52.         {
    53.             Outputs::print_error_page("Can't log in. Maybe something wrong with your login or password");
    54.         }
    55.         break;
    56.     case 'plusone': //счетчик плюс один
    57.         UserAuth::init($dblink);
    58.         $userdata=new Userdata($dblink, UserAuth::getlogin());
    59.         $userdata->counter_plus_one();
    60.         $innerpage=true;
    61.         break;
    62.     case 'logout': // разлогинивание
    63.         DbAndSession::killsession(); // добавлено благодаря mkramer
    64.         Outputs::print_login_page();
    65.         break;
    66. }
    67.  
    68. if (isset($innerpage)) // работа с внутренней страницей
    69. {
    70.     if (!isset($userdata)) $userdata=new Userdata($dblink, UserAuth::getlogin());
    71.     $counter=$userdata->getcounter();
    72.     Outputs::print_inner_page($counter);
    73. }
    74.  
    75. // Конец скрипта
    76.  
    77.  
    78.  
    79. class DbAndSession
    80. // Статичный класс, отвечает за коннект с бд и сессию.
    81. {
    82.     private static $link;
    83.    
    84.     private function __construct() {}
    85.    
    86.     public static function init($mysql_host, $mysql_login, $mysql_password, $mysql_dbname)
    87.     //старт сессии, подключение к бд, возвращает линк на бд
    88.     {
    89.         if (!(isset(self::$link)))
    90.         {
    91.             session_start();
    92.             self::$link=@mysqli_connect($mysql_host,$mysql_login,$mysql_password,$mysql_dbname);
    93.             if (!self::$link) throw new Exception ("Can't connect to db");
    94.         }
    95.         return self::$link;
    96.     }
    97.    
    98.     public static function killsession()
    99.     {
    100.         session_destroy();
    101.     }
    102.    
    103.     public static function create_tables()
    104.     {
    105.         mysqli_query(self::$link, 'Create table if not exists user_auth '
    106.         . '(`login` char(50) not null, `passhash` char(50) not null, '
    107.         . 'primary key (`login`) ) engine=InnoDB default charset=utf8');
    108.        
    109.         mysqli_query(self::$link, 'Create table if not exists user_data '
    110.         . '(`login` char(50) not null, `birthday` char(50) not null, counter char(50) not null, '
    111.         . 'primary key (`login`) ) engine=InnoDB default charset=utf8');
    112.     }
    113.    
    114.     public static function get_db_link()
    115.     //возвращает линк на бд
    116.     {
    117.         return self::$link;
    118.     }
    119. }
    120.  
    121. class UserAuth
    122. //Статичный класс, отвечает за авторизационные данные пользователя
    123. {
    124.     private static $link;
    125.    
    126.     private function __constuct() {}
    127.    
    128.     public static function init(mysqli $mysql_link)
    129.     // инициализация
    130.     {
    131.         self::$link=$mysql_link;
    132.     }
    133.     public static function register_user($user_login, $user_password)
    134.     {
    135.         //заведение в базу нового юзера, при неудаче возвращает false
    136.         if (($user_login!=htmlspecialchars($user_login)) or (strlen($user_login)<1)) return false;
    137.         $passhash=md5($user_password.$user_login);
    138.         $res=mysqli_query(self::$link, "Insert into user_auth set `login` = '$user_login', `passhash` = '$passhash'");
    139.         $_SESSION['login']=$user_login;
    140.         return $res;
    141.     }
    142.     public static function login($user_login, $user_password)
    143.     {
    144.         // проверяет пользователя в бд. если найдет возвращает true, иначе false
    145.         if (($user_login!=htmlspecialchars($user_login)) or (strlen($user_login)<1)) return false;
    146.         $passhash=md5($user_password.$user_login);     
    147.         $res=mysqli_query(self::$link, "Select login from user_auth where `login` = '$user_login' and `passhash` = '$passhash'");
    148.         if (!($row=mysqli_fetch_row($res))) return false;
    149.        
    150.         $_SESSION['login']=$user_login;
    151.         return $user_login;
    152.     }
    153.     public static function getlogin()
    154.     {
    155.         //возвращает логин юзера из сессии
    156.         return $_SESSION['login'];
    157.     }
    158. }
    159.  
    160. class Userdata
    161. {
    162.     //Класс отвечает за пользовательские данные, в частности счетчик
    163.     private $login;
    164.     private static $link;
    165.     public function __construct(mysqli $mysql_link, $user_login)
    166.     {
    167.         $this->login=$user_login;
    168.         self::$link=$mysql_link;
    169.     }
    170.     public function addnewuser($birth_day, $birth_month, $birth_year)
    171.     {
    172.         // добавить пользовательские данные для нового пользователя
    173.         $birthdate=$birth_day.'.'.$birth_month.'.'.$birth_year;
    174.         mysqli_query(self::$link, "Insert into user_data set `login` = '$this->login', `birthday` = '$birthdate', `counter` = '0'");
    175.     }
    176.    
    177.     public static function checkuserage($birth_day, $birth_month, $birth_year, $minage, $maxage)
    178.     {
    179.         //проверяет возраст пользователя.
    180.         //если старше $maxage, то возвращает 1, если моложе $minage, то -1
    181.         //если возраст в пределах $minage, $maxage возвращает 0
    182.         $birthdate=$birth_day.'.'.$birth_month.'.'.$birth_year;
    183.         $c_time=time();
    184.         $c_day=@date('j',$c_time);
    185.         $c_month=@date('n',$c_time);
    186.         $c_year=@date('Y',$c_time);
    187.        
    188.         $age360=($c_year-$birth_year)*360+($c_month-$birth_month)*30+($c_day-$birth_day);
    189.         if ($age360>$maxage*360) return 1;
    190.         if ($age360<$minage*360) return -1;
    191.         return 0;
    192.        
    193.     }
    194.     public function counter_plus_one()
    195.     {
    196.         $counter=$this->getcounter()+1;
    197.         $res=mysqli_query(self::$link,"Update user_data set `counter` = '$counter' where `login` = '$this->login'");
    198.     }
    199.  
    200.     public function getcounter()
    201.     {
    202.         $res=mysqli_query(self::$link,"Select counter from user_data where `login` = '$this->login'");
    203.         if($row=mysqli_fetch_row($res)) return $row[0]; else return false;
    204.     }
    205. }
    206.  
    207. class Outputs
    208. {
    209.     public static function print_login_page()
    210.     // печать главной странички
    211.     {
    212.         echo '
    213.         <html><body><h1><br><br></h1><center>
    214.         <form action="?act=login" method="post">
    215.         <p>Логин: <input type="text" name="login" /></p>
    216.         <p>Пароль: <input type="password" name="password" /></p>
    217.         <p><input type="submit" value="Войти"></p>
    218.         <p><button formaction="?act=regpage">Регистрация</button></p>
    219.         </form>
    220.         </center></body></html>
    221.         ';
    222.     }
    223.    
    224.     public static function print_register_page()
    225.     // печать странички регистрации
    226.     {
    227.         $months = array ('1' => 'январь', '2' => 'февраль', '3' => 'март',
    228.             '4' => 'апрель', '5' => 'май', '6' => 'июнь', '7' => 'июль',
    229.             '8' => 'август', '9' => 'сентябрь', '10' => 'октябрь',
    230.             '11' => 'ноябрь', '12' => 'декабрь');
    231.         echo '
    232.         <h1><br><br></h1><center>
    233.         <form action="?act=registeruser" method="post">
    234.         <p>Логин: <input type="text" name="login" /></p>
    235.         <p>Пароль: <input type="password" name="password" /></p>
    236.         <p>
    237.         Дата рождения:
    238.         <select name="b_day">';
    239.         for ($i=1 ; $i<=31 ; $i++) echo '<option value="'.$i.'">'.$i.'</option>';
    240.         echo ' </select>
    241.         <select name="b_month">';
    242.         for ($i=1 ; $i<=12 ; $i++) echo '<option value="'.$i.'">'.$months[$i].'</option>';
    243.         echo ' </select>
    244.         <select name="b_year">';
    245.         for ($i=1900 ; $i<=2015 ; $i++) echo '<option value="'.$i.'">'.$i.'</option>';
    246.         echo ' </select>
    247.         </p>
    248.         <p><input type="submit" value="Зарегистрировать"></p>
    249.         </form>
    250.         </center>
    251.         ';
    252.     }
    253.    
    254.     public static function print_error_page($message)
    255.     // печать страницу с ошибкой
    256.     {
    257.         echo '<h3><br><br>'.$message.'</h3>';
    258.     }
    259.    
    260.     public static function print_inner_page($counter)
    261.     // печать внутреннюю страницу
    262.     {
    263.         echo '<center><p style="font-size:70px">'.$counter.'</p> '
    264.         . '<form method=post>'
    265.         . '<p><button formaction="?act=plusone">+1</button></p>'
    266.         . '<p><button formaction="?act=logout">Выход</button></p>'
    267.         . '</form>'
    268.         . '</center>';
    269.     }
    270.    
    271.     public static function print_exit_page()
    272.     // печать страницу выхода
    273.     {
    274.         echo 'You have been exited. Have a good time.';
    275.     }
    276.    
    277. }
    Добавлено спустя 3 минуты 7 секунд:
    Тоже так подумал но смутило две вещи:

    1. mysqli_real_escape_string ( mysqli $link , string $escapestr )
    Зачем обрезальщику символов ссылка на объект типа mysqli ?

    2. В документации написано: "Предостережение. Безопасность: набор символов по умолчанию. Набор символов должен быть задан либо на стороне сервера, либо с помощью API функции mysqli_set_charset(). В противном случае mysqli_real_escape_string() работать не будет."
     
  8. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.600
    Симпатии:
    1.764
    1. Чтобы учесть кодировку, установленную для соединения. Иначе она может пропустить опасный символ. Она ничего не обрезает, она экранирует, чтоб mysql воспринимал эту строку как данные, а не как команду. И получится, что ничего страшного, если пользователь у вас будет иметь имя
    Код (Text):
    1.  
    2. '; delete from users;
    Пока все запросы к БД с его учаситем идут через mysqli_escape_string или через плейсхолдеры
    2. Ну собственно, в 1 есть ответ. Ну mysqli_set_charset() вы же не развалитесь вызвать?

    Добавлено спустя 2 минуты 31 секунду:
    Это не разлогинивание, это вывод страницы логина. Разлогинивание - это удаление из сессии сведений о том, что пользователь залогинен
     
  9. mirosas

    mirosas Активный пользователь

    С нами с:
    17 июл 2015
    Сообщения:
    236
    Симпатии:
    5
    Упс.. метод создал, а не использовал. Спасибо.

    Не поленюсь, если разберусь с полными списоком что туда ставить.
     
  10. mahmuzar

    mahmuzar Старожил

    С нами с:
    6 апр 2012
    Сообщения:
    4.631
    Симпатии:
    425
    Адрес:
    РД, г. Махачкала.
    хорошо было бы если куда-нибудь выложили, чтобы можно было потыкать:) гляди, еще ошибки найдете:)
     
  11. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.600
    Симпатии:
    1.764
    Да, выложите на бесплатный хостинг какой-нибудь, есть же такие. А то так непонятно. К тому же у Fell-x27 было там кое-что про вёрстку в ТЗ, посмотреть как выглядит. Не поднимать же у себя. Интересно, что никто из выполнявших задание не использовал фреймворки. Я бы взял какой-нибудь мини или микро-фреймворк, чтоб нормальная маршрутизация была.
     
  12. mirosas

    mirosas Активный пользователь

    С нами с:
    17 июл 2015
    Сообщения:
    236
    Симпатии:
    5
    Фрейморки я еще не изучал)).

    С маршрутизацией я чего-то недогоняю...((

    Добавлено спустя 45 минут 38 секунд:
    Домен: mirosasphp.tk
    IP: 31.170.166.23
     
  13. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.600
    Симпатии:
    1.764
    У вас если просто переходить по ссылке http://mirosasphp.tk/?act=plusone, не отправляя форму, тоже счётчик увеличивается.
     
  14. mirosas

    mirosas Активный пользователь

    С нами с:
    17 июл 2015
    Сообщения:
    236
    Симпатии:
    5
    Не думал что это баг, ок пофиксил.
     
  15. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.600
    Симпатии:
    1.764
    По-хорошему, менять данные могут только POST, PUT, DELETE-запросы, GET не должны
     
  16. mirosas

    mirosas Активный пользователь

    С нами с:
    17 июл 2015
    Сообщения:
    236
    Симпатии:
    5
    Пожалуй да.
     
  17. mahmuzar

    mahmuzar Старожил

    С нами с:
    6 апр 2012
    Сообщения:
    4.631
    Симпатии:
    425
    Адрес:
    РД, г. Махачкала.
    А ведь вопрос не стоял делать на фреймворке, поэтому я не стал делать на фреймворке. Так уж и быть. Я собирался делать на laravel теперь выложу вариант на symfony 2 :)
     
  18. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    код говно неплох, есть к чему стремиться
    Комбо: собачка + прямая работа с $_GET. Никогда не знаешь, что ждет тебя в этих переменных, и есть ли они вообще. Поэтому принято сначала проверять, потом класть данные в собственные переменные и уже с ними работать. Тогда и собачка не нужна.
    Ожидал бы
    Код (Text):
    1. Outputs::printLoginPage();
    ведь имена классов именно "верблюжьи"
    Полезность $dblink в этом месте чуть ниже нуля. Ведь синглтон DB это просто must have в поделке.
    А ведь вместо if - else отлично бы сработало исключение с указанием конкретной ошибки. В пределах одного блока можно было бы уместить и проверку возраста, и попытку регистрации. Кстати, почему длина имени проверяется там, а возраст - тут?
    вообще ты можешь написать свой sessions handler

    оу щит, у тебя же есть класс с static $link, так зачем его передавать в функции?

    Тебя от if спасет mysqli_report(MYSQLI_REPORT_STRICT);, и от собачки тоже.
    не, ну это не серьезно в плане защиты от инъекций. Когда-то многие функции переставали работать на null-byte. Что-то подобное может повториться.
    Лучше вообще не знать про md5(). Её время ушло, как md4 десять лет назад.
    Запрещенные года можно сразу запретить выбирать.
     
  19. Fell-x27

    Fell-x27 Суперстар
    Команда форума Модератор

    С нами с:
    25 июл 2013
    Сообщения:
    12.156
    Симпатии:
    1.771
    Адрес:
    :сердА
    Дожили, новое направление кодинга - бессмысленные и беспощадные оверхеды ради того, чтобы нетбинс не ругался.

    Автор, поставь себе уже phpStorm. Он создан для веба как ничто другое.

    Добавлено спустя 1 минуту 33 секунды:
    В логине должно быть возможным использовать любые символы юникода, mkramer прав. Если бы нужны были ограничения, они были бы прописаны.

    Добавлено спустя 2 минуты 57 секунд:
    Пожалей воробушков, пошто по ним с пушек-то? И меня пожалей. Если все начнут фреймворки юзать для такой фигни, мне, для того, чтобы понять, что тут накодено, и какого оно качества, придется только ради этого учить %тот_фреймворк_который_попал_под_руку_человеку%?

    Добавлено спустя 3 минуты 1 секунду:
    Зашел на
    http://mirosasphp.tk/?act=plusone

    Увидел счетчик, пусть и не работающий. И кнопку "выход". Не будучи зареганным. Не будучи залогиненным. Остальное пока не проверяю - это таки косяк, потому как для незалогиненного пользователя это показываться не должно вовсе. Сайт должен определить, что я не залогинен и вывести мне главную страницу с логинилкой/регалкой. Это, конечно, прямо не прописано, но, с точки зрения здравого смысла, это вполне ожидаемое поведение приложения.
     
  20. romach

    romach Старожил

    С нами с:
    26 окт 2013
    Сообщения:
    2.904
    Симпатии:
    719
    Начни с ларки. Конечно, задание Суриката уже как минимум наполовину реализовано в любом фреймворке, но если говорить о качестве кода, в духе фреймворка и со всеми общепринятыми нормами, то тут не все так очевидно как кажется на первый взгляд, особенно в симфони.

    Добавлено спустя 27 минут 5 секунд:
    На самом деле всё неплохо, не том плане что код хорош, а в том что есть стремление не говнокодить )

    1. Не надо все писать в одном файле:
    - Конфиг лучше выносить отдельно - это даст возможность обновить скрипт не боясь перезаписи настроек, а при использовании GIT его вообще можно будет отправить в игнор, что спасет от публикации паролей на весь мир, что довольно таки распространенная ошибка )
    - Один класс - один файл. Вообще, если в файле лежит класс, то там больше ничего не должно быть. В PHP появилась автозагрузка (spl_autoload_register), классы можно подключать на лету по мере необходимости. Что будет, если в подключенном файле само по себе выполняется лишнее действие?

    2. Один стиль. Либо camelCase либо snake_case, не надо делать оба варианта. В идеале UpperCamelCase для классов и неймспейсов и lowerCamelCase для функций и методов.

    3. "static", не то что бы зло, местами оно полезно, но сейчас по сути это самообман. Были функции, мы их засунули в класс, объявили как static и сказали что ООП. На самом же деле не изменилось ничего, кроме способа вызова этих функций.

    p.s. это чисто по оформлению кода, на логику пусть Сурикат смотрит ))
     
  21. mirosas

    mirosas Активный пользователь

    С нами с:
    17 июл 2015
    Сообщения:
    236
    Симпатии:
    5
    Поправлю код, позже. Не сейчас.

    Меня такой момент интересует. А что если файлы подключаемые выпадут из дискового кэша (ну там между чтением файлов кто-то скопировал файл большой например)? А диск у нас не твердотельный, а с пластинками.. А там 100-1000 классов, каждый в своем файле.

    Ничего хорошего, но можно и другими средствами этого избежать.

    Посомтрю, учту.

    Так и есть. Но по классам раскидано как-то красивше. нет?)