С головой в PHP. Пособие для новичков

от
PHP/MySQL

Здравствуй юный программист на PHP. Вероятно ты уже пишешь свои скрипты и в курсе синтаксиса этого языка, но тебя все равно обзывают быдлокодырем и смеются на форумах. И вот простое решение проблемы: просто смирись, программировать на PHP это уже смешно =)
Но! Уменьшить количество насмешек можно, просто выполняя несколько простых правил:


1. Использовать одинарные кавычки для строк.
У новичков это проблема номер один. Сами посудите, их даже набирать проще: тыкнул на клавишу и все. А для двойной кавычки нужно нажать целых две клавиши, сколько трудов пропадает зря, кошмар!
Пример:
  1. echo "Так делают быдлокодеры";
  2. echo 'Так делают кодеры';
Помимо нормального объяснения изложенного в пункте 2, в таком использовании кавычек есть свои плюсы. Например можно (а может и нужно) разграничивать кавычки для html и php. То есть в php вы всегда используете одиночные, а в html - всегда двойные, к примеру так:
  1. echo '<div class="bmenu">Заголовок</div>';
А не так:
  1. echo '<div class=\'bmenu\'>Заголовок</div>';
А ведь такое встречается очень часто, иногда целые леса из обратных слешей, от которых кровь из глаз идет.
И пусть мешанина php с html - это уже априори быдлокод (ищите инфу про шаблонизаторы), но хотя бы позволит его сделать куда более читаемым.


2. Выносить переменные за пределы строк.
Вот тут и нужны одинарные кавычки, дело в том что в них не происходит поиск переменных, поэтому они работаю чуточку быстрее, но нам это не сильно важно. Важно то, что вынесение переменных за пределы строки гарантирует правильное исполнение кода и отсутствие у вас проблем с поиском ошибки, например:
  1. echo "Вывод: $test['key'] оп-оп";
  2. echo "Вывод: test::$test оп-оп";
В зависимости от версии php выводят непредсказуемый для вас результат. Гораздо проще написать:
  1. echo 'Вывод: ' . $test['key'] . ' оп-оп';
  2. echo 'Вывод: ' . test::$test . ' оп-оп'
И быть уверенным что в выводе у вас появится то что нужно. И самое важное, такой код гораздо легче разбирать, потому как переменные не скрыты в недрах строк.


3. Использовать одинарные кавычки для ключей массивов
вместо того чтоб их вообще не использовать. Насчет двойных смотрите пункт 1.
Рассмотрим на примере:
  1. echo $array[key];
Поскольку key без кавычек, PHP проверит существование константы key и только потом поймет, что это просто название ключа в массиве. Мало того что это работает медленнее, так еще может выскакивать warning в зависимости от настроек сервера на котором исполнятся скрипт. Такая неопределенность может серьезно попортить нервы, поскольку ошибка может воспроизводится на боевом сервере, а у вас на локалке - нет. Поэтому делайте так:
  1. echo $array['key'];
и все будет в порядке.


4. Фильтровать переменные
Уязвимостям посвящают огромные статьи, но на самом деле 90% всего можно уложить в один абзац.
От пользователя часто приходит всякая ерунда, нам нужно разобраться что с этим делать.

a) Итак, мы ожидаем от пользователя число, обработать его нужно так:
  1. $id = abs(intval($_GET['id']));
И теперь с $id можно делать все что угодно, выводить, добавлять в базу и так далее.

b)Мы ожидаем строку, которую нужно вывести в браузер:
  1. $text = htmlentities($_GET['text'], ENT_QUOTES, 'UTF-8');
  2. echo $text;
Опять же уязвимости нет. Обрабатывать нужно все что приходит от пользователя: элементы из $_GET и $_POST данных, а также другие глобальные массивы типа $_SERVER, если вы направляете их в вывод. Все что так или иначе приходит от пользователя и выводится в браузер - уже уязвимость, если вы не предприняли выше обозначенных мер.

с) Мы используем переменную в sql-запросе, к примеру так:
  1. $text = $_GET['text'];
  2. mysql_query('SELECT * FROM `users` WHERE `user` = "' . $text . '";');
И это огромная дыра в вашем скрипте, чтоб такого не было, и какой-нибудь вредитель не наделал беды, делаем так:
  1. $text = $_GET['text'];
  2. mysql_query('SELECT * FROM `users` WHERE `user` = "'.mysql_real_escape_string($text).'";');
Все! Волшебная функция сделает все за нас. Но тут есть два момента. Первый: использовать mysql_real_escape_string() лучше в самом запросе, как в примере. Если отдельно перезаписать переменную:
  1. $text = mysql_real_escape_string($text);
при выводе в браузер в самой переменной перед спецсимволами появятся слэши. В итоге полученные данные не будут соответствовать первоначальным. Второе: если вы выводите текст из базы в браузер, его все равно нужно фильтровать согласно пункту 4b. Как вариант, в базу можно добавить сразу обработанный сразу двумя функциями текст:
  1. $text = $_GET['text'];
  2. $text = htmlentities($text, ENT_QUOTES, 'UTF-8');
  3. mysql_query('INSERT INTO `users` SET `user` = "' . mysql_real_escape_string($text) . '";');
тогда при выводе из базы о фильтрации можно не беспокоиться.

