Правила написания исходного кода на PHP

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

1. Форматирование кода

1.1. Структурирование текста

1.1.1. Длина строки

Нужно стараться избегать строк длиной более 120 символов. Если строка превышает этот размер, то нужно использовать правила переноса строки.

1.1.2. Правила переноса строки

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

  • переносить можно после запятой или перед оператором;
  • переносимая строка должна быть сдвинута относительно верхней на один символ табуляции;
  • переносы должны быть в стиле UNIX.

 

Пример 1:

для строки

$arAuthResult = $USER->ChangePassword($USER_LOGIN, $USER_CHECKWORD, $USER_PASSWORD, $USER_CONFIRM_PASSWORD, $USER_LID);

допустимым будет следующий вариант переноса:

$arAuthResult = $USER->ChangePassword($USER_LOGIN, $USER_CHECKWORD,
$USER_PASSWORD, $USER_CONFIRM_PASSWORD, $USER_LID);

Пример 2:

для строки

if(COption::GetOptionString("main", "new_user_registration", "N")=="Y" && $_SERVER['REQUEST_METHOD']=='POST' && 
$TYPE=="REGISTRATION" && (!defined("ADMIN_SECTION") || ADMIN_SECTION!==true))

допустимым будет следующий вариант переноса:

if (COption::GetOptionString("main", "new_user_registration", "N") == "Y"
&& $_SERVER['REQUEST_METHOD'] == 'POST' && $TYPE == "REGISTRATION"
&& (!defined("ADMIN_SECTION") || ADMIN_SECTION !== true))
{
}

1.1.3. Пробелы и табуляция

Для форматирования отступов в коде нужно использовать табуляцию. Использование пробелов запрещено. Причины:

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

1.1.4. Форматирование подчиненности

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

Пример:

не правильно писать так:

if ($a == 0) {$a = 10;
$b = 1;}

правильно писать так:

if ($a == 0)
{
$a = 10;
$b = 1;
}

1.2. Инструкции, выражения

1.2.1. Выражения

Желательно, чтобы в каждой строчке присутствовало только одно выражение.

Пример:

не правильно писать так:

$a = $b; $b = $c; $c = $a;

правильно писать так:

$a = $b;
$b = $c;
$c = $a;

1.2.2. Инструкции if, else, while и т.п.

Допустимы два вида написания инструкций:

  • если тела всех частей инструкции состоят не более чем из одного выражения, то инструкция может записываться в виде
    if (условие) действие1; else действие2;
  • если тело хотя бы одной из частей состоит более чем из одного выражения, то инструкция должна записываться в виде
    if (условие) { действие1; } else { действие2; действие3; }

При написании инструкций должно строго применяться правило «1.1.4 Форматирование подчиненности»: тело инструкции должно быть сдвинуто на один символ табуляции вправо от самой инструкции. Фигурные скобки должны находиться на отдельных строках и должны быть на одном уровне с инструкцией.

Пример:

не правильно писать так:

if ($a == 0) $a = 10;
else{
$a = 5;
$b = 10;}

правильно писать так:

if ($a == 0)
{
$a = 10;
}
else
{
$a = 5;
$b = 10;
}

1.2.3. Сложные инструкции

Сложные инструкции следует разбивать по строкам в соответствии с правилами пункта 1.2.2.

Например,

if(COption::GetOptionString("main", "new_user_registration", "N")=="Y" && $_SERVER['REQUEST_METHOD']=='POST' &&
$TYPE=="REGISTRATION" && (!defined("ADMIN_SECTION") || ADMIN_SECTION!==true))

можно записать как

if (COption::GetOptionString("main", "new_user_registration", "N") == "Y"
&& $_SERVER['REQUEST_METHOD'] == 'POST' && $TYPE == "REGISTRATION"
&& (!defined("ADMIN_SECTION") || ADMIN_SECTION !== true))
{
}

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

Например,

if((!(defined("STATISTIC_ONLY") && STATISTIC_ONLY && substr($APPLICATION->GetCurPage(), 0,
strlen(BX_ROOT."/admin/"))!=BX_ROOT."/admin/")) && COption::GetOptionString("main", "include_charset", "Y")=="Y"
&& strlen(LANG_CHARSET)>0)

можно записать так:

$publicStatisticOnly = False;
if (defined("STATISTIC_ONLY")
&& STATISTIC_ONLY 
&& substr($APPLICATION->GetCurPage(), 0, strlen(BX_ROOT."/admin/")) != BX_ROOT."/admin/")
{
$publicStatisticOnly = True;
} 

if (!$publicStatisticOnly && strlen(LANG_CHARSET) > 0
&& COption::GetOptionString("main", "include_charset", "Y") == "Y")
{
}

или так:

if (!defined("STATISTIC_ONLY") || ! STATISTIC_ONLY || substr($APPLICATION->GetCurPage(), 0, strlen(BX_ROOT."/admin/")) == BX_ROOT."/admin/") { if (strlen(LANG_CHARSET) > 0 && COption::GetOptionString("main", "include_charset", "Y") == "Y") { } }

1.2.4. Форматирование массивов

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

$arFilter = array(
"key1" => "value1",
"key2" => array(
"key21" => "value21",
"key22" => "value22",
)
);

