PHP Codesniffer: автоматизированная проверка стиля форматирования кода PHP кода при комитах
В свое время работая в узком кругу программистов, отдельными задачами и даже проектам, мы не задумывались о проблемах связанными с текучкой кадров. Точнее думать — думали, но ни каких мер не применяли, да и в целом коллектив был сплоченный никто не уходил и никого «не уходили». С ростом внутренних проектов и корпоративных клиентов, штат начал разрастаться и казалось, что все отлично — нас больше, значит будем больше успевать и делать, но не тут то было. Мы начали тратить кучу времени на “бесполезные” обсуждения, проверки, излишние проектирование и т.д, больше всего раздражает — это проверка кода. И тут я начал думать, что “мудрые и древние” наверняка решали эти проблемы с сотнями, тысячами программистов, неужели мы не справимся? Я решил провести эксперимент, под названием “автоматизированная проверка стиля кода при комитах”. Для большинства из Вас это не новость и наверняка вы этим пользуетесь, но поделиться опытом внедрения думаю, не будет лишним.
Воскресенье, 3:40
В это ранние время, сидя за работой и сделав перерыв на чай, я задумался, завтра нужно ехать в офис и снова утром тратить 2 — 3 часа на проверку кода, потом еще 2 часа на постановку задач, итого 5 часов, потом может быть поработать 2 часа и ехать домой. И понятное дело, что задачи которые поставили, я не могу решить за оставшиеся 2 — 3 часа, а решая эти проблемы в неурочное время могу разрушить свою семью и т.д. Так дальше не может продолжаться. В первую очередь я программер, а не «цербер» и нужно автоматизировать процесс проверки, это если не снимет все время проверки, то хотя бы сократит.
Не люблю изобретать колесо, потому сразу обратился к поисковику и задал запрос: «checking coding standards PHP», просматривая первый десяток результатов обратил внимание на часто встречающийся название “PHP_Codesniffer” и задав одноименный запрос — увидел, что это PEAR библиотека для автоматизированной проверки стиля кода, как говориться -«то что, доктор прописал!» и надежности разработчиков сомневаться не приходиться, что благотворно повлияет при возможном глобальном внедрении. Установив на сервер библиотеку:
pear install PHP_CodeSniffer-1.3.0RC1
Предполагаться, что у вас установлен дистрибутив PEAR на сервере. После установки станет доступна новая служба phpcs:
phpcs --standard=Zend your.php
Не много поигравшись, меня порадовало, что уже данное решение поддерживает многие стандарты: Squiz, MySource, PHPCS, Zend, PEAR. Меня это устраивало, так как мы утвердили в свое время, что будем кодить согласно стандарту Zend.
Про затачивания это библиотеки можно почитать в официально документации
Воскресенье, 4:00
Окрыленный своей находкой и быстрым ее “поднятием”, сон отступил. Первая задача была решена, теперь осталось подключить это решения к контролю версий при комите. В документации к PHP_CodeSniffer есть раздел посвященный описанию интеграции с SVN — это было хорошо, так как у нас SVN любят использовать, но я использую GIT и решил писать свой хук для git и тут думаю: “не верю, что на PEARовское решения нет описания интеграции c GIT”. И снова обратившись к поисковику нашел готовое решение phpcs-pre-commit:
git clone https://github.com/s0enke/git-hooks.git
Для интеграции этого хука нужно положить файлик pre-commit в папку hooks вашего git репозитория (.git/hooks). Кто интересовался git хуками — тот в теме.
И последние проверка коммита мне вывелась таблица с описание ошибок не соблюдения стиля. Не буду приводить пример того, как отображает таблицу ошибок phpcs, да и зачем. В .git/hooks/pre-commit нужно указать какой стиль вы хотите использовать:
PHPCS_CODING_STANDARD=Zend
Также укажите расширения файлов которые необходимо проверять:
PHPCS_FILE_PATTERN="\.(php|phtml)$"
Если при комите вам выдает ошибку:
error: cannot run .git/hooks/pre-commit: No such file or directory
Это значит, что указан не правильный путь к bash, измените его в .git/hooks/pre-commit
Задача в целом решена, плюс CodeSniffer в том, что с ним обязанность проверять “юношеские” ошибки, которые свойственны всем — отпадает. И главный плюс, что заставит “молодых” просматривать код как минимум при поиски ошибки стиля и возможно проведения минимального рефакторинга.
Теперь можно в понедельник ехать на работу и рассказать об нововведениях. Если это поможет нам, то можно будет идти лоббировать интеграцию данного решения для всех.
Понедельник, 17:00
В целом все было отлично, но проект на котором решил тестировать, довольно таки древний и некоторые моменты привести к стандарту Zend было накладно, например верблюжий стиль переменных. И пришлось создать свой стандарт для PHP_Codesniffer. Сами описания проверки стиля лежат в:
PEAR_PATH/PHP/CodeSniffer/Standards/
PEAR_PATH — это путь к папке с PEAR библиотеками у нас на сервере они располагаются в /usr/local/share/pear/.
Для создания своего стиля создайте папку YOUR_STYLE в PEAR_PATH/PHP/CodeSniffer/Standards/.
В паке вашего стиля нужно создать ruleset.xml. Про формат этого файла можно почитать тут, опишу только-то, что пригодилось мне.
В связи с тем, что требования кодированию максимально приближены к Zend стилю, просто скопировал содержание с PEAR_PATH/PHP/CodeSniffer/Standards/Zend/ruleset.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?xml version="1.0"?> <ruleset name="Zend"> <description>A coding standard based on an early Zend Framework coding standard. Note that this standard isout of date.</description> <!-- Include some sniffs from all around the place --> <rule ref="Generic.Functions.FunctionCallArgumentSpacing"/> <rule ref="Generic.Functions.OpeningFunctionBraceBsdAllman"/> <rule ref="Generic.PHP.DisallowShortOpenTag"/> <rule ref="Generic.WhiteSpace.DisallowTabIndent"/> <rule ref="PEAR.Classes.ClassDeclaration"/> <rule ref="PEAR.ControlStructures.ControlSignature"/> <rule ref="PEAR.Functions.FunctionCallSignature"/> <rule ref="PEAR.Functions.ValidDefaultValue"/> <rule ref="PEAR.WhiteSpace.ScopeClosingBrace"/> <rule ref="Squiz.Functions.GlobalFunction"/> <!-- Lines can be 80 chars long, show errors at 120 chars --> <rule ref="Generic.Files.LineLength"> <properties> <property name="lineLimit" value="120"/> <property name="absoluteLineLimit" value="140"/> </properties> <!-- Use Unix newlines --> <rule ref="Generic.Files.LineEndings"> <properties> <property name="eolChar" value="\n"/> </properties> </rule> </ruleset> |
1 |
<rule ref="Zend.Debug.CodeAnalyzer"/> |
PEAR_PATH/PHP/CodeSniffer/Standards/Zend/Sniffs/NamingConventions/ValidVariableNameSniff.php
в PEAR_PATH/PHP/CodeSniffer/Standards/YOUR_STYLE/Sniffs/NamingConventions/ValidVariableNameSniff.php.
Переименовав класс с Zend_Sniffs_NamingConventions_ValidVariableNameSniff на YOUR_STYLE_Sniffs_NamingConventions_ValidVariableNameSniff, добавил:
public $isCheckCamelCaps;
И добавил везде проверку на этот “флажок”. Теперь если нужно будет его переопределить — это можно будет сделать из ruleset.xml:
1 2 3 4 5 6 |
<rule ref="YOUR_STYLE.NamingConventions.ValidVariableName"> <properties> <property name="isCheckCa melCaps" value="1"/> </properties> </rule> |
Вторник, 19:00
Повсплывало много моментов на которые свое время закрывались глаза, но пришло время их приводить в порядок! Самой полезной проверкой стиля кода оказалась проверка на длину строки кода, и благодаря ее было зарефакторено много нечитабельных мест.
Правда столкнулись с проблемой с русскими комментариями, код весь хранится в UTF-8, а CodeSniffer длину строки проверяет стандартным strlen и логично что строки увеличивались в два раза. Не стал я заморачиваться на переопределения класса, что было бы правильнее, а просто в PEAR_PATH/PHP/CodeSniffer/Standards/Generic/Sniffs/Files/LineLengthSniff.php добавил:
1 |
public $charset = 'UTF-8'; |
1 |
mb_strlen($lineContent, $this->charset) |
Суббота
Прошла нелегкая неделя с CodeSniffer, и вот какие плюсы:
- снимается время и нервы на проверку стиля кода;
- при исправление ошибок длины строк, пересматриваешь логику кода, что благотворно влияет на читабельность кода и проведения рефакторинга мутной логики. Также снимается надобность объяснять когда одно-строчные if хорошо, а когда плохо;
- снимается обязанность следить за документацией стиля кодирования;
- поддерживает проверку всех популярных стандартов кодинга.
Минусы:
- не удобно внедрять в “исторические” проекты или чужие, можно потратить несколько дней, а то и недель на наведения икебаны;
- не удобно внедрять с контроль версиями, так как без напильника не подымешь, что для GIT, что для SVN. Особенно когда проектов несколько десятков и не для всех одинаковый стиль кодинга.