Правила программиста PHP

PHP. Советы

В интернете часто встречаются статьи типа "Best practises" для PHP, повторяющие и дополняющие друг друга. Я решил объединить интересные и наиболее полезные с учетом собственного опыта. Большинство советов "КЭПские", но до сих пор встречаются в реальных проектах. Некоторый материал является вольным переводом. Ссылки на используемые источники приведены в конце статьи.

Устаревшие функции и возможности в PHP 5.3.x

1. Избегайте коротких тегов

Короткие открывающие теги <? и <?= доступны только при включенной в php.ini опции short_open_tag (тег <?= начиная с PHP 5.4 доступен всегда). Поэтому, если есть хоть какая-то вероятность того, что ваш код нужно будет размещать на серверах, к настройке которых вы доступа не имеете, используйте длинный тег <?php для указания начала PHP-кода.

2. Не закрывайте PHP-теги

Проблема заключается в том, что если php-файл с закрытым тегом включается каким-то другим php-файлом (что обычно и бывает), то наличие символов после закрытого тега ломает заголовки, либо появляются неожиданные пробелы в выводимой информации. Закрывать теги имеет смысл только тогда, когда php-код является вставкой в html-коде.

<?php
class MyClass
{
    public function test()
    {
 
    }
}

3. Пишите код в едином (в рамках проекта) стандарте

Если во всем проекте используется стиль именования переменных и функций under_score, то его нужно использовать, даже если вам больше по душе camelCase. То же касается пробелов и tab-ов. Спорить, что из них лучше, можно бесконечно, но придерживайтесь выбранного стиля в рамках проекта, невзирая на сегодняшнюю моду. Если же стиль только предстоит выбрать, рекомендую ознакомиться со стандартами PSR, предназначенными для приведения стилей разных проектов к одному стандарту и утверждаемыми разработчиками лидирующих PHP-фреймворков. Настроить современные IDE под стандарты текущего проекта не представляет труда.

4. Документируйте код

Документация используемых методов, свойств, переменных не только упрощает понимание и поддержку кода, но и позволяет генерировать документацию по проекту.

/**
 * Description of the function goes here.
 *
 * @param int $num Small description about this parameter.
 * @return boolean Small description about the return value.
 */
public function isOne($num)
{
    return $num === 1;
}

5. Не смешивайте логику и представление

Если в проекте перемешивается HTML и логика на PHP, его становится сложно поддерживать. Воспользуйтесь готовыми шаблонизаторами типа Smarty или Twig, у которых кроме всех плюсов, связанных с кодом, есть еще и кэширование сгенерированных страниц, ознакомьтесь с паттерном MVC.

{% for user in users %}
    * {{ user.name }}
{% else %}
    No users have been found.
{% endfor %}

Если же применение указанных средств не целесообразно или невозможно, ознакомьтесь с рекомендованными способами изолирования PHP-кода.

6. Используйте кодировку UTF-8

Если нет конкретного требования использовать кодировку cp1251, используйте UTF-8. Из минусов UTF-8 на сегодняшний день можно выделить только один, в большинстве случаев незначительный, - два байта на один символ против одного у cp1251. А вот плюсов у UTF-8 полно. Среди них:

  • Поддержка любых символов Unicode.
  • Универсальность: если вы решите сделать арабскую версию сайта, ничего переделывать не придется.
  • Является стандартом де-факто для большинства инструментов, таких, как JSON или Ajax, используется в URL.
  • Подавляющее большинство сайтов и компонентов использует эту кодировку (UTF-8 рекомендован консорциумом W3C и PHP Framework Interop Group), поэтому интеграция с этими сайтам, при использовании на своем сайте UTF-8, упрощается.
<meta charset="utf-8">

7. Используйте системы контроля версий

Вне зависимости от того, на сколько прост ваш проект, используйте системы контроля версий. Они привносят целый ряд приятных бонусов, таких, как

  • возврат к коду любой версии, даже если часть файлов была случайно удалена,
  • легкая параллельная разработка разного функционала и быстрое переключением между ним,
  • удобство переноса кода в другое окручение и быстрое разворачивание на новом месте,
  • позволяет быстрее найти причину появившегося бага путем просмотра разницы кода между версиями,
  • возможность быстрого переноса изменений между версиями.

8. Используйте редирект после получения POST-запросов

