PHP-Coding-Style-Guide
PHP-Coding-Style-Guide copied to clipboard
PHP Coding Style Guide
PHP Coding Style Guide
Ключевые слова «НЕОБХОДИМО»/«ДОЛЖНО» («MUST»), «НЕДОПУСТИМО»/«НЕ ДОЛЖНО» («MUST NOT»), «ТРЕБУЕТСЯ» («REQUIRED»), «НУЖНО» («SHALL»), «НЕ ПОЗВОЛЯЕТСЯ» («SHALL NOT»), «СЛЕДУЕТ» («SHOULD»), «НЕ СЛЕДУЕТ» («SHOULD NOT»), «РЕКОМЕНДУЕТСЯ» («RECOMMENDED»), «ВОЗМОЖНО» («MAY») и «НЕОБЯЗАТЕЛЬНО» («OPTIONAL») в этом документе должны расцениваться так, как описано в RFC 2119
Содержание
- Общие требования
- Именование сущностей
- Основные требования к оформлению
- Оформление файлов
- Оформление классов
- Оформление методов
- Оформление управляющих структур
- Работа с массивами
- Работа с исключениями
- Требования к документированию кода
Общие требования
- Ключевые слова PHP ДОЛЖНЫ быть написаны в нижнем регистре
Неправильный вариант:
Do {
} WHILE(true);
Правильный вариант:
do {
} while(true);
- Константы PHP true, false и null ДОЛЖНЫ быть написаны в нижнем регистре
Неправильный вариант:
$isBool = True;
$nullable = NULL;
Правильный вариант:
$isBool = true;
$nullable = null;
- Отступы НЕОБХОДИМО оформлять в виде пробелов, а не табуляции. Один отступ — 4 пробела для PHP-кода и 2 символа для YAML-кода.
- Длину строки РЕКОМЕНДУЕТСЯ придерживать в 80 символов или менее. Более длинные строки СЛЕДУЕТ разбивать на несколько отдельных строк, длина каждой из которых не превышала бы 80 символов.
- В конце непустых строк НЕ ДОЛЖНО быть пробелов.
- Блоки кода НЕОБХОДИМО разделять пустыми строками для повышения удобочитаемости
Неправильный вариант:
$counter = 0;
$length = 4;
for ($i = 0; $i < $length; $i++) {
$counter++;
}
if ($counter > 0) {
return true;
}
Правильный вариант:
$counter = 0;
$length = 4;
for ($i = 0; $i < $length; $i++) {
$counter++;
}
if ($counter > 0) {
return true;
}
- НЕДОПУСТИМО использовать операторы
else,elseif,breakпосле условийifиcaseкоторые возвращают значение либо выбрасывают исключение
Неправильный вариант:
$a = true;
if ($a == true) {
throw new \Exception('Illegal data');
}
else {
return false;
}
Правильный вариант:
$a = true;
if ($a == true) {
throw new \Exception('Illegal data');
}
return false;
- Точку с запятой НЕ СЛЕДУЕТ переносить на новую строку
Неправильный вариант:
$this->setFormatter()
->setDelimeter()
;
Правильный вариант:
$this->setFormatter()
->setDelimeter();
- Название класса НЕДОПУСТИМО задавать строкой, НЕОБХОДИМО использовать конструкцию
::class
Неправильный вариант:
$className = '\Name\Space\ClassName';
Правильный вариант:
use \Name\Space\ClassName;
$className = ClassName::class;
- Для конкатенации строк НЕОБХОДИМО использовать функцию sprintf
Неправильный вариант:
$type = 'error';
$message = 'Some problems occured';
$exception = 'Exception ('.$type.'): ' . $message;
Правильный вариант:
$type = 'error';
$message = 'Some problems occured';
$exception = sprintf('Exception (%s): %s', $type, $message);
- НЕОБХОДИМО добавлять пробелы вокруг любого бинарного оператора (
==,&&, ...)
Неправильный вариант:
if ($a==1&&$b>3) {
//
}
Правильный вариант:
if ($a == 1 && $b > 3) {
//
}
- Унарные операторы (
!,--, ...) НЕ ДОЛЖНЫ быть отделены от переменной пробелом
Неправильный вариант:
$a = true;
$b = ! $a;
Правильный вариант:
$a = true;
$b = !$a;
Именование сущностей
- Для строковой переменной НУЖНО использовать существительное
Пример:
$serverMessage = 'hello server';
$componentHtml = '<h1>Hello</h1>';
$login = 'username';
$password = 'qwerty';
- Имена всех сущностей (классов, методов, переменных) ДОЛЖНЫ быть на английском языке
Неправильный вариант:
$moiTovar;
$cena;
$ssilka;
Правильный вариант:
$myGood;
$price;
$link;
- Имена сущностей НЕДОПУСТИМО сокращать
Неправильный вариант:
$usrCntr;
$err;
$msg;
Правильный вариант:
$userCounter;
$error;
$message;
- В именах сущностей НЕ СЛЕДУЕТ употреблять аббревиатуры.
- Переменные, функции и методы ДОЛЖНЫ именоваться с использованием семантики
«camelCase»
Неправильный вариант:
function get_email_by_id(int $user_id)
{
//
}
Правильный вариант:
function getEmailById(int $userId)
{
//
}
- В именах параметров и опциях (внутри массивов, конфигурациях и т.п.) ДОЛЖНА использоваться семантика
«snake_case»
Пример (YAML-файл):
- name: create folder
path_file:
folder_path: /var/www
mode: 0775
main_owner: deployer
main_group: deployer
Пример (PHP-массив):
$users = [
[
'id' => 1,
'last_name' => 'John',
'first_name' => 'Smith',
],
];
- В именах переменных РЕКОМЕНДУЕТСЯ использовать спецификаторы
- количество (count)
- код чего-либо (code)
- размер (size, length)
- номер (number)
- индекс (index)
Пример:
$keyCode = 15;
$numberFromEnd = 101;
$maxWindowSize = 900;
$minCharacterLength = 10;
$newMessageCount = 3;
- Имена булевых переменных ДОЛЖНЫ содержать в себе булевые спецификаторы (булевый спецификатор — это вопрос с ответом да или нет)
это (is)
содержит (has/contain)
может (can)
должен (should)
возможность (able)
$isPopupOpen = true;
$hasUpperLetters = true;
$containObject = false;
$shouldUpdate = false;
$disabled = true;
- РЕКОМЕНДУЕТСЯ начинать имена методов с глаголов, например:
find,get,setи т.д. Названия методов ДОЛЖНЫ отражать свое поведение. Имя метода не должно иметь более 1 глагола. Методы, содержащие более одного глагола часто являются code smell и признаком нарушения Single Responsibility Principle
Правильный вариант:
public function getBooksOfTheSameAuthor(Book $book)
{
//
}
public function sendEmailNotification()
{
//
}
Неправильный вариант:
public function activateUserAndSendWelcomeEmail(User $user)
{
//
}
Вышеприведённый метод должен быть отрефакторен в 2 метода 2 разных классов: $userRepository->activateUser(User $user) и $emailNotificationService->sendWelcomeEmail(User $user)
- В именах парных сущностей РЕКОМЕНДУЕТСЯ использовать симметричные пары
Общие:
min/max
up/down
old/new
begin/end
first/last
next/previous
Для функций:
open/close
get/set
add/remove
create/destroy
start/stop
insert/delete
increment/decrement
show/hide
suspend/resume
- Имена методов, возвращающих логическое значение, РЕКОМЕНДУЕТСЯ начинать c глаголов:
is,has,inи т.п.
Пример
public function isActive(Book $book): bool
{
//
}
public function inArray(int $itemId): bool
{
//
}
- Массивы это существительные во множественном числе. Переменные, указывающие на массивы ДОЛЖНЫ иметь окончание на
-sи-es
Неправильный вариант:
$user = [
[
'name' => 'Peter',
],
[
'name' => 'John',
],
];
$letter = [
'A',
'B',
'C',
];
$code = [
21,
37,
];
Правильный вариант:
$users = [
[
'name' => 'Peter',
],
[
'name' => 'John',
],
];
$letters = [
'A',
'B',
'C',
];
$codes = [
21,
37,
];
- Классы ДОЛЖНЫ именоваться с использованием
«CapitalizedCamelCase»(каждое слово начинается с большой буквы, между словами нет разделителей)
Пример;
class UserRepository
{
//
}
- Объекты классов ДОЛЖНЫ именоваться с использованием
«concreteCamelCase»(со строчной буквы)
Пример:
$userRepository = new UserRepository();
-
Имена методов ДОЛЖНЫ именоваться с использованием
«camelCase»(первое слово пишется в нижнем регистре, далее каждое слово начинается с большой буквы, а между словами нет разделителей). -
НЕ СЛЕДУЕТ использовать числа в именах переменных, свойств и методов.
-
РЕКОМЕНДУЕТСЯ придерживаться практики, что чем длинее название переменной, тем шире скоуп её видимости
Пример:
class UserRepository
{
/**
* Returns some data
*
* @param int $id
*
* @return array
*/
public function getData(int $id): array
{
$returned = []; // виден только в методе, описание в одно слово
for ($i = 0; $i < $id; $i++) { // переменная $i временная и её скоуп ограничен только циклом
$returned[] = $this->userService->get($i); // $this->userService необходим для всего класса, поэтому даём полное название, на, например $this->us
}
return $returned;
}
}
Основные требования к оформлению
Оформление файлов
- Файлы НЕОБХОДИМО представлять только в кодировке UTF-8 без BOM-байта.
- В файлах НЕОБХОДИМО использовать исключительно теги
<?phpи<?=. - Закрывающий тег
?>ДОЛЖЕН отсутствовать в файлах, содержащих только PHP-код. - После определения пространства имён (
namespace) и после блока импорта пространств имён (use) ДОЛЖНА быть одна пустая строка
Пример:
<?php
namespace App\Services;
use App\Repositories\UserRepository;
use App\Repositories\BookRepository;
class OrderService
{
/**
* @var UserRepository
*/
private $userRepository;
/**
* @var BookRepository
*/
private $bookRepository;
/**
* OrderServices constructor.
*
* @param UserRepository $userRepository
* @param BookRepository $bookRepository
*/
public function __construct(UserRepository $userRepository, BookRepository $bookRepository)
{
$this->userRepository = $userRepository;
$this->bookRepository = $bookRepository;
}
}
- В блоке
useНЕ ДОЛЖНО быть классов, интерфейсов, трейтов, констант, которые не используются в текущем файле. - В одном PHP-файле НЕ ПОЗВОЛЯЕТСЯ объявление более одного класса (трейта, интерфейса, абстрактного класса).
- Неиспользуемые переменные СЛЕДУЕТ убирать из кода.
Оформление классов
- Открывающая фигурная скобка
{в определении класса ДОЛЖНА располагаться на новой строке, а закрывающая фигурная скобка ДОЛЖНА располагаться на следующей строке после тела класса (используетсяAllman style braces)
Неправильный вариант:
class Order {
//
}
Правильный вариант:
class Order
{
//
}
- Абстрактные классы ДОЛЖНЫ иметь префикс
Abstract
Пример:
class AbstractValidator
{
//
}
- Имена интерфейсов ДОЛЖНЫ оканчиваться словом
Interface
Пример:
class ValidatorInterface
{
//
}
- Имена трейтов ДОЛЖНЫ оканчиваться словом
Trait
Пример:
class ValidationTrait
{
//
}
- Имена исключений ДОЛЖНЫ оканчиваться словом
Exception
Пример:
class ValidatorException
{
//
}
- Константы классов ДОЛЖНЫ быть объявлены в верхнем регистре с использованием символа подчёркивания в качестве разделителя слов
Пример:
class VendorLibrary
{
const VERSION = '1.0';
const DATE_APPROVED = '2012-06-01';
}
- Ключевые слова
extendsиimplementsДОЛЖНЫ находиться на той же строке, на которой находится имя класса
Неправильный вариант:
class Order
extends
AbstractOrder
implements
\Serializable
{
//
}
Правильный вариант:
class Order extends AbstractOrder implements \Serializable
{
//
}
- Список реализуемых интерфейсов МОЖЕТ быть разделён на несколько строк, каждая из которых дополнена слева одним отступом (четырьмя пробелами). В таком случае первый элемент списка интерфейсов ДОЛЖЕН начинаться с новой строки, и в каждой строке ДОЛЖЕН быть указан только один интерфейс
Пример:
class Order extends AbstractOrder implements
\ArrayAccess,
\Countable,
\Serializable
{
//
}
- Конструктор класса служит только для инициализации объекта и НЕ ДОЛЖЕН содержать бизнес логику
Неправильный вариант:
class OrderDto
{
/**
* Id of order
*
* @var integer
*/
private $id;
/**
* Title of order
*
* @var string
*/
private $title;
/**
* OrderDto constructor.
*
* @param int $id
* @param string $title
*/
public function __construct(int $id, string $title)
{
if (empty($title)) {
throw new \InvalidArgumentException('Title can not be empty');
}
$this->id = $id;
$this->title = str_replace($title, ' ', '');
}
}
Правильный вариант:
class OrderDto
{
/**
* Id of order
*
* @var integer
*/
private $id;
/**
* Title of order
*
* @var string
*/
private $title;
/**
* OrderDto constructor.
*
* @param int $id
* @param string $title
*/
public function __construct(int $id, string $title)
{
$this->id = $id;
$this->title = $title;
}
}
- Внедрение зависимостей ДОЛЖНО осуществляться через конструктор класса. В случае, если зависимость контекстно используется только в 1 методе, то использовать
method attribute injection
Пример:
class PaymentService
{
public function __construct(
OrderRepository $orderRepository,
ResponseBuilder $responseBuilder
)
{
//
}
public function pay(int $orderId, User $user)
{
//
}
}
- В конструкторе класса НЕ РЕКОМЕНДУЕТСЯ использовать более 5 зависимостей
Неправильный вариант:
class Order
{
public function __construct(
UserRepository $userRepository,
OrderRepository $orderRepository,
BookRepository $bookRepository,
AddressRepository $addressRepository,
BookTransformer $bookTransformer,
ResponseBuilder $responseBuilder
)
{
//
}
}
Класс с таким количеством зависимостей, скорее всего нарушает SRP. В этом случае, стоит часть логики и зависимостей вынести в отдельный класс и уже этот класс инжектить в текущий.
- Свойства класса ДОЛЖНЫ объявляться раньше методов.
- Список свойств класса, указывающих на зависимости этого класса (dependencies) ДОЛЖЕН идти выше, чем список свойств самого класса (properties)
Неправильный вариант:
class Order
{
/**
* @var BookRepository $bookRepository
*/
protected $bookRepository;
/**
* Count of books
*
* @var integer $attemptCount
*/
protected $count;
/**
* @var UserRepository $userRepository
*/
protected $userRepository;
/**
* Price of book
*
* @var float $price
*/
protected $price;
}
Правильный вариант:
class Order
{
/**
* @var BookRepository $bookRepository
*/
protected $bookRepository;
/**
* @var UserRepository $userRepository
*/
protected $userRepository;
/**
* Count of books
*
* @var integer $attemptCount
*/
protected $count;
/**
* Price of book
*
* @var float $price
*/
protected $price;
}
- В классе публичные (
public) методы ДОЛЖНЫ объявляться первыми, защищенные (protected) вторыми и приватные (private) третьими. Исключением из правила являются конструкторы и методыsetUpиtearDown, которые должны всегда объявляться первыми, в целях улучшения читаемости кода. - НЕДОПУСТИМО дублировать название объекта в названии свойства или метода
Неправильный вариант:
class User
{
/**
* Name of user
*
* @var string
*/
private $userName;
/**
* Name getter
*
* @return string
*/
public function getUserName(): string
{
return $this->userName;
}
}
Правильный вариант:
class User
{
/**
* Name of user
*
* @var string
*/
private $name;
/**
* Name getter
*
* @return string
*/
public function getName(): string
{
return $this->name;
}
}
- НЕДОПУСТИМО использование публичных свойств классов. Для работы со свойствами класса вы ДОЛЖНЫ использовать геттеры и сеттеры
Неправильный вариант:
class Order
{
/**
* @var integer
*/
public $id;
}
Правильный вариант:
class Order
{
/**
* @var integer
*/
private $id;
/**
* Order id setter
*
* @param int $id
*
* @return Order
*/
public function setId(int $id): Order
{
$this->id = $id;
return $this;
}
/**
* Order id getter
*
* @return int
*/
public function getId(): int
{
return $this->id;
}
}
- В классах НЕ РЕКОМЕНДУЕТСЯ использовать магические методы.
- Методы ДОЛЖНЫ быть отделены друг от друга и от полей классов 1 пустой строкой.
Оформление методов
- После имени метода НЕ ДОЛЖНО быть пробела. Открывающая фигурная скобка
{ДОЛЖНА находиться на отдельной строке, а закрывающая фигурная скобка}ДОЛЖНА находиться на следующей за телом метода строке. НЕ ДОЛЖНО быть пробелов после открывающей(и перед закрывающей круглыми скобками)в определении метода. В списке аргументов НЕ ДОЛЖНО быть пробела перед запятыми, но ДОЛЖЕН быть пробел после каждой запятой
Неправильный вариант:
class User
{
/**
* Name of user
*
* @var string
*/
private $name;
/**
* Sets name of user
*
* @param string $name
*
* @return User
*/
public function setName ( string $name ): User {
$this->name = $name;
return $this;
}
}
Правильный вариант:
class User
{
/**
* Name of user
*
* @var string
*/
private $name;
/**
* Sets name of user
*
* @param string $name
*
* @return User
*/
public function setName(string $name): User
{
$this->name = $name;
return $this;
}
}
- Аргументы со значениями по умолчанию ДОЛЖНЫ располагаться в конце списка (после аргументов без значений по умолчанию)
Пример:
namespace Vendor\Package;
class ClassName
{
public function foo($arg1, &$arg2, $arg3 = [])
{
//
}
}
- Список аргументов МОЖЕТ быть разделён на несколько строк. В этом случае первый элемент списка аргументов ДОЛЖЕН начинаться с новой строки, и в каждой строке ДОЛЖЕН быть указан только один аргумент, дополненный слева одним отступом (четырьмя пробелами). Открывающая круглая скобка
(остаётся на строке с именем метода. Закрывающая круглая скобка)и открывающая фигурная скобка{ДОЛЖНЫ располагаться вместе на своей отдельной строке, а между ними должен быть один пробел
Пример:
namespace Vendor\Package;
class ClassName
{
public function aVeryLongMethodName(
ClassTypeHint $arg1,
&$arg2,
array $arg3 = []
) {
// тело метода
}
}
- Список аргументов метода и функции ДОЛЖЕН придерживаться следующей последовательности: сначала идут аргументы в виде скалярных примитивов, потом массивы и уже потом зависимости в виде классов (интерфейсов)
Неправильный вариант:
class PaymentOrder
{
/**
* //
*/
public function __construct(
array $orderItems,
PaymentService $paymentService,
int $oderId,
int $userId,
OrderRepository $orderRepository
) {
//
}
}
Правильный вариант:
class PaymentOrder
{
/**
* //
*/
public function __construct(
int $oderId,
int $userId,
array $orderItems,
PaymentService $paymentService,
OrderRepository $orderRepository
) {
//
}
}
- Все методы сеттеры ДОЛЖНЫ возращать
$this(fluent interface)
Пример:
class Order
{
/**
* @var integer
*/
private $id;
/**
* @var string
*/
private $title;
/**
* Order id setter
*
* @param int $id
*
* @return Order
*/
public function setId(int $id): Order
{
$this->id = $id;
return $this;
}
/**
* Order title setter
*
* @param string $title
*
* @return Order
*/
public function setTitle(string $title): Order
{
$this->title = $title;
return $this;
}
}
- При объединении методов в цепочку СЛЕДУЕТ каждый метод переносить на новую строку с отступом (четыре пробела). Метод отдающий fluent interface ДОЛЖЕН вызываться на первой строке.
Неправильный вариант:
$message = $this
->getMessageBuilder()
->setReceiver('[email protected]')
->setSubject('subject')
->setMessage('message body')
->build();
Правильный вариант:
$message = $this->getMessageBuilder()
->setReceiver('[email protected]')
->setSubject('subject')
->setMessage('message body')
->build();
- Оператору
returnДОЛЖНА предшествовать пустая линия, за исключением случая когдаreturnединственная строка внутри управляющей структуры (например if, function)
Пример 1:
class Order
{
/**
* @var integer
*/
private $id;
/**
* Order id getter
*
* @return int
*/
public function getId(): int
{
return $this->id;
}
}
Пример 2:
class Order
{
/**
* @var integer
*/
private $id;
/**
* Order id getter
*
* @return int
*/
public function getId(): int
{
if (empty($this->id)) {
return 0;
}
return $this->id;
}
}
-
НЕОБХОДИМО использовать
return null;, когда функция должна возвращать значениеnull, и НЕОБХОДИМО использоватьreturn;в случаях когда функция возвращает пустое значение (void). -
Код методов ДОЛЖЕН иметь не более 1 уровня вложенности (за исключением некоторых особых ситуаций). Else блоки и лишние уровни вложенности РЕКОМЕНДУЕТСЯ убирать при помощи return early и extract method
Пример:
function sendEmailNotificationIfRequired(Transaction $transaction)
{
if ($transaction->type() == 'purchase') {
// 1
if ($transaction->completed()) {
// 2
if ($this->timeService->hours() > 19) {
// 3
return false
} else {
// 4
return $this->notificationService->sendTransactionNotification($transaction);
}
} else {
// 2
return false;
}
} else {
// 1
return false;
}
}
Отрефакторенный пример:
function sendEmailNotificationIfRequired(Transaction $transaction)
{
if ($transaction->type() != 'purchase') { // 1
return false;
}
if (!$transaction->completed()) { // 2
return false;
}
if ($this->timeService->hours() > 19) { // 3
return false;
}
return $this->notificationService->sendTransactionNotification($transaction); // 4
}
- В коде вызова функций и методов НЕ ДОЛЖНО быть пробела между именем функции или метода и открывающей круглой скобкой. НЕ ДОЛЖНО быть пробела после открывающей круглой скобки. НЕ ДОЛЖНО быть пробела перед закрывающей круглой скобкой. В списке аргументов НЕ ДОЛЖНО быть пробелов перед запятыми, но ДОЛЖЕН быть пробел после каждой запятой
Пример:
bar();
$foo->bar($arg1);
Foo::bar($arg2, $arg3);
- Список аргументов МОЖЕТ быть разделён на несколько строк, каждая из которых дополнена слева одним отступом (четырьмя пробелами). В таком случае первый элемент списка аргументов ДОЛЖЕН начинаться с новой строки, и в каждой строке ДОЛЖЕН быть указан только один аргумент
Пример:
$foo->bar(
$longArgument,
$longerArgument,
$muchLongerArgument
);
- Для каждого аргумента метода и функции РЕКОМЕНДУЕТСЯ использовать
Type Hinting. В качестве зависимостей РЕКОМЕНДУЕТСЯ использовать контракты (интерфейсы сущности) вместоDirect Type Hinting(упоминания конкретного класса).Direct Type Hintingиспользуется при невозможности использовать контракт.
Оформление управляющих структур
- В управляющих конструкциях после ключевого слова и перед открывающей круглой скобкой
(ДОЛЖЕН располагаться один пробел. Открывающая фигурная скобка{в управляющих конструкциях ДОЛЖНА располагаться на той же строке, что и сама конструкция, а закрывающая фигурная скобка}ДОЛЖНА располагаться на следующей строке после тела конструкции. После открывающей круглой скобки(и перед закрывающей круглой скобкой)в управляющих конструкциях НЕ ДОЛЖНО быть пробела. НЕОБХОДИМО добавлять пробел после каждой запятой разделителя. Между закрывающей круглой скобкой)и открывающей фигурной скобкой{ДОЛЖЕН быть один пробел. Тело конструкции ДОЛЖНО быть дополнено одним отступом (четырьмя пробелами)
Пример:
while (true) {
//
}
- Тело каждой управляющей конструкции ДОЛЖНО быть заключено в фигурные скобки. Это позволяет стандартизировать внешний вид управляющих конструкций с снизить риск возникновения ошибок при добавлении новых строк в тело конструкции
Неправильный вариант:
if (true)
return true;
Правильный вариант:
if (true) {
return true;
}
- Ключевое слово
elseifСЛЕДУЕТ использовать вместо отдельного сочетанияelseиif. Так конструкция будет представлять собой одно слово. - Слова
elseиelseifДОЛЖНЫ располагаться на той же строке, что и закрывающая фигурная скобка предшествующего тела конструкции
if ($expr1) {
//
} elseif ($expr2) {
//
} else {
//
}
- Конструкция
switchДОЛЖНА выглядеть следующим образом
switch ($expr) {
case 0:
echo 'First case, with a break';
break;
case 1:
echo 'Second case, which falls through';
// no break
case 2:
case 3:
case 4:
echo 'Third case, return instead of break';
return;
default:
echo 'Default case';
break;
}
- Конструкция
whileДОЛЖНА выглядеть следующим образом
while ($expr) {
// тело конструкции
}
- Конструкция
do whileДОЛЖНА выглядеть следующим образом
do {
// тело конструкции
} while ($expr);
- Конструкция
forДОЛЖНА выглядеть следующим образом
for ($i = 0; $i < 10; $i++) {
// тело for
}
- Конструкция
foreachДОЛЖНА выглядеть следующим образом
foreach ($iterable as $key => $value) {
// тело foreach
}
- Блоки конструкции
try catchДОЛЖНЫ выглядеть следующим образом
try {
// тело try
} catch (FirstExceptionType $e) {
// тело catch
} catch (OtherExceptionType $e) {
// тело catch
}
Работа с исключениями
- Выбрасывание исключений класса
\ExceptionНЕДОПУСТИМО. Для всех выбрасываемых исключений ДОЛЖНЫ создаваться пользовательские исключения, наследующие класс\Exception. Выбрасываться ДОЛЖНЫ либо пользовательские исключения, наследуемые от\Exception, либо исключения, наследуемые от пользовательских исключений. - Вы ДОЛЖНЫ обрабатывать исключения, которые кидает вызываемый класс в своём коде. Если вы не можете обработать исключение нижнего слоя, вам НЕОБХОДИМО обернуть это исключение в исключение своего слоя и выбросить его выше. Обработка исключений нижнего слоя ДОЛЖНА представлять собой набор блоков
catchв каждом из которых обрабатываются различные исключения вызывающего класса. Последним блокомcatchДОЛЖЕН быть блок с обработкой\Throwableи/или\Exception
Пример:
namespace App\Order\Exception;
class OrderException extends \Exception
{
//
}
namespace App\Order\Exception;
class OrderNotFoundException extends OrderException
{
//
}
namespace App\Order\Exception;
class OrderAlreadyPayed extends OrderException
{
//
}
namespace App\Service;
use App\Order\Exception\OrderException;
use App\Order\Exception\OrderNotFoundException;
use App\Order\Exception\OrderAlreadyPayed;
use App\Order\Repository\ModelNotFoundException;
use App\Service\PaymentService;
use App\Repository\OrderRepository;
class PaymentOrder
{
/**
* @var PaymentService
*/
private $paymentService;
/**
* @var OrderRepository
*/
private $orderRepository;
/**
* PaymentOrder constructor.
*
* @param PaymentService $paymentService
* @param OrderRepository $orderRepository
*/
public function __construct(PaymentService $paymentService, OrderRepository $orderRepository)
{
$this->paymentService = $paymentService;
$this->orderRepository = $orderRepository;
}
/**
* @param int $id
*
* @return bool
*
* @throws OrderAlreadyPayed
* @throws OrderException
* @throws OrderNotFoundException
*/
public function payment(int $id): bool
{
try {
$order = $this->orderRepository->get($id);
} catch (ModelNotFoundException $e) {
throw new OrderNotFoundException(sprintf('Order id %s not found', $id));
} catch (\Throwable $e) {
throw new OrderException(sprintf('An error occurred for order id %s', $id));
}
if ($order->isPayed()) {
throw new OrderAlreadyPayed(sprintf('An order id %s already payed', $id));
}
return $this->paymentService->pay($id);
}
}
- Конструктор класса НЕ ДОЛЖЕН кидать исключения.
Работа с массивами
- Массивы ДОЛЖНЫ определяться при помощи конструкции
[], использование конструкцииarray()НЕДОПУСТИМО. - Во всех уместных случаях вместо
foreachРЕКОМЕНДУЕТСЯ использовать соответствующие методы обработки массивов
Пример 1:
$identifiers = [];
foreach ($books as $book) {
$identifiers[] = $book->id;
}
Отрефакторенный пример 1:
$bookMapper = function($book)
{
return $book->id;
}
$identifiers = array_map($bookMapper, $books);
Пример 2:
$activeBooks = [];
foreach($books as $book) {
if ($book->active) {
$activeBooks[] = $book;
}
}
Отрефакторенный пример 2:
$bookFilter = function(Book $book)
{
return $book->active;
}
$activeBooks = array_filter($books, $bookFilter);
- При инициализации массива, каждый элемент массива ДОЛЖЕН быть на новой строке. НЕОБХОДИМО добавлять запятую в конце каждой строки многострочного массива (даже после последней)
Неправильный вариант:
$books = [
'Harry Potter and the Cursed Child'
];
$items = ['Fridge', 'Toaster'];
Правильный вариант:
$books = [
'Harry Potter and the Cursed Child',
];
$items = [
'Fridge',
'Toaster',
];
Требования к документированию кода
- Документацию РЕКОМЕНДУЕТСЯ писать на английском языке.
- В файлах проекта НЕ ДОЛЖНО быть комментариев, которые автоматически сгенерировала IDE (например,
Created by ...) - Для
Type HintingвPHPDocsи приведения типов ДОЛЖНЫ использоваться следующие нотации:bool(вместоbooleanиBoolean),int(вместоinteger) иfloat(вместоdoubleиreal). Данные требования необходимы для совместимости с нотациями использующимися в PHP7. - Типы свойств класса, получаемые магическими методами ДОЛЖНЫ быть обозначены при помощи
PHPDocблока@property
Пример:
/**
* Class Order
*
* @property string $title
* @property integer $id
*/
class Order
{
//
}
- Типы методов класса, получаемые магическими методами ДОЛЖНЫ быть обозначены при помощи
PHPDocблока@method
Пример:
/**
* Class Order
*
* @method setTitle(string $title)
* @method string getTitle()
*/
class Order
{
//
}
- Для комментариев НЕ СЛЕДУЕТ использовать решетку
Неправильный вариант:
# somme comment
Правильный вариант:
// somme comment
- У каждого свойства класса ДОЛЖНА присутствовать документация содержащая краткое описание свойства и его тип
Пример:
class Order
{
/**
* Order id
*
* @var int
*/
private $id;
/**
* Order title
*
* @var string
*/
private $title;
}
- У каждого метода класса ДОЛЖНА присутствовать документация содержащая краткое описание метода, список принимаемых аргументов, список выбрасываемых исключений и тип возвращаемого значения
Пример:
class Order
{
/**
* Order id setter
*
* @param int $id
*
* @return Order
*
* @throws \InvalidArgumentException
*/
public function setId(int $id): Order
{
if ($id > 10) {
throw new \InvalidArgumentException('Order is does not correct');
}
$this->id = $id;
return $this;
}
}
- Блоки описания к свойству или методу, описания типа переменной
@var, списка принимаемых агрументов метода@param, возвращаемого значения@return, списка выбрасываемых исключений@throwsДОЛЖНЫ быть отделены друг от друга пустой строкой
Неправильный вариант:
class Order
{
/**
* Current balance
* @var float
*/
private $balance;
/**
* Sends money between addresses
* @param string $from
* @param string $to
* @param float $amount
* @return bool
* @throws \InvalidArgumentException
* @throws InvalidAmountException
*/
public function sendMoney(string $from, string $to, float $amount): bool
{
if (empty($from)) {
throw new \InvalidArgumentException('From address can not be empty');
}
if ($amount < 0) {
throw new InvalidAmountException('An amount must be greater than zero');
}
return true;
}
}
Правильный вариант:
class Order
{
/**
* Current balance
*
* @var float
*/
private $balance;
/**
* Sends money between addresses
*
* @param string $from
* @param string $to
* @param float $amount
*
* @return bool
*
* @throws \InvalidArgumentException
* @throws InvalidAmountException
*/
public function sendMoney(string $from, string $to, float $amount): bool
{
if (empty($from)) {
throw new \InvalidArgumentException('From address can not be empty');
}
if ($amount < 0) {
throw new InvalidAmountException('An amount must be greater than zero');
}
return true;
}
}
- Все итераторы, не поддерживающие
Type Hinting, ДОЛЖНЫ использовать inline-PHPDoc директиву@varдля обозначения типа локальной переменной
Пример:
foreach ($books as $book) {
/* @var Book $book */
$book->save();
}