komissarovea/hexlet-psr-linter

View on GitHub
src/Linter.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

namespace HexletPsrLinter;

use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter;
use Colors\Color;
use Symfony\Component\Yaml\Yaml;

function lint($input, $rules = BASE_RULES)
{
    $result = [];
    $errors = [];
    try {
        $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
        $traverser = new NodeTraverser();
        $visitor = new HplNodeVisitor($rules);
        $traverser->addVisitor($visitor);

        $stmts = $parser->parse($input);
        if (count($stmts) === 0 || $stmts[0] instanceof Node\Stmt\InlineHTML) {
            $errors[] = new HplError(null, ['message' => 'PHP statements were not found.']);
        } else {
            $stmts = $traverser->traverse($stmts);
            $errors = $visitor->getErrors();
            $result['allStatements'] = $stmts;
        }
    } catch (\Throwable $e) {
        $errors[] = new HplError(null, ['message' => $e->getMessage()]);
    }
    $result['errors'] = $errors;
    return $result;
}

function fix($result)
{
    if (isset($result['allStatements'])) {
        $errors = $result['errors'];
        $allStatements = $result['allStatements'];
        foreach ($errors as $error) {
            $rule = $error->getRule();
            if (isset($rule['fixFunction'])) {
                $fixed = $rule['fixFunction']($error->getNode());
                $error->setFixed($fixed);
            }
        }

        $prettyPrinter = new PrettyPrinter\Standard;
        $fixedCode = $prettyPrinter->prettyPrintFile($allStatements);
        return $fixedCode;
    }
    return null;
}

function buildReport($sourcePath, $errors, $format)
{
    $report = "";
    $dict = convertErrorsToDictionary($errors);
    $dict['sourcePath'] = isset($sourcePath) ? realpath($sourcePath) : "";
    switch ($format) {
        case 'json':
            $report = json_encode($dict);
            break;
        case 'yml':
            $report = Yaml::dump($dict);
            break;
        default:
            $report = array_reduce($dict['errors'], function ($acc, $error) {
                $line = (new Color("$error[line]:"))->blue;
                $name = sprintf("%-7s", $error['name']);
                $name = $error['fixed'] ? (new Color($name))->green : (new Color($name))->red;
                $statement = "Statement: '$error[statement]'.";
                $message = (new Color($error['message']))->white;
                $acc = "$acc $line $name $statement $message" . PHP_EOL;
                return $acc;
            }, "");
            $header = (new Color($dict['sourcePath']))->white->underline . PHP_EOL;
            $footer = "Total errors: $dict[totalErrors]" . PHP_EOL;
            $footer = $dict['totalErrors'] > 0 ? (new Color($footer))->red : (new Color($footer))->green;
            $report = "$header$report $footer";
            //break;
    }
    return $report;
}

function convertErrorsToDictionary($errors)
{
    $dict = [];
    $dict['errors'] = array_map(function ($error) {
        return [
            'line' => $error->getLine(),
            'name' => $error->getName(),
            'fixed' => $error->getFixed(),
            'statement' => $error->getStmtName(),
            'message' => $error->getMessage()
        ];
    }, $errors);
    $dict['totalErrors'] = count($errors);

    return $dict;
}