Last active
August 29, 2015 14:02
-
-
Save sjparkinson/253ab1adf7f3261debc5 to your computer and use it in GitHub Desktop.
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/php | |
<?php | |
print(Utils::getColourString("Checking staged files for common mistakes...", "cyan") . "\n"); | |
$errors = false; | |
foreach(Utils::getStagedFiles() as $file) | |
{ | |
printf("Checking %s file %s.\n", $file->getFormattedGitStatus(), $file->getLocalFileLocation()); | |
$check = new Check($file); | |
// Lint PHP files | |
$check->lintPHP(); | |
// Check for NOCOMMIT | |
$check->noCommitTag(); | |
// Check for unix line endings | |
$check->lineEndings(); | |
$check->leadingLine(); | |
// Print any errors | |
if ($check->hasErrors()) { | |
$errors = true; | |
$check->printErrors(); | |
} | |
} | |
print(Utils::getColourString("Finished checking files. ", "cyan")); | |
if ($errors === false) { | |
print(Utils::getColourString("✔ Success.", "green") . "\n"); | |
print(Utils::getColourString("Have you tested everything?", "gray") . "\n"); | |
exit(0); | |
} else { | |
print(Utils::getColourString("✘ Please fix the errors.", "red") . "\n"); | |
exit(1); | |
} | |
class Check | |
{ | |
// The file to check for issues. | |
private $_file; | |
// A list of errors found when checking. | |
private $_errors; | |
// PHP file extensions to check. | |
private static $_phpExtensions = ['php', 'phtml']; | |
// All file extensions to check. | |
private static $_allExtensions = ['php', 'phtml', 'js', 'less', 'css']; | |
/** | |
* Creates a new instance of the Check class. | |
* | |
* @param File $file The file to check. | |
*/ | |
public function __construct(File $file) | |
{ | |
$this->_file = $file; | |
} | |
/** | |
* Checks PHP files for basic syntax errors. | |
* | |
* Currently uses `php -l` to capture any basic syntax | |
* errors. | |
*/ | |
public function lintPHP() | |
{ | |
if (in_array($extension, self::$_phpExtensions)) { | |
exec('php -l ' . $file->getFileLocation(), $output, $status); | |
if ($status !== 0) { | |
foreach (array_filter($output) as $error) { | |
if (strpos($error, $needle) !== false) { | |
$errorDetail = substr($error, strlen('Parse error: parse error, ')); | |
$this->addError("PHP Lint Error: $errorDetail\n"); | |
} | |
} | |
} | |
} | |
} | |
/** | |
* Checks all files for the NOCOMMIT tag. | |
*/ | |
public function noCommitTag() | |
{ | |
if (in_array($this->_file->getExtension(), self::$_allExtensions)) { | |
exec(sprintf('cat %s | grep "NOCOMMIT"', $this->_file->getFileLocation()), $output); | |
if ($output) { | |
$this->addError(sprintf("No Commit Error: NOCOMMIT found in %s\n", $this->_file->getLocalFileLocation())); | |
} | |
} | |
} | |
/** | |
* Checks all files for unix line endings. | |
* | |
* If the file contains any windows/dos line endings (\r\n) | |
* then the check adds an error. | |
*/ | |
public function lineEndings() | |
{ | |
if (in_array($this->_file->getExtension(), self::$_allExtensions)) { | |
exec(sprintf('file %s | grep "CRLF"', $this->_file->getFileLocation()), $output); | |
if ($output) { | |
$this->addError(sprintf("Line Ending Error: %s contains CRLF line endings.\n", $this->_file->getLocalFileLocation())); | |
} | |
} | |
} | |
/** | |
* Checks only a php file for it's leading line. | |
* | |
* If the PHP file doesn't begin with the correct first line | |
* the check will add an error. This should stop any whitespace | |
* creaping in and being returned to the browser. | |
*/ | |
public function leadingLine() | |
{ | |
if ($this->_file->getExtension() === 'php') { | |
exec(sprintf('head -n 1 %s', $this->_file->getFileLocation()), $output); | |
if (! in_array($output[0], ['<?php', '#!'])) { | |
$this->addError(sprintf("Leading Line Error: %s doesn't start with <?php or #!.\n", $this->_file->getLocalFileLocation())); | |
} | |
} | |
} | |
/** | |
* Checks if the check contains any errors. | |
* | |
* @return bool | |
*/ | |
public function hasErrors() | |
{ | |
return ($this->_errors); | |
} | |
/** | |
* Prints each error in the list to the console, | |
* colourized red. | |
*/ | |
public function printErrors() | |
{ | |
foreach ($this->_errors as $error) { | |
print(Utils::getColourString($error, 'red')); | |
} | |
} | |
/** | |
* Adds a given error to the list of errors. | |
* | |
* @param string $error The error to add. | |
*/ | |
private function addError($error) | |
{ | |
$this->_errors[] = $error; | |
} | |
} | |
class File | |
{ | |
// The path of the file as provided by git. | |
private $_fileLocation; | |
// The shorthand status of the file returned by git. | |
private $_fileStatus; | |
/** | |
* Creates a new instance of the File class. | |
* | |
* If `$file_details` is provided it will populate the instance | |
* from the git status string. | |
* | |
* @param $file_details A tab seperated string of the files git status. | |
*/ | |
function __construct($file_details = null) | |
{ | |
if ($file_details !== null && is_string($file_details)) { | |
list($file_status, $file_location) = explode("\t", $file_details); | |
$this->_fileLocation = $file_location; | |
$this->_fileStatus = $file_status; | |
} | |
} | |
/** | |
* Returns the name of the file including its extension. | |
* | |
* @return string | |
*/ | |
public function getFileName() | |
{ | |
return pathinfo($this->_fileLocation, PATHINFO_FILENAME); | |
} | |
/** | |
* Returns the local path to the file | |
* from the base of the git repository. | |
* | |
* @return string | |
*/ | |
public function getLocalFileLocation() | |
{ | |
return $this->_fileLocation; | |
} | |
/** | |
* Returns the full path to the file. | |
* | |
* @return string | |
*/ | |
public function getFileLocation() | |
{ | |
return getcwd() . '/' . $this->_fileLocation; | |
} | |
/** | |
* Returns the files extension. | |
* | |
* @return string The extension without a period. | |
*/ | |
public function getExtension() | |
{ | |
return pathinfo($this->_fileLocation, PATHINFO_EXTENSION); | |
} | |
/** | |
* Returns the short hand git status of the file. | |
* | |
* @return string | |
*/ | |
public function getGitStatus() | |
{ | |
return $this->_fileStatus; | |
} | |
/** | |
* Returns the git status of the file as a word. | |
* | |
* @return string | |
* | |
* @throws UnexpectedValueException If the recorded status is not an expected value. | |
*/ | |
public function getFormattedGitStatus() | |
{ | |
switch($this->_fileStatus) { | |
case 'A': | |
return 'added'; | |
case 'C': | |
return 'copied'; | |
case 'M': | |
return 'modified'; | |
case 'R': | |
return 'renamed'; | |
default: | |
throw new UnexpectedValueException("Unknown file status: $this->_fileStatus."); | |
} | |
} | |
} | |
class Utils | |
{ | |
/** | |
* Dictonary of bash colour prefixes. | |
*/ | |
private static $_foregroundPrefix = [ | |
'black' => '0;30', 'blue' => '0;34', | |
'green' => '0;32', 'cyan' => '0;36', | |
'red' => '0;31', 'purple' => '0;35', | |
'brown' => '0;33', 'gray' => '0;37', | |
]; | |
/** | |
* Gets a list of the files currently staged under git. | |
* | |
* Returns either an empty array or a tab seperated list of staged files and | |
* their git status. | |
* | |
* @link http://git-scm.com/docs/git-status | |
* | |
* @return array | |
*/ | |
public static function getStagedFiles() | |
{ | |
exec('git diff --cached --name-status --diff-filter=ACMR', $output); | |
$files = []; | |
foreach ($output as $file) { | |
$files[] = new File($file); | |
} | |
return $files; | |
} | |
/** | |
* Formats a string for colourized outputting to the command line. | |
* | |
* @param string $string The string to colourize. | |
* @param string $foreground The colour to set the text to. | |
* | |
* @return string | |
*/ | |
public static function getColourString($string, $foreground = null) | |
{ | |
$builder = ""; | |
// Check if given foreground color found | |
if (self::$_foregroundPrefix[$foreground]) { | |
$builder .= "\033[" . self::$_foregroundPrefix[$foreground] . "m"; | |
} | |
// Add string and end coloring | |
$builder .= $string . "\033[0m"; | |
return $builder; | |
} | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment