Skip to content

Instantly share code, notes, and snippets.

@yriveiro
Forked from jmauerhan/pre-commit.php
Created July 13, 2018 22:48
Show Gist options
  • Save yriveiro/a33f35f8a9955442a4585b03bfb3d32c to your computer and use it in GitHub Desktop.
Save yriveiro/a33f35f8a9955442a4585b03bfb3d32c to your computer and use it in GitHub Desktop.
A pre-commit hook written in php to fix code style and alert about phpmd rules
#!/usr/bin/php
<?php
require __DIR__ . '/../../vendor/autoload.php';
/**
* Dependencies:
* - Symfony Console Component: symfony/console
* - Symfony Process Component: symfony/process
* - PHP Mess Detector: phpmd/phpmd
* - PHP CS Fixer: fabpot/php-cs-fixer
**/
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\ProcessBuilder;
use Symfony\Component\Console\Application;
class CodeQualityTool extends Application
{
private $output;
private $input;
private $projectRoot;
//The locations of the files you want to measure. Add/remove as needed.
const PHP_FILES_IN_SRC = '/^src\/(.*)(\.php)$/';
const PHP_FILES_IN_APPLICATION = '/^application\/(.*)(\.php)$/';
public function __construct()
{
/** OS agnostic */
$this->projectRoot = realpath(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR);
parent::__construct('Code Quality Tool', '1.0.0');
}
/**
* @param $file
*
* @return bool
*/
private function shouldProcessFile($file)
{
$applicationFile = preg_match(self::PHP_FILES_IN_APPLICATION, $file);
$srcFile = preg_match(self::PHP_FILES_IN_SRC, $file);
return ($applicationFile || $srcFile);
}
public function doRun(InputInterface $input, OutputInterface $output)
{
$this->input = $input;
$this->output = $output;
$output->writeln('<fg=white;options=bold;bg=cyan> -- Code Quality Pre-Commit Check -- </fg=white;options=bold;bg=cyan>');
$output->writeln('<info>Fetching files</info>');
$files = $this->extractCommitedFiles();
$output->writeln('<info>Fixing code style</info>');
$this->codeStyle($files);
$output->writeln('<info>Checking composer</info>');
if (!$this->checkComposer($files)) {
throw new Exception('composer.lock must be commited if composer.json is modified!');
}
$output->writeln('<info>Checking for messy code with PHPMD</info>');
if (!$this->checkPhpMd($files)) {
throw new Exception(sprintf('There are PHPMD violations!'));
}
$output->writeln('<fg=white;options=bold;bg=green> -- Code Quality: Passed! -- </fg=white;options=bold;bg=green>');
}
/**
* @return array
*/
private function extractCommitedFiles()
{
$files = [];
$output = [];
exec("git diff --cached --name-status --diff-filter=ACM", $output);
foreach ($output as $line) {
$action = trim($line[0]);
$files[] = trim(substr($line, 1));
}
return $files;
}
/**
* @param $files
*
* This function ensures that when the composer.json file is edited
* the composer.lock is also updated and commited
*
* @throws \Exception
*/
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) {
return false;
}
return true;
}
/**
* @param array $files
*
* @return bool
*/
private function codeStyle(array $files)
{
$commandLineArgs = [
'bin' . DIRECTORY_SEPARATOR . 'php-cs-fixer',
'fix',
null,
'--level=psr2'
];
foreach ($files as $file) {
if (!$this->shouldProcessFile($file)) {
continue;
}
$commandLineArgs[2] = $file;
$processBuilder = new ProcessBuilder($commandLineArgs);
$processBuilder->setWorkingDirectory($this->projectRoot);
$phpCsFixer = $processBuilder->getProcess();
$phpCsFixer->run();
exec('git add ' . $file);
}
}
/**
* @param $files
*
* @return bool
*/
private function checkPhpMd($files)
{
$succeed = true;
foreach ($files as $file) {
if (!$this->shouldProcessFile($file)) {
continue;
}
$processBuilder = new ProcessBuilder([
'bin/phpmd',
$file,
'text',
'phpmd-rules.xml'
]);
$processBuilder->setWorkingDirectory($this->projectRoot);
$process = $processBuilder->getProcess();
$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())));
$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