Skip to content

Latest commit

 

History

History
449 lines (370 loc) · 25.1 KB

devel.coding_style.ru.md

File metadata and controls

449 lines (370 loc) · 25.1 KB

Стиль кодирования Cotonti CMF

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

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

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

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

Нет жесткого органичения длины строки, но желательно чтобы строки не превышали 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.
  • Код без документации недопустим.
  • Все файлы классов должны содержать блок документации в начале файла и блок документации непосредственно перед каждым классом.

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

Все 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 может "путать" других разработчиков.

2.2.2 Классы

Не забывайте документировать классы. Классам нужен свой тег @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

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

Документируйте функции. Каждая функция должна как минимум иметь описание того, что она делает. Также рекомендуется документировать параметры. Используйте @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 exists
 */
public function countRows($tableName)

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

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

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

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

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

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

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

Имена переменных ДОЛЖНЫ быть в 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);
   }
}

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

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

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

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

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

2.3.3 Итоги

Основная философия здесь заключается в том, чтобы не нарушать читабельность и понятность кода из-за лени. Но тут нужно соблюдать баланс и следовать здравому смыслу. Например 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 post '
    . 'WHERE id = 121 ';

2.6 Массивы

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

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

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

$mail = [
    'to'  => '[email protected]',
    'from' => ['[email protected]', 'SiteTitle'],
    'cc' => [['[email protected]', 'User2'], '[email protected]', 'User4 <[email protected]>'],
];

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';
        break;
}

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

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

// Так плохо
($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()
    {
        // ...
    }
}

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

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

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

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' => $category, 'topicId' => $topicId]
);

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

  • Оператор не равно описанный в стандарте 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. Полезные ссылки и уилиты