/**
* @param string[] $files
* @param \Closure|null $progressCallback
* @return string[]|\PHPStan\Analyser\Error[] errors
*/
public function analyse(array $files, \Closure $progressCallback = null) : array
{
$errors = [];
if ($this->bootstrapFile !== null) {
if (!is_file($this->bootstrapFile)) {
return [sprintf('Bootstrap file %s does not exist.', $this->bootstrapFile)];
}
try {
require_once $this->bootstrapFile;
} catch (\Throwable $e) {
return [$e->getMessage()];
}
}
foreach ($this->ignoreErrors as $ignoreError) {
try {
\Nette\Utils\Strings::match('', $ignoreError);
} catch (\Nette\Utils\RegexpException $e) {
$errors[] = $e->getMessage();
}
}
if (count($errors) > 0) {
return $errors;
}
foreach ($files as $file) {
try {
if ($this->isExcludedFromAnalysing($file)) {
if ($progressCallback !== null) {
$progressCallback($file);
}
continue;
}
$fileErrors = [];
$this->nodeScopeResolver->processNodes($this->parser->parseFile($file), new Scope($this->broker, $this->printer, $file), function (\PhpParser\Node $node, Scope $scope) use(&$fileErrors) {
if ($node instanceof \PhpParser\Node\Stmt\Trait_) {
return;
}
$classes = array_merge([get_class($node)], class_parents($node));
foreach ($this->registry->getRules($classes) as $rule) {
$ruleErrors = $this->createErrors($node, $scope->getAnalysedContextFile(), $rule->processNode($node, $scope));
$fileErrors = array_merge($fileErrors, $ruleErrors);
}
});
if ($progressCallback !== null) {
$progressCallback($file);
}
$errors = array_merge($errors, $fileErrors);
} catch (\PhpParser\Error $e) {
$errors[] = new Error($e->getMessage(), $file);
} catch (\PHPStan\AnalysedCodeException $e) {
$errors[] = new Error($e->getMessage(), $file);
} catch (\Throwable $t) {
$errors[] = new Error(sprintf('Internal error: %s', $t->getMessage()), $file);
}
}
$unmatchedIgnoredErrors = $this->ignoreErrors;
$errors = array_values(array_filter($errors, function (string $error) use(&$unmatchedIgnoredErrors) : bool {
foreach ($this->ignoreErrors as $i => $ignore) {
if (\Nette\Utils\Strings::match($error, $ignore) !== null) {
unset($unmatchedIgnoredErrors[$i]);
return false;
}
}
return true;
}));
foreach ($unmatchedIgnoredErrors as $unmatchedIgnoredError) {
$errors[] = sprintf('Ignored error pattern %s was not matched in reported errors.', $unmatchedIgnoredError);
}
return $errors;
}