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

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

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

Используйте отступ в два пробела. Не используйте табы для смещения кода.

В конце строки не должно быть лишних пробелов.

Перенос строк должен осуществляться при помощи спецсимвола \n, (Unix формат), а не \r\n (Windows формат).

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

Операторы

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

Правильно:

$foo = $bar;

Не правильно:

$foo=$bar;

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

Правильно:

$i++;

Не правильно:

$i ++;

Приведение типов

Между типом, к которому приводится и переменной должен стоять пробел:

$foo = (int) $bar;

Операторы управления

Под операторами управления понимаются операторы if, for, while, switch и т.д. Вот пример правильного кодирования с использованием оператора if.

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

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

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

Стандарты для оператора switch:

switch (condition) {
  case 1:
    action1;
    break;
 
  case 2:
    action2;
    break;
 
  default:
    defaultaction;
}

Для оператора do-while:

do {
  actions;
} while ($condition);

Длина и перенос строк

В общем случае длина строки не должна превышать 80 символов.

Строкам, содержащим длинные имена функций, описание функций/классов, объявление переменных и т.д. позволяется превышать лимит в 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) {
   ...
}

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

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

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

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

Как уже упоминалось ранее, по обе стороны от бинарного оператора (знака "равно") должны стоять пробелы.

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

$short         = foo($bar);
$long_variable = foo($bar);

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

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

function funstuff_system($field, $description = 'test') {
  $system['description'] = $description;
  return FALSE;
}

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

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

$foo = new MyClassName();

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

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

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

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

Массивы

При перечислении элементов массива необходимо добавлять пробел после каждой запятой, и по пробелу с каждой стороны оператора =>, если он присутствует:

$some_array = array('hello', 'world', 'foo' => 'bar');

Отметим, что если объявление массива превышает установленный лимит в 80 символов, то каждый элемент массива должен быть перенесён на новую строку, со смещением по уровню:

$form['title'] = array(
  '#type' => 'textfield',
  '#title' => t('Title'),
  '#attributes' => array(
    'class' => array('search-form')
  ),
  '#maxlength' => 128,
  '#description' => t('The title of your node.'),
);

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

Кавычки

Drupal не имеет жёсткого стандарта для использования одинарных или двойных кавычках. По возможности, придерживайтесь одинакового стиля касательно кавычек хотя бы в пределах одного проекта/модуля. А если вы разрабатываете проект не один, то уважайте личные предпочтения других разработчиков касательно кавычек.

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

1. Включение переменных в небольшие строки:

<?php echo "<h2>$title</ h2>"; ?>

2. Перевод строки, где можно избежать удаления одинарных кавычек, заключив строку в двойные кавычки. Пример:

"He's a good person."

С одинарными кавычками было бы

'He\'s a good person.'

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

Конкатенация (объединение) строк

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

<?php
  $string = 'Foo' . $bar;
  $string = $bar . 'foo';
  $string = bar() . 'foo';
  $string = 'foo' . 'bar';
?>

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

<?php
  $string = "Foo $bar";
?>

Когда используется присваивающий оператор для конкатенации .=, используйте пробел с каждой его стороны:

<?php
  $string .= 'Foo'; 
  $string .= $bar;
  $string .= baz();
?>

Написание комментариев

Стандарты написания комметнариев обсуждаются на отдельной странице на друпал.орг (текст на английском).

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

В любом месте, где вы без условий включаете файл класса, используйте require_once(). Если же вы включаете файл при каком-то условии, используйте include_once(). Обе эти функции гарантируют, что файл класса будет включен в код лишь единожды. Эти функции используют общий список подключенных файлов, поэтому не стоит переживать по поводу смешанного использования require_once() и include_once().

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

Когда подключаете файл из той же директории, или суб-директории, начинайте путь файла с точки:

include_once ./includes/mymodule_formatting.inc

Начиная с Drupal 7 можно использовать константу DRUPAL_ROOT:

require_once DRUPAL_ROOT . '/' . variable_get('cache_inc', 'includes/cache.inc');

Тэги PHP кода

Всегда используйте <?php ?> для вставки php кода, а не его сокращённую версию <? ?>. Во-первых, того требуют правила кодирования Друпала, а во-вторых, написание таким образом даёт наиболее удобную возможность миграции php кода на различных операционных системах и установках.