Это не специфика PHP. Проблема заключается в том, что отправив данные с формы, пользователь может нажать F5, и браузер отправит эти данные повторно, даже если сама форма уже не видна. Эта же проблема появляется, если браузер восстанавливает вкладки после закрытия. Да, большинство современных браузеров научились спрашивать разрешения перед повторной отправкой, но все же лучше этого не допускать. Более подробно можно почитать в Wiki: Post/Redirect/Get.

// Обработка данных формы
 
// Если обработка прошла успешно
if ($success) {
    //Выполняем редирект
    header('Location: page.php?msg=success');
    exit;
}

9. Не используйте deprecated-функции

В последние годы PHP очень активно развивается, делая семимильные шаги. И разработчикам необходимо следить за этими изменениями, т.к. вместе с функционалом исправляется много багов, которые могут быть критическими для вашего проекта. И, рано или поздно, придется переходить на более новую версию. Если при этом активно использовались инструменты, давно отмеченные как deprecated и уже не поддерживающиеся, переход может стать очень дорогим.

Список всех устаревших функций между версиями PHP 5.3 - 5.6

На момент написания этой заметки к таким можно отнести:

  • PHP 5.6
    • Вызов методов с несовместимым контекстом
      class A {
          function f() { echo get_class($this); }
      }
       
      class B {
          function f() { A::f(); }
      }
       
      (new B)->f();
                                                  
    • Суперглобальная переменная $HTTP_RAW_POST_DATA и настройка always_populate_raw_post_data использовались для работы с "сырыми" POST-данными.

      В качестве замены рекомендуется использование php://input.

  • PHP 5.5
    • Расширение mysql

      Это расширение было написано для версии MySQL 3.23 (на момент написания статьи актуальной является версия 5.6) и с тех пор не развивалось, оно не поддерживает целый ряд полезных инструментов, таких как транзакции, хранимые процедуры, асинхронные запросы и т.д.

      В качестве альтернативы предлагаются MySQLi и PDO_MySQL. Также, стоит присмотреться к какой-нибудь ORM, которые, кроме удобных методов для работы с БД добавят слой дополнительный абстракции, позволяющий безболезненно менять СУБД.

    • Модификатор /e в функции preg_replace()

      Этот модификатор использовался для указания того, что код в регулярном выражении должен выполняться как PHP-выражение.

      В качестве замены предлагается использование preg_replace_callback().

    • Фунций шифрования mcrypt_cbc(), mcrypt_cfb(), mcrypt_ecb(), mcrypt_ofb().

      Вместо них следует использовать mcrypt_encrypt(), указывая режим через четвертый параметр mode.

    • IntlDateFormatter::setTimeZoneID() и datefmt_set_timezone_id().

      Как видно из названия, эти функции использовались для указания временной зоны.

      Если, вдруг, применяли их у себя, переходите на IntlDateFormatter::setTimeZone() или datefmt_set_timezone().

  • PHP 5.4
    • mcrypt_generic_end()

      Функция прерывала шифрование и иногда приводила к падениям при использовании mcrypt_module_close().

      Заменили на mcrypt_generic_deinit().

    • mysql_list_dbs()

      Функция возвращала список баз данных, доступных на сервере.

      В качестве альтернативы предлагается использование MySQLi и PDO_MySQL

  • PHP 5.3
    • INI-директивы

      • define_syslog_variables
      • register_globals
      • register_long_arrays
      • safe_mode
      • magic_quotes_gpc
      • magic_quotes_runtime
      • magic_quotes_sybase

      Также, начиная с этой версии, считаются усталевшими комментарии в php.ini, начинающиеся с # (вместо # нужно использовать;).

    • call_user_method() заменили на call_user_func().

      call_user_method_array() заменили на call_user_func_array().

    • define_syslog_variables() использовалась для инициализации syslog-переменных.

    • ld() загружала расширения во время выполнения кода.

      Грузите через php.ini.

    • Функции ereg(), ereg_replace(), eregi(), eregi_replace() для работы со строками через регулярные выражения.

      Используйте соответственно preg_match(), preg_replace(), preg_match() c модификатором i, preg_replace() c модификатором i

    • set_magic_quotes_runtime() и ее синоним magic_quotes_runtime().

      Придназначались для работы с магическими кавычками и были выпилены вместе со всей поддержкой этих самых кавычек.

    • Функции session_register(), session_unregister(), session_is_registered(). теперь не рекомендуется использовать.

      Работайте напрямую с суперглобальным массиов $_SESSION.

    • Вместо set_socket_blocking() теперь рекомендуют использовать stream_set_blocking().

    • split() и spliti() заменяйте на preg_split().

    • sql_regcase() использовалась для создания регулярного выражения для регистронезависимого сравнения.

      Не могу придумать задачи, чтобы сказать аналог этой функции :)

    • mysql_db_query() и mysql_escape_string() рекомендовали заменять на mysql_select_db(), mysql_query() и mysql_real_escape_string(), но мы то с вами знаем, что даже эти три указанные замены сегодня не рекомендуется использовать (см. список устаревших функций у PHP 5.5).

    • Передача строки, задающей категорию функций, на которые будет влиять установка локали, сейчас считается устаревшим вариантом.

      Используйте вместо этого семейство констант LC_*. Что это за константы такие, я не смог разобраться. Если знаете, подскажите.

    • Параметр is_dst в функции mktime().

      Вместо него используйте новые функции работы с временными зонами.

10. Изучайте особенности инструментов, которыми пользуетесь. Присматривайтесь к аналогичным технологиям

Используете в своем проекте MySQL? Скажите, какой тип данных лучше выбрать для стоимости товара: FLOAT, DECIMAL, DOUBLE? Вы, случаем, не сохраняете даты в VARCHAR? А можете рассказать различия между innodb и MyISAM? А пробовали ли вы MariaDB? А может посмотреть в сторону NoSQL решений? Чтобы достичь максимальных показателей производительности на своем проекте, необходимо хотя бы иметь представления о том, что дает нам рынок ПО на текущий момент и из чего можно выбирать. И нельзя сказать, что инструмент А лучше инструмента Б, каждый имеет свои плюсы при определенных условиях. Поэтому решение, какой из них использовать, зависит от конкретной задачи.

11. Не доверяйте данным, получаемым от пользователя

При написании веб-приложения проверяйте, фильтруйте все, что приходит от пользователя. Ведите разработку с мыслью, что ваши пользователи будут активно пытаться взломать ваше приложение. На подмене данных основаны такие атаки, как PHP-инъекции, SQL-инъекции, XSS.

$file = $_GET['file']; // "../../etc/passwd\0"
if (file_exists('/home/wwwrun/'.$file.'.php')) {
    // file_exists will return true as the file /home/wwwrun/../../etc/passwd exists
    include '/home/wwwrun/'.$file.'.php';
    // the file /etc/passwd will be included
}

12. Защищайтесь от XSS

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

Для защиты пользуйтесь функциями htmlspecialchars() или htmlentities().

echo htmlspecialchars($userComment, ENT_QUOTES, 'utf-8');

13. Избегайте микрооптимизации

А вы знаете, что чем длиннее имена у свойств и методов класса, тем больше класс занимает места в памяти? А еще существует мнение, что копирование свойств обекта ближе к методу, в котором они используются, ускоряет выполнение кода? А если убрать все лишние символы в коде (пробелы, переносы строк и т.п.), то код начнет выполняться быстрее? Может быть вы не слышали, что объединение нескольких скриптов в один быстрее, чем несколько include? Эту тему я могу продолжать почти бесконечно. Помните, если у вас возникает желание сделать микрооптимизацию (если вы не вконтакте разрабатываете, конечно), чтобы ускорить ваше приложение, значит в вашем приложении что-то очень неправильно организовано. Зачастую экономия на микрооптимизациях выливается в очень дорогую поддержку этого оптимизированного кода.

14. Не выполняйте SQL запросы в циклах

Выполнение запросов внутри большого цикла могут быть очень дорогими, особенно с учетом того, что в большинстве случаев количество итераций цикла будет со временем расти. Это будет приводить ко все более и более медленной загрузке страницы и вызывать лишние нагрузки на базу данных. Вместо циклов в таких случаях нужно применять JOIN-ы, а в случаях со вставкой данных - множественные вставки изагрузку из файлов.

15. Хэшируйте пользовательские пароли

Не храните пароли в открытом виде, т.к. если дамп вашей базы попадет "не в те руки", у злоумышленника появится доступ к любому аккаунту вашего сайта (а в большинстве случаев, и к аккаунтам этих пользователей в других разных популярных сайтах). Существует множество способов хэширования разной сложности и эффективности, но с версии 5.5 в PHP появилась стандарнтая функция хэшированияpassword_hash(), а если вы застряли на более ранней версии, можете воспольоваться функцией на языке PHP, полностью совместимой со стандартной password_hash().

$hash = password_hash($password, PASSWORD_BCRYPT);
if (password_verify($password, $hash)) {
    /* Valid */
} else {
    /* Invalid */
}

16. Избегайте использование глобальных переменных

Глобальные переменные вносят путаницу в логику кода. Куски, в которых используются такие переменные, не могут никуда быть перенесены. Зачастую они могут быть изменены в каком-то из используемых методов, что приведет к неожиданным результатам. С другой стороны, изменять значения глобальных переменных приходится с опаской, мало ли в каком месте код может сломаться из-за этого. Еще о них нужно знать, их имена нужно помнить, когда пишешь новый метод.

Вместо глобальных переменных пользуйтесь тем инструментарием, который дает нам ООП: передавайте в качестве параметров функций, пользуйтесь пространствами имен, константами классов, свойствами, применяйте геттеры-сеттеры.

17. Избегайте использование "or die()"

Очень часто можно в самодельных сайтах можно встретить такие конструкции

$link = mysql_connect("localhost", "mysql_user", "mysql_password") or die("Could not connect: " . mysql_error());
print ("Connected successfully");
mysql_close($link);

Про функции mysql_* мы уже поговорили в пункте про deprecated функции, а сейчас коснемся второй части этого выражения (пример, между прочим, с официального сайта PHP). Означает оно буквально следующее: если mysql_query возвращает false (т.е. запрос не удалось выполнить), то закончить выполнение с выводом текста ошибки. Проблема die() в том, что вы, как разработчик, даже не узнаете о ней, нет никаких способов перехватить die().

Будет гораздо лучше, если вы воспользуетесь механизмом исключений, которые могут быть перехвачены через try - catch, либо через свой обработчик исключений.

$res = mysql_query($sql, $conn);
if($res === false){
    throw new Exception(mysql_error($conn));
}

18. Пользуйтесь системой ошибок при разработке

При разработке установите директивы php.ini следующим образом:

display_errors = On
display_startup_errors = On
error_reporting = -1
log_errors = On

Это позволит видеть все ошибки. В боевом же режиме (на production) ошибки нужно прятать, чтобы ими не воспользовались потенциальные злоумышленники, или чтобы эти ошибки не смущали пользователей и не ломали верстку сайта:

display_errors = Off
display_startup_errors = Off
error_reporting = E_ALL
log_errors = On

Ошибки всё также будут записываться в лог ошибок веб сервера, но не будут показаны пользователю.

19. Кэшируйте данные

Скорости интернета уже давно приучили пользователей к тому, что после клика на ссылке страница должна сразу же отобразиться в браузере. Задержки даже в пару секунд в большинстве случаев вызывают раздражение и желание покинуть сайт. Поэтому нужно кэшировать все, что возможно. Кэшировать нужно все, что можно закэшировать: выбираемые из БД данные, формируемые страницы, PHP-код. Кэширование - это отдельная большая тема для разговора, которой я планирую посвятить несколько следующих статей.

20. Попробуйте фреймворки

Фреймворки содержат каркас приложения с готовыми часто используемыми инструментами: вам не нужно писать механизмы авторизации и уровней доступа, роутинга, обработки форм, работы с базой данных, миграций и многого другого. Они обеспечивают стабильность и безопасность вашего приложения. Благодаря стандартизованным подходам, новому разработчику, имеющему опыт работы с фреймворками, гораздо проще, а значит и быстрее, вникнуть в код приложения.

Фреймворков на PHP написано превеликое множество, поэтому однозначного совета по выбору быть не может. Подобрать фреймворк под задачу можно, воспользовавшись этим сервисом.

21. Не доверяйте слепо советам, в том числе и этим

Несмотря на то, что большая часть советов основана на опыте (на моем в том числе), все же рекомендую не следовать слепо всему, что написано здесь, либо где-то еще. Проверяйте, пробуйте нарушать и наблюдайте, делайте свои собственные выводы.

Приятного кодинга и красивого кода!

 

Источник http://vanchester.ru/php_sovety


Rambler's Top100
Rambler's Top100 Check Page Rank