Стиль кодирования Yii 2 framework
=================================

Описанный ниже стиль кодирования используется при разработке ядра Yii 2.x и его официальных расширений. Если вы хотите участвовать в разработке фреймворка, постарайтесь придерживаться данного стиля. Мы не принуждаем вас использовать этот стиль при разработке ваших приложений с использованием Yii 2. В данном случае можете использовать тот стиль, который вам больше подходит.

Пример конфигурационного файла для CodeSniffer вы можете найти здесь: https://github.com/yiisoft/yii2-coding-standards

## 1. Обзор

В общем, мы используем совместимый со стандартом [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) стиль, так что все положения [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) вполне применимы к нашему стилю кодирования.

- ДОЛЖНЫ использоваться только открывающие теги `<?php` или `<?=`;
- В конце файла должна быть пустая строка;
- Файлы с PHP кодом ДОЛЖНЫ содержать только символы в кодировке UTF-8 без BOM;
- Для выравнивания кода НУЖНО использовать 4 пробела вместо табуляции;
- Имена классов ДОЛЖНЫ быть объявлены используя `StudlyCaps`;
- Константы класса ДОЛЖНЫ быть объявлены в верхнем регистре с подчеркиванием в качестве разделителей;
- Имена методов ДОЛЖНЫ быть объявлены используя `camelCase`;
- Имена свойств ДОЛЖНЫ быть объявлены используя `camelCase`;
- Имена свойств ДОЛЖНЫ начинаться с подчеркивания если они объявлены с использованием модификатора `private`;
- Всегда используйте `elseif` вместо `else if`.

## 2. Файлы

### 2.1. Теги PHP

- В PHP коде ДОЛЖНЫ использоваться теги `<?php ?>` или `<?=`; НЕ ДОЛЖНЫ использоваться другие теги, такие как `<?`;
- Если файл содержит только PHP код, тогда закрывающий тег `?>` не нужен;
- Не добавляйте лишние пробелы в конец строки;
- Все файлы, содержащие PHP код, должны иметь расширение `.php`.

### 2.2. Кодировка символов

PHP код должен содержать только символы в кодировке UTF-8 без BOM.

## 3. Имена Классов

Имена классов ДОЛЖНЫ быть определены используя `StudlyCaps`. Например, `Controller`, `Model`.

## 4. Классы

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

- При именовании классов следует использовать `CamelCase`;
- Открывающая фигурная скобка всегда должна быть на следующей строке после имени класса;
- Все классы должны быть документированы в соответствии с PHPDoc;
- Весь код класса должен быть выровнен с использованием 4 пробелов;
- В одном PHP файле должен быть только один класс;
- Все классы должны использовать пространства имен;
- Имя класса должно совпадать с именем файла. Пространство имен класса должно соответствовать структуре каталогов.

```php
/**
 * Документация
 */
class MyClass extends \yii\base\BaseObject implements MyInterface
{
    // код
}
```

### 4.1. Константы

Константы класса ДОЛЖНЫ быть объявлены в верхнем регистре с подчеркиванием в качестве разделителей.
Пример:

```php
<?php
class Foo
{
    const VERSION = '1.0';
    const DATE_APPROVED = '2012-06-01';
}
```
### 4.2. Свойства

- При объявлении общедоступных (public) членов класса нужно использовать ключевое слово `public`;
- Общедоступные и защищенные (protected) переменные должны быть объявлены в начале класса, раньше объявления методов;
  Закрытые (private) переменные, так же, должны быть объявлены в начале класса, но могут быть добавлены и непосредственно перед методами, использующими их, в случае, если эти переменные используются только небольшой частью методов класса;
- Порядок объявления свойств класса должен соответствовать их видимости: общедоступные, защищенные и закрытые;
- Ограничений на порядок свойств одной области видимости нет;
- Для улучшения читаемости, следует не оставлять пустых строк между объявлением свойств и оставлять две пустые строки между объявлениями переменных и методов. Между объявлениями свойств разной области видимости должна быть добавлена одна пустая строка;
- Закрытые переменные должны иметь имя вида `$_varName`;
- Общедоступные члены класса и отдельные переменные должны использовать `$camelCase`
  с первой буквой в нижнем регистре;
- Используйте понятные имена. Переменные вроде `$i` и `$j` лучше не использовать.

Пример:

```php
<?php
class Foo
{
    public $publicProp1;
    public $publicProp2;

    protected $protectedProp;

    private $_privateProp;


    public function someMethod()
    {
        // ...
    }
}
```

