Skip to content

Instantly share code, notes, and snippets.

@steinkel
Last active January 5, 2016 03:15
Show Gist options
  • Save steinkel/9771687 to your computer and use it in GitHub Desktop.
Save steinkel/9771687 to your computer and use it in GitHub Desktop.
git php pre-receive hook to detect syntax and phpcs errors for CakePHP projects
#!/usr/bin/php
<?php
/**
* This hook is a rewrite of http://git.661346.n2.nabble.com/Odd-results-writing-a-Git-pre-receive-hook-to-syntax-check-PHP-files-td5471120.html
* Thanks to Chris Patti for this thread
* @todo: detect when a new subdirectory is comitted and process all files recursive
*/
function finish($exitCode = 0, $message = '') {
if (!empty($message)) {
echo $message;
echo PHP_EOL;
}
if ($exitCode) {
echo PHP_EOL;
echo "https://www.youtube.com/watch?v=XLUSqtqHxXc";
echo PHP_EOL;
}
exit(intval($exitCode));
}
/**
* Get the old and new refs from the input params
* @return array
*/
function parseHookInput() {
$fpstdin = fopen("php://stdin", "r");
$line = fgets($fpstdin);
list($old_sha1, $new_sha1, $refname) = explode(" ", $line);
return array($old_sha1, $new_sha1);
}
/**
* Check if this is a new branch
* @param type $old_sha1
* @param type $new_sha1
* @return boolean
*/
function detectNewBranch($oldSha1, $newSha1) {
return ($oldSha1 == "0000000000000000000000000000000000000000");
}
function parseDiff($oldSha1, $newSha1, $diffCmd, $regex) {
$diff = array();
$returnCode = 0;
exec($diffCmd, $diff, $returnCode);
if ($returnCode != 0) {
finish(0, "Can't execute git ls-tree. Failing gracefully and allowing this push");
}
//If we can execute the git diff I'm assuming we have access to a working git.
foreach ($diff as &$diffLine) {
$matches = array();
preg_match($regex, $diffLine, $matches);
//check file not deleted
if (isset($matches[1]) && isset($matches[2]) && $matches[1] != '0000000') {
$blob = $matches[1];
$filename = $matches[2];
processFile($blob, $filename);
}
}
}
/**
* Process a file, lint & phpcs
* @param type $blob
* @param type $filename
*/
function processFile($blob, $filename) {
$needle = '/(\.php)$/';
if (preg_match($needle, $filename)) {
//echo "Checking $filename\n";
$output = array();
$returnCode = 0;
exec("git show $blob | /usr/bin/php -l 2>&1", $output, $returnCode);
if ($returnCode != 0) {
echo $filename . '---------------';
echo PHP_EOL;
echo(implode(PHP_EOL, $output));
finish(1);
}
//@todo: dry refactor this
exec("git show $blob | phpcs --standard=CakePHP", $output, $returnCode);
if ($returnCode != 0) {
echo $filename . '---------------';
echo PHP_EOL;
echo(implode(PHP_EOL, $output));
finish(1);
}
}
}
function parseCommit($oldSha1, $newSha1) {
//if this is the first commit on a new branch, $old_sha1 wil be a bunch of zeroes, and so
//git diff --raw will fail, since there's no old ref to compare against. So, we parse the
//results of git diff-tree -root=$new_sha1 instead to get the blob and filename we'll need.
$isNewBranch = detectNewBranch($oldSha1, $newSha1);
if ($isNewBranch) {
$diffCmd = "git diff-tree --root $newSha1";
$regex = "/\:\d+ \d+ \w+ (\w+) \w\t(.+)/";
} else {
$diffCmd = "git diff --raw $oldSha1 $newSha1";
$regex = "/\:\d+ \d+ \w+... (\w+)... \w\t(.+)/";
}
parseDiff($oldSha1, $newSha1, $diffCmd, $regex);
}
//End function definitions. Main code body starts here.
//This pre-receive git hook gets passed the ref before the push, and the ref that would be
//created if the push succeeds.
list($oldSha1, $newSha1) = parseHookInput();
parseCommit($oldSha1, $newSha1);
finish(0);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment