Skip to content

[PHP] Командный стандарт кодирования

Notifications You must be signed in to change notification settings

kulizh/php-standards

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 

Repository files navigation

Командный стандарт кодирования


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

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

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

1. Общие требования

1.1. Файлы

1.1.1 Файлы должны начинаться с тега <?php (за редким исключением допускается использовать короткий открывающий тег).

1.1.2. Файлы должны сохраняться в кодировке UTF‐8 без метки BOM.

1.1.3. Файлы должны завершаться одним пустым переводом строки.

1.1.4. Можно опустить закрывающий тег, если файл содержит только PHP-код.

1.2. Пространства имён и классы

1.2.1. Пространства имён и классы должны следовать стандарту PSR-4.

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

1.2.3. Класс должен именоваться в соответствии с нотацией UpperCamelCase.

1.3. Ключевые слова и типы

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

1.3.2. Вместо длинных названий типов следует использовать короткие: bool вместо boolean, int вместо integer.

1.4. Объявление пространств имён и оператор use

1.4.1. Заголовок PHP файла, содержащего объявление класса, может состоять из разного набора блоков. Рекомендуется придерживаться следующей структуры:

  1. Открывающий тег
  2. Описание файла
  3. Декларирование использования строгой типизации declare(strict_types=1); (без пробелов)
  4. Пространство имён
  5. USE-блоки (допустимо отделять их пустой строкой исходя из логики разбиения)
  6. Краткая документация класса
<?php

/**
 * Файл содержит пример объявления класса
 */

declare(strict_types=1); // Опционально 

namespace Codeguide\PHP\Example;

use Codeguide\PHP\Example\Variables;
use Codeguide\PHP\Notations;

use function Codeguide\Utils\Functions\textToUpperCase;

use const Codeguide\CONSTANT_NOTATION;

class Declaring extends Base
{
	// ...
}

1.4.2. При расширении нескольких классов-родителей (или реализации нескольких интерфейсов) допускается их разделение переводом строки. При этом каждый класс (интерфейс) должен находиться на новой строке.

<?php

class RoomsIterator extends IteratorAbstract implements
	\Countable,
	\Serializable
{
	// ... some php magic here ...
}

1.5. Классы, свойства, методы

Термин «класс» подразумевает также интерфейсы и трейты.

1.5.1. При объявлении нового класса всегда должны быть скобки (даже если конструктор не принимает аргументы).

1.5.2. Ключевые слова extends и implements должны быть на одной строке с объявлением имени класса.

1.5.3. Открывающая фигурная скобка должна находиться на следующей строке после объявления имени класса.

1.5.4. Закрывающая фигурная скобка должна находиться на следующей строке после тела класса.

1.6. Трейты

1.6.1. Импорт трейта допускается только в начале тела класса после открывающей фигурной скобки.

1.6.2. Каждый трейт должен быть объявлен на новой строке.

1.6.2. Операторы insteadof и as должны быть оформлены следующим образом:

<?php

class ExampleTrait
{
    use FirstTrait;
    use SecondTrait {
        FirstTrait::FooBar insteadof SecondTrait;
    }
    use ThirdTrait {
        SecondTrait::Beauty insteadof ThirdTrait;
        ThirdTrait::SomeField as SomeAlias;
    }
}

1.7. Свойства и константы

1.7.1. Каждое свойство должно быть объявлено с указанием области видимости.

1.7.2. При использовании версии PHP выше 7.1 константы должны быть объявлены с указанием области видимости.

1.7.3. Не допускается использование ключевого слова var для объявления переменных

<?php

class RoomsIterator
{
	private const VERSION = '1.4.4';

    private int $steps = 0;
    private static string $errorMessage = 'Unexpected Error caught';
}

1.8. Методы и функции

1.8.1. Каждый метод должен быть объявлен с указанием области видимости.

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

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

1.8.4. Методы именуются согласно нотации lowerCamelCase.

1.8.5. Функции вне классов именуются согласно нотации underscore.

<?php

class Router
{
    public function handleRoutes($arg1, $arg2, $arg3 = []): array
    {
        // rocket science

        return [];
    }
}
<?php

function set_arguments($arg1, &$arg2, $arg3 = []): bool
{
    // setting settings :)

    return true;
}

1.8.6. Методы класса должны объявляться по мере сужения области видимости. public→protected→private.

1.9. Аргументы методов и функций

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

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

