Написание кода

Изучите стандарты и процессы Elgg, чтобы ваши изменения были приняты как можно быстрее.

Лицензионное соглашение

Отправляя патч, вы соглашаетесь лицензировать код под лицензией GPLv2 и лицензией MIT.

Запросы на слияние (Pull requests)

Запросы на слияние (PR) — лучший способ внести код в ядро Elgg. Команда разработчиков ядра использует их даже для самых незначительных изменений.

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

Контрольные списки

Используйте эти контрольные списки в формате markdown для новых PR на GitHub, чтобы обеспечить высокое качество вклада и помочь всем понять статус открытых запросов.

PR с исправлениями ошибок:

- [ ] Commit messages are in the standard format
- [ ] Includes regression test
- [ ] Includes documentation update (if applicable)
- [ ] Is submitted against the correct branch
- [ ] Has LGTM from at least one core developer

PR с новыми функциями:

- [ ] Commit messages are in the standard format
- [ ] Includes tests
- [ ] Includes documentation
- [ ] Is submitted against the correct branch
- [ ] Has LGTM from at least two core developers

Выбор ветки для отправки

В следующей таблице предполагается, что последний стабильный релиз — 2.1.

Тип изменения

Ветка для отправки

Исправление безопасности

Не отправляйте! Напишите на security@elgg.org для получения указаний.

Исправление ошибки

1.12 (или 2.1, если исправление для 1.12 слишком сложное)

Производительность

2.x

Устаревание

2.x

Незначительная функция

2.x

Крупная функция

master

Содержит критические изменения

master

Если вы не уверены, в какую ветку отправлять, просто спросите!

Различие между незначительной и крупной функцией субъективно и определяется командой ядра.

Формат сообщения коммита

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

  1. Начните с type, выбрав последнюю подходящую категорию из этого списка:

    • docs — обновляется только документация

    • chore — включает рефакторинг, изменения стиля кода, добавление недостающих тестов, настройки CI и т.д.

    • perf — основная цель — улучшение производительности

    • fix — исправление ошибки

    • deprecate — изменение объявляет устаревшей какую-либо часть API

    • break — изменение нарушает работу какой-либо части API

    • feature — добавление новой функции для пользователя или разработчика

    • removed — удаление функции для пользователя или разработчика

    • security — изменение каким-либо образом затрагивает проблему безопасности. Пожалуйста, не отправляйте этот коммит в публичный репозиторий. Вместо этого свяжитесь с security@elgg.org.

    Например, если ваш коммит рефакторит код для исправления ошибки, это всё равно «fix». Однако если эта ошибка связана с безопасностью, тип должен быть «security», и вам следует написать на security@elgg.org перед продолжением. В случае сомнений сделайте наилучшее предположение, и рецензент даст указания.

  2. В скобках добавьте component — короткую строку, описывающую изменяемую подсистему.

    Некоторые примеры: views, i18n, seo, a11y, cache, db, session, router, <plugin_name>.

  3. Добавьте двоеточие, пробел и краткое summary изменений, которое появится в журнале изменений.

    Ни одна строка не должна превышать 100 символов, поэтому делайте сводку краткой.

    Хорошая сводка

    Плохая сводка (проблема)

    владельцы страниц видят свои блоки владельца на страницах

    исправление ошибки (расплывчато)

    представление bar больше не завершается ошибкой, если „foo“ не установлен

    обновляет views/default/bar.php, чтобы представление bar больше не… (избыточная информация)

    сужает макет river для отображения на iPhone

    изменяет макет river (расплывчато)

    elgg_foo() обрабатывает массивы для $bar

    в elgg_foo() теперь можно передать массив для $bar, и функция будет… (переместите детали в описание)

    удаляет цвет ссылки из заголовка комментариев в river

    исправляет базу данных, чтобы… (избыточная информация)

    требует непустой заголовок при сохранении страниц

    можно сохранять страницы без заголовка (запутанно описывает старое поведение)

  4. (рекомендуется) Пропустите строку и добавьте description изменений. Укажите мотивацию для их внесения, информацию об обратной или прямой совместимости и обоснование, почему изменение должно быть выполнено именно так. Пример:

    Мы ускоряем миграцию таблицы Remember Me, используя один запрос INSERT INTO … SELECT вместо построчной обработки. Эта миграция происходит при обновлении до версии 1.9.

    Если ваше изменение не тривиально/очевидно, требуется описание.

  5. Если коммит решает проблему GitHub, пропустите строку и добавьте Fixes #, за которым следует номер проблемы. Например, Fixes #1234. Вы можете указать несколько проблем, разделяя их запятыми.

    GitHub автоматически закроет проблему при слиянии коммита. Если вы просто хотите сослаться на проблему, используйте вместо этого Refs #.