### 4.3. Методы

- При именовании методов и функций следует использовать `camelCase` с первой буквой в нижнем регистре;
- Имена должны быть понятными и отражать назначение функции;
- Методы классов должны быть объявлены с использованием одного из модификаторов `private`, `protected` или
  `public`. Использование `var` недопустимо;
- Открывающая фигурная кавычка должна быть расположена на строке, следующей за строкой объявления функции.

```php
/**
 * Документация
 */
class Foo
{
    /**
     * Документация
     */
    public function bar()
    {
        // код
        return $value;
    }
}
```

### 4.4 Блоки Документации

`@param`, `@var`, `@property` и `@return` должны описывать типы `bool`, `int`, `string`, `array` или `null`. Вы можете использовать и имена классов, например `Model` или `ActiveRecord`. Для типизированных массивов используйте `ClassName[]`.

### 4.5 Конструкторы

- Используйте `__construct` вместо старого стиля, применяемого в PHP 4.

## 5 PHP

### 5.1 Типы Данных

- Все типы данных и значения PHP должны быть в нижнем регистре. Это относится и к `true`, `false`, `null` и `array`.

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


```php
public function save(Transaction $transaction, $argument2 = 100)
{
    $transaction = new Connection; // плохо
    $argument2 = 200; // хорошо
}
```

### 5.2 Строки

- Если строка не содержит переменных или одинарных кавычек, используйте одинарные кавычки;

```php
$str = 'Например так.';
```

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

#### Подстановка переменных

```php
$str1 = "Привет, $username!";
$str2 = "Привет, {$username}!";
```

Следующий вариант запрещен:

```php
$str3 = "Привет, ${username}!";
```

#### Конкатенация

При конкатенации строк выделяйте точку пробелами:

```php
$name = 'Yii' . ' Framework';
```

Для длинных строк используйте следующий подход:

```php
$sql = "SELECT *"
    . "FROM `post` "
    . "WHERE `id` = 121 ";
```

### 5.3 Массивы

Для массивов мы используем краткий синтаксис, появившийся в PHP 5.4.

#### Индексированные массивы

- Не используйте отрицательные числа в качестве индексов массива.

Используйте следующий стиль:

```php
$arr = [3, 14, 15, 'Yii', 'Framework'];
```

При большом количестве элементов:

```php
$arr = [
    3, 14, 15,
    92, 6, $test,
    'Yii', 'Framework',
];
```

#### Ассоциативные массивы

Для ассоциативных массивов используйте следующий стиль:

```php
$config = [
    'name'  => 'Yii',
    'options' => ['usePHP' => true],
];
```

### 5.4 Управляющие конструкции

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

```php
if ($event === null) {
    return new Event();
}
if ($event instanceof CoolEvent) {
    return $event->instance();
}
return null;


// такой код недопустим:
if (!$model && null === $event)
    throw new Exception('test');
```

