Skip to content

Instantly share code, notes, and snippets.

@Swop
Last active July 15, 2016 06:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Swop/3dcc744af3b53f837dc5 to your computer and use it in GitHub Desktop.
Save Swop/3dcc744af3b53f837dc5 to your computer and use it in GitHub Desktop.
Console app to check quality of a project (for a pre-commit hook) (source: http://carlosbuenosvinos.com/write-your-git-hooks-in-php-and-keep-them-under-git-control/)
{
"scripts": {
"pre-update-cmd": "Swop\\Composer\\Script\\Hooks::checkHooks",
"pre-install-cmd": "Swop\\Composer\\Script\\Hooks::checkHooks"
}
}
#!/usr/bin/php
<?php
require __DIR__ . '/../../vendor/autoload.php';
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;
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>Atrapalo 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 dude!</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 = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
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)$/';
$succeed = true;
foreach ($files as $file) {
if (!preg_match($needle, $file)) {
continue;
}
$processBuilder = new ProcessBuilder(array('php', '-l', $file));
$process = $processBuilder->getProcess();
$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;
}
$processBuilder = new ProcessBuilder(['php', 'bin/phpmd', $file, 'text', 'controversial']);
$processBuilder->setWorkingDirectory($rootPath);
$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())));
if ($succeed) {
$succeed = false;
}
}
}
return $succeed;
}
private function unitTests()
{
$processBuilder = new ProcessBuilder(array('php', 'bin/phpunit'));
$processBuilder->setWorkingDirectory(__DIR__ . '/../..');
$processBuilder->setTimeout(3600);
$phpunit = $processBuilder->getProcess();
$phpunit->run(function ($type, $buffer) {
$this->output->write($buffer);
});
return $phpunit->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';
}
$processBuilder = new ProcessBuilder(array('php', 'bin/php-cs-fixer', '--dry-run', '--verbose', 'fix', $file, '--fixers='.$fixers));
$processBuilder->setWorkingDirectory(__DIR__ . '/../../');
$phpCsFixer = $processBuilder->getProcess();
$phpCsFixer->run();
if (!$phpCsFixer->isSuccessful()) {
$this->output->writeln(sprintf('<error>%s</error>', trim($phpCsFixer->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;
}
$processBuilder = new ProcessBuilder(array('php', 'bin/phpcs', '--standard=PSR2', $file));
$processBuilder->setWorkingDirectory(__DIR__ . '/../../');
$phpCsFixer = $processBuilder->getProcess();
$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();
<?php
namespace Swop\Composer\Script;
use Composer\Script\Event;
class Hooks
{
public static function checkHooks(Event $event)
{
$io = $event->getIO();
$gitHook = @file_get_contents(__DIR__.'/../../../../.git/hooks/pre-commit');
$docHook = @file_get_contents(__DIR__.'/../../../../docs/hooks/pre-commit');
$result = true;
if ($gitHook !== $docHook) {
$io->write('<error>You, motherfucker, please, set up your hooks!</error>');
$result = false;
}
return $result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment