Coding Style

Описанный ниже стиль кодирования используется при разработке ядра Cotonti и его официальных расширений

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

#1. Основные правила

Эти основные правила касаются всех файлов с программным кодом в проекте (HTML, PHP, CSS, JS и т.д.), если не оговорено другое.

  • Файлы с кодом ДОЛЖНЫ содержать только символы в кодировке UTF-8 без BOM.
  • Символ перевода строки ДОЛЖЕН быть LF.
  • Для выравнивания кода НУЖНО использовать 4 пробела вместо табуляции.
  • В конце строк не должно быть лишних пробелов. (настройте Ваш редактор на удаление пробелов в конце сток при сохранении файла).

Нет жесткого органичения длины строки, но желательно чтобы строки не превышали 120 символов. Переносить строку следует там, где это имеет наибольший смысл, а не тогда, когда ее длина приближается к 120 символам.

 

#2. PHP

#2.1. Общий обзор

…в дополнение к основным правилам:

PHP код должен следовать стандарту написания кода PSR-12, и PSR-1, и стандарту автозагрузки php-файлов PSR-4.

Если вы используете PHPStorm, вы можете включить инструменты для форматирования кода в SettingsEditorCode StylePHPSet fromPSR12.

  • Открывающие теги ДОЛЖНЫ быть только <?php или <?= и НЕ ДОЛЖНО использоваться никаких других вариаций тегов, например <?.
  • Если файл содержит только PHP код, то закрывающий тег ?> не нужен.
  • Все операторы ДОЛЖНЫ иметь по одному пробелу до и после оператора, за исключением инкремента/декремента, например $a + $b, $string = $foo . 'bar', $i++, но не $a+$b.
  • Используйте один пробел после запятой $one, $two, но не $one,$two.
  • Используйте операторы строго равенства === и неравенства !==.
  • Однострочные комментарии ДОЛЖНЫ начинаться с //, а не с #.
  • Избегайте использования глобальных переменных. Не используйте неопределенные переменные.

#2.2. Документирование

  • Информации по синтаксису документации PHPDoc.
  • Код без документации недопустим.
  • Все файлы классов должны содержать блок документации в начале файла и блок документации непосредственно перед каждым классом.

Заголовки файлов

Все PHP файлы в самом начале должны содержать описание в формате PHPDoc.

Очень хорошо, когда в заголовке описаны переменные, которые используются в этом файле, в определены где-то в другом месте.

Стандартный заголовок для файлов (этот шаблон заголовка должен быть в начале каждого файла Cotonti):

/**
 * @package {PACKAGENAME}
 * @version {VERSION}
 * @copyright (c) 2008-2023 Cotonti Team
 * @license BSD License
 * 
 * @var array<string, mixed> $user
 * @var XTemplate $t
 */

@versionне обязательно. Например, если вы создаете расширение, версия уже прописана в установочном файле и тег @version может "путать" других разработчиков.

Классы

Не забывайте документировать классы. Классам нужен свой тег @package, тот же что и в заголовке.

/**
 * Cotonti MySQL Database adapter class.
 * A compact extension to standard PHP PDO class with slight Cotonti-specific needs,
 * handy functions and query builder.
 * 
 * @package cotonti
 * @see http://www.php.net/manual/en/class.pdo.php
 *
 * @property-read int $affectedRows Number of rows affected by the most recent query
 * @property-read int $count Total query count
 * @property-read int $timeCount Total query execution time
 */
class MySQL extends Adapter

Методы и функции

Документируйте функции. Каждая функция должна как минимум иметь описание того, что она делает. Также рекомендуется документировать параметры. Используйте @param, @return, @throws в таком порядке.

/**
 * Returns total number of records contained in a table
 * @param string $tableName Table name
 * @return int
 * @throws Exception if the table name is not exists
 */
public function countRows($tableName)

#2.3. Именование классов, функций м переменных

Все имена должны быть описательными, но краткими. Читая имя класса или переменной должно быть понятно зачем они нужны. Например: $CurrentUser, getUserData($id).