Старайтесь избегать использования `else` после `return` там, где это возможно.
Используйте [граничные операторы](https://refactoring.guru/ru/replace-nested-conditional-with-guard-clauses).

```php
$result = $this->getResult();
if (empty($result)) {
    return true;
} else {
    // дальнейшие вычисления
}
```

лучше переписать так:

```php
$result = $this->getResult();
if (empty($result)) {
    return true;
}

// дальнейшие вычисления
```

#### switch

Используйте следующий стиль для switch:

```php
switch ($this->phpType) {
    case 'string':
        $a = (string) $value;
        break;
    case 'integer':
    case 'int':
        $a = (int) $value;
        break;
    case 'boolean':
        $a = (bool) $value;
        break;
    default:
        $a = null;
}
```

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

```php
doIt(2, 3);

doIt(['a' => 'b']);

doIt('a', [
    'a' => 'b',
    'c' => 'd',
]);
```

### 5.6 Анонимные (lambda) функции

Не забывайте про пробелы между ключевыми словами `function`/`use` и открывающими круглыми скобками:

```php
// правильно
$n = 100;
$sum = array_reduce($numbers, function ($r, $x) use ($n) {
    $this->doMagic();
    $r += $x * $n;
    return $r;
});

// неправильно
$n = 100;
$mul = array_reduce($numbers, function($r, $x) use($n) {
    $this->doMagic();
    $r *= $x * $n;
    return $r;
});
```

Документация
-------------

- Для получения информации по синтаксису документации обратитесь к первоисточнику [PHPDoc](https://phpdoc.org/);
- Код без документации недопустим;
- Все файлы классов должны содержать блок документации в начале файла и блок документации непосредственно перед каждым классом;
- Нет необходимости использовать тег `@return` если метод не возвращает значение;
- Все виртуальные свойства классов, наследованных от `yii\base\BaseObject`, документируются тегом `@property` в блоке документации класса;
  Аннотации геттеров и сеттеров автоматически генерируются из соответствующих тегов `@return` or `@param`
  посредством выполнения команды `./build php-doc` в соответствующем каталоге;
  Вы можете добавить дополнительный тег `@property` для геттера или сеттера для пояснения назначения переменной метода, если это необходимо.
  Например:

```php
<?php
/**
 * Returns the errors for all attribute or a single attribute.
 * @param string $attribute attribute name. Use `null` to retrieve errors for all attributes.
 * @property array An array of errors for all attributes. Empty array is returned if no error.
 * The result is a two-dimensional array. See [[getErrors()]] for detailed description.
 * @return array errors for all attributes or the specified attribute. Empty array is returned if no error.
 * Note that when returning errors for all attributes, the result is a two-dimensional array, like the following:
 * ...
 */
public function getErrors($attribute = null)
```

#### Файл

```php
<?php
/**
 * @link https://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license https://www.yiiframework.com/license/
 */
```

#### Класс

```php
/**
 * Component is the base class that provides the *property*, *event* and *behavior* features.
 *
 * @include @yii/docs/base-Component.md
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
class Component extends \yii\base\BaseObject
```


#### Функция / метод

```php
/**
 * Returns the list of attached event handlers for an event.
 * You may manipulate the returned [[Vector]] object by adding or removing handlers.
 * For example,
 *
 * ```
 * $component->getEventHandlers($eventName)->insertAt(0, $eventHandler);
 * ```
 *
 * @param string $name the event name
 * @return Vector list of attached event handlers for the event
 * @throws Exception if the event is not defined
 */
public function getEventHandlers($name)
{
    if (!isset($this->_e[$name])) {
        $this->_e[$name] = new Vector;
    }
    $this->ensureBehaviors();
    return $this->_e[$name];
}
```

#### Разметка

Как вы можете видеть в примерах выше, мы используем специальную разметку для форматирования комментариев PHPDoc.

Ниже описан дополнительный синтаксис для описания связей между классами, методами и свойствами в документации:

- `[[canSetProperty]]` создаст ссылку на метод или свойство `canSetProperty` этого класса;
- `[[Component::canSetProperty]]` создаст ссылку на метод `canSetProperty` класса `Component` того же пространства имен;
- `[[yii\base\Component::canSetProperty]]` создаст ссылку на метод `canSetProperty` класса `Component` в пространстве имен `yii\base`;
- `[[Component]]` создаст ссылку на класс `Component` в том же пространстве имен. Здесь так же возможно явное указание пространства имен.

Для явного указания текста ссылки возможно использование следующего синтаксиса:

```
... as displayed in the [[header|header cell]].
```

Часть до | это имя свойства, метода или класса для ссылки, а часто поле | это текст ссылки.

Так же, возможно создание ссылок на Руководство:

```markdown
[Руководство](guide:file-name.md)
[Раздел руководства](guide:file-name.md#subsection)
```


#### Комментарии

- Однострочные комментарии должны начинаться с `//`, а не с `#`;
- Однострочные комментарии должны располагаться на отдельной строке.

Дополнительные правила
----------------

### `=== []` или `empty()`

Используйте `empty()`, где это возможно.

### Несколько точек возврата

Не допускайте запутанных вложенных условных конструкций, используйте return. Для коротких методов это не актуально.

### `self` или `static`

Всегда используйте `static`, за исключением следующих случаев:

- доступ к константам ДОЛЖЕН осуществляться через `self`: `self::MY_CONSTANT`;
- доступ к защищенным статическим свойствам ДОЛЖЕН осуществляться через `self`: `self::$_events`;
- допустимо использовать `self` для рекурсивного обращения к текущему классу, вместо класса наследника.

### Значение "ничего не делать"

Свойства указывающее компоненту на отсутствие необходимости что-либо делать, должны принимать значение `false`. `Null`, `''`, или `[]` не должны использоваться в таких случаях.

### Каталоги/пространства имен

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