<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0"
  xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
  xmlns:atom="http://www.w3.org/2005/Atom">

<channel>

<title>Максим Кузнецов: заметки с тегом phpstan</title>
<link>https://maxkuznetsov.ru/tags/phpstan/</link>
<description>Простыми словами о веб-разработке</description>
<author>Максим Кузнецов</author>
<language>ru</language>
<generator>E2 (v3559; Aegea)</generator>

<itunes:owner>
<itunes:name>Максим Кузнецов</itunes:name>
<itunes:email></itunes:email>
</itunes:owner>
<itunes:subtitle>Простыми словами о веб-разработке</itunes:subtitle>
<itunes:image href="" />
<itunes:explicit></itunes:explicit>

<item>
<title>Как настроить php-cs-fixer и phpstan на локалке</title>
<guid isPermaLink="false">39</guid>
<link>https://maxkuznetsov.ru/all/code-quality-local/</link>
<pubDate>Tue, 01 Dec 2020 17:34:55 +0300</pubDate>
<author>Максим Кузнецов</author>
<comments>https://maxkuznetsov.ru/all/code-quality-local/</comments>
<description>
&lt;h2&gt;Проблема 1&lt;/h2&gt;
&lt;p&gt;При совместной разработке (от 2х человек) code style становится критичным. Если каждый будет писать в своём стиле, вскоре проект превратится в бардак, где по разному стилю можно будет оценивать возраст кода будто по кольцам на деревьях.&lt;/p&gt;
&lt;p&gt;На собеседованиях я часто слышу от кандидатов, что мы пользуемся проверками, встроенными в IDE. Но это полрешения, так как такие проверки носят рекомендательный, а не обязательный характер. Очень легко не заметить предложение по правке от IDE. А кроме того, приходится синхронизировать настройки IDE между всей командой. А что делать, если у кого-то другой редактор? Результат непредсказуем, а значит и ненадёжен.&lt;/p&gt;
&lt;h2&gt;Проблема 2&lt;/h2&gt;
&lt;p&gt;Чем больше кодовая база проекта, тем проще допустить ошибку или опечатку. Вручную проверка кода перед каждым коммитом возможна, но требует много времени и большой внимательности. Результат опять непредсказуем и ненадёжен.&lt;/p&gt;
&lt;h2&gt;Решение&lt;/h2&gt;
&lt;p&gt;Нужно договориться об общих правилах с командой и автоматизировать процесс проверки кода, сделав её независимой от IDE, платформы, настроения, желания и внимательности человека.&lt;/p&gt;
&lt;p&gt;Такие инструменты уже есть — code style и статические анализаторы кода.&lt;/p&gt;
&lt;h3&gt;Единый стиль кода&lt;/h3&gt;
&lt;p&gt;Поскольку я специализируюсь на Symfony, то для код-стайла выбираю &lt;a href="https://github.com/FriendsOfPHP/PHP-CS-Fixer"&gt;PHP-CS-Fixer&lt;/a&gt;. Он написан при участии создателя Symfony и имеет из коробки поддержку рекомендованного сообществом &lt;a href="https://symfony.com/doc/current/contributing/code/standards.html"&gt;Symfony Code Standards&lt;/a&gt;. Есть ещё &lt;a href="https://github.com/squizlabs/PHP_CodeSniffer"&gt;PHPCodeSniffer&lt;/a&gt;, но он умеет только обнаруживать ошибки, но не исправляет их автоматически, поэтому я рекомендую именно PHP-CS-Fixer.&lt;/p&gt;
&lt;p&gt;Совет: обязательно изучите и следуйте code style и рекомендациям вашего фреймворка. С большой вероятностью в будущих проектах вам встретится именно общепринятый в сообществе фреймворка code style, поэтому будет проще вписаться в новую команду, если вы придёте к этому стилю как можно раньше. К тому же, уже принятый в сообществе стиль поможет избежать ненужных холиваров внутри команды.&lt;/p&gt;
&lt;p&gt;Устанавливаем через composer и настраиваем PHPCSFixer в Symfony-проекте:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;cd /ваш/проект
composer req --dev friendsofphp/php-cs-fixer
# Если используете docker-compose, то устанавливайте в нём
docker-compose exec web composer req --dev friendsofphp/php-cs-fixer&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Создаём в корне проекта файл &lt;tt&gt;.php_cs&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;&amp;lt;?php

$finder = PhpCsFixer\Finder::create()
    -&amp;gt;in([
        __DIR__ . '/src',
        __DIR__ . '/tests'
    ])
;

return PhpCsFixer\Config::create()
    -&amp;gt;setRules([
        '@Symfony' =&amp;gt; true,
        'array_syntax' =&amp;gt; ['syntax' =&amp;gt; 'short'],
        'concat_space' =&amp;gt; ['spacing' =&amp;gt; 'one'],
        'increment_style' =&amp;gt; ['style' =&amp;gt; 'post'],
        'no_extra_blank_lines' =&amp;gt; ['tokens' =&amp;gt; [
            'extra',
            'parenthesis_brace_block',
            'square_brace_block',
            'throw',
            'use',
        ]],
        'no_superfluous_phpdoc_tags' =&amp;gt; false,
        'phpdoc_align' =&amp;gt; false,
        'phpdoc_annotation_without_dot' =&amp;gt; false,
        'trailing_comma_in_multiline_array' =&amp;gt; false,
        'yoda_style' =&amp;gt; false
    ])
    -&amp;gt;setFinder($finder)