Имена классов ДОЛЖНЫ быть в PascalCase. Например: Controller, Model.

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

Названия папок/пространств имен в нижнем регистре.

Мы НЕ ИСПОЛЬЗУЕМ нижнее подчеркивание для обозначения приватных свойств и методов классов.

Именование переменных

Имена переменных ДОЛЖНЫ быть в camelCase. Каждое слово, кроме первого, должно начинаться с заглавной буквы. Например: $currentUser - правильно, а $current_user и $currentuser - нет.

Ситуации, когда допускаются односимвольные имена для переменных: языковые строки: $L, строковые ресурсы: $R и когда переменная является индексом для цикла.

Индексы циклов:
В этом случае индекс цикла верхнего уровня всегда должен быть $i. Если цикл содержит вложенный цикл, его индекс должен быть $j, далее $k, и т.д. Если индексом цикла является какая-либо существующая переменная, то это правило не применяется. Например:

for ($i = 0; $i < $outerSize; $i++) {
  for ($j = 0; $j < $innerSize; $j++) {
    foo($i, $j);
  }
}

Имена функций и методов

Имена функций и методов переменных ДОЛЖНЫ быть в camelCase. Исключение - функции ядра Cotonti. Пример хороших имен функций: printLoginStatus(), getUserData(), и т.п.

В Cotonti мы используем стандартный префикс cot_ для всех имен функций, чтобы избежать конфликта имен. Это стандарт для функций API ядра системы: cot_mailPrepareAddress($address).

Но мы не требуем использовать префикс cot_ в ваших расширениях до тех пор, пока эти функции не используются функциями ядра. При желании вы можете использовать свой префикс.

Для аргументов функций используются те же правила что и для имен переменных.

Итоги

Основная философия здесь заключается в том, чтобы не нарушать читабельность и понятность кода из-за лени. Но тут нужно соблюдать баланс и следовать здравому смыслу. Например printLoginStatusForAGivenUser() будет уже через чур. Вот это название функции будет лучше: printUserLoginStatus(), или просто  printLoginStatus().

#2.4. Типы Данных

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

Для приведения типов мы используем операторы, а не функции. И только их короткие формы. После оператора ставим один пробел.

$value1 = (int) $argument1;     // верно
$value2 = (bool) $argument2;    // верно

$value3 = (integer) $argument3;  // не верно
$value4 = invtal($argument2, 8); // только если это действительно необходимо

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

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

#2.5. Строки

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

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

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

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

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

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

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

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

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

$name = 'Cotonti' . ' CMF';

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

$sql = 'SELECT * '
. 'FROM posts '
. 'WHERE id = 121 ';

#2.6. Массивы

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

$arr = [3, 14, 15, 'Cotonti', 'CMF'];

При большом количестве элементов, каждый элемент располагается на отдельной строке. После последнего элемента СТАВИМ запятую.

$mail = [
  'to'  => 'user@domain.com',
  'from' => ['admin@site.com', 'SiteTitle'],
  'cc' => [['user2@example.com', 'User2'], 'user3@example.com', 'User4 <user4@example.com>'],
];

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

  • Управляющие конструкции ДОЛЖНЫ содержать по одному пробелу перед открывающей круглой скобкой и после закрывающей круглой скобки.
  • Операторы внутри круглых скобок ДОЛЖНЫ разделяться одним пробелом с обеих сторон.
  • Открывающая фигурная скобка ДОЛЖНА быть на той же строке.
  • Закрывающая фигурная скобка ДОЛЖНА быть на новой строке.
  • ВСЕГДА используйте фигурные скобки, даже для однострочных выражений.

if, elseif, else

if ($expr1) {
  // if body
} elseif ($expr2) {
  // elseif body
} else {
  // else body;
}

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

Всегда ДОЛЖНО использоваться elseif а не else if. Т.е. все ключевые слова состоят из одного слова.

Длинные выражения в скобках разбивайте на несколько строк, где каждая строка имеет как минимум один отступ. В этом случае первое условие ДОЛЖНО быть на следующей строке. Закрывающая круглая скобка и открывающая фигурная ДОЛЖНЫ быть на своей отдельной строке с одним пробелом между ними. Булевы операторы между условиями всегда ДОЛЖНЫ быть в начале строки.

if (
  $expr1
  && $expr2
) {
  // if body
}

Избегайте использования else после ключевого слова return:

$result = $this->getResult();
if (empty($result)) {
  return false;
} else {
  // process result
}

так на много лучше:

$result = $this->getResult();
if (empty($result)) {
  return false;
}

// process result

switch, case

Конструкция 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';
}

Тернарный оператор

Тернарные операторы следует использовать только для очень простых вещей. Желательно, чтобы они использовались только для присваивания значений, а не для вызовов функций или чего-то более сложного. Иначе это вредит читабельности кода и усложняет отладку. Примеры:

// Так плохо
($i < $size && $j > $size) ? do_stuff($foo) : do_stuff($bar);

// А так хорошо
$min = ($i < $j) ? $i : $j;

#2.8. Классы

Здесь под термином "класс" понимаются все классы, интерфейсы и трейты.

  • Открывающая фигурная скобка для класса ДОЛЖНА располагаться на отдельной строке; закрывающая фигурная скобка для класса ДОЛЖНА идти на следующей строке после тела класса.
  • Имена классов ДОЛЖНЫ быть в PascalCase.
  • У каждого класса ДОЛЖЕН быть блок документации соответствующий PHPDoc.
  • В одном php файле должен быть только один класс.
  • У каждого класса должно быть пространство имен.
  • Для всех свойств и методов ДОЛЖНА быть объявлена видимость.
  • Ключевые слова extends и implements ДОЛЖНЫ быть объявлены в той же строке, что и имя класса. Список implements (extends для интерфейсов) может быть разделен на несколько строк, где каждая строка имеет один отступ. При этом первый элемент в списке ДОЛЖЕН находиться в следующей строке, и в каждой строке должен быть только один интерфейс.

Свойства

Общедоступные (public) и защищенные (protected) свойства должны быть объявлены в начале класса, раньше объявления методов; Закрытые (private) свойства, так же, должны быть объявлены в начале класса, но могут быть добавлены и непосредственно перед методами, использующими их, в случае, если эти переменные используются только небольшой частью методов класса.

<?php

/**
 * Doc блок файла
 */
 
namespace Vendor\Package;

use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

/**
 * Doc блок класса
 */
class ClassName extends ParentClass implements \ArrayAccess, \Countable
{
  public $publicProp1;
  public $publicProp2;

  protected $protectedProp;

  private $privateProp;


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

Методы и функции

Открывающая фигурная кавычка ДОЛЖНА быть расположена на строке, следующей за строкой объявления функции, а закрывающая скобка ДОЛЖНА находиться в следующей строке после тела функции.

В списке аргументов ДОЛЖЕН быть один пробел после запятой.

function foo(int $arg1, &$arg2, $arg3 = [])
{
  // тело функции
}

Список аргументов может быть разбит на несколько строк, где каждая строка имеет один отступ. В этом случае первый аргумент ДОЛЖЕН быть на следующей строке и ДОПУСКАЕТСЯ только один агрумент на строку.

public function aVeryLongMethodName(
  ClassTypeHint $arg1,
  &$arg2,
  array $arg3 = []
) {
  // тело метода
}

При вызове функции или метода НЕ ДОЛЖНО быть пробела между именем функции и открывающейся скобкой. НЕ ДОЛЖНО быть пробела после открывающейся скобки и перед закрывающейся. В списке аргументов НЕ ДОЛЖНО быть пробела перед запятой и ДОЛЖЕН быть один после

#3. SQL

SQL запросы зачастую нечитабельны без форматирования, а временами они бывают очень большими. Форматирование значительно повышает читаемость кода. SQL запросы следует форматировать следующим образом, ориентируясь на ключевые слова:

$sql = 'SELECT p.*, u.* ' 
    . 'FROM ' . Cot::$db->pages . ' AS p ' .
    . 'LEFT JOIN ' . Cot::$db->pages . ' AS u ON u.user_id = p.page_ownerid '
    . "WHERE p.page_ownerid = 1 AND p.page_cat = 'news' " 
    . 'LIMIT 10';

Используйте параметры для безопасной подстановки в запрос данных, введенных пользователями (даже если вы уверены, что переменная не может содержать одинарных кавычек. Никогда не доверяйте данным, передаваемым пользователями):

$result = Cot::$db->query(
  'SELECT * FROM ' . Cot::$db->forum_topics . ' WHERE ft_cat = :cat  AND ft_id = :topicId',
  ['cat' => $section, 'topicId' => $param]
);

Избегайте конструкций, специфичных для определенных типов БД

