- PHP Code Style Conventions
- Правила именования файлов и папок
- Папки
- Файлы
- Правила именования пространств имен, классов, методов и переменных
- Пространства имен
- Классы
- Методы
- Переменные
- Правила оформления кода
- Комментирование кода
- Правила написания кода
- PHP RFC: Class Naming
- Proposal
- PascalCase except Acronyms/Initialisms
- Always PascalCase
- Backward Incompatible Changes
PHP Code Style Conventions
В данной статье рассматривается подход к написанию и оформлению PHP кода. Нижеизложенные моменты были сформированы путем анализа существующих подходов компаний и личного опыта.
Правила именования файлов и папок
Все названия для папок и файлов должны быть осмысленными и говорящими (не требующие дополнительного разъяснения).
Папки
Все папки именуются в нижнем регистре с разделением слов с использованием символа — (минус).
Если папка хранит в себе классы, которые относятся к пространству имен (namespace), то папка именуется в соответствии с названием пространства имен (namespace).
Разрешенные символы для именования папок: латинские буквы и символ — (минус).
Файлы
Все файлы, относящиеся к проекту, именуются в нижнем регистре с разделением слов с использованием символа — (минус).
Если файл является файлом класса, то именуется в соответствии с названием класса.
Правила именования пространств имен, классов, методов и переменных
Все названия должны быть осмысленными и говорящими (не требующие дополнительного разъяснения).
Пространства имен
Названия пространств имен должны быть в нижнем регистре и состоять из одного слова. В случае необходимости именовать пространств имен более одного слова производится дробление на составные части, являющиеся вложенными пространствами имен.
Классы
Названия классов должны соответствовать PascalCase . В PascalCase каждое слово начинается с заглавной буквы.
Трейты имеют постфикс Trait . Интерфейсы имеют постфикс Interface . Абстрактные классы имеют префикс Abstract .
Методы
Названия методов должны соответствовать camelCase . camelCase должен начинаться со строчной буквы, а первая буква каждого последующего слова должна быть заглавной. Все слова при этом пишутся слитно между собой
К названиям методов применяются следующие правила:
- Название метода должно передавать намерения программиста
- Название метода должно сообщить, почему этот метод существует, что он
делает и как используется ( isPostRequest , getRequestType , parseSchemaElement , renderPageWithSetupsAndTeardowns ) - Название метода не должно быть коротким
- Название метода должно начинаться с глагола
- Названия boolean методов должны содержать глагол is , has или can
Переменные
Названия переменных должны соответствовать camelCase . camelCase должен начинаться со строчной буквы, а первая буква каждого последующего слова должна быть заглавной. Все слова при этом пишутся слитно между собой
Константы должны быть соответствовать UPPER_CASE_SNAKE_CASE . В UPPER_CASE_SNAKE_CASE все слова пишутся заглавными буквами, а пробелы заменяются знаками подчеркивания.
К названиям переменных применяются следующие правила:
- Название переменной должно передавать намерения программиста
- Название переменной должно сообщить, почему эта переменная существует, что она делает и как используется
- Название переменной не должно быть коротким
- В названии переменной не должен использоваться тип данных. Исключение составляет Map ( $typesMap , $statesMap ), т.к. в ином случае его не отличить от массива с данными.
- Если переменная хранит признак, то он должен быть включен в название ( unpaidProject )
- Переменные, отражающие свойства объекта, должны включать название объекта ( userIsAdmin , messageIsSend , figureCanBePainted , projectName )
- Переменные и свойства объекта должны являться существительными и называться так, чтобы они правильно читались при использовании, а не при инициализации
Плохо:
$object->expire_at $object->setExpireAt($date); $object->getExpireAt();
$object->expiration_date; $object->setExpirationDate($date); $object->getExpirationDate();
if ($project->isInvalid()) < // . >if ($project->isNotValid()) < // . >if ($accessManager->isAccessDenied()) < // . >
if (!$project->isValid()) < // . >if (!$accessManager->isAccessAllowed()) < // . >if ($accessManager->canAccess()) < // . >
Правила оформления кода
В первую очередь ставится пространство имен (namespace), которое используется (если есть). Далее пишется конструкции использования классов ( use ). В случае использования нескольких
классов одного пространства имен происходит группировка с помощью конструкции <. >. Далее идет объявление класса.
Фигурные скобки ставятся на той же строке и разделяются пробелом.
- Внутри не разделяются пробелом.
- Снаружи разделяются пробелами управляющие конструкции
- После названия метода/функции — пробел не ставится.
Каждая переменная должна быть объявлена на новой строке.
Условия и служебные вызовы методов разделяются переносом строки, переменные и работа с ними переносами строк не разделяются.
Внутри условий и циклов дополнительный перенос на новую строку не ставится.
Содержимое класса разделяется сверху одной пустой строкой.
class InterfaceType < private $property = 'myProp'; public function getProperty():string < return $this->property; > >
Перед возвращаемым значением( return ) обязательно ставится перенос строки, если метод не состоит из единственной строки.
Если условие является большим, то обязательно выделить его в одно или несколько смысловых выражений и использовать его (их) в условии.
if (IS_REGISTRATOR() && (($params.status === 'W' || $params.status === 'D' || $params.status === 'A') && $params.remark && (($params.subres_level == 0 && ($user_info->selected_title->tid == $params.boss || $user_info->selected_title->tid == $doc_signer_tid || !$params.usertid) || $params.subres_level > 0 && $user_info->selected_title->tid == $params.usertid)))
$docIsInWorkAcceptOrDraft = . ; $bossHasSignerPriviledge = . ; $userCanSign = . ; if ($docIsInWorkAcceptOrDraft && $bossHasSignerPriviledge && $userCanSign) < // . >
Комментирование кода
В общем случае комментарии запрещены (НЕ «всегда»). Любой участок кода, который необходимо выделить или прокомментировать, надо выносить в отдельный метод.
Комментарии должны быть расположены перед объявлением классов, переменных и методов и быть оформлены в соответствии с PHPDoc. Комментарий перед классом должен содержать описание бизнес-логики и отражать его назначение с приведением примеров использования.
Однострочный комментарии обозначаются символами // , а многострочный /*. */ .
Готовые алгоритмы, взятые из внешнего источника, должны быть помечены ссылкой на источник.
Правила написания кода
Везде, где имеет смысл, должнно быть прописано declare(strict_types=1);
В функциях/методах вместо отсутствующего скалярного значения используется null . 0 и пустую строку нельзя использовать в качестве показателя отсутствия значения.
function sendEmail(string $title, string $message = null, string $date = null): void < // . >// сообщение не было передано $object->sendEmail('Title', null, '2017-01-01'); // было передано пустое сообщение $object->sendEmail('Title', '', '2017-01-01');
Нельзя изменять переменные, которые передаются в метод на вход (исключение — если эта переменная объект).
Нельзя нескольким переменным присваивать одно и то же значение. Для проверки наличия ключа в ассоциативном массиве используется array_key_exists , а не isset . Нельзя смешивать в массиве строковые и числовые ключи. Нельзя сортировать ассоциативные массивы.
Строки обрамляются одинарными кавычками. Двойные кавычки используются только, если:
- Внутри строки должны быть одинарные кавычки
- Внутри строки используется подстановка переменных
- Внутри строки используются спец. символы \n , \r , \t и т.д.
Вместо лишней конкатенации используем подстановку переменных в двойных кавычках
В методах должна быть использована максимально возможная типизация, включая тип возвращаемого значения( : type ). Все параметры и их типы должны быть описаны в объявлении метода либо в PHPDoc. Методы названия, которых начинаются c check и validate , должны выбрасывать исключения и не возвращать значения.
Все методы класса по умолчанию должны быть private . Если метод используется наследниками класса, то он объявляется protected . Если используется сторонними классами, тогда public .
Если метод может вернуть null , то желательно реализовать шаблон проектирования Null object, или выбросить исключение, или вернуть объект особого случая (пример: пустой массив).
При возврате из метода данных типа json — недопустимо писать return true , всегда использовать конструкцию return [‘success’ => [‘message’ => ‘. ‘]] или [‘error’ => [‘message’ => ‘. ‘]] . message приведен для примера, можно использовать любые ключи в неограниченном количестве.
Метод должен явно отличать нормальные ситуации от исключительных.
По умолчанию тексты исключений не должны показываться пользователю. Они предназначены для логирования и отладки. Текст исключения можно показать пользователю, если оно явно для этого предназначено.
В условном операторе должно проверяться исключительно boolean значение. В сравнении не boolean переменных используется строгое сравнение с приведением типа ( === ), автоматическое приведение и нестрогое сравнение не используются.
if ($user) < // . >if ($request->postData('amount') == 100) < // . >if (!$request->postData('amount')) < // . >
if ($user === null) < // $user является типом object // . >if ((int)$request->postData('amount') === 100) < // . >if ($booking->comment === '') < // . >
При использовании в условном выражении одновременно операторов И и ИЛИ обязательно выделять приоритет скобками.
PHP RFC: Class Naming
The PHP coding standard does not cover how class names should be written. This leads to friction within the userland community that is now largely following the standard recommendation PSR-1. Extending our current coding standard to cover edge cases about abbreviations and acronyms/initialisms would resolve any future discussion.
Proposal
Extend the coding standard to explicitly specify how abbreviations and acronyms/initialisms are to be handled when writing user-level class names. The current rule is:
Classes should be given descriptive names. Avoid using abbreviations where possible. Each word in the class name should start with a capital letter, without underscore delimiters (CamelCaps starting with a capital letter). The class name should be prefixed with the name of the ‘parent set’ (e.g. the name of the extension)::
While it is stated that abbreviations should be avoided, it is silent on what to do if they are used; especially in the case of acronyms/initialisms. There are essentially three choices possible now:
PascalCase except Acronyms/Initialisms — which is how the majority of user-level class names are written, and it matches the approach of many other programming languages.
Always PascalCase — which is basically what PSR-1 defines, however, it would make most of the currently existing user-level class names invalid.
Do Nothing — which of course automatically means that any approach is allowed, and the community discussions around this topic will continue.
Regardless of the outcome of this RFC , existing user-level class names are not required to be changed. Although it would be possible (class names are case-insensitive). The reason why renaming is not proposed is simple: this RFC would most probably fail because too many people are against such purely cosmetic changes.
PascalCase except Acronyms/Initialisms
Class names should be descriptive nouns in PascalCase and as short as possible. Each word in the class name should start with a capital letter, without underscore delimiters. The class name should be prefixed with the name of the “parent set” (e.g. the name of the extension) if no namespaces are used. Abbreviations and acronyms as well as initialisms should be avoided wherever possible, unless they are much more widely used than the long form (e.g. HTTP or URL ). Abbreviations start with a capital letter followed by lowercase letters, whereas acronyms and initialisms are written according to their standard notation. Usage of acronyms and initialisms is not allowed if they are not widely adopted and recognized as such.
Good:
‘Curl’
‘CurlResponse’
‘HTTPStatusCode’
‘ URL ‘
‘BTreeMap’ (B-tree Map)
‘Id’ (Identifier)
‘ID’ (Identity Document)
‘Char’ (Character)
‘Intl’ (Internationalization)
‘Radar’ (Radio Detecting and Ranging)
Bad:
‘curl’
‘curl_response’
‘HttpStatusCode’
‘Url’
‘BtreeMap’
‘ID’ (Identifier)
‘CHAR’
‘INTL’
‘RADAR’ (Radio Detecting and Ranging)
Always PascalCase
Class names should be descriptive nouns in PascalCase and as short as possible. Each word in the class name should start with a capital letter, without underscore delimiters. The class name should be prefixed with the name of the “parent set” (e.g. the name of the extension) if no namespaces are used. Abbreviations and acronyms as well as initialisms should be avoided wherever possible, unless they are much more widely used than the long form (e.g. HTTP or URL ). Abbreviations, acronyms, and initialisms follow the same letter-casing as any other word.
Good:
‘Curl’
‘CurlResponse’
‘HttpStatusCode’
‘Url’
‘BTreeMap’ (B-tree Map)
‘Id’ (Identifier and Identity Document)
‘Char’ (Character)
‘Intl’ (Internationalization)
‘Radar’ (Radio Detecting and Ranging)
Bad:
‘curl’
‘curl_response’
‘HTTPStatusCode’
‘ URL ‘
‘BtreeMap’
‘ID’
‘CHAR’
‘INTL’
‘RADAR’
Backward Incompatible Changes
None, we only update the coding standards for the future.