Skip to main content

Руководство по стилю кода: PHP

Предупреждение:

  • Гайд основан на https://spatie.be/guidelines/laravel-php
  • Соглашения, принятые в уже устоявшейся кодовой базе, имеют приоритет над данным гайдом

Общие PHP правила

Стиль кода должен соответствовать PSR-1, PSR-2 и PSR-12. Вообще говоря, все, что имеет вид строки и не является общедоступным, должнол иметь стиль camelCase. Подробные примеры разбросаны по всему руководству в соответствующих разделах.

ОБЩИЕ ПРАВИЛА ДЛЯ КЛАССОВ

По умолчанию мы не используем final

ВОЗВРАЩАЕМЫЙ ТИП VOID

Если метод ничего не возвращает, он должен быть отмечен с помощью void. Это делает более понятными ваши намерения при написании кода.

👍 # Хорошо

public function scopeArchived(Builder $query): void
{
$query->where(/* ... */);
}

Типизированные свойства

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

👍 # Хорошо

class Foo
{
public string $bar;
}

💩 # Плохо

class Foo
{
/** @var string */
public $bar;
}

Блок с документацией

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

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

👍 # Хорошо

class Url
{
public static function fromString(string $url): Url
{
// ...
}
}

💩 # Плохо: Описание избыточно, а метод полностью типизирован.

class Url
{
/**
* Create a url from a string.
*
* @param string $url
*
* @return \Foo\Url\Url
*/
public static function fromString(string $url): Url
{
// ...
}
}

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

👍 # Хорошо

/** @var string */
/** @test */

💩 # Плохо

/**
* @test
*/

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

👍 # Хорошо

/** @var \Foo\Goo\Bar|null */

💩 # Плохо

/** @var null|\Foo\Goo\Bar */

Уровень доступа свойства в конструкторе

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

👍 # Хорошо

class MyClass {
public function __construct(
protected string $firstArgument,
protected string $secondArgument,
) {}
}

💩 # Плохо

class MyClass {
protected string $secondArgument

public function __construct(protected string $firstArgument, string $secondArgument)
{
$this->secondArgument = $secondArgument;
}
}

Трейты

Каждый трейт при подключении должен использовать ключевое слово use и размещаться на новой строке. Это позволит сразу заметить, что какой-то трейт был добавлен или удален. 👍 # Хорошо

class MyClass
{
use TraitA;
use TraitB;
}

💩 # Плохо

class MyClass
{
use TraitA, TraitB;
}

gs

По возможности предпочитайте интерполяцию строк функции sprintf и оператору '.' . 👍 # Хорошо

$greeting = "Hi, I am {$name}.";

💩 # Плохо

$greeting = 'Hi, I am ' . $name . '.';

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

Каждая часть тернарного выражения должна быть на отдельной строке, если только это не очень короткое выражение.

👍 # Хорошо

$name = $isFoo ? 'foo' : 'bar';

💩 # Плохо

$result = $object instanceof Model ?
$object->name :
'A default value';

Операторы If

ФИГУРНЫЕ СКОБКИ

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

👍 # Хорошо

if ($condition) {
...
}

💩 # Плохо

if ($condition) ...

УДАЧНЫЙ ПУТЬ

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

👍 # Good

if (! $goodCondition) {
throw new Exception;
}

// do work

💩 # Bad

if ($goodCondition) {
// do work
}

throw new Exception;

ИЗБЕГАЙТЕ ELSE

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

👍 # Хорошо

if (! $conditionA) {
// условие A не выполнено

return;
}

if (! $conditionB) {
// условие A выполнено, B нет

return;
}

// условия A и B выполнены

💩 # Плохо

if ($conditionA) {
if ($conditionB) {
// условия A и B выполнены
}
else {
// condition A выполнено, B нет
}
}
else {
// условие A не выполнено
}

Другой вариант рефакторинга else - использование тернарных операторов

💩 # Плохо

if ($condition) {
$this->doSomething();
}
else {
$this->doSomethingElse();
}

👍 # Хорошо

$condition
? $this->doSomething();
: $this->doSomethingElse();

Объединение IF'ов

Как правило, отдельные операторы if предпочтительнее составных условий. Это упрощает отладку кода.

👍 # Хорошо

if (! $conditionA) {
return;
}

if (! $conditionB) {
return;
}

if (! $conditionC) {
return;
}

// что-то делаем

💩 # Плохо

if ($conditionA && $conditionB && $conditionC) {
// что-то делаем
}

Комментарии

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

// Перед однострочным комментарием должен быть пробел.

/*
* Если вам требуется длинное объяснение, вы можете использовать блок комментариев.
* Обратите внимание на один символ * в первой строке. Блок с комментарием не обязательно
* должен быть в три строки
*/

Возможный вариант рефакторинга - создать функцию, название которой будет заменять комментарий

👍 # Хорошо

$this->calculateLoans();

💩 # Плохо

// Начинает расчет кредита

Пробел

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

👍 # Хорошо

public function getPage($url)
{
$page = $this->pages()->where('slug', $url)->first();

if (! $page) {
return null;
}

if ($page['private'] && ! Auth::check()) {
return null;
}

return $page;
}

💩 # Плохо: Все вместе.

public function getPage($url)
{
$page = $this->pages()->where('slug', $url)->first();
if (! $page) {
return null;
}
if ($page['private'] && ! Auth::check()) {
return null;
}
return $page;
}

👍 # Хорошо: Последовательность однострочных эквивалентных операций.

public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->rememberToken();
$table->timestamps(6);
});
}

Не добавляйте лишних пустых строк между скобками

👍 # Хорошо

if ($foo) {
$this->foo = $foo;
}

💩 # Плохо

if ($foo) {

$this->foo = $foo;

}