По завершении сообщение вашего коммита будет иметь формат:

type(component): summary

Optional body
Details about the solution.
Opportunity to call out as breaking change.

Closes/Fixes/Refs #123, #456, #789

Вот пример хорошего сообщения коммита:

perf(upgrade): speeds up migrating remember me codes

We speed up the Remember Me table migration by using a single INSERT INTO ... SELECT query instead of row-by-row.
This migration takes place during the upgrade to 1.9.

Fixes #6204

Переписывание сообщений коммитов

Если ваш запрос не соответствует стандартному формату сообщения коммита, мы попросим вас переписать его.

Чтобы отредактировать только последний коммит:

  1. Измените коммит: git commit --amend (git откроет сообщение в текстовом редакторе).

  2. Измените сообщение и сохраните/выйдите из редактора.

  3. Принудительно отправьте вашу ветку: git push -f your_remote your_branch (ваш запрос будет обновлён).

  4. Переименуйте заголовок запроса, чтобы он соответствовал

В противном случае вам может потребоваться выполнить интерактивный rebase:

  1. Выполните rebase последних N коммитов: git rebase -i HEAD~N, где N — число. (Git откроет файл git-rebase-todo для редактирования)

  2. Для коммитов, которые нужно изменить, замените pick на r (для переименования) и сохраните/выйдите из редактора.

  3. Измените сообщение(я) коммита(ов), сохраните/выйдите из редактора (git представит файл для каждого коммита, требующего переименования).

  4. git push -f your_remote your_branch для принудительной отправки ветки (обновление вашего запроса).

  5. Переименуйте заголовок запроса, чтобы он соответствовал

Стандарты кодирования

Elgg использует набор стандартов, частично основанных на стандартах PEAR и PSR2. Вы можете просмотреть набор правил в vendor/elgg/sniffs/src/Elgg/ruleset.xml.

Чтобы проверить ваш код на нарушения стандартов (при условии, что вы установили Elgg с зависимостями для разработки), выполните:

phpcs --standard=vendor/elgg/sniffs/src/Elgg -s path/to/dir/to/check

Чтобы автоматически исправить исправляемые нарушения, выполните:

phpcbf --standard=vendor/elgg/sniffs/src/Elgg path/to/dir/to/fix

Тестирование

Elgg имеет автоматизированные тесты как для функциональности PHP, так и для JavaScript. Все новые вклады должны сопровождаться соответствующими тестами.

Общие рекомендации

Разбивайте тесты по поведениям, которые вы хотите протестировать, и используйте имена, описывающие поведение. Например:

  • Не очень хорошо: один большой метод testAdd().

  • Лучше: методы testAddingZeroChangesNothing и testAddingNegativeNumberSubtracts

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

Тесты PHP

PHPUnit

Расположен в engine/tests/phpunit, это наш предпочтительный набор тестов. Он не использует доступ к базе данных и имеет только поверхностный доступ к API сущностей.

  • Мы рекомендуем создавать компоненты, которые можно протестировать в этом наборе, если это возможно.

  • Подумайте о разделении хранилища от вашего компонента, чтобы хотя бы бизнес-логика могла быть протестирована здесь.

  • Зависите от классов Elgg\Filesystem\*, а не используйте функции файловой системы PHP.

Тестирование взаимодействий между сервисами

В идеале ваши тесты должны создавать собственные изолированные графы объектов для прямого управления, но это не всегда возможно.

Если ваш тест полагается на внутренние сервисы Elgg (_elgg_services() возвращает Elgg\Di\InternalContainer), учтите, что он поддерживает экземпляр синглтона для большинства предоставляемых сервисов, и многие сервисы также хранят свои собственные локальные ссылки на эти сервисы.

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

  • Сервис events имеет методы backup() и restore().

  • Сервис logger имеет методы disable() и enable().

Лучшие практики кодирования

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

Общее кодирование

Не повторяйтесь (DRY)

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

Например, если вы находите представления, идентичные за исключением одного значения, рефакторите их в одно представление, принимающее опцию.

Примечание: В релизе с исправлениями ошибок некоторое дублирование предпочтительнее рефакторинга. Исправляйте ошибки максимально простым способом и выполняйте рефакторинг для уменьшения дублирования в следующей ветке минорного релиза.

Используйте SOLID и GRASP

Используйте эти принципы ООП-дизайна для решения проблем с использованием слабосвязанных компонентов и старайтесь сделать все компоненты и код интеграции тестируемыми.

