Verifikasi kode otomatis dalam 5 menit

Tutorial ini menunjukkan cara mengotomatiskan pemeriksaan gaya kode dalam proyek php Anda.



Mari kita lihat bagaimana penataan akan terlihat di proyek baru.



Langkah 1 - Kami menginisialisasi komposer (yang sudah mengonfigurasi, lewati)



Untuk melakukan ini, jalankan perintah di root proyek Anda. Jika Anda belum menginstal komposer, Anda dapat merujuk ke dokumentasi resmi getcomposer.org



composer init

      
      





Langkah 2 - Tambahkan .gitignore



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

/vendor/

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

      
      





Langkah 3 - Tambahkan perpustakaan yang diperlukan



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

      
      





Langkah 4 - Tambahkan Penangan Hook



Handlernya sendiri bisa ditulis di apapun, tapi karena artikelnya tentang php, kita akan menulis kode di situ.



Buat file di folder 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();

      
      









Dalam contoh ini, kode akan melewati 3 pemeriksaan:

- memeriksa kesalahan sintaks

- memeriksa PSR2 melalui sniffer kode



PSR2 dapat diganti dengan yang lain yang mendukung sniffer kode. Daftar standar yang didukung dapat dilihat dengan memasukkan perintah



 vendor/bin/phpcs -i

      
      







Langkah 5 - Mengonfigurasi komposer untuk menerapkan pemeriksaan pra-komit mulai otomatis



Agar kode verifikasi berjalan pada hook pre commit, kita perlu meletakkan file dengan kode yang kita buat pada langkah 3, meletakkannya di folder .git / hooks / pre-commit. Ini dapat dilakukan secara manual, tetapi jauh lebih nyaman untuk mengotomatiskan masalah ini. Untuk melakukan ini, kita perlu menulis penangan yang akan menyalin file ini dan menggantungnya pada acara yang dipanggil setelah penginstalan komposer. Untuk melakukan ini, lakukan hal berikut.

5.1 Buat penangannya sendiri yang akan menyalin file pre-commit.php ke folder git hooks



Buat file 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



- sebelum install dan update, handler lama dihapus



post-install-cmd, post-update-cmd



- setelah install dan update, handler akan diset ke pre commit



Sebagai hasilnya, filekik composer.json akan mengambil form berikut



composer.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"
        }
    }
}

      
      









Jalankan penginstalan komposer lagi sehingga file disalin jika perlu.



Semuanya sudah siap, sekarang jika Anda mencoba memasukkan kode dengan gaya kode bengkok, konsol git akan memberi tahu Anda.



Sebagai contoh, mari buat file MyClass.php di folder src dengan konten berikut.



<?php

namespace App;

class MyClass
{

    private $var1; private $var2;

    public function __construct() {
    }

    public function test() {

    }
}

      
      





Kami mencoba untuk melakukan dan mendapatkan kesalahan validasi kode.



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!                      

      
      





Hore, semuanya bekerja.



All Articles