Этот пост как заметка, ради самодисциплины и порядка в коде, не полный копипаст какого-то стиля, а сборная солянка стилей оформления кода PHP из PSR-1,2, BSD и Битрикс, которая нравится и привычна лично мне, но в большей степени это стиль Алмена (Eric Allman) или стиль BSD.
Оглавление
- Общие рекомендации
- Процедурное программирование
- Основы синтаксиса
- Переменные
- Константы и глобальные переменные
- Массивы
- Функции
- Управляющие конструкции
- Объектно-ориентированное программирование
- Работа с БД
- Формат документации
- Заключение
1. Общие рекомендации
1.1. Файлы
- В файлах использовать только теги
<?php
и<?=
. -
Закрывающий тэг
?>
необходимо удалять из файлов, которые содержат только код PHP. - В файлах использовать только UTF-8 без BOM.
- Все PHP файлы заканчивать одной пустой строкой.
- Для подключения файлов используйте
require_once()
.
1.2. Строки
-
В качестве отступов использовать табуляцию длиной равной 4.
- Длина строки кода 80-120 символов, жестко ограничивать не надо.
- Использовать пустые строки для улучшения читаемости и обозначения связанных блоков/логических сегментов кода.
- Нельзя писать на одной строке более одной инструкции.
1.3. Именования
-
Всем файлам, переменным, константам, функциям, классам и методам давать "говорящие" названия, чтобы они отражали свою суть, а не были набором из 2-3 символов.
-
Простые переменные писать строчными (в нижнем регистре).
Составные имена переменных писать в верблюжьей нотации, где первое слово начинается с маленькой буквы, все остальные с большой в стилесamelCase
.
Для явного указания типа переменной использовать префикс отражающий их тип - $dbUser, $obUser, $arUser, $strUser, $bUser... и т.д. -
Имена констант и глобальных переменных всегда должны быть в верхнем регистре в стиле
UPPER_CASE
.
Составные имена констант разделять символом подчеркивания.
В качестве префикса в именах констант должно использоваться имя пакета/класса, в котором они используются. -
Функции/методы класса писать в верблюжьей нотации, где первое слово начинается с маленькой буквы, все остальные с большой в стиле
сamelCase()
.
В функциях/методах класса использовать префиксы: is (обозначение вопроса), get (получить значение), set (установить значение).
-
Имена классов в стиле
StudlyCaps
.
Каждое слово в имени класса с большой буквы и никаких символов, кроме букв латинского алфавита.
2. Процедурное программирование
2.1. Основы синтаксиса
Многострочный комментарий в стиле С (/* */) использовать либо вначале файла, для его описания, либо для временного комментирования участка кода.
Однострочный комментарий в стиле C++ (// ) использовать можно везде.
Комментарии писать грамотно с заглавной буквы и делать отступ в один пробел слева, кроме однострочных.
<?php
//==============================================================================
// CATEGORY LARGE FONT
//==============================================================================
//------------------------------------------------------------------------------
// Sub-Category Smaller Font
//------------------------------------------------------------------------------
/* Title Here Notice the First Letters are Capitalized */
/**
* This is a detailed explanation
* of something that should require
* several paragraphs of information.
*/
//This is a single line quote.
?>
В качестве отступов использовать табуляцию длиной равной 4-м и не лепить код к левому краю.
<?php
echo 'Это тест';
?>
Ключевые слова PHP true
, false
, null
писать в нижнем регистре.
$a = true;
$b = false;
$c = null;
Операторы, ключевые слова, круглые и фигурные скобки отделять пробелами.
$x = 10;
$k = $x > 15 ? 1 : 2;
if ($k > 0)
echo '$k=' . $k;
Нельзя писать на одной строке более одной инструкции.
//Неправильно
$a = $b; $b = $c; $c = $a;
//Правильно
$a = $b;
$b = $c;
$c = $a;
Нет необходимости ставить точку с запятой в конце последней строки блока с PHP-кодом.
<?php
echo 'Это тест';
?>
<?= 'Это тест' ?>
<?= 'Мы опустили последний закрывающий тег';
Не использовать оператор echo для вывода html, а использовать «Изолирование от HTML»
<p>Это будет проигнорировано PHP и отображено браузером.</p>
<?= 'А это будет обработано PHP' ?>
<p>Это тоже будет проигнорировано PHP и отображено браузером.</p>
В строке html-кода подставлять переменные по возможности используя апострофы и конкатенацию, а не двойные кавычки или фигурные скобки.
//Неправильно
echo "<a href=\"$sPageHref\">$sPageName</a>";
echo "<a href='{$sPageHref}'>{$sPageName}</a>";
//Правильно
echo '<a href="'. $sPageHref .'">'. $sPageName .'</a>';
Придерживаясь ограничения длины строки 80-120 символов использовать конкатенацию и перенос строк.
echo '<offer id="' . $arItem['ARTNUMBER'] . '">'
. '<url>' . $arItem['DETAIL_PAGE_URL'] . '</url>'
. '<price>' . $arItem['PRICE'] . '</price>'
. '<currencyId>' . $arItem['СURRENCY'] . '</currencyId>'
. '<categoryId>' . $arItem['IBLOCK_SECTION_ID'] . '</categoryId>'
. '<delivery>' . $arItem['DELIVERY'] . '</delivery>'
. '<name>' . $arItem['NAME'] . '</name>'
. '</offer>';
if (isset($foo) && is_int($foo) && $id
&& $a === $b && !$bar && $page > 0
&& $c == $d && count($var) > 4
)
{
//тело функции
}
В тернарном операторе заключать в скобки только условие, при сложном условии стоит заменить его на if/else
. И не стесняться его использовать.
$var = (условие выполняется) ? 'делаем это' : 'иначе это';
Короткие строки писать выше длинных.
$x = 10;
$arResult = array();
$strSearch = '';
$bFoundName = false;
2.2. Переменные
Простые переменные пишем строчными в нижнем регистре.
$summ = $x + $y;
Составные переменные в верблюжьем стиле, где первая буква строчная, а каждое следующее логическое слово (аббревиатура) начинается с большой буквы.
$userName = 'Петр';
Для явного указания типа переменной использовать префикс отражающий их тип:
$bUser, $intUser, $flUser, $strUser, $dbUser, $obUser, $arUser и т.д.
//Строки с префиксом str
$strName, $strLastName
//Массивы с префиксом ar
$arResult, $arParams, $arUser
//Объекты с префиксом ob
$obElement, $obUser
//Объекты БД CDBResult с префиксом db
$dbUser, $dbResult
При множественной инициализации переменных выравнивать значения между ними по правому краю используя доп. пространство.
$x = 10;
$arResult = array();
$strSearch = '';
$bFoundName = false;
2.3. Константы и глобальные переменные
- Имена констант и глобальных переменных всегда должны быть в верхнем регистре.
- Составные имена констант писать с подчеркиваниями для разделения слов.
- В качестве префикса в именах констант должно использоваться имя пакета/класса, в котором они используются.
//Константы
HOST;
SITE_ID;
CACHE_MANAGER;
//Глобальные переменные
global $HOST, $SITE_ID, $CACHE_MANAGER;
$HOST;
$SITE_ID;
$CACHE_MANAGER;
2.4. Массивы
Массивы форматировать в таком виде.
array(
'ID' => GetMessage('ID'),
'CRM_HOST' => GetMessage('CRM_HOST'),
'CRM_LOGIN' => GetMessage('CRM_LOGIN'),
'CRM_PASSWORD' => GetMessage('CRM_PASSWORD'),
'CRM_AUTH' => GetMessage('CRM_AUTH'),
'CRM_PORT' => GetMessage('CRM_PORT'),
'CRM_PATH' => GetMessage('CRM_PATH'),
);
2.5. Функции
- При вызове функции пробелы между названием функции и открывающей круглой скобкой недопустимы.
- Пробелы после открывающей круглой скобки и перед закрывающей недопустимы.
- В списке аргументов необходим один пробел после каждой запятой и недопустимы пробелы перед запятыми.
- Тело функции в одну строку при одном выражении писать с фигурными скобками.
//Объявление функции
function getFunction()
{
//тело функции
}
//Вызов функции
getFunction();
//Сокращенная форма записи
function foo() { return 0; }
Большой список аргументов можно разбивать на несколько строк с одним отступом/табуляцией.
Первый аргумент также переносить на следующую строку и в каждой строке должен быть только один аргумент.
//вызов метода с аргументами
$obMyClass->getFunction(
$arg1,
$arg2,
$arg3
);
2.6. Управляющие конструкции
- Между ключевым словом и открывающей круглой скобкой один пробел;
- Между закрывающей круглой скобкой и открывающей фигурной скобкой один пробел;
- Тело управляющей конструкции смещать на один отступ/таб;
- Тело каждой управляющей конструкции заключать в фигурные скобки;
- Открывающую и закрывающую фигурные скобки с новой строки;
- Пробелы между круглыми скобками и их содержимым в условии недопустимы;
- Не экономить вертикальное пространоство, если это позволяет монитор.
- Вместо
else if
лучше использовать слитноеelseif
, чтобы избежать проблем в условиях с двоеточием.
Допустимы два вида написания конструкций:
-
если тела всех инструкций состоят из одного выражения, то фигурные скобки опускаем:
if ($expr1)
//действие1
elseif ($expr2)
//действие1
else
//действие1 -
если тело хотя бы одной из частей состоит более чем из одного выражения, то необходимы скобки у всех инструкций:
if ($expr1)
{
//действие1
//действие2
}
elseif ($expr2)
{
//действие1
}
else
{
//действие1
}
Открывающую фигурную скобку ставить под началом выражения и на одном уровне вертикально с закрывающей
if($strUserName)
{
echo 'Привет ' . $sUserName;
}
Избегать лишних фигурных скобок если в условии/блоке одно выражение.
if($strUserName)
echo 'Привет ' . $strUserName;
2.6.1. if, elseif, else
//Неправильно
if ($expr1){ /*тело if*/ }
elseif ($expr2){ /*тело elseif*/ }
else{ /*тело else;*/ }
//Правильно, но не рекомендуется :
if($a > $b):
echo $a." больше, чем ".$b;
elseif($a == $b): // Заметьте, тут одно слово.
echo $a." равно ".$b;
else:
echo $a." не больше и не равно ".$b;
endif;
//Правильно
if ($expr1)
{
//тело if
}
elseif ($expr2)
{
//тело elseif
}
else
{
//тело else;
}
2.6.2. switch, case
Простой switch
с одним выражением пишем без фигурных скобок в case
, иначе выражение в case
заключаем в фигурные скобки.
Где нет break
пишем комментарий вроде //no break
.
switch ($expr)
{
case 0:
echo 'Первый case, заканчивается на break';
break;
case 1:
echo 'Второй case, с умышленным проваливанием';
//no break
case 2:
case 3:
case 4:
echo 'Третий case, завершается словом return вместо braek';
return;
default:
echo 'По-умолчанию';
break;
}
2.6.3. while, do while
while ($expr)
{
//structure body
}
do
{
//structure body;
}
while ($expr);
2.6.4. for
for ($i = 0; $i < 10; $i++)
{
//тело for
}
2.6.5. foreach
foreach ($iterable as $key => $value)
{
//тело foreach
}
2.6.6. try, catch
try
{
//тело try
}
catch (ExceptionType $e)
{
//тело catch
}
3. Объектно-ориентированное программирование
3.1. Пространства имён и оператор use
- После объявления пространства имен
namespace
необходима одна пустая строка. -
Оператор
use
должен быть после объявления простарнства имёнnamespace
. -
На одно объявление (импорт или создание псевдонима) использовать только один оператор
use.
-
После блока операторов
use
должна быть одна пустая строка.
namespace Vendor\Package;
use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;
// ... тут php-код ...
3.2. Классы, свойства и методы
-
Имена классов в стиле
StudlyCaps
. - Каждый класс должен располагаться в отдельном файле и находиться в пространстве имён как минимум первого уровня.
- Открывающую и закрывающую фигурные скобки класса должны быть с новой строки.
namespace Vendor\Model;
class ClassName
{
//константы, свойства, методы
}
3.2.1 Extends и implements
-
Ключевые слова
extends
иimplements
располагать на одной строке с именем класса.
-
Список
implements
можно разбить на несколько строк, каждая из которых с одним отступом, при этом, первый интерфейс в списке необходимо перенести на следующую строку, и в каждой строке указать только один интерфейс.
namespace Vendor\Package;
use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;
class ClassName extends ParentClass implements
\ArrayAccess,
\Countable,
\Serializable
{
//константы, свойства, методы
}
3.2.2. Константы
- Имя константы класса всегда должно быть в верхнем регистре.
- Составные имена констант разделять символом подчеркивания.
- В качестве префикса в именах констант может использоваться имя пакета/класса, в котором они используются, например: BX_FILE_PERMISSIONS, API_FILE_PERMISSIONS
namespace Vendor\Model;
class Version
{
const VERSION = '15.0.1';
const VERSION_DATE = '2014-11-12 09:00:00';
}
3.2.3. Свойства
- Для всех свойств и методов класса необходимо объявлять область видимости public, static, protected, private;
- Разделять пустой строкой константы и группы свойств по области видимости.
-
Недопустимо использовать ключевое слово
var
для объявления свойств. - Недопустимо в одном объявлении указывать более одного свойства.
- Не следует начинать название свойства с подчёркивания для обозначения приватной или защищённой видимости.
namespace Vendor\Model;
class Foo
{
const HTTP_1_1 = "1.1";
const HTTP_GET = "GET";
const HTTP_POST = "POST";
public $foo = null;
public $bar = array();
protected $proxyHost;
protected $proxyPort;
protected $proxyUser;
protected $proxyPassword;
}
3.2.4. Методы
-
Имя метода пишется в верблюжьей нотации, где первое слово начинается с маленькой буквы, все остальные с большой в стиле
сamelCase()
. - Для всех методов класса необходимо объявлять область видимости public, static, protected, private;
- В методах класса использовать префиксы: is (обозначение вопроса), get (получить значение), set (установить значение).
- Открывающая и закрывающая фигурные скобки должны быть с новой строки и на одном уровне.
-
Недопустимо объявлять методы с пробелом между названием и круглой скобкой.
- Недопустимо добавлять пробел после открывающей круглой скобки и перед закрывающей.
- Недопустимо начинать название метода с подчёркивания для обозначения приватной или защищённой видимости.
В примере ниже обратите внимание на расположение запятых, пробелов, круглых, квадратных и фигурных скобок.
namespace Vendor\Model;
class ClassName
{
public function getMark($arg1, &$arg2, $arg3 = [])
{
//тело метода
}
}
3.2.5. Аргументы методов
- В списке аргументов необходим один пробел после каждой запятой и недопустимы пробелы перед запятыми.
- Аргументы метода со значениями по-умолчанию располагать в конце списка аргументов.
namespace Vendor\Package;
class ClassName
{
public function foo($arg1, &$arg2, $arg3 = [])
{
//тело метода
}
}
-
Большое количество аргументов необходимо разбивать на несколько строк, каждая из которых с одним отступом/табуляцией слева.
- Первый аргумент в списке также необходимо переносить на следующую строку и в каждой строке должен быть только один аргумент.
- Когда список аргументов разбит на несколько строк, закрывающую круглую скобку и открывающую фигурную необходимо располагать на разных строках, в соответствии с общим форматирование скобок.
namespace Vendor\Package;
class ClassName
{
public function aVeryLongMethodName(
ClassTypeHint $arg1,
&$arg2,
array $arg3 = []
)
{
//тело метода
}
}
-
Стараться избегать большого количества аргументов, гораздо удобней делать так:
$http = new \Bitrix\Main\Web\HttpClient;
$http->setHeader('User-Agent', 'Test');
$http->setAuthorization('demo', 'demo');
$http->setTimeout(10);
$http->get($url);
3.2.6. abstract, final и static
- Ключевые слова
abstract
иfinal
пишутся перед модификаторам видимости. - Ключевое слово
static
пишется после модификатора видимости.
namespace Vendor\Model;
abstract class ClassName
{
protected static $property;
abstract protected function foo();
final public static function bar()
{
//тело метода
}
}
3.2.7. Вызов методов
- При вызове метода пробелы между названием метода и открывающей круглой скобкой недопустимы.
- Пробелы после открывающей круглой скобки и перед закрывающей недопустимы.
- В списке аргументов необходим один пробел после каждой запятой и недопустимы пробелы перед запятыми.
$foo->bar($arg1);
Foo::bar($arg1, $arg2, $arg3);
- Большой список аргументов можно разбивать на несколько строк с одним отступом/табуляцией.
- Первый аргумент также переносить на следующую строку и в каждой строке должен быть только один аргумент.
$foo->bar(
$longArgument,
$longerArgument,
$muchLongerArgument
);
4. Работа с БД
4.1. Стиль написания запросов
- Запрос необходимо обрамлять двойными кавычками;
- Названия таблиц, полей и алиасов в нижнем регистре (в Битрикс поля в верхнем регистре);
- Все операторы и служебные команды БД пишем в верхнем регистре;
- Таблицы и поля нужно обрамлять обратными кавычками, а алиасы и префиксы не надо обрамлять;
- "Целые числа" не надо обрамлять кавычками.
Простые запросы пишем в одну строку
$sql = "SELECT `ID` FROM `b_user` WHERE `LOGIN` = '".$sqlHelper->forSql($login, 50)."'";
Сложные составные запросы необходимо писать в несколько строк, чтобы видеть в какой строке ошибка, и не объединять строки конкатенацией.
$connection = Bitrix\Main\Application::getConnection();
$sqlHelper = $connection->getSqlHelper();
$sql = "INSERT INTO `".$this->table."`
SET `DATE` = NOW(),
`TITLE` = '".$sqlHelper->forSql($_title)."',
`KEYWORDS` = '".$sqlHelper->forSql($keywords)."',
`DESCRIPTION` = '".$sqlHelper->forSql($description)."',
`DETAIL_TEXT` = '".$sqlHelper->forSql($detail_text)."',
`ACTIVE` = 1
WHERE `SECTION_ID` = 1
AND `ELEMENT_ID` = 1024
GROUP BY `NAME`
ORDER BY `ID`
";
$result = $connection->query($sql);
5. Формат документации
- Все комментарии/блоки документации должны быть на английском языке.
- Все блоки документации "doc-блоки" должны быть совместимы с форматом phpDocumentor
- Необходимо описывать в стиле PHPDoc все функции, файлы с классами, сами классы и их публичные методы.
5.1. Файлы
Каждый файл, содержащий PHP-код и класс должен иметь заголовочный doc-блок в начале файла, содержащий следующие phpDocumentor-теги.
/**
* Short description
*
* Multiline detailed
* description (if any)
*
* NOTE: Requires PHP version 5.3 or later
*
* @package API
* @subpackage CAPIReviews
* @author Tuning-Soft (tuning-soft.ru)
* @copyright © 1984-2015 Tuning-Soft
* @license MIT License
* @link tuning-soft.ru
* @version 1.0.0
* @date 01.01.2015
*/
Минимальный doc-блок для файла.
/**
* Created by Tuning-Soft
*
* @package API
* @subpackage CAPIReviews
* @copyright © 1984-2015 Tuning-Soft
* @version 1.0.0
*/
5.2. Классы
Каждый класс должен иметь doc-блок, содержащий следующие phpDocumentor-теги:
/**
* Short description
*
* Multiline detailed
* description (if any)
*
* NOTE: Requires PHP version 5.3 or later
*
* @package API
* @subpackage CAPIReviews
* @author Tuning-Soft (tuning-soft.ru)
* @copyright © 1984-2015 Tuning-Soft
* @license MIT License
* @example tuning-soft.ru
* @version Release: @package_version@
* @since 1.5.0
* @deprecated Class deprecated in Release 2.0.0
*/
Минимальный doc-блок для класса.
/**
* Class HostRestriction
*
* @since 14.0.6
* @version Release: @package_version@
* @package Bitrix\Security
*/
5.3. Функции/методы
- Каждая функция/метод класса должна иметь doc-блок, содержащий как минимум:
- Описание функции
- Все аргументы
- Все возможные возвращаемые значения
- Нет надобности использовать тег @access, т.к. область видимости уже известна из ключевых слов public, private, protected используемых при определении функции/метода.
- Если функция/метод может выбрасывать исключение, используйте тег @throws:
@throws exceptionclass [описание]
Пример doc-блока для функции/метода:
/**
* Checking host by host restriction policy
*
* @param string $host Host for checking.
*
* @return bool Return true for valid (allowed) host.
* @throws \Bitrix\Main\ArgumentTypeException
*/
public function isFoo($host)
{
/* some PHP-code */
return true;
}
6. Заключение
Рекомендую, особенно новичкам, изучить стиль и придерживаться его с самого начала, потом будет тяжелее привыкать, т.к. уже выработается железная привычка писать, как попало и где придется.
Стиль хорош для всех, но чаще всего есть разница между именованием функций, классов, переменных и переносом фигурных скобок.
К именованиям можно привыкнуть, они практически идеальны, логичны, понятны, а вот с переносом скобок точно возникнет метание, что лучше, вот так:
function getFunction()
{
//тело функции
}
или так:
function getFunction() {
//тело функции
}
- В первом случае мы расходуем вертикальное пространство, но так лучше ориентироваться новичкам по вертикальным линиям, которые подсвечивает IDE для открывающей/закрывающей фигурных скобок, и вложенность условий будет наглядна.
- Во втором случае мы экономим вертикальное пространство, это будет лучше для продвинутых, но новичок при нескольких вложенных условиях и выражениях будет путаться, сложно читать такой код, искать к какому условию относится выражение.