Created
May 28, 2022 21:40
-
-
Save purdy/e76fe49a10cb26e5a01981c54726773d to your computer and use it in GitHub Desktop.
This is a file named pre-commit that we use in our repo's ~/.git/hooks to check our php code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env php | |
<?php | |
// Credit: https://carlosbuenosvinos.com/write-your-git-hooks-in-php-and-keep-them-under-git-control/ | |
require __DIR__ . '/../../vendor/autoload.php'; | |
use Symfony\Component\Console\Input\InputInterface; | |
use Symfony\Component\Console\Output\OutputInterface; | |
use Symfony\Component\Process\Process; | |
use Symfony\Component\Console\Application; | |
class CodeQualityTool extends Application | |
{ | |
private $output; | |
private $input; | |
const PHP_FILES_IN_SRC = '/^src\/(.*)(\.php)$/'; | |
const PHP_FILES_IN_CLASSES = '/^classes\/(.*)(\.php)$/'; | |
public function __construct() | |
{ | |
parent::__construct('Code Quality Tool', '1.0.0'); | |
} | |
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>Check composer</info>'); | |
$this->checkComposer($files); | |
$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</info>'); | |
// if (!$this->codeStyle($files)) { | |
// throw new Exception(sprintf('There are coding standards violations!')); | |
// } | |
// $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>Checking code mess with PHPMD</info>'); | |
// if (!$this->phPmd($files)) { | |
// throw new Exception(sprintf('There are PHPMD violations!')); | |
// } | |
// $output->writeln('<info>Running unit tests</info>'); | |
// if (!$this->unitTests()) { | |
// throw new Exception('Fix the fucking unit tests!'); | |
// } | |
$output->writeln('<info>Good job!</info>'); | |
} | |
private function checkComposer($files) | |
{ | |
$composerJsonDetected = false; | |
$composerLockDetected = false; | |
foreach ($files as $file) { | |
if ($file === 'composer.json') { | |
$composerJsonDetected = true; | |
} | |
if ($file === 'composer.lock') { | |
$composerLockDetected = true; | |
} | |
} | |
if ($composerJsonDetected && !$composerLockDetected) { | |
throw new Exception('composer.lock must be commited if composer.json is modified!'); | |
} | |
} | |
private function extractCommitedFiles() | |
{ | |
$output = array(); | |
$rc = 0; | |
exec('git rev-parse --verify HEAD 2> /dev/null', $output, $rc); | |
$against = '945a25fc22a5ffd70947f98cf2f5263e664d3663'; | |
if ($rc == 0) { | |
$against = 'HEAD'; | |
} | |
exec("git diff-index --cached --name-status $against | egrep '^(A|M)' | awk '{print $2;}'", $output); | |
return $output; | |
} | |
private function phpLint($files) | |
{ | |
$needle = '/(\.php)|(\.inc)|(\.module)$/'; | |
$succeed = true; | |
foreach ($files as $file) { | |
if (!preg_match($needle, $file)) { | |
continue; | |
} | |
$process = new Process(array('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; | |
} | |
private function phPmd($files) | |
{ | |
$needle = self::PHP_FILES_IN_SRC; | |
$succeed = true; | |
$rootPath = realpath(__DIR__ . '/../../'); | |
foreach ($files as $file) { | |
if (!preg_match($needle, $file) || preg_match('/src\/AtrapaloLib\/ORM\/Doctrine\/DBAL\/Driver\/Adodb/', $file)) { | |
continue; | |
} | |
$process = new Process(['php', 'bin/phpmd', $file, 'text', 'controversial']); | |
$process->setWorkingDirectory($rootPath); | |
$process->run(); | |
if (!$process->isSuccessful()) { | |
$this->output->writeln($file); | |
$this->output->writeln(sprintf('<error>%s</error>', trim($process->getErrorOutput()))); | |
$this->output->writeln(sprintf('<info>%s</info>', trim($process->getOutput()))); | |
if ($succeed) { | |
$succeed = false; | |
} | |
} | |
} | |
return $succeed; | |
} | |
private function unitTests() | |
{ | |
$process = new Process(array('php', 'bin/phpunit')); | |
$process->setWorkingDirectory(__DIR__ . '/../..'); | |
$process->setTimeout(3600); | |
$process->run(function ($type, $buffer) { | |
$this->output->write($buffer); | |
}); | |
return $process->isSuccessful(); | |
} | |
private function codeStyle(array $files) | |
{ | |
$succeed = true; | |
foreach ($files as $file) { | |
$classesFile = preg_match(self::PHP_FILES_IN_CLASSES, $file); | |
$srcFile = preg_match(self::PHP_FILES_IN_SRC, $file); | |
if (!$classesFile && !$srcFile) { | |
continue; | |
} | |
$fixers = '-psr0'; | |
if ($classesFile) { | |
$fixers = 'eof_ending,indentation,linefeed,lowercase_keywords,trailing_spaces,short_tag,php_closing_tag,extra_empty_lines,elseif,function_declaration'; | |
} | |
$process = new Process(array('php', 'bin/php-cs-fixer', '--dry-run', '--verbose', 'fix', $file, '--fixers='.$fixers)); | |
$process->setWorkingDirectory(__DIR__ . '/../../'); | |
$process->run(); | |
if (!$process->isSuccessful()) { | |
$this->output->writeln(sprintf('<error>%s</error>', trim($process->getOutput()))); | |
if ($succeed) { | |
$succeed = false; | |
} | |
} | |
} | |
return $succeed; | |
} | |
private function codeStylePsr(array $files) | |
{ | |
$succeed = true; | |
$needle = self::PHP_FILES_IN_SRC; | |
foreach ($files as $file) { | |
if (!preg_match($needle, $file)) { | |
continue; | |
} | |
$process = new Process(array('php', 'bin/phpcs', '--standard=PSR2', $file)); | |
$process->setWorkingDirectory(__DIR__ . '/../../'); | |
$process->run(); | |
if (!$process->isSuccessful()) { | |
$this->output->writeln(sprintf('<error>%s</error>', trim($process->getOutput()))); | |
if ($succeed) { | |
$succeed = false; | |
} | |
} | |
} | |
return $succeed; | |
} | |
} | |
$console = new CodeQualityTool(); | |
$console->run(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment