jc21
3/5/2018 - 5:07 AM

PHP Lint wrapper script with optional recursion and exclusions

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;
}