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