PHP Lint wrapper script with optional recursion and exclusions
<?php
// Optionally put this before the opening tag and chmod +x to make this script binary-like:
#!/usr/bin/php
// ===========
// = Globals =
// ===========
$count = 0; // total files checked
$ignored_count = 0;
$errors = array();
$options = getOptions();
if ($options['quiet']) {
ob_start();
}
// =============
// = Scan path =
// =============
$files = getPipedFiles();
$path = getcwd();
// Piped files present
if ($files) {
foreach ($files as $file) {
checkFile("$path/$file");
}
} else {
// Use arguments
if ($_SERVER['argc'] > 1) {
$last = end($_SERVER['argv']);
if (substr($last, 0, 1) != '-') {
$path = $last; // snag last argument, if it wasn't an option switch
}
}
if (is_dir($path)) {
checkDirectoryContents($path);
} else if (is_file($path)) {
checkFile($path);
} else {
print "$path is not a file or directory." . PHP_EOL;
showHelp();
exit(1);
}
}
if ($options['quiet']) {
ob_end_clean();
}
print PHP_EOL . "$count files checked, " . count($errors) . ' errors' . (count($options['ignores']) ? ', ' . $ignored_count . ' ignored errors' : '') . '.';
print PHP_EOL . implode(PHP_EOL, $errors);
if (count($errors)) {
exit(10);
}
/**
* checkDirectoryContents
*
* @param string $dir
*/
function checkDirectoryContents($dir)
{
global $options, $i, $errors, $count;
$contents = scandir($dir);
foreach($contents as $content) {
if ($content == '.' || $content == '..') {
continue;
}
$path = "$dir/$content";
// Recurse into directories
if (is_dir($path) && $options['recurse']) {
checkDirectoryContents($path);
} else {
checkFile($path);
}
}
}
/**
* checkFile
*
* @param string $path
*/
function checkFile($path)
{
global $count, $errors, $options, $ignored_count;
// Skip non-php files
if (substr($path, -4) != '.php') {
return false;
}
if (($count % 60 == 0)) {
print PHP_EOL;
}
$error = `php -l $path 2>&1 1> /dev/null`;
if ($error) {
$ignore = false;
foreach ($options['ignores'] as $find) {
if (strpos($error, $find) !== false) {
$ignore = true;
break;
}
}
if ($ignore) {
$ignored_count += 1;
} else {
$errors[] = $error;
}
print 'E';
} else {
print '.';
}
$count++;
}
/**
* getPipedFiles
*
* @return array
*/
function getPipedFiles()
{
$files = array();
stream_set_blocking(STDIN, false);
while ($line = trim(fgets(STDIN))) {
$files[] = $line;
}
return $files;
}
/**
* getOptions
*
* @return array
*/
function getOptions()
{
$options = array(
'quiet' => false,
'recurse' => false,
'ignores' => array(),
);
$args = getopt('qRhi:', array('quiet', 'recursive', 'help'));
foreach ($args as $arg => $value) {
switch ($arg) {
case 'q':
case 'quiet':
$options['quiet'] = true;
break;
case 'R':
case 'recursive':
$options['recurse'] = true;
break;
case 'i':
case 'ignores':
$options['ignores'] = explode(';', $value);
break;
case 'h':
case 'help':
default:
showHelp();
exit(0);
}
}
return $options;
}
/**
* showHelp
*
* @return bool
*/
function showHelp()
{
print <<<HELP
usage: lint [-qR] [path]
options:
-q, --quiet: disable verbose output
-R, --recursive: recurse into subdirectories
-i ignore errors where these file path(s) are found. ie: -i=vendor;zend2framework
-h, --help: display this help screen
HELP;
return true;
}