Стандарты кодирования PHP

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

Отступы и пробелы

Для отступов используйте 2 пробела. Табы запрещены.

Файлы должны быть отформатированы в Unix формате. Это значит, что переход на новую строку должен быть \n, а не \r\n.

Все файлы с кодом и текстом должны заканчиваться пустой строкой. Это решает проблемы с "\ No newline at end of file" и позволяет легче читать патчи.

Все блоки в начале PHP файла должны быть разделены пустой линией, это также касается /** @file */ блока, объявления неймеспейсов, использование use и весь последующий код.

Примеры правильного форматирования:

<?php

namespace This\Is\The\Namespace;

use Drupal\foo\Bar;

/**
 * Provides examples.
 */
class ExampleClassName {
<?php

/**
 * @file
 * Provides example functionality.
 */

use Drupal\foo\Bar;

/**
 * Implements hook_help().
 */
function example_help($route_name) {

Операторы

Все двоичные операторы (которые идут между двумя значениями), такие как +, -, =, !=, ==, >, и т.д., должны иметь пробел перед и после оператора, для читаемости. Например, присвоение переменной должно выглядеть как $foo = $bar;, а не $foo=$bar;.

Для одинарных операторов (которые управляют только одним значением), таких как ++, пробелы не ставятся, как до, так и после.

Для сравнения преобразованных типов мы используем !=, использование <> запрещено.

Преобразование типа переменной

При преобразовании типа переменной ставится пробел между типом и самой переменной. Например (int) $foo.

Управляющие конструкции

Управляющие конструкции включают в себя if, for, while, switch и т.д.

Пример if условия со всеми возможными вариантами:

if (condition1 || condition2) {
  action1;
}
elseif (condition3 && condition4) {
  action2;
}
else {
  defaultaction;
}

Не используйте else if, всегда должно быть elseif.

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

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

Пример switch:

switch (condition) {
  case 1:
    action1;
    break;

  case 2:
    action2;
    break;

  default:
    defaultaction;
}

Пример do-while:

do {
  actions;
} while ($condition);

Длина линий и обертка

  • Все линии кода не должны быть длиннее 80 символов.
  • Линии содержащие названия функций длиннее данного значения, переменные, объявления и т.д., могут превышать это ограничение.
  • Управляющие структуры условий могут превышать 80 символов, если их при этом остается легко читать и понимать.

Пример допустимых вариантов:

  if ($something['with']['something']['else']['in']['here'] == mymodule_check_something($whatever['else'])) {
    ...
  }
  if (isset($something['what']['ever']) && $something['what']['ever'] > $infinite && user_access('galaxy')) {
    ...
  }
  // Неочевидные и сложные условия допустимы, но должны быть всегда задокументированы,
  // описывая зачем та или иная проверка производится.
  if (preg_match('@(/|\\)(\.\.|~)@', $target) && strpos($target_dir, $repository) !== 0) {
    return FALSE;
  }
  • Условия не должны быть обернуты в несколько линий.
  • Управляющие структуры условий также не должны пытаться выиграть Награду за самое компактное условие при меньшем числе строк кода™:
  // Никогда не делайте такого!
  if ((isset($key) && !empty($user->uid) && $key == $user->uid) || (isset($user->cache) ? $user->cache : '') == ip_address() || isset($value) && $value >= time())) {
    ...
  }

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

Правильный пример того что выше:

  // Ключ валидный, только если он равен ID текущего пользователя, в противном
  // случае, другие пользователи смогут получить доступ к любым данным.
  $is_valid_user = isset($key) && !empty($user->uid) && $key == $user->uid;

  // IP адрес должен равняться кешу, для предотвращения подмены сессии.
  $is_valid_cache = isset($user->cache) ? $user->cache == ip_address() : FALSE;

  // Альтернативно, если параметр запроса находится в будущем, то он всегда
  // действителен, потому что галактика всё равно взорвется и рухнет.
  $is_valid_query = $is_valid_cache || (isset($value) && $value >= time());

  if ($is_valid_user || $is_valid_query) {
    ...
  }

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

Заметка

Обратите внимание, в примерах используются комментарии на русском языке в демонстрационных целях. Писать комментарии на отличном от английского языке в Drupal запрещено!

Вызов функций

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

$var = foo($bar, $baz, $quux);

Объявление функций

function funstuff_sustem($field) {
  $system['description'] = t('This module inserts funny text into posts randomly.');
  return $system[$field];
}

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

Анонимные функции должны иметь пробел между function и его скобками, как в следующем примере:

array_map(function ($item) use ($id) {
  return $item[$id];
}, $items);

Вызов конструктора класса

Когда вызываете конструктор без аргументов, всегда добавляйте скобки:

$foo = new MyClassName();

Это позволяет соблюдать последовательность с конструкторами, имеющие аргументы:

$foo = new MyClassName($arg1, $arg2);

Если название класса является переменной, то используйте следующий пример:

$bar = 'MyClassName';
$foo = new $bar();
$foo = new $bar($arg1, $arg2);

Массивы

Массивы должны быть отформатированы коротким синтаксисом, значения которого разделяются пробелом после каждого элемента (запятой), а также пробелами между оператором ассоциации =>.

$some_array = ['hello', 'world', 'foo' => 'bar'];

Если объявление массива длиннее 80 символов, то каждый элемент должен быть на своей собственной линии с увеличением вложенности на 1 уровень.

$form['title'] = [
  '#type' => 'textfield',
  '#title' => t('Title'),
  '#size' => 60,
  '#maxlength' => 128,
  '#description' => t('The title of your node.'),
];

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

Кавычки

Drupal не имеет жестких стандартов на счет одиночных или двойных кавычек. Где возможно, соблюдайте последовательность с модулем и уважайте стиль других разработчиков.

Имя это ввиду, одиночные кавычки должны использовать по умолчанию. Они рекомендуются для использования кроме двух случаев:

  1. Интерполяция переменных в строке "<h2>$header</h2>".
  2. Переводимые строки, где требуется эскейпинг данных. Например "He's a good person." — предпочтительный и правильный вариант 'He\'s a good person.'. Подобный эскейпинг может быть неправильно обработан генератором .pot файлов (переводов), и просто странно выглядит.

Объединение строк

Всегда используйте пробел между точкой и объединяемыми частями для улучшения читаемости:

$string = 'Foo' . $bar;
$string = $bar . 'foo';
$string = bar() . 'foo';
$string = 'foo' . 'bar';

Когда вы объединяете значение простой переменной, используйте двойные кавычки, вместо одинарных:

$string = "Foo $bar";

Когда используете объединение при помощи оператора .=, используйте пробел по сторонам оператора:

$string .= 'Foo';
$string .= $bar;
$string .= baz();

"Включение" кода

Везде, где вы подключаете файл с кодом без условий, используйте require_once().

Когда подключаете код с использованием условий, используйте include_once(). Таким образом, файл будет импортирован лишь единожды.

Заметка

include_once() и require_once() — это выражения, не функции. Вам не нужно заключать путь до файла в скобки.

Когда вы внедряете код из текущей директории или подпапки, путь до файла должен начинаться с точки . (текущая директория в файловых системах): include_once ./includes/mymodyle_formatting.inc.

Используйте константу DRUPAL_ROOT чтобы указать путь до файла относительно корня Drupal. Корнем является директория, где находится файл index.php. Например: require_once DRUPAL_ROOT . '/' . variable_get('cache_inc', 'includes/cache.inc');.

Для того чтобы подгрузить код из конкретного модуля:

module_load_include('inc', 'node', 'node.admin');

Тэги PHP

Всегда используйте <?php ?> для PHP кода. Не используйте краткую запись <? ?>. Это требование Drupal, а также позволяет делать код максимально портативным, так как включение кода на PHP, на различных системах, может отличаться.

У PHP файлов, не указывайте закрывающий ?> в конце файла. Это сделано специально:

  • Удаление данного тега решает проблему с нежелательными пустыми строками в конце файла, которые приводят к ошибкам "header already sent", проблем с валидацией XHTML/XML и ряд других проблем.
  • Закрывающий тег является опциональным.
  • PHP.net также удаляют закрывающий тег в конце файла (например: prepend.inc), что можно расценивать как "лучшая практика".

Точка с запятой

В PHP точка с запятой обязательна почти во всех ситуациях, но разрешает их опускать в конце блоков кода. Drupal стандарты требуют указывать их, даже в конце блоков кода.

<?php print $tax; ?> -- Правильно
<?php print $tax ?> -- Не правильно

Примеры ссылок

Если вы хотите указать пример ссылки, всегда используйте домен "example.com" для таких ссылок согласно RFC 2606.

Соглашение об именовании

Функции и переменные

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

Переменные должны быть в нижнем регистре, а слова разделены либо заглавными буквами (пример: $lowerCamelCase), либо нижним подчеркиванием (например: $snake_case). Будьте постоянны, не смешивайте оба варианта внутри файла.

Постоянные переменные

Постоянные переменные — те что установлены при помощи \Drupal::state(), должны всегда быть в нижнем регистре, а слова разделены нижним подчеркиванием. Они также должны иметь префикс в виде модуля, для исключения пересечения с переменными других модулей.

Константы

  • Константы всегда должны быть в верхнем регистре, с нижним подчеркиванием в качестве разделителя слов. Это правило также касается предустановленных констант в PHP, таких как TRUE, FALSE и NULL.
  • Константы объявленные модулем, должны иметь в качестве префикса название модуля, также в верхнем регистре.
  • Начиная с Drupal 8 и выше, все константы должны быть объявлены при помощи ключевого слова const (подробнее), вместо define(), так как это лучше для производительности.
/**
 * Indicates that the item should be removed at the next general cache wipe.
 */
 const CACHE_TEMPORARY = -1;

Обратите внимание что const не работает с выражениями PHP. Используйте define(), когда объявляете константу при определенных условиях или значениях литералов:

if (!defined('MAINTENANCE_MODE')) {
  define('MAINTENANCE_MODE', 'error');
}

Глобальные переменные

Если вам необходимо объявить глобальную переменную, то не забудьте добавить к названию в качестве префикса название модуля, а слова разделяйте нижним подчеркиванием.

Название файлов

Все файлы документации должны иметь расширение ".txt", для того чтобы просмотр на Windows системах был проще. Также, название данных файлов должно быть в верхнем регистре (например README.txt, вместо readme.txt), тогда как расширение остается в нижнем.

Примеры: README.txt, INSTALL.txt, TODO.txt, CHANGELOG.txt и т.д.

Ссылки

🌱 Помогите нам сделать документацию лучше!

Вся документация Druki с отрытым исходным кодом. Нашли ошибку или неточность? Создайте pull request.

Редактировать текущий документ Обсудить улучшение

Или узнайте как контрибутить.

🤔 По-прежнему нужна помощь?

Не нашли ответа на свой вопрос? Попросите помощи у сообщества!

Задайте вопрос на GitHub Смотрите другие ресурсы сообщества