Самым популярным инструментом для анализа кода PHP является Codesniffer.
Установить его можно как глобально, так и локально. Через композер выглядит это так:
1 2 3 |
docker run --rm --interactive --tty --volume ${PWD}:/app composer require --dev "squizlabs/php_codesniffer" |
Запуск через:
1 2 3 |
vendor/bin/phpcs путь_до_директории_или_файла |
Настройка Codesniffer это отдельная тема, сейчас пойдет речь про авто запуск этой команды. Чтобы не запускать каждый раз перед коммитом или пушем, можно привязать запуск на git webhooks.
Все хуки лежат в папке .git\hooks. Возьмем к примеру файл pre-commit.sample, событие вызывается до коммита. Этот хук легче всего тестировать локально, так что и другие скрипты, необходимые для других хуков, можно потренировать «на кошках»
Для начала нужно переименовать или создать новый файл с именем pre-commit.
Вставить туда вот такой код
1 2 3 4 5 6 7 |
#!/bin/sh vendor/bin/phpcs src exit 0 |
exit 0 — программа завершено успешно, коммит принят, иначе коммит отклонен.
vendor/bin/phpcs src — при ошибке вызывает код отличный от 0
Тестируем через команду:
1 2 3 |
git commit --allow-empty -m "Testing hook" |
—allow-empty — закоммитить даже если ничего не изменено
Работает успешно, вывод терминала:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
FILE: /opt/project/src/Farm.php ---------------------------------------------------------------------- FOUND 7 ERRORS AFFECTING 6 LINES ---------------------------------------------------------------------- 2 | ERROR | [ ] Missing file doc comment 5 | ERROR | [ ] Missing doc comment for class Farm 7 | ERROR | [ ] Missing short description in doc comment 8 | ERROR | [ ] Missing parameter comment 8 | ERROR | [x] Tag value for @param tag indented incorrectly; | | expected 2 spaces but found 1 9 | ERROR | [ ] Tag @return cannot be grouped with parameter tags | | in a doc comment 14 | ERROR | [ ] Expected "if (...) {\n"; found "if(...) {\n" |
Отлично, значит этот хук смогут использовать все участники проекта?
Нет, все хуки, хранимые в папке .git/hooks являются локальными. Если разработчик создал новый хук, то работать он будет только на его локальной машине. Закоммитить его он не сможет, запушить в репозиторий тоже.
Как быть, отправлять новый хук всем по почте?
Можно, но лучше воспользоваться инструментами деплоя. Например, если в компании используется ансибль или что другое, можно добавлять хуки через него.
Но прозрачнее будет сделать это через композер.
- Создать файл bin/pre-commit в корне проекта
- Добавить в composer.json
12345"scripts": {"post-install-cmd": "cp bin/pre-commit .git/hooks/pre-commit"}
Теперь при установке зависимостей через композер composer install файл с хуком скопируется в папку .git/hooks/pre-commit
1 2 3 4 5 6 7 |
Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Nothing to install or update Generating autoload files > cp bin/pre-commit .git/hooks/pre-commit |
Каждый раз phpcs проверяет все файлы, это слишком долго
Нужно проверять только измененные файлы.
1 2 3 4 5 6 7 8 9 10 11 12 |
#!/usr/bin/env bash files=$(git diff --name-only master --diff-filter=MARC | grep .php$) if [ -n "$files" ]; then vendor/bin/phpcs "$files" exit 0 else echo 'files is empty' exit 1 fi |
git diff —name-only master — вернет измененные названия файлов по сравнению с мастером
Пример:
1 2 3 4 5 6 7 |
root@65a552bf19bb:/opt/project# git diff --name-only master CONTRIBUTING.md -- удален composer.json - изменен src/Cow.php - изменен tests/CowTest.php - изменен |
—diff-filter=MARC — вернет только M — modified, A — added, R -renamed, С — copied. CONTRIBUTING.md будет удален из списка, ведь анализатор не может проверить удаленные файлы.
grep .php$ — вернет только файлы с расширением .php
if -n — if string is non-zero
примеры
Примеры можно скачать по ссылке https://github.com/Carsak/CodeAnalyzerExample
ОБНОВЛЕНИЕ(2024-10-09): Важно! При проверке diff допущена ошибка. При команде git diff —name-only master —diff-filter=MARC сравнивается ваша ветка с мастером И ветка мастер с вашей. То есть, если в ветке мастер есть новые коммиты, то они попадут в дифф. Чтобы избежать этого, нужно использовать команду
1 2 3 |
git diff --name-only master...HEAD --diff-filter=MARC |
которая сравнивает только вашу ветку с мастером, но не наоборот
Источник https://git-scm.com/docs/git-diff
git diff [<options>] <commit>…<commit> [—] [<path>…]
This form is to view the changes on the branch containing and up to the second <commit>, starting at a common ancestor of both <commit>.
git diff A...B
is equivalent togit diff $(git merge-base A B) B
. You can omit any one of <commit>, which has the same effect as using HEAD instead.
· Permalink
Отличный материал!
· Permalink
Спасибо!