1.3. Пустые строки и пробелы

1.3.1. Пустые строки

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

1.3.2. Пробелы

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

Пояснения:

Один пробел используется в объявлении методов после запятой, но не перед скобками:

TestMethod($a, $b, $c);

Пример неправильного использования:

TestMethod($a,$b,$c);

либо

TestMethod( $a, $b, $c );

Так же одиночный пробел может быть использован для выделения операторов:

$a = $b * $c / $d;

Пример неправильного использования:

$a=$b*$c/$d;

Также пробелы используются при форматировании циклов:

for ($i = 0; $i < 10; $i++)

Пример неправильного использования:

for($i=0;$i<10;$i++)

Табличное форматирование с помощью символов табуляции не должно использоваться.

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

$arArray = array(
"key1" => "value1",
"key2" => "value2",
);

Замечание: присутствие или отсутствие пробела после if правилами не регламентируется.

1.4. Прочее

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

Пример:

$r = $a + ($b * $c);

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

2.1. Общие понятия

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

Старайтесь давать переменным, методам и пр. «говорящие» названия. Предпочтительно использовать имена, которые ясно и четко описывают предназначение и/или смысл сущности.

Старайтесь делать имена идентификаторов как можно короче (но не в ущерб читабельности).

2.2. Именование переменных

Первое логическое слово должно начинаться с маленькой буквы, остальные логические слова — с большой (стиль Кэмел). Например: $testCounter,$userPassword.

2.3. Именование методов и функций

Каждое логическое слово должно начинаться с заглавной буквы (стиль Паскаль). Например: CountVariable, ChangeUserPassword.

2.4. Префиксы переменных

PHP — не особо типизированый язык и в нем различаются по смыслу только три группы типов: скалярные, массивы и объекты.

Массивы следует именовать с префиксом «ar», при этом следующее логическое слово в названии начинается с большой буквы. Например, $arResult,$arModifiedUsers.

Объекты следует именовать с префиксом «ob», при этом следующее логическое слово в названии начинается с большой буквы. Например, $obElement, $obUser.

Объект класса CDBResult следует начинать с префикса «db», при этом следующее логическое слово в названии начинается с большой буквы. Например,$dbResult.

Скалярные типы следует начинать с префиксов только в том случае, если точно известно, что они имеют заданный тип. Например в коде

$userID = $_REQUEST["var"];

$userID = IntVal($userID);

переменная идет без префикса, так как ее тип меняется по ходу выполнения программы.

В коде

$bFlag = (($aaa > 0)? True : False);

переменная идет с префиксом, так как ее тип в общем известен и не меняется.

2.5. Именование классов

Имя класса должно начинаться с буквы «C». Если класс принадлежит модулю, то дальше должно идти «фирменное» название модуля. Каждое логическое слово должно начинаться с заглавной буквы.

Пример: CIBlockElement, CIBlockType, CSaleAffiliate.

Если класс различается для разных СУБД и соответственно имеет базовый класс с общими для всех СУБД методами, то этот базовый класс должен в своем имени после символа «C» содержать символы «All».

Пример: CAllSaleAffiliate.

2.6. Доступность членов-переменных и методов класса

Так как у нас нет других методов организации видимости и доступности членов-переменных и методов классов, то следует применять следующие правила:

  • члены-переменные и методы, которые являются приватными и к которым не может обращаться никто, кроме самого модуля (т.е. ни публичная часть, ни другие модули), должны начинаться с двух символов подчеркивания. Например, __CheckEmail, __arData. Эти методы не описываются в документации и могут быть изменены без обеспечения совместимости;
  • члены-переменные и методы, которые являются внутренними и к которым могут обращаться только модули продукта (т.е. публичная часть не может), должны начинаться с одного символа подчеркивания. Например, _CheckEmail, _arData. Эти методы не описываются в публичной документации (но хорошо бы описать во внутренней) и могут быть изменены без обеспечения совместимости только после уведомления всех сотрудников;
  • остальные методы и члены-переменные считаются публичными, должны быть описаны в документации и не могут быть изменены без обеспечения совместимости.

2.7. Именование констант

Константы должны писаться большими буквами и иметь префикс «BX_». Например, BX_ROOT, BX_FILE_PERMISSIONS.

3. Комментарии

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

Необходимо избегать очевидных комментариев типа:

$i = $i + 1; // добавить к i единицу

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

Перед логическими секциями кода желательно добавлять комментарии о том, что данная секция будет делать.

Комментарии должны быть только на английском языке.

4. Идиомы программирования

4.1. Общее понятие

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

for ($i = 0, $cnt = count($arArray); $i < $cnt; $i++)
{

}

или

foreach ($arArray as $key => $value)
{

}

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

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

Пример:

код

reset($arHashLink);

while(list($hash, $arData)=each($arHashLink))

{

}

лучше переписывать в таком виде

foreach ($arHashLink as $hash => $arData)

{

}

4.2. Примеры идиом

Идиома оператора «?»

$res = ($bTrue? "True" : "False");

5. SQL запросы

Каждая операция SELECT, FROM, WHERE, ORDER BY, GROUP BY, HAVING должна начинаться с новой строки.

Правило переноса длинной строки такое же как в PHP, новая строка с табуляции.