Vérification automatique du code en 5 minutes

Ce tutoriel montre comment automatiser la vérification du style de code dans votre projet php.



Voyons Ă  quoi ressemblera la configuration dans un nouveau projet.



Étape 1 - Nous initialisons le composeur (qui l'a dĂ©jĂ  configurĂ©, sautez-le)



Pour ce faire, exécutez la commande à la racine de votre projet. Si vous n'avez pas installé de composer, vous pouvez vous référer à la documentation officielle de getcomposer.org



composer init

      
      





Étape 2 - Ajouter .gitignore



###> phpstorm ###
.idea
###< phpstorm ###

/vendor/

###> friendsofphp/php-cs-fixer ###
/.php_cs
/.php_cs.cache
###< friendsofphp/php-cs-fixer ###

      
      





Étape 3 - Ajouter les bibliothùques requises



composer require --dev friendsofphp/php-cs-fixer symfony/process symfony/console  squizlabs/php_codesniffer

      
      





Étape 4 - Ajouter un gestionnaire de crochet



Le gestionnaire lui-mĂȘme peut ĂȘtre Ă©crit sur n'importe quoi, mais comme l'article concerne php, nous allons Ă©crire du code dessus.



Créez un fichier dans le dossier hooks / pre-commit.php
#!/usr/bin/php

<?php
define('VENDOR_DIR', __DIR__.'/../../vendor');
require VENDOR_DIR.'/autoload.php';

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Application;
use Symfony\Component\Process\Process;

class CodeQualityTool extends Application
{
    /**
     * @var OutputInterface
     */
    private $output;
    /**
     * @var InputInterface
     */
    private $input;

    const PHP_FILES_IN_SRC = '/^src\/(.*)(\.php)$/';

    public function __construct()
    {
        parent::__construct('Ecombo Quality Tool', '1.0.0');
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     *
     * @return void
     * @throws \Exception
     */
    public function doRun(InputInterface $input, OutputInterface $output)
    {
        $this->input = $input;
        $this->output = $output;
        $output->writeln('<fg=white;options=bold;bg=red>Code Quality Tool</fg=white;options=bold;bg=red>');
        $output->writeln('<info>Fetching files</info>');
        $files = $this->extractCommitedFiles();
        $output->writeln('<info>Running PHPLint</info>');

        if (! $this->phpLint($files)) {
            throw new \Exception('There are some PHP syntax errors!');
        }

        $output->writeln('<info>Checking code style with PHPCS</info>');

        if (! $this->codeStylePsr($files)) {
            throw new \Exception(sprintf('There are PHPCS coding standards violations!'));
        }

        $output->writeln('<info>Well done!</info>');
    }

    /**
     * @return array
     */
    private function extractCommitedFiles()
    {
        $output = array();
        $against = 'HEAD';
        exec("git diff-index --cached --name-status $against | egrep '^(A|M)' | awk '{print $2;}'", $output);

        return $output;
    }

    /**
     * @param array $files
     *
     * @return bool
     *
     * @throws \Exception
     */
    private function phpLint($files)
    {
        $needle = '/(\.php)|(\.inc)$/';
        $succeed = true;

        foreach ($files as $file) {
            if (! preg_match($needle, $file)) {
                continue;
            }

            $process = new Process(['php', '-l', $file]);
            $process->run();

            if (! $process->isSuccessful()) {
                $this->output->writeln($file);
                $this->output->writeln(sprintf('<error>%s</error>', trim($process->getErrorOutput())));

                if ($succeed) {
                    $succeed = false;
                }
            }
        }

        return $succeed;
    }

    /**
     * @param array $files
     *
     * @return bool
     */
    private function codeStylePsr(array $files)
    {
        $succeed = true;
        $needle = self::PHP_FILES_IN_SRC;
        $standard = 'PSR2';

        foreach ($files as $file) {
            if (! preg_match($needle, $file)) {
                continue;
            }

            $phpCsFixer = new Process([
                'php',
                VENDOR_DIR.'/bin/phpcs',
                '-n',
                '--standard='.$standard,
                $file,
            ]);

            $phpCsFixer->setWorkingDirectory(__DIR__.'/../../');
            $phpCsFixer->run();

            if (! $phpCsFixer->isSuccessful()) {
                $this->output->writeln(sprintf('<error>%s</error>', trim($phpCsFixer->getOutput())));

                if ($succeed) {
                    $succeed = false;
                }
            }
        }

        return $succeed;
    }
}

$console = new CodeQualityTool();
$console->run();

      
      









Dans cet exemple, le code passera 3 vérifications:

- vérifier les erreurs de syntaxe

- vérifier PSR2 via le renifleur de code



PSR2 peut ĂȘtre remplacĂ© par tout autre qui prend en charge le renifleur de code. La liste des normes prises en charge peut ĂȘtre consultĂ©e en entrant la commande



 vendor/bin/phpcs -i

      
      







Étape 5 - Configuration de composer pour implĂ©menter la vĂ©rification de prĂ©-validation de dĂ©marrage automatique



Pour que le code de vĂ©rification s'exĂ©cute sur le hook prĂ©-commit, nous devons mettre le fichier avec le code que nous avons créé Ă  l'Ă©tape 3, le placer dans le dossier .git / hooks / pre-commit. Cela peut ĂȘtre fait manuellement, mais il est beaucoup plus pratique d'automatiser cette question. Pour ce faire, nous devons Ă©crire un gestionnaire qui copiera ce fichier et le suspendra Ă  l'Ă©vĂ©nement appelĂ© aprĂšs l'installation de composer. Pour ce faire, procĂ©dez comme suit.

5.1 CrĂ©ez le gestionnaire lui-mĂȘme qui copiera le fichier pre-commit.php dans le dossier git hooks



Créez un fichier src / Composer / ScriptHandler.php
<?php

namespace App\Composer;

use Composer\Script\Event;

class ScriptHandler
{
    /**
     * @param Event $event
     *
     * @return bool
     */
    public static function preHooks(Event $event)
    {
        $io = $event->getIO();
        $gitHook = '.git/hooks/pre-commit';

        if (file_exists($gitHook)) {
            unlink($gitHook);
            $io->write('<info>Pre-commit hook removed!</info>');
        }

        return true;
    }

    /**
     * @param Event $event
     *
     * @return bool
     *
     * @throws \Exception
     */
    public static function postHooks(Event $event)
    {
        /** @var array $extras */
        $extras = $event->getComposer()->getPackage()->getExtra();

        if (! array_key_exists('hooks', $extras)) {
            throw new \InvalidArgumentException('The parameter handler needs to be configured through the extra.hooks setting.');
        }
        $configs = $extras['hooks'];
        if (! array_key_exists('pre-commit', $configs)) {
            throw new \InvalidArgumentException('The parameter handler needs to be configured through the extra.hooks.pre-commit setting.');
        }

        if (file_exists('.git/hooks')) {
            /** @var \Composer\IO\IOInterface $io */
            $io = $event->getIO();
            $gitHook = '.git/hooks/pre-commit';
            $docHook = $configs['pre-commit'];
            copy($docHook, $gitHook);
            chmod($gitHook, 0777);
            $io->write('<info>Pre-commit hook created!</info>');
        }

        return true;
    }
}

      
      





5.2 composer

composer.json



    "scripts": {
        "post-install-cmd": [
            "App\\Composer\\ScriptHandler::postHooks"
        ],
        "post-update-cmd": [
            "App\\Composer\\ScriptHandler::postHooks"
        ],
        "pre-update-cmd": "App\\Composer\\ScriptHandler::preHooks",
        "pre-install-cmd": "App\\Composer\\ScriptHandler::preHooks"
    },
    "extra": {
        "hooks": {
            "pre-commit": "hooks/pre-commit.php"
        }
    }

      
      









pre-update-cmd, pre-install-cmd



- avant l'installation et la mise Ă  jour, l'ancien gestionnaire



post-install-cmd, post-update-cmd



est supprimé - aprÚs l'installation et la mise à jour, le gestionnaire sera défini sur pre commit



En conséquence, le fichierkik composer.json prendra la forme suivante



compositeur.json
{
    "name": "admin/test",
    "authors": [
        {
            "name": "vitaly.gorbunov",
            "email": "cezar62882@gmail.com"
        }
    ],
    "minimum-stability": "stable",
    "require": {},
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "scripts": {
        "post-install-cmd": [
            "App\\Composer\\ScriptHandler::postHooks"
        ],
        "post-update-cmd": [
            "App\\Composer\\ScriptHandler::postHooks"
        ],
        "pre-update-cmd": "App\\Composer\\ScriptHandler::preHooks",
        "pre-install-cmd": "App\\Composer\\ScriptHandler::preHooks"
    },
    "require-dev": {
        "friendsofphp/php-cs-fixer": "^2.16",
        "symfony/process": "^5.0",
        "symfony/console": "^5.0",
        "squizlabs/php_codesniffer": "^3.5"
    },
    "extra": {
        "hooks": {
            "pre-commit": "hooks/pre-commit.php"
        }
    }
}

      
      









Exécutez à nouveau l'installation de composer afin que le fichier soit copié si nécessaire.



Tout est prĂȘt, maintenant si vous essayez de valider le code avec un style de code tordu, la console git vous en parlera.



À titre d'exemple, crĂ©ons un fichier MyClass.php dans le dossier src avec le contenu suivant.



<?php

namespace App;

class MyClass
{

    private $var1; private $var2;

    public function __construct() {
    }

    public function test() {

    }
}

      
      





Nous essayons de commettre et d'obtenir des erreurs de validation de code.



MBP-Admin:test admin$ git commit -am 'test'

Code Quality Tool
Fetching files
Running PHPLint
Checking code style with PHPCS
FILE: /Users/admin/projects/test/src/MyClass.php
----------------------------------------------------------------------
FOUND 5 ERRORS AFFECTING 5 LINES
----------------------------------------------------------------------
  8 | ERROR | [x] Each PHP statement must be on a line by itself
 10 | ERROR | [x] Opening brace should be on a new line
 13 | ERROR | [x] Opening brace should be on a new line
 15 | ERROR | [x] Function closing brace must go on the next line
    |       |     following the body; found 1 blank lines before
    |       |     brace
 16 | ERROR | [x] Expected 1 newline at end of file; 0 found
----------------------------------------------------------------------
PHPCBF CAN FIX THE 5 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------

Time: 49ms; Memory: 6MB

In pre-commit line 53:
                                                
  There are PHPCS coding standards violations!                      

      
      





Hourra, tout fonctionne.



All Articles