Блог Мазепина Василия

Пишу о том, что кажется интересным

Symfony КодСтайл

2019-12-08 15:00 | Комментарии

Доброго времени суток, мои дорогие читатели!!! Спустя немаленькое время, отдав на ревью свой symfony-код, получил обратную связь в том числе о том, что кодстайл не соблюдается, и это плохо. У Symfony есть стандарт оформления, он для контрибьютеров, но придерживаться его следует, ведь Symfony дурного не посоветует. Решил освежить знания и пройтись по стандарту, о чем прошу под кат.

Этот стандарт базируется на PSR-1, PSR-2, PSR-4, так что некоторые аспекты нам уже могут быть известны.

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

Структурные соглашения

  • Добавлять один пробел после каждой запятой:
example.php
1
2
3
4
5
6
7
<?php

// Правильно
$a = [1, 2, 3, 4, 5, 'example', null];

// Неправильно
$a = [1,2,3,4,5,'example',null];
  • Обрамлять пробелом бинарные операторы ==, &&, ||, ..., кроме оператора конкатенации .:
example.php
1
2
3
4
5
6
7
8
9
10
11
<?php

// Правильно
if ($a && $b) {
    return $b == $c;
}

// Неправильно
if ($a&&$b) {
    return $b==$c;
}
  • Место унарных операторов рядом с затронутой переменной:
example.php
1
2
3
4
5
6
7
8
9
10
11
<?php

// Правильно
if (!$a){
    $b = !$c;
}

// Неправильно
if (! $a){
    $b = $c ++;
}
  • Всегда используйте тождественные сравнения(с проверкой типа), если вам не нужно жонглирование типом:
example.php
1
2
3
4
5
6
7
8
9
10
11
12
<?php

// Правильно
if ($a === $b){
    return $c;
}

// Неправильно, можем нарваться на плавающий баг в случае, когда при приведении типов условие будет ложный результат.
// Ну и дисциплинирует к внимательности к типу.
if ($a == $b){
    return $c;
}
  • Используйте Йода-стиль написания при проверке переменной, чтобы избежать случайного присвоения.

Про это требование знал, но не знал что он так называется):

example.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

// Правильно
if (true === $a) {
    return null;
}

// Неправильно, можем по невнимательности поставить один символ равно вместо 2х и случайно присвоим значение, получив баг
if ($a == true){
    return $c;
}
if ($b = true){
    return $d;
}
  • Добавьте запятую после каждого элемента массива в многострочном массиве, даже после последнего

Удобство этого правила заключается в легком перещении элементов массива в IDE, а так же дублирования строки массива:

example.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php

// Правильно
$a = [
    1,
    2,
    3,
    4,
    5,
];

// Неправильно.
$a = [
    1,
    2,
    3,
    4,
    5
];

Добавьте пустую строку перед return, кроме случаев когда return находится внутри операторов типа if:

example.php
1
2
3
4
5
6
7
8
9
<?php

if ('string' === $dummy) {
    if ('values' === $mergedOptions['some_default']) {
        return substr($dummy, 0, 5);
    }
    // без пустой строки плохо
    return ucwords($dummy);
}
  • Используйте return null; когда дейтвительно нужно вернуть null, и используйте return; когда нужно вернуть void;
  • Определяйте один класс в одном файле.

Это не относится к частным вспомогательным классам, которые не предназначены для создания экземпляров извне и поэтому не относятся к стандартам PSR-0 и PSR-4;

  • Фигурные скобки используются для обозначения тела структуры управления независимо от количества содержащихся в нем операторов:
example.php
1
2
3
4
5
6
7
8
9
<?php
// Правильно
if ('true' === $dummy) {
    return true;
}

// Неправильно
if ('true' === $dummy)
    return true;
  • Декларируйте свойства класса перед методами;

  • Сначала объявите public методы, затем protected и, наконец, private.

Исключением из этого правила являются конструктор класса и методы setUp и tearDown тестов PHPUnit, которые всегда должны быть первыми методами для повышения читаемости;

  • Объявите все аргументы в той же строке, что и имя метода/функции, независимо от количества аргументов:
example.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
// Правильно
function test(string $text, DateTime $date, bool $isDev)
{
    // любой код
}

