Статический анализ кода
Как анализировать и улучшать код на PHP в автоматическом режиме
В чём вообще проблема
Код пишут и читают люди, разные люди. А стиль написания кода, логика и навыки разработчика всегда отличаются.
Различие в форматировании может усложнять чтение кода и его отладку для других разработчиков. А мёртвые участки, наличие копипасты и переусложненный код, кроме того, что тоже усложняет чтение и отладку, может ещё и нести в себе скрытые проблемы.
За всем этим нужно как-то следить, даже если вы работаете над проектом в одиночку, так как требования и функционал постоянно меняются, проект живёт.
И как маленький бонус к проблемам, работа над форматированием кода создаёт лишние правки в системе контроля версий.
Какие есть решения
Есть несколько способов унифицировать и проверять код на проекте. Подробнее о каждом.
1. Править код тимлиду
При этом подходе тимлид время от времени проверяет код других разработчиков на проекте, а также свой код.
Плюсы
- Качество. Поскольку проверка выполняется человеком, причём с опытом, то качество проверки довольно хорошее.
- Время внедрения. Плюс не надо ничего настраивать, старший разработчик может начать анализировать код, просто увидев его, например, в репозитории.
- Гибкость. Человек не машина, и его поведение ничем не ограничено.
Минусы
- Внимательность. Каким бы классным ни был специалист, он всё же человек и может пропускать какие-то детали.
- Цена. Вторым минусом является то, что для анализа и рефакторинга относительно большого проекта нужны часы каждый раз. Часы, которые кто-то должен оплачивать, часы, которые не тратятся на разработку.
2. Использовать для этих целей IDE
На примере PhpStorm.
Плюсы
- Автоматизация. Можно поправить код, просто набрав комбинацию клавиш.
- Гибкость. Те, кто хоть раз настраивал стили кода в PhpStorm, знают сколько там есть правил для многих языков и как гибко их можно настраивать.
Минусы
- Доступность. Во-первых, не у всех есть IDE. Тот же шторм, например, платный.
- Доверие. Во-вторых, форматирование кода также остаётся на совести разработчика. Он может использовать свои правила или вообще не форматировать код.
3. Инструменты статического анализа
Плюсы
- Автоматизация.
- Доступность. Статический анализ можно запускать уже по хукам гита, причём даже не на компьютере разработчика, а на удалённом сервере.
- Обязательность. Тот же подход помогает сделать правку и/или проверку кода обязательной для всех. - Причём в едином стандарте компании/проекта.
- Порог вхождения рядовых разработчиков. Отдельным разработчикам не нужно изучать никакие дополнительные инструменты, разворачивать системы. Всем этим занимается тимлид или кто-то другой компетентный.
Минусы
- Порог вхождения для компании. Для настройки этой системы хотя бы один разработчик должен уметь разбираться с инструментами статического анализа, работать с гитом на продвинутом уровне, да и вообще мочь в CI и всяких сервисах доставки кода.
- Гибкость. Если не писать анализатор с нуля или не расширять репозиторий существующего, набор правил весьма ограничен для многих языков.
- Из всех вариантов мы выбрали статические анализаторы. Их плюсы перевешивают для нас минусы, и они хорошо масштабируются.
Какие инструменты мы выбрали
Мы веб-студия и пишем бекенд на php, поэтому в первую очередь искали инструменты для него. Из всего многообразия мы отметили для себя три инструмента.
1. PHP CS Fixer
Плюсы
- Помимо проверок может править форматирование кода в автоматическом режиме.
- Можно настраивать под себя или использовать один из стандартов коробки (psr-2, symfony).
Минусы
- Не всё можно настроить (сейчас).
- Шторм всё-таки удобнее и гибче (хотя код библиотеки открыт, можно расширять своими силами).
- Работает только с php файлами.
- Иногда просто отказывается работать с файлом, говоря, что там ошибки и приходится методом тыка переписывать разные моменты в файле, чтобы фиксер не отказывался с ним работать.
Как на деле
Пример файла "до":
'love dogs', 1=> 'pay for beer', 2 => 'love pineapple on pizza', ); $cool = 'cool'; $notCool = 'not cool'; foreach($people as $person){ if(isHeCool($person)){ echo "It's ".$cool." to ".$person; } }
Пример файла "после":
'love dogs', 1 => 'pay for beer', 2 => 'love pineapple on pizza', ]; $cool = 'cool'; $notCool = 'not cool'; foreach ($people as $person) { if (isHeCool($person)) { echo "It's ".$cool." to ".$person; } }
'love dogs', 1 => 'pay for beer', 2 => 'love pineapple on pizza', ]; $cool = 'cool'; $notCool = 'not cool'; foreach ($people as $person) { if (isHeCool($person)) { echo "It's ".$cool." to ".$person; } }
### 2. PHP Copy Paste Detector
Инструмент для нахождения дублированных участков кода.
Плюсы
- Отлично справляется со своей задачей.
- И, кстати, от того же разработчика, что сделал phpunit, если кому-то важно.
Минусы
- В Битриксе, из-за специфики хранения вёрстки в компонентах, бывают ложноположительные срабатывания.
Как на деле
Пример отчёта с одного из наших проектов:
C:\Project>vendor\bin\phpcpd local phpcpd 3.0.0 by Sebastian Bergmann. Found 6 clones with 575 duplicated lines in 11 files:C:\Project\local\components\spectr\order.info\class.php:43-72 C:\Project\local\php_interface\lib\ComponentsParams.php:81-191 C:\Project\local\php_interface\lib\ComponentsParams.php:193-303 C:\Project\local\php_interface\lib\ComponentsParams.php:828-907 C:\Project\local\php_interface\lib\ComponentsParams.php:1046-1125 C:\Project\local\templates\main\components\bitrix\news.list\articles\template.php:2-66 C:\Project\local\templates\main\components\bitrix\news.list\news\template.php:2-66 C:\Project\local\templates\main\components\bitrix\news.list\rubber-tv\template.php:2-66 C:\Project\local\templates\main\components\bitrix\sale.basket.basket.line\main\ajax_template.php:2-84 C:\Project\local\templates\main\components\bitrix\sale.basket.basket.line\personal-index\ajax_template.php:2-84 C:\Project\local\templates\main\components\bitrix\sale.basket.basket.line\success-add-order\ajax_template.php:2-84 C:\Project\local\templates\main\components\bitrix\catalog.section\element-detail-similar-products\template.php:2-67 C:\Project\local\templates\main\components\bitrix\sale.recommended.products\main\template.php:2-67
5.76% duplicated lines out of 9979 total lines of code.
### 3. PHP Code Sniffer
То же самое, что и фиксер, но может не только в php.
Плюсы
- Может работать и с другими типами файлов.
Минусы
- Съедает очень много памяти. Так что даже не смогли потестить нормально.
Может быть, проверить на новом компе.
## Внедрение в разработку
Итак, если вы хотите внедрить инструменты статического анализа к себе на проекты, то вот инструкция от нас.
### Установка инструментов
Ставить все инструменты мы будем через composer. Соответственно, вам нужно его предварительно поставить, чтобы двигаться дальше. Получить его можно здесь https://getcomposer.org/.
Не забудьте, что вам также нужен будет интерпретатор php в командной строке. Если у вас установлен, например, xampp, то эта проблема уже решена.
Ниже полный код нашего composer.json:
{ "require": { "sebastian/phpcpd": "^3.0", "squizlabs/php_codesniffer": "", "friendsofphp/php-cs-fixer": "^2.4" }, "scripts": { "pre-install-cmd": [ "rm -rf .git/hooks", "ln -s local/git-hooks .git/hooks" ], "post-install-cmd": [ "rm -rf .git/hooks", "ln -s local/git-hooks .git/hooks" ] } } { "require": { "sebastian/phpcpd": "^3.0", "squizlabs/php_codesniffer": "", "friendsofphp/php-cs-fixer": "^2.4" }, "scripts": { "pre-install-cmd": [ "rm -rf .git/hooks", "ln -s local/git-hooks .git/hooks" ], "post-install-cmd": [ "rm -rf .git/hooks", "ln -s local/git-hooks .git/hooks" ] } }
Пока не обращайте внимание на секцию scripts, мы вернёмся к ней позже.
Главное, что здесь нужно увидеть, что в блоке required мы запрашиваем все 3 инструмента, о которых мы говорили. Соответственно, чтобы скачать себе инструменты, нужно просто выполнить команду composer install в корне проекта.
Хуки
Мы будем запускать фиксер по событию коммита. Для этого нужно создать файл с названием pre-commit в папке .git/hooks со следующим кодом:
#!/usr/bin/env bash # get the list of changed files staged_files=$(git diff --cached --name-only) # command to fix files cmd='vendor/bin/php-cs-fixer fix %s -q' if [ -f 'php_cs_fixer_rules.php' ]; then cmd='vendor/bin/php-cs-fixer fix %s -q --config=php_cs_fixer_rules.php' fi for staged in ${staged_files}; do # # work only with existing files if [[ -f ${staged} && ${staged} == *.php ]]; then # eval '$(printf "$cmd" "$staged")' # use php-cs-fixer to correct the file $(git add "$staged") # changes addition into the commit fi done exit 0 # do commit Установка хуков Хуки хранятся в .git/hooks, и эту папку нельзя включать под git. Поэтому мы идём на хитрость: закидываем код хуков, например, в local/git-hooks, а при установке проекта через композер копируем файлы в нужную папку.
Как раз об этом был блок scripts из composer.json (код выше).
Итог Теперь каждый раз, когда программист будет коммитить код, этот код будет отформатирован в соответствии с правилами.
Настройка правил форматирования
У фиксера есть возможность создавать специальный файл, в котором прописываются правила. Подробнее о правилах вы можете прочитать здесь https://github.com/FriendsOfPHP/PHP-CS-Fixer .
Наш файл с правилами:
in(DIR); return PhpCsFixer\Config::create() ->setIndent("\t")// использовать табы ->setLineEnding("\r\n") // иначе могут быть проблемы с коммитами ->setRules([ '@PSR2' => true, 'full_opening_tag' => true, // [ // перенос скобочек 'position_after_anonymous_constructs' => 'same', 'position_after_control_structures' => 'same', 'position_after_functions_and_oop_constructs' => 'same', 'allow_single_line_closure' => true, ], 'object_operator_without_whitespace' => false, // это скобочки вокруг else и т.п. 'array_syntax' => [ 'syntax' => 'short', // работа с массивом только через [], а не Array() ], 'no_extra_consecutive_blank_lines' => ['return'], // не очень помогает с лишними пустыми строками, но лучше чем extra, которое по умолчанию 'binary_operator_spaces' => [ 'align_double_arrow' => false, // выравнивание массивов. true работает через раз, null не делает ничего, false ставит по одному пробелу вокруг "=>" 'align_equals' => false, // выравнивание присвоений ], ]) ->setFinder($finder);
##Что дальше Дальше нужно, конечно же, нарабатывать правила для инструментов в соответствии со своим видением и стандартами компании. Возможно, дорабатывать инструменты, если правил не хватает. Код Fixer'а, например, открыт.
Плюс мы рассмотрели только автоматическое форматирование кода. Но в идеальном мире нам нужны более сложные и интеллектуальные вещи, например, проверки:
количества узлов в коде; количества параметров в функции; наличия else в коде (совсем уж идеальный мир).