Files
glastree/vendor/laravel/pao/src/Drivers/Phpstan/Starter.php
T

209 lines
5.5 KiB
PHP

<?php
declare(strict_types=1);
namespace Laravel\Pao\Drivers\Phpstan;
use Laravel\Pao\Drivers\Starter as BaseStarter;
use Laravel\Pao\UserFilters\CaptureFilter;
/**
* @internal
*
* @codeCoverageIgnore
*/
final class Starter extends BaseStarter
{
public function name(): string
{
return 'phpstan';
}
public function start(): void
{
$this->registerNullFilter();
$this->silenceStderr();
/** @var array<int, string> $argv */
$argv = $_SERVER['argv'];
$argv = $this->ensureErrorFormatJson($argv);
$argv = $this->ensureNoProgress($argv);
$_SERVER['argv'] = $argv;
$this->silenceStdout();
}
/**
* @return array<string, mixed>|null
*/
public function parse(): ?array
{
$captured = trim(CaptureFilter::output());
CaptureFilter::reset();
if ($captured === '') {
return null;
}
$start = strpos($captured, '{');
if ($start !== false && $start > 0) {
$captured = substr($captured, $start);
}
/** @var array<string, mixed>|null $data */
$data = json_decode($captured, associative: true);
if (! is_array($data) || ! isset($data['totals'])) {
return null;
}
/** @var array<string, list<array{line: int, message: string, identifier: string, ignorable?: bool, tip?: string}>> $errorDetails */
$errorDetails = [];
$totalFileErrors = 0;
/** @var array<string, array{errors: int, messages: list<array{message: string, line: int, identifier?: string, ignorable?: bool, tip?: string}>}> $files */
$files = is_array($data['files'] ?? null) ? $data['files'] : [];
foreach ($files as $file => $fileData) {
foreach ($fileData['messages'] as $message) {
$totalFileErrors++;
$detail = [
'line' => $message['line'],
'message' => $message['message'],
'identifier' => $message['identifier'] ?? 'unknown',
];
if (isset($message['ignorable']) && $message['ignorable'] === false) {
$detail['ignorable'] = false;
}
if (isset($message['tip']) && $message['tip'] !== '') {
$detail['tip'] = $message['tip'];
}
$errorDetails[$file][] = $detail;
}
}
/** @var list<string> $errors */
$errors = is_array($data['errors'] ?? null) ? $data['errors'] : [];
/** @var list<string> $generalErrors */
$generalErrors = array_values(array_filter($errors, static fn (string $error): bool => $error !== ''));
$totalErrors = $totalFileErrors + count($generalErrors);
/** @var array<string, mixed> $result */
$result = [
'result' => $totalErrors > 0 ? 'failed' : 'passed',
'errors' => $totalErrors,
];
if ($errorDetails !== []) {
$verbose = $this->isVerbose();
$limit = 30;
if (! $verbose && $totalFileErrors > $limit) {
$result['error_details'] = $this->truncateGrouped($errorDetails, $limit);
$result['truncated'] = true;
$result['hint'] = 'Pass -v to see all errors.';
} else {
$result['error_details'] = $errorDetails;
}
}
if ($generalErrors !== []) {
$result['general_errors'] = $generalErrors;
}
return $result;
}
/**
* @param array<string, list<array{line: int, message: string, identifier: string, ignorable?: bool, tip?: string}>> $grouped
* @return array<string, list<array{line: int, message: string, identifier: string, ignorable?: bool, tip?: string}>>
*/
private function truncateGrouped(array $grouped, int $limit): array
{
$result = [];
$count = 0;
foreach ($grouped as $file => $errors) {
foreach ($errors as $error) {
if ($count >= $limit) {
break 2;
}
$result[$file][] = $error;
$count++;
}
}
return $result;
}
private function isVerbose(): bool
{
/** @var array<int, string> $argv */
$argv = $_SERVER['argv'] ?? [];
foreach ($argv as $arg) {
if (in_array($arg, ['-v', '-vv', '-vvv', '--verbose'], true)) {
return true;
}
}
return false;
}
/**
* @param array<int, string> $argv
* @return array<int, string>
*/
private function ensureErrorFormatJson(array $argv): array
{
$filtered = [];
$skipNext = false;
foreach ($argv as $arg) {
if ($skipNext) {
$skipNext = false;
continue;
}
if (str_starts_with($arg, '--error-format=')) {
continue;
}
if ($arg === '--error-format') {
$skipNext = true;
continue;
}
$filtered[] = $arg;
}
$filtered[] = '--error-format=json';
return $filtered;
}
/**
* @param array<int, string> $argv
* @return array<int, string>
*/
private function ensureNoProgress(array $argv): array
{
if (! in_array('--no-progress', $argv, true)) {
$argv[] = '--no-progress';
}
return $argv;
}
}