d) Register_globals всегда должен быть отключен!!
Хоть эта опция на большинстве хостингов и так отключена, лишний раз проверить не помешает.
Если все же нарвались на плохой хостинг, отключить можно в корневом в .htaccess дописав строчку "php_flag register_globals off" (без кавычек) или в php.ini, или в настройках панели управления.
Дело в том, что, если вы явно не инициализируете переменную (как это нужно делать в любой другом языке) с включенным register_globals, можно получить уязвимость откуда не ждали.

e) Инициализация переменных - штука не обязательная, но желательная. Разные версии php, в зависимости от настроек, могут выдавать или не выдавать warning'и, если вы отправите переменную прямо в бой, поэтому лучше делать так:
  1. $text = ''; //$text = 'Какой-то текст'; - тоже считается
  2. $array = array();
  3. $intvar = 0;
Проверять вероятно не существующие переменные нужно так:
  1. if(isset($text)) {}
а не так:
  1. if($text) {}
поскольку если переменной не существовало, будет сгенерировано предупреждение, которое выскакивает в зависимости он настроек PHP, что нам совершенно не нужно.


5. Использовать апострофы в sql-запросах.
Опять же это нужно делать для избежания неприятных ошибок, когда "вроде правильно", а ничерта не работает. То есть не так:
  1. mysql_query('INSERT INTO users SET user = "' . mysql_real_escape_string($text) . '";');
А так:
  1. mysql_query('INSERT INTO `users` SET `user` = "' . mysql_real_escape_string($text) . '";');
Все названия таблиц и полей нужно заключать в апострофы. Да, первый вариант тоже будет работать, но не всегда. Если имя таблицы или поля совпадет с каким-нибудь зарезервированным словом в mysql, вы получите ошибку. Так что вместо того чтоб учить список зарезервированных слов, просто используйте апострофы (можно набрать клавишей под ESC, кто не знал).


6. Форматировать код.
Ну пожалуйста, не надо такого:
  1. function CAN($what, $editing_rights){
  2. if(core::$user_id){
  3. $editing_rights = intval(abs($editing_rights));
  4. $rights_arr = core::$all_rights;
  5. if(core::$rights < $editing_rights)
  6. return FALSE;
  7. else{
  8. if(!empty($rights_arr[core::$rights][$what]))
  9. return TRUE;
  10. else
  11. return FALSE;
  12. }
  13. }else
  14. return FALSE;
  15. }
Этот совершенно не читаемый код, добавив пробелов (или табуляции, кому как удобно), можно превратить это в конфетку:
  1. function CAN($what, $editing_rights)
  2. {
  3.   if(core::$user_id)
  4.   {
  5.     $editing_rights = abs(intval($editing_rights));
  6.     $rights_arr = core::$all_rights;
  7.  
  8.     if(core::$rights < $editing_rights)
  9.       return FALSE;
  10.     else
  11.     {
  12.       if(!empty($rights_arr[core::$rights][$what]))
  13.         return TRUE;
  14.       else
  15.         return FALSE;
  16.     }
  17.   }
  18.   else
  19.     return FALSE;
  20. }
Стилей форматирования очень много, воспользуйтесь поисковиком и выберите наиболее подходящий вам.


7. Следить за двойным знаком равенства.
Это поможет избежать трудно находимых ошибок. Просто каждый раз набирая этот символ, обращайте внимание на то что у вас получилось.
Если вы все же растяпа и уже раза три четыре искали ошибку пару часов к ряду, то можно воспользоваться маленькой хитростью: при сравнении переменных с числом или строкой просто меняйте их местами. То есть не так:
  1. if($var == 'string')
А вот так:
  1. if('string' == $var)
Если вы наберете всего одно "равно", выскочит ошибка, которая укажет на нужную вам строку.


8. Не сравнивать числа с плавающей точкой.
В силу их реализации в PHP результат может быть удивительным, например:
  1. $a = 15.9;    //=> float 15.9
  2. $b = 5.3 * 3; //=> то же, float 15.9
  3.  
  4. var_dump($a == $b); //=> думаете равно? false
оригинал by L!MP

Если хотите сделать это правильно (подсказано reaper) то следует использовать библиотеку libbcmath:
  1. var_dump(bccomp(15.9, 5.3 * 3) == 0); // true
Или сравнивать через погрешность (спасибо aNNiMON):
  1. $equals = ( abs($a - $b) < EPS );
Где EPS - погрешность (0.1, 0.0001, 0.00000000001) в зависимости от задачи.

Или самый простой (но самый некрасивый), это преобразовывать float в string и сравнивать числа как строки:
  1. $a = 15.9;    //=> float 15.9
  2. $b = 5.3 * 3; //=> то же, float 15.9
  3. var_dump((string)$a == (string)$b); //true


9. Комментировать код.
Не обязательно добавлять к каждой строчке по комментарию, или писать огромные описания в PHPDoc, просто делая сложный алгоритм опишите вкратце принцип работы, чтоб было понятно хотя бы вам. На начальном этапе ваш код будете видеть только вы. А вот необходимость комментировать придет с опытом, когда посидите с десяток раз над своим же кодом или получите по шапке от вашего коллеги.


Заключение:
Эти парочку правил могут серьезно упростить вашу программистскую долю, а главное сэкономить нервы и вам и вашим коллегам, которым, скорей всего, придется разбирать ваш код.

PS: Если я забыл о чем упомянуть, напишите в комментариях. Хочется вынести всякие спорные моменты в одну статью, чтоб просто посылать на нее новичков, а не распинаться каждый раз на форуме и в личке.
  • +23
  • views 5368