Пробелы бесплатны

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

Имена переменных

Используйте самодокументирующиеся имена переменных. $group_guids лучше, чем $array.

Избегайте двойных отрицаний. Предпочитайте $enable = true вместо $disable = false.

Имена интерфейсов

Используйте шаблон Elgg\{Namespace}\{Name}.

Не включайте префикс I или суффикс Interface.

Мы не включаем никаких префиксов или суффиксов, чтобы побудить:

  • называть классы реализации более описательно (имя «default» уже занято).

  • использовать type-hint для интерфейсов, потому что это самый короткий и простой способ.

Называйте реализации как Elgg\{Namespace}\{Interface}\{Implementation}.

Функции

По возможности делайте так, чтобы функции/методы возвращали один тип. Используйте пустые значения, такие как array(), "" или 0, чтобы указать на отсутствие результатов.

Будьте осторожны там, где допустимые возвращаемые значения (например, "0") могут быть интерпретированы как пустые.

Функции, не выбрасывающие исключение при ошибке, должны возвращать false при неудаче.

Примечание

Особенно низкоуровневые, не являющиеся частью API функции/методы, которые не должны завершаться ошибкой в нормальных условиях, должны выбрасывать исключение вместо возврата false.

Функции, возвращающие только булево значение, должны начинаться с is_ или has_ (например, elgg_is_logged_in(), elgg_has_access_to_entity()).

Тернарный синтаксис

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

Минимизируйте сложность

Минимизируйте вложенные блоки и различные пути выполнения в коде. Используйте ранний возврат для уменьшения уровней вложенности и когнитивной нагрузки при чтении кода.

Эффективно используйте комментарии

Хорошие комментарии описывают «почему». Хороший код описывает «как». Например:

Плохо:

// increment $i only when the entity is marked as active.
foreach ($entities as $entity) {
        if ($entity->active) {
                $i++;
        }
}

Хорошо:

// find the next index for inserting a new active entity.
foreach ($entities as $entity) {
        if ($entity->active) {
                $i++;
        }
}

Всегда включайте комментарий, если не очевидно, что что-то должно быть сделано определённым образом. Другие разработчики, просматривающие код, должны быть удержаны от рефакторинга, который может сломать код.

// Can't use empty()/boolean: "0" is a valid value
if (elgg_is_empty($str)) {
    throw new \Elgg\Exceptions\Http\BadRequestException(elgg_echo('foo:string_cannot_be_empty'));
}

Эффективно коммитьте

  • Отдавайте предпочтение атомарным коммитам, которые сфокусированы на изменении одного аспекта системы.

  • Избегайте смешивания несвязанных изменений или обширных изменений пробелов. Коммиты со многими изменениями пугают и затрудняют рецензирование запросов на слияние.

  • Используйте визуальные инструменты git для создания высокоточных и читаемых диффов.

Включайте тесты

По возможности включайте модульные тесты для кода, который вы добавляете или изменяете.

Сохраняйте исправления ошибок простыми

Избегайте соблазна рефакторить код для релиза с исправлениями ошибок. Это, как правило, вносит регрессии, нарушая функциональность в том, что должно быть стабильным релизом.

Рекомендации по PHP

Это обязательные стандарты кодирования для ядра Elgg и всех встроенных плагинов. Разработчикам плагинов настоятельно рекомендуется придерживаться этих стандартов.

Разработчикам следует сначала прочитать Руководство по стандарту кодирования PSR-2.

Стандарты Elgg расширяют PSR-2, но отличаются следующим образом:

  • Используйте один символ табуляции для отступов, а не пробелы.

  • Открывающие фигурные скобки для классов, методов и функций должны находиться на той же строке.

  • Если строка превышает 100 символов, подумайте о рефакторинге (например, введите переменные).

  • Соблюдение PSR-1 приветствуется, но не является строго обязательным.

Документация

  • Включайте комментарии PHPDoc для функций и классов (все методы; объявленные свойства, когда это уместно), включая типы и описания всех параметров.

  • В списках объявлений @param начала имён переменных и описаний должны быть выровнены.

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

  • Используйте // или /* */ при комментировании.

  • Используйте только комментарии // внутри тел функций/методов.

Именование

  • Используйте подчёркивания для разделения слов в именах функций, переменных и свойств. Имена методов используют camelCase.

  • Имена функций для публичного использования должны начинаться с elgg_.

  • Все остальные имена функций должны начинаться с _elgg_.

  • Называйте глобальные переменные и константы в ALL_CAPS (ACCESS_PUBLIC, $CONFIG).

Разное

Требования к PHP см. в composer.json.

Не используйте короткие теги PHP <? или <%. Допустимо использовать <?=, так как он всегда включён начиная с PHP 5.4.

При создании строк с переменными:

  • используйте строки в двойных кавычках

  • оборачивайте переменные в фигурные скобки

Плохо (трудно читать):

echo 'Hello, '.$name."!  How is your {$time_of_day}?";

Хорошо:

echo "Hello, {$name}!  How is your {$time_of_day}?";

Удаляйте завершающие пробелы в конце строк.

Валидация значений

При работе с пользовательским вводом подготавливайте ввод вне метода валидации.

Плохо:

function validate_email($email) {
        $email = trim($email);

        // validate
}

$email = get_input($email);

if (validate_email($email)) {
        // the validated email value is now out of sync with an actual input
}

Хорошо:

function validate_email($email) {
        // validate
}

$email = get_input($email);
$email = trim($email);

if (validate_email($email)) {
        // green light
}

Используйте исключения

Не бойтесь использовать исключения. С ними легче работать, чем со смешанным выводом функций:

Плохо:

/**
* @return string|bool
*/
function validate_email($email) {
        if (empty($email)) {
                return 'Email is empty';
        }

        // validate

        return true;
}

Хорошо:

/**
* @return void
* @throws \Elgg\Exceptions\InvalidArgumentException
*/
function validate_email($email) {
        if (empty($email)) {
                throw new \Elgg\Exceptions\InvalidArgumentException('Email is empty');
        }

        // validate and throw if invalid
}

Документирование возвращаемых значений

Не используйте @return void для методов, которые возвращают значение или null.

Плохо:

/**
* @return bool|void
*/
function validate_email($email) {
        if (empty($email)) {
                return;
        }

        // validate

        return true;
}

Хорошо:

/**
* @return bool|null
*/
function validate_email($email) {
        if (empty($email)) {
                return null;
        }

        // validate

        return true;
}

Рекомендации по CSS

Сохраняйте CSS в файлах с расширением .css.

Используйте сокращённую запись, где это возможно

Плохо:

background-color: #333333;
background-image:  url(...);
background-repeat:  repeat-x;
background-position:  left 10px;
padding: 2px 9px 2px 9px;

Хорошо:

background: #333 url(...) repeat-x left 10px;
padding: 2px 9px;

Используйте дефисы, а не подчёркивания

Плохо:

.example_class {}

Хорошо:

.example-class {}

Примечание

Вы должны добавлять префикс к вашим id и именам классов текстом, идентифицирующим ваш плагин.

Одно свойство на строку

Плохо:

color: white;font-size: smaller;

Хорошо:

color: white;
font-size: smaller;

Объявления свойств

Они должны быть оформлены так: property: value;

Плохо:

color:value;
color :value;
color : value;

Хорошо:

color: value;

Префиксы вендоров

  • Группируйте префиксы вендоров для одного свойства вместе

  • Сначала самая длинная версия с префиксом вендора

  • Всегда включайте версию без префикса вендора

  • Добавляйте дополнительную пустую строку между группами с префиксами вендоров и другими свойствами

Плохо:

-moz-border-radius: 5px;
border: 1px solid #999999;
-webkit-border-radius: 5px;
width: auto;

Хорошо:

border: 1px solid #999999;

-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;

width: auto;

Группируйте под-свойства

Плохо:

background-color: white;
color: #0054A7;
background-position: 2px -257px;

Хорошо:

background-color: white;
background-position: 2px -257px;
color: #0054A7;

Рекомендации по JavaScript

Применяются те же стандарты форматирования, что и для PHP.

Связанные функции должны находиться в модуле ECMAScript с пространством имён.

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

Объявление API устаревшими

Иногда функции и классы должны быть объявлены устаревшими в пользу новых замен. Поскольку авторы сторонних плагинов полагаются на стабильный API, обратная совместимость должна поддерживаться, но не бесконечно, так как от авторов плагинов ожидается правильное обновление их плагинов. Для поддержания обратной совместимости устаревшие API будут следовать этим рекомендациям:

  • Минорная версия (1.x), объявляющая API устаревшим, должна включать функцию-обёртку/класс (или иное подходящее средство) для поддержания обратной совместимости, включая любые ошибки в исходной функции/классе. Этот слой совместимости использует elgg_deprecated_notice('...', '1.11') для логирования устаревания функции.

  • Следующий мажорный релиз (2.0) удаляет слой совместимости. Любое использование устаревшего API должно быть исправлено до этого.