1.9.3. Аргументы могут располагаться на разных строках. Первый элемент — на следующей строке после объявления метода. Допускается использование только одного аргумента на строке. Элементы отбиваются табуляцией.

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

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

<?php

class RouteHandler
{
    public function handleCustomRoutes(
        HttpClient\Request $arg1,
        &$arg2,
        array $arg3 = []
    ): bool
	{
        // method body
    }
}

1.9.6. При объявлении Nullable-типа не должно быть пробела между знаком вопроса и типом.

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

1.10. Abstract, final, static

1.10.1. Ключевые слова abstract, final должны предшествовать объявлению области видимости.

1.10.2. Ключевое слово static должно идти после объявления области видимости

1.11. Вызов методов и функций

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

1.11.2. Аргументы могут располагаться на разных строках. Первый элемент — на следующей строке после объявления метода. Допускается использование только одного аргумента на строке. Элементы отбиваются табуляцией.

<?php

$router->get('/client/{id}', function ($id) use ($http_router) {
    return 'Set ' . $http_router->sendPost('client/add/' . $id);
})

$foo->bar(
    $long_argument,
    $longer_argument,
    $argument_that_is_too_long_to_be_valid
);

bar();

1.12. if, elseif, else

1.12.1. Недопустимо использование конструкции else if.

1.12.2. Ограничители блока кода пишутся каждый с новой строки без отступа. Содержимое блока пишется с одинарным отступом.

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

<?php

if (
    $valid
    && $exists
) 
{
    // php magic
} elseif (
    $expression_1
    && $expression_2
) 
{
    // elseif php magic
}

1.13. switch, case

1.13.1. Ограничители блока кода пишутся каждый с новой строки без отступа. Содержимое блока пишется с одинарным отступом.

<?php

switch ($expression) 
{
    case 0:
        echo 'First case, with a break';
        break;
    case 1:
        echo 'Second case, which falls through';
    case 2:
    case 3:
    case 4:
        echo 'Third case, return instead of break';
        return;
    default:
        echo 'Default case';
        break;
}

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

1.14. while, do while

1.14.1. Ограничители блока кода пишутся каждый с новой строки без отступа. Содержимое блока пишется с одинарным отступом.

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

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

<?php

do 
{
    // do not use do while construction!
} while (
    $valid
    && $exists
);

1.15. for

1.15.1. Ограничители блока кода пишутся каждый с новой строки без отступа. Содержимое блока пишется с одинарным отступом.

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

1.16. foreach

1.16.1. Ограничители блока кода пишутся каждый с новой строки без отступа. Содержимое блока пишется с одинарным отступом.

<?php

foreach ($iterable as $key => $value) 
{
    // foreach body
}

1.17. try, catch, finally

1.17.1. Ограничители блока кода пишутся каждый с новой строки без отступа. Содержимое блока пишется с одинарным отступом.

<?php

try 
{
    // try body
} 
catch (ThrowableType $e) 
{
    // catch body
} 
catch (RequestException | HttpException $e) 
{
    // catch body
} 
finally 
{
    // finally body
}

1.18. Унарные операторы

1.18.1. Операторы инкремента/декремента не должны иметь пробела между операндом и оператором.

1.18.2. Операторы приведения к типу не должны иметь пробелов внутри скобок, но должны отделяться пробелом от операнда.

<?php

$room_area = (float) $area_from_source;

1.19. Бинарные операторы

1.19.1. Все бинарные операторы (арифметические, сравнения, присвоения, логические, строковые, типов, битовые) должны отделяться пробелами

1.20. Тернарные операторы

1.20.1. Оба символа тернарного оператора должны отделяться пробелами от операндов.

1.21. Замыкания

1.21.1. Замыкания должны отделяться пробелом от ключевых слов function и use.

1.21.2. Ограничители блока кода пишутся каждый с новой строки без отступа. Содержимое блока пишется с одинарным отступом.

1.21.3. Аргументы функций не отбиваются пробелами от скобок.

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

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

1.21.6. При объявлении типа возвращаемого значения должен быть один пробел после двоеточия. Двоеточие и отделение должны быть на одной строке с последней закрывающей скобкой.

<?php

$argumented_closure = function ($arg1, $arg2) 
{
    // body
};

$argumented_closure_with_vars = function ($arg1, $arg2) use ($var1, $var2) 
{
    // body
};

$argumented_closure_with_vars_and_return = function ($arg1, $arg2) use ($var1, $var2): bool 
{
    // body
};

$foo->bar(
    $arg1,
    function ($arg2) use ($var1) 
	{
        // body
    },
    $arg3
);

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

1.22. Анонимные классы

1.22.1. На анонимные классы распространяются те же правила, что и на замыкания.

2. Рекомендации

2.1. Общие

2.1.1. Код должен быть понятным и явным. Недопустимо использование магических методов set(), get(), call(). То же самое относится к операторам и функциям завершения процесса (exit, die).

2.1.2. Любое неочевидное действие в коде должно быть задокументировано. Для этого лучше всего использовать стандарт PHPDoc (https://docs.phpdoc.org/latest/index.html).

2.1.3. Любой код, который не нужен для работы системы, считается неиспользуемым и должен быть удалён. К нему же относятся закомментированные фрагменты старого кода, оставленные «на всякий случай». Для «всяких случаев» существуют системы контроля версий.

2.1.4. Код должен быть последовательным, т. е. читаться сверху вниз. Стоит избегать обратных циклов (do {} while ();). Программист не должен возвращаться назад, чтобы разобраться, что происходит в том или ином фрагменте.

2.1.5. Цикломатическая сложность кода должна быть минимально возможной. Иными словами, чем меньше условий, тем лучше.

2.2. Переменные

2.2.1. Названия переменных соответствуют содержанию. Нельзя использовать в работе переменные $a, $vv, $i. Также нужно стараться избегать общих названий для переменных ($type, $value, $map).

2.2.2. В названии переменной не должно быть указания типа.

2.2.3. Часто упоминаемые объекты именуются одинаково во всём проекте.

2.2.4. Переменные именуются через underscore в случае, если им присваиваются:

  • Скалярные типы данных (int, float, bool, string);
  • Специальные типы данных (resource, null);
  • Смешанные типы данных за исключением object (array, callable, iterable).

2.2.5. К обсуждению. Для того, чтобы обозначить, что переменной присвоен экземпляр класса, стоит именовать её через camelCase. Это нужно, чтобы разработчик быстро понимал, что перед ним объект и мог работать с его методами.

2.2.6. Если переменной присваивается объект, то его признак добавляется к названию. Например, если мы работаем с забронированной квартирой, переменную следует назвать $booked_room. То же самое со свойством объекта: в название переменной нужно включать название свойства.

2.2.7. По возможности, стоит именовать переменные согласно правилам английского языка (если не используется префикс, типа status). То есть, вместо $rooms_sold лучше использовать $sold_rooms.

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

2.2.9. Названия переменных типа boolean должны содержать is, has, can и иметь форму утверждения, а не вопроса. Например, вместо is_room_free стоит использовать $room_is_free. Такое именование упрощает чтение кода, как набора инструкций на английском.

2.2.10. Нельзя использовать отрицательные логические названия, например, $room_is_not_free.

2.3. Аргументы функций

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

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

function clean_text(string &$text): void
{
	//...
}
function clean_text(string $text): string
{
	$text = trim($text);
	//..
}
function clean_text(string $text): string
{
	$trimmed_text = trim($text);
	// ...
	return $result;
}

2.3.3. Запрещается использовать результат операции присваивания.

$this->setBuildingTitle($title = get_title());
$building_title = get_title();

$this->setBuildingTitle($title);

2.3.4. Не стоит использовать булевы переменные как параметры функции. Флаг в качестве параметра это признак того, что функция делает больше одной вещи, нарушая SRP.

2.4. Методы, свойства, константы

2.4.1. Нельзя использовать константы через метод constant().

2.4.2. Методы, свойства и константы класса должны иметь минимально допустимую область видимости.

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

2.4.4. Должна быть использована максимально возможная типизация для текущей версии PHP. Все параметры и их типы должны быть описаны в объявлении метода либо в PHPDoc. Возвращаемое значение тоже.

2.4.5. Все возможные типы должны быть определены в PHPDoc

2.4.6. Название метода должно начинаться с глагола и соответствовать правилам именования переменных.

2.4.7. Методы названия, которых начинаются c check и validate, должны выбрасывать исключения и не возвращать значения.

2.4.8. Nullable параметры должны быть помечены ?, даже если указано значение по умолчанию.

2.4.9. Метод всегда должен возвращать только одну структуру данных (или null) или ничего не возвращать.

2.4.10. Если метод возвращает один объект (или скалярный тип), то в случае, если объект не найден, возвращается null.

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

2.4.12. Если никакой ошибки не произошло, но отсутствует результат, то это null (или пустой массив), однако если все же произошла исключительная ситуация, которая не заложена системой, то должно кидаться исключение.

2.4.13. Метод с большим количеством необязательных параметров (А) может быть заменен chain-объектом.

2.5. Классы

2.5.1. Трейты имеют постфикс Trait.

2.5.2. Интерфейсы имеют постфикс Interface.

2.5.3. Абстрактные классы имеют префикс Abstract.

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

2.6. Массивы

2.6.1. Для конкатенации массивов стоит использовать функцию array_merge() вместо +.

2.7. Строки

2.7.1. Двойные кавычки используются только, если:

  • Внутри строки должны быть одинарные кавычки
  • Внутри строки используется подстановка переменных
  • Внутри строки используются спец. символы \n\r\t и т.д.

2.7.2. Иногда вместо конкатенации можно использовать подстановку в двойных кавычках

$string = "Object with type \"{$object->type()}\" has been removed";

2.8. Даты

2.8.1. Дата должна быть представлена как DateTime.

2.8.2. Временной интервал должен быть представлен как DateInterval.

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

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

2.9. Комментирование

2.9.1. В общем случае комментарии запрещены. Желание добавить комментарий — признак плохо читаемого кода. Любой участок кода, который вы хотели бы выделить или прокомментировать, надо выносить в отдельный метод.

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

2.10. Запросы внешних данных

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

2.10.2. Для каждой записи в хранилище должно быть понятна дата её создания.

2.13. Литералы

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

2.14. Условия

2.14.1. В условном операторе должно проверяться исключительно boolean значение.

2.14.2. В сравнении не boolean переменных используется строгое сравнение с приведением типа.

2.14.3. Автоматическое приведение типов разрешено только, когда один из операндов — литерал с фиксированным типом. При сравнении двух переменных с неизвестными типами для читающего код человека не очевидно, к чему они будут приведены интерпретатором. Если же тип одного из операндов известен, то всё становится очевидно.

2.14.4. Не надо сравнивать boolean с true/false.

2.14.5. Проверять переменные надо на наличие позитивного вхождения, а не отсутствие негативного.

2.14.6. При использовании в условном выражении одновременно операторов И и ИЛИ обязательно выделять приоритет скобками

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

2.14.8. Использовать цепочки из тернарных операторов ?: допустимо только при указании значения по умолчанию

3. Принципы и методологии

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

KISS.

Keep it simple, stupid! — делайте вещи проще. Отсутствие лишних абстракций, функций и библиотек только усложняет сопровождение проекта.

SOLID

Single Responsibility (принцип единственной отвественности) — каждый объект имеет одну обязанность, которая инкапсулирована в класс. Все его сервисы направлены на обеспечение этой обязанности. Иными словами, чем меньше «умеет» объект, тем проще его использовать повторно.

Полезная ссылка: https://habr.com/ru/articles/465507/

Open-closed (принцип открытости / закрытости) — классы, модули, функции и т. п. должны быть открыты для расширения и закрыты для изменения. То есть, согласно этому принципу, любое ПО изменяется за счёт добавления нового кода, а не изменения существующего. Так сохраняется стабильность нетронутого функционала и в то же время реализуется новый.

Полезная ссылка: https://habr.com/ru/companies/tinkoff/articles/472186/

Liskov substitution (принцип подстановки Барбары Лисков) — при построении иерархии наследования дочерние сущности должны корректно реализовывать поведение базового типа. Наследник класса дополняет, но не изменяет поведение класса-родителя. В любом месте кодовой базы замена базового класса на дочерний не должна вызывать ошибок.

Полезная ссылка: https://habr.com/ru/articles/83269/

Interface segregation (принцип разделения интерфейса) — клиенты не должны зависеть от методов, которые они не используют. То есть если какой-то метод интерфейса не используется клиентом, то изменения этого метода не должны приводить к необходимости внесения изменений в клиентский код.

Полезная ссылка: https://makedev.org/principles/solid/isp.html

Dependency inversion (принцип инверсии зависимостей) — модели высокого уровня не должны зависеть от низкоуровневых. Зависимость должна формироваться не от конкретной реализации, а от реализуемого зависимостью интерфейса.

Полезная ссылка: https://habr.com/ru/articles/313796/

DRY.

Don't repeat yourself — избегайте дублирования кода. Следование этому принципу приводит к модульной архитектуре приложения и к чёткому разделению ответственности за бизнес-логику между программными классами.

About

[PHP] Командный стандарт кодирования

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published