Отдельно хочу отметить, что начиная с Друпала версии 4.7 в конце файла с php кодом тэг ?> является абсолютно лишним. Это распространяется на все php файлы, включая .module и .inc. Вот несколько причин не указывать закрывающий тэг:

  • Удаление ?> позволяет избежать ошибки "Заголовки уже высланы" (headers already sent).
  • Лишними пробелами также может вызываться ошибка валидации XHTML/XML и другие ошибки, отловить которые будет достаточно проблематично.
  • Закрывающие символы необязательны.
  • Неиспользование закрывающих символов считается хорошим тоном программирования на PHP.

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

Синтаксис языка PHP требут ставить точку с запятой в конце практически каждой строки (каждого действия). Однако он позволяет не ставить её в конце вставки блока кода. Стандарты кодирования Друпала требуют их наличия даже в конце этих самый блоков кода.

Правильно:

<?php print $tax; ?>

Не правильно:

<?php print $tax ?>

Примеры URL'ов

Правила кодирования Друпала требуют указания адреса example.com в качестве примера для всех url'ов (согласно RFC 2606).

Правила наименований

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

Название функций и переменных должно быть преведено к нижнему регистру. Слова должны быть разделены символом подчёркивания. Так же в начале имени функции следует указывать имя темы/модуля, в котором эта функция написана, для избежания кофликтов имён с фунциями в других темах/модулях.

function mymodule_foo_load($foo) {
  ...
}

Устойчивые (persistent) переменные.

Устойчивые (persistent) переменные (переменные, определяемые функциями variable_get()/variable_set()), должны быть названы используя только буквы в нижнем регистре, а слова должны быть разделены символом подчеркивания. Они должны использовать имя темы/модуля в качестве префикса, чтобы избежать конфликтов имен между модулями.

$foo = variable_get('mymodule_foo_name', '');
variable_set('mymodule_foo_name', $foo);

Константы

Название констант всегда должно быть приведено к верхнему регистру. Слова должны быть разделены подчёркиваниями. Константы, определённые в модуле, должны начинаться с имени модуля.

define('MYMODULE_FOO_CONSTANT', 1);

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

Если вам нужно создать глобальные переменные, то их названия должны начинаться с одного подчеркивания, затем имя темы/модуля, затем ещё одно подчёркивание, и далее уже название переменной.

Классы

Имена классов должны подчиняться стилю "CamelCase." (каждое новое слово с большой буквы). Пример:

abstract class DatabaseConnection extends PDO {

Имена методов и свойств внутри класса должны именоваться по принципу "lowerCamelCase" (первое слово с маленькой буквы, остальные - с больших):

public $lastStatement;

Использования приватных (private) методов и свойств класса следует избегать. Вместо них настоятельно рекомендуется использовать защищённые (protected) методы и свойства. В этом случае, при наследовании от этого класса, дочерние классы смогут перекрывать параметры родительского класса.

Защищённые (protected) и общие (public) методы и свойства не должны быть названы с использованием подчёркиваний, как это было популярно в эпоху PHP 4.

Имена файлов

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

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

Дополнительные модули

На drupal.org есть контриб модули, которые могут помочь с проверкой кода на его совместимость со стандартами кодирования. Самый популярный модуль - coder. Для его использования выполните несколько действий:

  • Установите модуль Coder.
  • Нажмите на ссылку "Code Review" в навигационном меню.
  • Пролистайте вниз до набора полей "Select Specific Modules".
  • Выберите модуль, который вы хотите проверить, и нажмите на подтверждающую кнопку внизу страницы.

В модуле Coder можно найти скрипт командной строки под названием Coder Format, который не только проверяет код на соответствие стандартам кодирования, но и исправляет ошибки. Однако пользуйтесь им с большой осторожностью - машины, они ведь такие неаккуратные :)

Комментарии

Аватар пользователя vegas
vegas написал:

Опечатка в названии абзаца:
НаписЕние комментариев
;)

13.02.2012 06:50
Аватар пользователя Spleshka
Spleshka написал:

Спасибо, поправил :)

13.02.2012 15:56
Аватар пользователя drup.by
drup.by написал:
$string .= 'Foo';
$string .= $bar;
$string .= baz();

или

  $string .= 'Foo';
  $string .= $bar;
  $string .= baz();
13.06.2012 17:19
Аватар пользователя Spleshka
Spleshka написал:

В контексте статьи - конечно второе.

14.06.2012 11:48

Комментировать