;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Теперь вы можете запустить проверку всего проекта (или конкретного пути) с помощью команды&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;# только поиск ошибок
./vendor/bin/php-cs-fixer  fix --dry-run --diff ./
# поиск ошибок и их автоматический фикс
./vendor/bin/php-cs-fixer fix ./&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Ошибки и качество кода&lt;/h3&gt;
&lt;p&gt;Для отлова опечаток и даже поиска более сложных ошибок мы воспользуемся статическим анализатором кода. Прелесть анализаторов в том, что они не выполняют код, а просто читают и анализируют — это очень быстро. Кроме того, каждый из них имеет свои уровни строгости, поэтому можно внедрять их в существующий проект постепенно: фиксим ошибки с минимальным уровнем, коммитим, повышаем уровень, фиксим, коммитим, снова повышаем.&lt;/p&gt;
&lt;p&gt;Самые популярные статичиские анализаторы кода:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/phpstan/phpstan"&gt;PHPStan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vimeo/psalm"&gt;Psalm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/phan/phan"&gt;Phan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Я покажу, как настроить и внедрить PHPStan, а вы можете добавить в проект все три по аналогии. Устанавливаем через composer сам PHPStan и несколько полезных плагинов — PHPUnit, Symfony, Doctrine.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;cd /ваш/проект
composer req --dev phpstan/phpstan phpstan/phpstan-doctrine phpstan/phpstan-phpunit phpstan/phpstan-symfony&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Должен создаться в корне проекта файл &lt;tt&gt;.phpstan.neon.dist&lt;/tt&gt;, редактируем его:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;includes:
    - vendor/phpstan/phpstan-doctrine/extension.neon
    - vendor/phpstan/phpstan-phpunit/extension.neon
    - vendor/phpstan/phpstan-phpunit/rules.neon
    - vendor/phpstan/phpstan-symfony/extension.neon
parameters:
    reportUnmatchedIgnoredErrors: false     #to avoid throwing of errors ... if no errors matching the ignored one is raised
    scanFiles:  #we need this for some twig based functions
        - '%rootDir%/../../../vendor/twig/twig/src/Extension/CoreExtension.php'
    bootstrapFiles:
        - '%rootDir%/../../../vendor/bin/.phpunit/phpunit-X.X.X/vendor/autoload.php'
    ignoreErrors:
        - message: '/^Service &amp;quot;[^&amp;quot;]+&amp;quot; is private.$/'
          path: '%rootDir%/../../../tests/'
    level: 0 
    symfony:
        container_xml_path: '%rootDir%/../../../var/cache/development/App_KernelDevelopmentDebugContainer.xml'&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Внимательно пройдитесь по этим настройкам и укажите корректные пути. Особое внимание:&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;bootstrapFiles — включает путь до текущего phpunit. Если его не используете, то можете закоментить.&lt;/li&gt;
