Auth2FA/vendor/nette/neon/bin/neon-lint

155 lines
3.2 KiB
PHP

#!/usr/bin/env php
<?php
declare(strict_types=1);
if (
!(is_file($file = __DIR__ . '/../vendor/autoload.php') && include $file) &&
!(is_file($file = __DIR__ . '/../../../autoload.php') && include $file)
) {
fwrite(STDERR, "Install packages using Composer.\n");
exit(1);
}
echo '
NEON linter
-----------
';
if ($argc < 2) {
echo "Usage: neon-lint [--debug] <path>\n";
exit(1);
}
$debug = in_array('--debug', $argv, true);
if ($debug) {
echo "Debug mode\n";
}
$path = end($argv);
try {
$linter = new NeonLinter(debug: $debug);
$ok = $linter->scanDirectory($path);
exit($ok ? 0 : 1);
} catch (Throwable $e) {
fwrite(STDERR, $debug ? "\n$e\n" : "\nError: {$e->getMessage()}\n");
exit(2);
}
class NeonLinter
{
/** @var string[] */
public array $excludedDirs = ['.*', '*.tmp', 'temp', 'vendor', 'node_modules'];
public function __construct(
private readonly bool $debug = false,
) {
}
public function scanDirectory(string $path): bool
{
$this->initialize();
echo "Scanning $path\n";
$counter = 0;
$errors = 0;
foreach ($this->getFiles($path) as $file) {
$file = (string) $file;
echo preg_replace('~\.?[/\\\]~A', '', $file), "\x0D";
$errors += $this->lintFile($file) ? 0 : 1;
echo str_pad('...', strlen($file)), "\x0D";
$counter++;
}
echo "Done (checked $counter files, found errors in $errors)\n";
return !$errors;
}
public function lintFile(string $file): bool
{
if ($this->debug) {
echo $file, "\n";
}
$s = file_get_contents($file);
if (str_starts_with($s, "\xEF\xBB\xBF")) {
$this->writeError('WARNING', $file, 'contains BOM');
$s = substr($s, 3);
}
try {
Nette\Neon\Neon::decode($s);
return true;
} catch (Nette\Neon\Exception $e) {
if ($this->debug) {
echo $e;
}
$this->writeError('ERROR', $file, $e->getMessage());
return false;
}
}
private function initialize(): void
{
if (function_exists('pcntl_signal')) {
pcntl_signal(SIGINT, function (): never {
pcntl_signal(SIGINT, SIG_DFL);
echo "Terminated\n";
exit(1);
});
} elseif (function_exists('sapi_windows_set_ctrl_handler')) {
sapi_windows_set_ctrl_handler(function (): never {
echo "Terminated\n";
exit(1);
});
}
set_time_limit(0);
}
private function getFiles(string $path): Iterator
{
$it = match (true) {
is_file($path) => new ArrayIterator([$path]),
is_dir($path) => $this->findNeonFiles($path),
(bool) preg_match('~[*?]~', $path) => new GlobIterator($path),
default => throw new InvalidArgumentException("File or directory '$path' not found."),
};
return new CallbackFilterIterator($it, fn($file) => is_file((string) $file));
}
private function findNeonFiles(string $dir): Generator
{
foreach (scandir($dir) as $name) {
$path = ($dir === '.' ? '' : $dir . DIRECTORY_SEPARATOR) . $name;
if ($name !== '.' && $name !== '..' && is_dir($path)) {
foreach ($this->excludedDirs as $pattern) {
if (fnmatch($pattern, $name)) {
continue 2;
}
}
yield from $this->findNeonFiles($path);
} elseif (str_ends_with($name, '.neon')) {
yield $path;
}
}
}
private function writeError(string $label, string $file, string $message): void
{
fwrite(STDERR, str_pad("[$label]", 13) . ' ' . $file . ' ' . $message . "\n");
}
}