  • Оператор not equals описанный в стандарте SQL-92 -  это <>. != также поддерживается большинством СУБД, но лучше использовать <>.
  • INSERT ... ON DUPLICATE KEY UPDATE - это MySQL конструкция и не работает в других СУБД.
  • Обратные кавычки ` для названий таблиц и столбцов - специфично для MySQL. Лучше используйте CotDB::quoteTableName() и CotDB::quoteColumnName() или сокращенные варианты этих методов: CotDB::quoteT(), CotDB::quoteC().

#4. Javascript

  • Используйте JSDoc для документирования классов, свойств и методов.
  • Правила документирования, комментирования, именования применимы от пункта 2. PHP.
  • Не используйте jQuery там, где без него можно обойтись.
  • Используйте let, а не var для объявления переменных и const для объявления констант.
  • Константы, которые жёстко заданы всегда, во время всей программы, именуются в верхнем регистре со знаком подчеркивания в качестве разделителя слов. Например: const COLOR_ORANGE = "#ffa500". Большинство переменных – константы в другом смысле: они не меняются после присвоения. Но при разных запусках функции это значение может быть разным. Для таких переменных следует использовать const и camelCase в имени.
  • Всегда ставьте точку с занятой в конце строки.
  • Всегда используйте строгое сравнение: равенство ===, неравенство !==.

#4.1. Строки

  • Используйте одинарные кавычки: let single = 'single-quoted';.
  • Если строка содержит одинарную кавычку, используйте двойные кавычки во избежание излишнего экранирования.
  • Для подстановки выражений используйте обратные кавычки: alert(`1 + 2 = ${sum(1, 2)}.`); // 1 + 2 = 3..

#5. Полезные ссылки и уилиты

 

 

Предыдущая версия стиля написания кода


1. Kabak  01.06.2023 05:25

Насчёт стиля по закрывающим фигурным скобкам - очень спорный вопрос.  Мне удобнее когда скобки находятся друг над или под другом - сразу видно какая скобка к чему относится и компиляторы подсвечивают блоки кода и таким образом расположенные скобки. У вас почему-то приняли за ствндарт когда открывающая скобка в конце строки открывающей блок кода , а закрываюзщая где-то там под блоком.  По-моему это плохой стиль для таких блоков.

2. Dayver  01.06.2023 20:52

Касательно расположения скобок я того же мнения + привык за всю историю существования кота именно к такому положению. Но принятый за основу psr-12 гласит именно о таком "кривом" расположении ... типа этот стандарт международно признаный ... 

3. Kort  12.06.2023 11:16

Предлагаемый вариант с фигурными скобками хорош тем, что экономит место на экране. Занимать целую строку открывающей скобкой так себе дело. Плюс практически в любом редакторе есть индикация закрывающей/открывающей скобки. Так что здесь рациональнее именно так.

По замене табуляторов на 4 пробела не совсем интересно. Хотя табуляторы в разных редакторах имеют разную длину, так что пробелами нерационально, зато гарантировано ровно будет. ((

Добавление комментариев доступно только зарегистрированным пользователям