// Неправильно
function test(
    string $text,
    DateTime $date,
    bool $isDev
)
{
    // любой код
}
  • Используйте круглые скобки при создании экземпляров классов независимо от количества аргументов конструктора:
example.php
1
2
3
4
5
6
<?php
// Правильно
$entity = new User();

// Неправильно
$entity = new User;
  • Строки сообщений об исключениях и ошибках должны быть объединены с помощью sprintf:
example.php
1
2
3
4
5
6
7
8
9
<?php
// Правильно
throw new \RuntimeException(sprintf('Неверный код подтверждения "%s"', $code));

// Неправильно
throw new \RuntimeException('Неверный код подтверждения: ' . $code));

// Так же неправильно
throw new \RuntimeException("Неверный код подтверждения: $code"));
  • Вызов trigger_error с типом E_USER_DEPRECATED должен быть осуществлен с помощью оператора @:
example.php
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
// Правильно
    /**
     * @return string
     *
     * @deprecated
     */
    public function someDeprecatedMethod()
    {
        @trigger_error(sprintf('The %s() method is deprecated since vendor-name/package-name 2.8 and will be removed in 3.0. Use Acme\Baz::someMethod() instead.', __METHOD__), E_USER_DEPRECATED);

        return Baz::someMethod();
    }
  • Не используйте else, elseif, break после if и case условия, которые возвращают или бросают что-то:
example.php
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
    // Правильно
    if ($isInvalidCode) {
        throw new \RuntimeException(sprintf('Неверный код подтверждения "%s"', $code));
    }
    $user->login();

    // Неправильно
    if ($isInvalidCode) {
      throw new \RuntimeException(sprintf('Неверный код подтверждения "%s"', $code));
    } else {
       $user->login();
    }
  • Не используйте пробелы вокруг [ и перед ]:
example.php
1
2
3
4
5
6
<?php
    // Правильно
    $user = $list['user'];

    // Неправильно
    $user = $list [ 'user' ];
  • Добавьте оператор use для каждого класса, который не является частью глобального пространства имен;

  • Когда теги PHPDoc, такие как @param или @return, включают null и другие типы, всегда помещайте null в конец списка типов:

example.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
    // Правильно
    /**
     * @param string $login
     * @param string | null $password
     */
    public function login($login, $password)
    {
       // Ваш код
    }

    // Неправильно
    /**
     * @param string $login
     * @param null | string $password
     */
    public function login($login, $password)
    {
       // Ваш код
    }

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

  • Используйте camelCase для именования переменных, функций, методов, и аргументов(ex: generateResponse($message, $statusCode));

  • Используйте snake_case для конфигурационных параметров, и Twig-переменных (ex: framework.csrf_protection, http_status_code);

  • Используйте пространства имен для всех классов PHP и UpperCamelCase для их имен (например, ConsoleLogger);

  • Используйте префикс Abstract для всех абстрактных классов, кроме PHPUnit *TestCase;
  • Используйте окончание Interface в именовании интерфейсов;
  • Используйте окончание Trait в именовании трейтов;
  • Используйте окончание Exception в именовании исключений;
  • Используйте camelCase для именования PHP файлов (LoginUserService.php);
  • Используйте snake_case для именования Twig-шаблонов и ассетов (section_layout.html.twig, index.scss);
  • При указании типов используйте bool (вместо boolean, Boolean), int (вместо integer), float (вместо double, real);
  • Не забудьте подглядеть в дополнительное соглашение, в котором так же есть рекомендации к именованию.

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

  • Наименование сервиса должно полностью совпадать с полным именем класса, представляющим этот сервис(App\EventSubscriber\UserSubscriber);
  • Используйте строчные буквы для имен параметров (за исключением ссылок на переменные среды с синтаксисом %env(VARIABLE_NAME)%);
  • Добавьте псевдонимы классов для публичных сервисов(Symfony\Component\Something\ClassName => something.service_name);

Соглашение об оформлении документации

  • Добавьте блоки PHPDoc для всех классов, методов и функций;
  • Сгруппируйте аннотации таким образом, чтобы аннотации одного типа немедленно следовали друг за другом, а аннотации другого типа разделялись одной пустой строкой;
  • Опустите тег @return, если метод ничего не возвращает;
  • Аннотации @package и @subpackage не используются;
  • Если есть хотя бы одна аннотация - докблок многострочный (нельзя вот так /** {@inheritdoc} */)

Комментарии