С головой в PHP. Пособие для новичков
от web_demon
Здравствуй юный программист на PHP. Вероятно ты уже пишешь свои скрипты и в курсе синтаксиса этого языка, но тебя все равно обзывают быдлокодырем и смеются на форумах. И вот простое решение проблемы: просто смирись, программировать на PHP это уже смешно =)
Но! Уменьшить количество насмешек можно, просто выполняя несколько простых правил:
1. Использовать одинарные кавычки для строк.
У новичков это проблема номер один. Сами посудите, их даже набирать проще: тыкнул на клавишу и все. А для двойной кавычки нужно нажать целых две клавиши, сколько трудов пропадает зря, кошмар!
Пример:
Помимо нормального объяснения изложенного в пункте 2, в таком использовании кавычек есть свои плюсы. Например можно (а может и нужно) разграничивать кавычки для html и php. То есть в php вы всегда используете одиночные, а в html - всегда двойные, к примеру так:
А не так:
А ведь такое встречается очень часто, иногда целые леса из обратных слешей, от которых кровь из глаз идет.
И пусть мешанина php с html - это уже априори быдлокод (ищите инфу про шаблонизаторы), но хотя бы позволит его сделать куда более читаемым.
2. Выносить переменные за пределы строк.
Вот тут и нужны одинарные кавычки, дело в том что в них не происходит поиск переменных, поэтому они работаю чуточку быстрее, но нам это не сильно важно. Важно то, что вынесение переменных за пределы строки гарантирует правильное исполнение кода и отсутствие у вас проблем с поиском ошибки, например:
В зависимости от версии php выводят непредсказуемый для вас результат. Гораздо проще написать:
И быть уверенным что в выводе у вас появится то что нужно. И самое важное, такой код гораздо легче разбирать, потому как переменные не скрыты в недрах строк.
3. Использовать одинарные кавычки для ключей массивов
вместо того чтоб их вообще не использовать. Насчет двойных смотрите пункт 1.
Рассмотрим на примере:
Поскольку key без кавычек, PHP проверит существование константы key и только потом поймет, что это просто название ключа в массиве. Мало того что это работает медленнее, так еще может выскакивать warning в зависимости от настроек сервера на котором исполнятся скрипт. Такая неопределенность может серьезно попортить нервы, поскольку ошибка может воспроизводится на боевом сервере, а у вас на локалке - нет. Поэтому делайте так:
и все будет в порядке.
4. Фильтровать переменные
Уязвимостям посвящают огромные статьи, но на самом деле 90% всего можно уложить в один абзац.
От пользователя часто приходит всякая ерунда, нам нужно разобраться что с этим делать.
a) Итак, мы ожидаем от пользователя число, обработать его нужно так:
И теперь с $id можно делать все что угодно, выводить, добавлять в базу и так далее.
b)Мы ожидаем строку, которую нужно вывести в браузер:
Опять же уязвимости нет. Обрабатывать нужно все что приходит от пользователя: элементы из $_GET и $_POST данных, а также другие глобальные массивы типа $_SERVER, если вы направляете их в вывод. Все что так или иначе приходит от пользователя и выводится в браузер - уже уязвимость, если вы не предприняли выше обозначенных мер.
с) Мы используем переменную в sql-запросе, к примеру так:
И это огромная дыра в вашем скрипте, чтоб такого не было, и какой-нибудь вредитель не наделал беды, делаем так:
Все! Волшебная функция сделает все за нас. Но тут есть два момента. Первый: использовать mysql_real_escape_string() лучше в самом запросе, как в примере. Если отдельно перезаписать переменную:
при выводе в браузер в самой переменной перед спецсимволами появятся слэши. В итоге полученные данные не будут соответствовать первоначальным. Второе: если вы выводите текст из базы в браузер, его все равно нужно фильтровать согласно пункту 4b. Как вариант, в базу можно добавить сразу обработанный сразу двумя функциями текст:
тогда при выводе из базы о фильтрации можно не беспокоиться.
d) Register_globals всегда должен быть отключен!!
Хоть эта опция на большинстве хостингов и так отключена, лишний раз проверить не помешает.
Если все же нарвались на плохой хостинг, отключить можно в корневом в .htaccess дописав строчку "php_flag register_globals off" (без кавычек) или в php.ini, или в настройках панели управления.
Дело в том, что, если вы явно не инициализируете переменную (как это нужно делать в любой другом языке) с включенным register_globals, можно получить уязвимость откуда не ждали.
e) Инициализация переменных - штука не обязательная, но желательная. Разные версии php, в зависимости от настроек, могут выдавать или не выдавать warning'и, если вы отправите переменную прямо в бой, поэтому лучше делать так:
Проверять вероятно не существующие переменные нужно так:
а не так:
поскольку если переменной не существовало, будет сгенерировано предупреждение, которое выскакивает в зависимости он настроек PHP, что нам совершенно не нужно.
5. Использовать апострофы в sql-запросах.
Опять же это нужно делать для избежания неприятных ошибок, когда "вроде правильно", а ничерта не работает. То есть не так:
А так:
Все названия таблиц и полей нужно заключать в апострофы. Да, первый вариант тоже будет работать, но не всегда. Если имя таблицы или поля совпадет с каким-нибудь зарезервированным словом в mysql, вы получите ошибку. Так что вместо того чтоб учить список зарезервированных слов, просто используйте апострофы (можно набрать клавишей под ESC, кто не знал).
6. Форматировать код.
Ну пожалуйста, не надо такого:
Этот совершенно не читаемый код, добавив пробелов (или табуляции, кому как удобно), можно превратить это в конфетку:
Стилей форматирования очень много, воспользуйтесь поисковиком и выберите наиболее подходящий вам.
7. Следить за двойным знаком равенства.
Это поможет избежать трудно находимых ошибок. Просто каждый раз набирая этот символ, обращайте внимание на то что у вас получилось.
Если вы все же растяпа и уже раза три четыре искали ошибку пару часов к ряду, то можно воспользоваться маленькой хитростью: при сравнении переменных с числом или строкой просто меняйте их местами. То есть не так:
А вот так:
Если вы наберете всего одно "равно", выскочит ошибка, которая укажет на нужную вам строку.
8. Не сравнивать числа с плавающей точкой.
В силу их реализации в PHP результат может быть удивительным, например:
оригинал by L!MP
Если хотите сделать это правильно (подсказано reaper) то следует использовать библиотеку libbcmath:
Или сравнивать через погрешность (спасибо aNNiMON):
Где EPS - погрешность (0.1, 0.0001, 0.00000000001) в зависимости от задачи.
Или самый простой (но самый некрасивый), это преобразовывать float в string и сравнивать числа как строки:
9. Комментировать код.
Не обязательно добавлять к каждой строчке по комментарию, или писать огромные описания в PHPDoc, просто делая сложный алгоритм опишите вкратце принцип работы, чтоб было понятно хотя бы вам. На начальном этапе ваш код будете видеть только вы. А вот необходимость комментировать придет с опытом, когда посидите с десяток раз над своим же кодом или получите по шапке от вашего коллеги.
Заключение:
Эти парочку правил могут серьезно упростить вашу программистскую долю, а главное сэкономить нервы и вам и вашим коллегам, которым, скорей всего, придется разбирать ваш код.
PS: Если я забыл о чем упомянуть, напишите в комментариях. Хочется вынести всякие спорные моменты в одну статью, чтоб просто посылать на нее новичков, а не распинаться каждый раз на форуме и в личке.
Но! Уменьшить количество насмешек можно, просто выполняя несколько простых правил:
1. Использовать одинарные кавычки для строк.
У новичков это проблема номер один. Сами посудите, их даже набирать проще: тыкнул на клавишу и все. А для двойной кавычки нужно нажать целых две клавиши, сколько трудов пропадает зря, кошмар!
Пример:
- echo "Так делают быдлокодеры";
- echo 'Так делают кодеры';
- echo '<div class="bmenu">Заголовок</div>';
- echo '<div class=\'bmenu\'>Заголовок</div>';
И пусть мешанина php с html - это уже априори быдлокод (ищите инфу про шаблонизаторы), но хотя бы позволит его сделать куда более читаемым.
2. Выносить переменные за пределы строк.
Вот тут и нужны одинарные кавычки, дело в том что в них не происходит поиск переменных, поэтому они работаю чуточку быстрее, но нам это не сильно важно. Важно то, что вынесение переменных за пределы строки гарантирует правильное исполнение кода и отсутствие у вас проблем с поиском ошибки, например:
- echo "Вывод: $test['key'] оп-оп";
- echo "Вывод: test::$test оп-оп";
- echo 'Вывод: ' . $test['key'] . ' оп-оп';
- echo 'Вывод: ' . test::$test . ' оп-оп'
3. Использовать одинарные кавычки для ключей массивов
вместо того чтоб их вообще не использовать. Насчет двойных смотрите пункт 1.
Рассмотрим на примере:
- echo $array[key];
- echo $array['key'];
4. Фильтровать переменные
Уязвимостям посвящают огромные статьи, но на самом деле 90% всего можно уложить в один абзац.
От пользователя часто приходит всякая ерунда, нам нужно разобраться что с этим делать.
a) Итак, мы ожидаем от пользователя число, обработать его нужно так:
- $id = abs(intval($_GET['id']));
b)Мы ожидаем строку, которую нужно вывести в браузер:
- $text = htmlentities($_GET['text'], ENT_QUOTES, 'UTF-8');
- echo $text;
с) Мы используем переменную в sql-запросе, к примеру так:
- $text = $_GET['text'];
- mysql_query('SELECT * FROM `users` WHERE `user` = "' . $text . '";');
- $text = $_GET['text'];
- mysql_query('SELECT * FROM `users` WHERE `user` = "'.mysql_real_escape_string($text).'";');
- $text = mysql_real_escape_string($text);
- $text = $_GET['text'];
- $text = htmlentities($text, ENT_QUOTES, 'UTF-8');
- 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'и, если вы отправите переменную прямо в бой, поэтому лучше делать так:
- $text = ''; //$text = 'Какой-то текст'; - тоже считается
- $array = array();
- $intvar = 0;
- if(isset($text)) {}
- if($text) {}
5. Использовать апострофы в sql-запросах.
Опять же это нужно делать для избежания неприятных ошибок, когда "вроде правильно", а ничерта не работает. То есть не так:
- mysql_query('INSERT INTO users SET user = "' . mysql_real_escape_string($text) . '";');
- mysql_query('INSERT INTO `users` SET `user` = "' . mysql_real_escape_string($text) . '";');
6. Форматировать код.
Ну пожалуйста, не надо такого:
- function CAN($what, $editing_rights){
- if(core::$user_id){
- $editing_rights = intval(abs($editing_rights));
- $rights_arr = core::$all_rights;
- if(core::$rights < $editing_rights)
- return FALSE;
- else{
- if(!empty($rights_arr[core::$rights][$what]))
- return TRUE;
- else
- return FALSE;
- }
- }else
- return FALSE;
- }
- function CAN($what, $editing_rights)
- {
- if(core::$user_id)
- {
- $editing_rights = abs(intval($editing_rights));
- $rights_arr = core::$all_rights;
- if(core::$rights < $editing_rights)
- return FALSE;
- else
- {
- if(!empty($rights_arr[core::$rights][$what]))
- return TRUE;
- else
- return FALSE;
- }
- }
- else
- return FALSE;
- }
7. Следить за двойным знаком равенства.
Это поможет избежать трудно находимых ошибок. Просто каждый раз набирая этот символ, обращайте внимание на то что у вас получилось.
Если вы все же растяпа и уже раза три четыре искали ошибку пару часов к ряду, то можно воспользоваться маленькой хитростью: при сравнении переменных с числом или строкой просто меняйте их местами. То есть не так:
- if($var == 'string')
- if('string' == $var)
8. Не сравнивать числа с плавающей точкой.
В силу их реализации в PHP результат может быть удивительным, например:
- $a = 15.9; //=> float 15.9
- $b = 5.3 * 3; //=> то же, float 15.9
- var_dump($a == $b); //=> думаете равно? false
Если хотите сделать это правильно (подсказано reaper) то следует использовать библиотеку libbcmath:
- var_dump(bccomp(15.9, 5.3 * 3) == 0); // true
- $equals = ( abs($a - $b) < EPS );
Или самый простой (но самый некрасивый), это преобразовывать float в string и сравнивать числа как строки:
- $a = 15.9; //=> float 15.9
- $b = 5.3 * 3; //=> то же, float 15.9
- var_dump((string)$a == (string)$b); //true
9. Комментировать код.
Не обязательно добавлять к каждой строчке по комментарию, или писать огромные описания в PHPDoc, просто делая сложный алгоритм опишите вкратце принцип работы, чтоб было понятно хотя бы вам. На начальном этапе ваш код будете видеть только вы. А вот необходимость комментировать придет с опытом, когда посидите с десяток раз над своим же кодом или получите по шапке от вашего коллеги.
Заключение:
Эти парочку правил могут серьезно упростить вашу программистскую долю, а главное сэкономить нервы и вам и вашим коллегам, которым, скорей всего, придется разбирать ваш код.
PS: Если я забыл о чем упомянуть, напишите в комментариях. Хочется вынести всякие спорные моменты в одну статью, чтоб просто посылать на нее новичков, а не распинаться каждый раз на форуме и в личке.