&lt;li&gt;level — уровень строгости проверок &lt;a href="https://phpstan.org/user-guide/rule-levels"&gt;от 0 до 8&lt;/a&gt;. 0 — самый слабый уровень, подходит для первого внедрения.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Теперь вы можете запустить проверку всего проекта (или конкретного пути) с помощью команды&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;./vendor/bin/phpstan analyse src tests&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Советы по внедрению&lt;/h3&gt;
&lt;p&gt;Несколько советов по внедреню PHP-CS-Fixer в существующий проект с большой командой.&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Выберите одного ответственного, кто запустит скрипты и пофиксит весь проект у себя на локалке, а потом создаст ПР / смерджит в develop или master.&lt;/li&gt;
&lt;li&gt;Лучшее время — первое утро нового спринта. Обычно после спринта все ветки смёрджены, и это позволит залить все найденные правки в основную ветку проекта без конфликтов.&lt;/li&gt;
&lt;li&gt;В статических анализаторах используйте для начала минимальный уровень строгости проверок. У каждого анализатора для этого своя школа, читайте документацию.&lt;/li&gt;
&lt;li&gt;Первый фикс — самый критичный момент, так как правок может быть очень много. Все последующие фиксы обычно не создают ощутимых конфликтов и гит их смёрдживает самостоятельно.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Чтобы не запоминать длинные пути до скриптов, мы добавим несколько задач (tasks) в composer.json:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;{
    &amp;quot;require&amp;quot;: { ... },
    &amp;quot;require-dev&amp;quot;: { ... },
    ...
    &amp;quot;scripts&amp;quot;: {
        ...
        &amp;quot;csfix&amp;quot;: &amp;quot;./vendor/bin/php-cs-fixer fix&amp;quot;,
        &amp;quot;csfix-validate&amp;quot; : &amp;quot;./vendor/bin/php-cs-fixer fix --dry-run --diff&amp;quot;,
        &amp;quot;phpstan&amp;quot;: &amp;quot;./vendor/bin/phpstan analyse src tests&amp;quot;,
        &amp;quot;code-quality&amp;quot;: [
            &amp;quot;@phpcsfixer-validate&amp;quot;,
            &amp;quot;@phpstan&amp;quot;
        ]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Теперь мы можем запускать проверки в упрощённом формате:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;composer csfix
composer phpstan
composer code-quality&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Обязательный запуск проверок перед коммитом&lt;/h2&gt;
&lt;p&gt;Внедрение этих инструментов позволяет удостовериться, что код соответствует принятому в команде стилю и не имеет очевидных опечаток и синтаксических ошибок. Однако это ещё не решает проблему забывчивости программистов и необязательности проверок.&lt;/p&gt;
&lt;p&gt;Воспользуемся Git-хуками (git hooks) — это скрипты, которые мы можем попросить git выполнить при возникновении какого-то события: момент перед созданием коммита, момент после создания коммита, момент добавления сообщения в коммит и т. п.&lt;/p&gt;
&lt;p&gt;Полный список git-хуков вы можете увидеть у себя в репозитории:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;$ ls -al .git/hooks
-rwxr-xr-x   1 mk  staff   478 Jul 13 13:04 applypatch-msg.sample
-rwxr-xr-x   1 mk  staff   896 Jul 13 13:04 commit-msg.sample
-rwxr-xr-x   1 mk  staff  3327 Jul 13 13:04 fsmonitor-watchman.sample
-rwxr-xr-x   1 mk  staff   189 Jul 13 13:04 post-update.sample
-rwxr-xr-x   1 mk  staff   424 Jul 13 13:04 pre-applypatch.sample
-rwxr-xr-x   1 mk  staff   382 Dec  1 14:46 pre-commit
-rwxr-xr-x   1 mk  staff  1638 Jul 13 13:04 pre-commit.sample
-rwxr-xr-x   1 mk  staff   416 Jul 13 13:04 pre-merge-commit.sample
-rwxr-xr-x   1 mk  staff  1348 Jul 13 13:04 pre-push.sample
-rwxr-xr-x   1 mk  staff  4898 Jul 13 13:04 pre-rebase.sample
-rwxr-xr-x   1 mk  staff   544 Jul 13 13:04 pre-receive.sample
-rwxr-xr-x   1 mk  staff  1492 Jul 13 13:04 prepare-commit-msg.sample
-rwxr-xr-x   1 mk  staff  3610 Jul 13 13:04 update.sample&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Создадим скрипт, который будет запускать проверки каждый раз перед попыткой создать коммит.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;vim .git/hooks/pre-commit&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Добавляем внутрь файла:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;#!/bin/sh
export PATH=/usr/local/bin:$PATH
# Если вы используете docker-compose (рекомендуется), то добавьте
export COMPOSE_INTERACTIVE_NO_CLI=1

if [ -t 1 ]; then
    # If we're in a terminal, redirect stdout and stderr to /dev/tty and
    # read stdin from /dev/tty. Allow interactive mode for CaptainHook.
    exec &amp;gt;/dev/tty 2&amp;gt;/dev/tty &amp;lt;/dev/tty
fi

errors=0

composer code-quality
# или, если вы используете docker-compose (рекомендуется), то
docker-compose exec -T web php bin/composer code-quality

if [ &amp;quot;$?&amp;quot; -ne 0 ]; then
    errors=1
fi

if [ &amp;quot;$errors&amp;quot; -eq 1 ]; then
    echo &amp;quot;Errors detected!&amp;quot;
    exit 1
fi&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Теперь можете попробовать сделать ошибку в .php файле и создать коммит. Git должен ругнуться:&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://maxkuznetsov.ru/pictures/Screen-Shot-2020-12-01-at-17.04.25.png" width="1344" height="826" alt="" /&gt;
&lt;/div&gt;
&lt;h2&gt;Устанавливаем git-hook автоматически&lt;/h2&gt;
&lt;p&gt;Финальный шаг — добавить git-hook в .git/hooks/ папку при запуске composer. Тогда ни один разработчик не сможет пропустить запуск проверок по невнимательности. Для этого добавим в composer.json такую строку:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;{
    ...,
    &amp;quot;scripts&amp;quot;: {
        &amp;quot;post-autoload-dump&amp;quot;: &amp;quot;mkdir -p .git/hooks &amp;amp;&amp;amp; cp config/pre-commit .git/hooks/pre-commit&amp;quot;,
        ...
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Итог&lt;/h2&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Мы добавили в проект PHPCsFixer для код-стайла и PHPStan для поиска ошибок и опечаток.&lt;/li&gt;
&lt;li&gt;Запускаем их при каждой попытке создать коммит.&lt;/li&gt;
&lt;li&gt;И всё это начинает работать автоматически после первого же &lt;tt&gt;composer install&lt;/tt&gt;.&lt;/li&gt;
&lt;/ol&gt;
</description>
</item>


</channel>
</rss>