Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions conf/services.neon
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ services:
composerAutoloaderProjectPaths: %composerAutoloaderProjectPaths%
allConfigFiles: %allConfigFiles%
configPhpVersion: %phpVersion%
simpleRelativePathHelper: @simpleRelativePathHelper
autowired: false

# not registered using attributes because there is 2+ instances
Expand Down
6 changes: 0 additions & 6 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -234,12 +234,6 @@ parameters:
count: 1
path: src/DependencyInjection/NeonAdapter.php

-
rawMessage: Creating new ReflectionClass is a runtime reflection concept that might not work in PHPStan because it uses fully static reflection engine. Use objects retrieved from ReflectionProvider instead.
identifier: phpstanApi.runtimeReflection
count: 1
path: src/Diagnose/PHPStanDiagnoseExtension.php

-
rawMessage: 'Parameter #1 $path of function dirname expects string, string|false given.'
identifier: argument.type
Expand Down
5 changes: 4 additions & 1 deletion src/Analyser/Analyser.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function __construct(
/**
* @param string[] $files
* @param Closure(string $file): void|null $preFileCallback
* @param Closure(int, list<string>): void|null $postFileCallback
* @param Closure(int, list<string>=): void|null $postFileCallback
* @param string[]|null $allAnalysedFiles
*/
public function analyse(
Expand Down Expand Up @@ -75,6 +75,7 @@ public function analyse(
$dependencies = [];
$usedTraitDependencies = [];
$exportedNodes = [];
$allProcessedFiles = [];
foreach ($files as $file) {
if ($preFileCallback !== null) {
$preFileCallback($file);
Expand All @@ -92,6 +93,7 @@ public function analyse(
$filteredPhpErrors = array_merge($filteredPhpErrors, $fileAnalyserResult->getFilteredPhpErrors());
$allPhpErrors = array_merge($allPhpErrors, $fileAnalyserResult->getAllPhpErrors());
$processedFiles = $fileAnalyserResult->getProcessedFiles();
$allProcessedFiles = array_merge($allProcessedFiles, $processedFiles);

$locallyIgnoredErrors = array_merge($locallyIgnoredErrors, $fileAnalyserResult->getLocallyIgnoredErrors());
$linesToIgnore[$file] = $fileAnalyserResult->getLinesToIgnore();
Expand Down Expand Up @@ -143,6 +145,7 @@ public function analyse(
exportedNodes: $exportedNodes,
reachedInternalErrorsCountLimit: $reachedInternalErrorsCountLimit,
peakMemoryUsageBytes: memory_get_peak_usage(true),
processedFiles: $allProcessedFiles,
);
}

Expand Down
10 changes: 10 additions & 0 deletions src/Analyser/AnalyserResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ final class AnalyserResult
* @param array<string, array<string>>|null $dependencies
* @param array<string, array<string>>|null $usedTraitDependencies
* @param array<string, array<RootExportedNode>> $exportedNodes
* @param list<string> $processedFiles
*/
public function __construct(
private array $unorderedErrors,
Expand All @@ -43,6 +44,7 @@ public function __construct(
private array $exportedNodes,
private bool $reachedInternalErrorsCountLimit,
private int $peakMemoryUsageBytes,
private array $processedFiles,
)
{
}
Expand Down Expand Up @@ -169,4 +171,12 @@ public function getPeakMemoryUsageBytes(): int
return $this->peakMemoryUsageBytes;
}

/**
* @return list<string>
*/
public function getProcessedFiles(): array
{
return $this->processedFiles;
}

}
3 changes: 3 additions & 0 deletions src/Analyser/AnalyserResultFinalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ public function finalize(AnalyserResult $analyserResult, bool $onlyFiles, bool $
exportedNodes: $analyserResult->getExportedNodes(),
reachedInternalErrorsCountLimit: $analyserResult->hasReachedInternalErrorsCountLimit(),
peakMemoryUsageBytes: $analyserResult->getPeakMemoryUsageBytes(),
processedFiles: $analyserResult->getProcessedFiles(),
), $collectorErrors, $locallyIgnoredCollectorErrors);
}

Expand All @@ -167,6 +168,7 @@ private function mergeFilteredPhpErrors(AnalyserResult $analyserResult): Analyse
exportedNodes: $analyserResult->getExportedNodes(),
reachedInternalErrorsCountLimit: $analyserResult->hasReachedInternalErrorsCountLimit(),
peakMemoryUsageBytes: $analyserResult->getPeakMemoryUsageBytes(),
processedFiles: $analyserResult->getProcessedFiles(),
);
}

Expand Down Expand Up @@ -231,6 +233,7 @@ private function addUnmatchedIgnoredErrors(
exportedNodes: $analyserResult->getExportedNodes(),
reachedInternalErrorsCountLimit: $analyserResult->hasReachedInternalErrorsCountLimit(),
peakMemoryUsageBytes: $analyserResult->getPeakMemoryUsageBytes(),
processedFiles: $analyserResult->getProcessedFiles(),
),
$collectorErrors,
$locallyIgnoredCollectorErrors,
Expand Down
1 change: 1 addition & 0 deletions src/Analyser/ResultCache/ResultCacheManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,7 @@ public function process(AnalyserResult $analyserResult, ResultCache $resultCache
exportedNodes: $exportedNodes,
reachedInternalErrorsCountLimit: $analyserResult->hasReachedInternalErrorsCountLimit(),
peakMemoryUsageBytes: $analyserResult->getPeakMemoryUsageBytes(),
processedFiles: $analyserResult->getProcessedFiles(),
), $saved);
}

Expand Down
7 changes: 7 additions & 0 deletions src/Command/AnalyseApplication.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public function analyse(
$collectedData = [];
$savedResultCache = false;
$memoryUsageBytes = memory_get_peak_usage(true);
$processedFiles = [];
if ($errorOutput->isVeryVerbose()) {
$errorOutput->writeLineFormatted('Result cache was not saved because of ignoredErrorHelperResult errors.');
}
Expand Down Expand Up @@ -121,9 +122,12 @@ public function analyse(
exportedNodes: $intermediateAnalyserResult->getExportedNodes(),
reachedInternalErrorsCountLimit: $intermediateAnalyserResult->hasReachedInternalErrorsCountLimit(),
peakMemoryUsageBytes: $intermediateAnalyserResult->getPeakMemoryUsageBytes(),
processedFiles: $intermediateAnalyserResult->getProcessedFiles(),
);
}

$processedFiles = $intermediateAnalyserResult->getProcessedFiles();

$resultCacheResult = $resultCacheManager->process($intermediateAnalyserResult, $resultCache, $errorOutput, $onlyFiles, true);
$analyserResult = $this->analyserResultFinalizer->finalize(
$this->switchTmpFileInAnalyserResult($resultCacheResult->getAnalyserResult(), $insteadOfFile, $tmpFile),
Expand Down Expand Up @@ -184,6 +188,7 @@ public function analyse(
$memoryUsageBytes,
$isResultCacheUsed,
$changedProjectExtensionFilesOutsideOfAnalysedPaths,
$processedFiles,
);
}

Expand Down Expand Up @@ -239,6 +244,7 @@ private function runAnalyser(
exportedNodes: [],
reachedInternalErrorsCountLimit: false,
peakMemoryUsageBytes: memory_get_peak_usage(true),
processedFiles: [],
);
}

Expand Down Expand Up @@ -346,6 +352,7 @@ private function switchTmpFileInAnalyserResult(
exportedNodes: $exportedNodes,
reachedInternalErrorsCountLimit: $analyserResult->hasReachedInternalErrorsCountLimit(),
peakMemoryUsageBytes: $analyserResult->getPeakMemoryUsageBytes(),
processedFiles: $analyserResult->getProcessedFiles(),
);
}

Expand Down
14 changes: 9 additions & 5 deletions src/Command/AnalyseCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}

if ($generateBaselineFile !== null) {
$this->runDiagnoseExtensions($container, $inceptionResult->getErrorOutput());
$this->runDiagnoseExtensions($container, $inceptionResult->getErrorOutput(), $analysisResult->getProcessedFiles());
if (count($internalErrorsTuples) > 0) {
foreach ($internalErrorsTuples as [$internalError]) {
$inceptionResult->getStdOutput()->writeLineFormatted($internalError->getMessage());
Expand Down Expand Up @@ -493,11 +493,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$analysisResult->getPeakMemoryUsageBytes(),
$analysisResult->isResultCacheUsed(),
$analysisResult->getChangedProjectExtensionFilesOutsideOfAnalysedPaths(),
$analysisResult->getProcessedFiles(),
);

$exitCode = $errorFormatter->formatErrors($analysisResult, $inceptionResult->getStdOutput());

$this->runDiagnoseExtensions($container, $inceptionResult->getErrorOutput());
$this->runDiagnoseExtensions($container, $inceptionResult->getErrorOutput(), $analysisResult->getProcessedFiles());

$errorOutput->writeLineFormatted('⚠️ Result is incomplete because of severe errors. ⚠️');
$errorOutput->writeLineFormatted(' Fix these errors first and then re-run PHPStan');
Expand Down Expand Up @@ -649,7 +650,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
}

$this->runDiagnoseExtensions($container, $inceptionResult->getErrorOutput());
$this->runDiagnoseExtensions($container, $inceptionResult->getErrorOutput(), $analysisResult->getProcessedFiles());

return $inceptionResult->handleReturn(
$exitCode,
Expand Down Expand Up @@ -847,7 +848,10 @@ private function runFixer(InceptionResult $inceptionResult, Container $container
);
}

private function runDiagnoseExtensions(Container $container, Output $errorOutput): void
/**
* @param list<string> $processedFiles
*/
private function runDiagnoseExtensions(Container $container, Output $errorOutput, array $processedFiles = []): void
{
if (!$errorOutput->isDebug()) {
return;
Expand All @@ -857,7 +861,7 @@ private function runDiagnoseExtensions(Container $container, Output $errorOutput
$phpstanDiagnoseExtension = $container->getService('phpstanDiagnoseExtension');

// not using tag for this extension to make sure it's always first
$phpstanDiagnoseExtension->print($errorOutput);
$phpstanDiagnoseExtension->print($errorOutput, $processedFiles);

/** @var DiagnoseExtension $extension */
foreach ($container->getServicesByTag(DiagnoseExtension::EXTENSION_TAG) as $extension) {
Expand Down
1 change: 1 addition & 0 deletions src/Command/AnalyserRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public function runAnalyser(
exportedNodes: [],
reachedInternalErrorsCountLimit: false,
peakMemoryUsageBytes: memory_get_peak_usage(true),
processedFiles: [],
);
}

Expand Down
11 changes: 11 additions & 0 deletions src/Command/AnalysisResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ final class AnalysisResult
* @param list<string> $warnings
* @param list<CollectedData> $collectedData
* @param array<string, string> $changedProjectExtensionFilesOutsideOfAnalysedPaths
* @param list<string> $processedFiles
*/
public function __construct(
array $fileSpecificErrors,
Expand All @@ -37,6 +38,7 @@ public function __construct(
private int $peakMemoryUsageBytes,
private bool $isResultCacheUsed,
private array $changedProjectExtensionFilesOutsideOfAnalysedPaths,
private array $processedFiles = [],
)
{
usort(
Expand Down Expand Up @@ -148,6 +150,14 @@ public function getChangedProjectExtensionFilesOutsideOfAnalysedPaths(): array
return $this->changedProjectExtensionFilesOutsideOfAnalysedPaths;
}

/**
* @return list<string>
*/
public function getProcessedFiles(): array
{
return $this->processedFiles;
}

/**
* @api
* @param list<Error> $fileSpecificErrors
Expand All @@ -166,6 +176,7 @@ public function withFileSpecificErrors(array $fileSpecificErrors): self
$this->peakMemoryUsageBytes,
$this->isResultCacheUsed,
$this->changedProjectExtensionFilesOutsideOfAnalysedPaths,
$this->processedFiles,
);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Command/DiagnoseCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$phpstanDiagnoseExtension = $container->getService('phpstanDiagnoseExtension');

// not using tag for this extension to make sure it's always first
$phpstanDiagnoseExtension->print($output);
$phpstanDiagnoseExtension->print($output, []);

/** @var DiagnoseExtension $extension */
foreach ($container->getServicesByTag(DiagnoseExtension::EXTENSION_TAG) as $extension) {
Expand Down
1 change: 1 addition & 0 deletions src/Command/FixerWorkerCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ private function runAnalyser(LoopInterface $loop, Container $container, array $f
exportedNodes: [],
reachedInternalErrorsCountLimit: false,
peakMemoryUsageBytes: memory_get_peak_usage(true),
processedFiles: [],
));
}

Expand Down
3 changes: 3 additions & 0 deletions src/Command/WorkerCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ private function runWorker(
$dependencies = [];
$usedTraitDependencies = [];
$exportedNodes = [];
$processedFiles = [];
foreach ($files as $file) {
try {
if ($file === $insteadOfFile) {
Expand All @@ -242,6 +243,7 @@ private function runWorker(
$dependencies[$file] = $fileAnalyserResult->getDependencies();
$usedTraitDependencies[$file] = $fileAnalyserResult->getUsedTraitDependencies();
$exportedNodes[$file] = $fileAnalyserResult->getExportedNodes();
$processedFiles = array_merge($processedFiles, $fileAnalyserResult->getProcessedFiles());
foreach ($fileErrors as $fileError) {
$errors[] = $fileError;
}
Expand Down Expand Up @@ -283,6 +285,7 @@ private function runWorker(
'usedTraitDependencies' => $usedTraitDependencies,
'exportedNodes' => $exportedNodes,
'files' => $files,
'processedFiles' => $processedFiles,
'internalErrorsCount' => $internalErrorsCount,
]]);
});
Expand Down
50 changes: 48 additions & 2 deletions src/Diagnose/PHPStanDiagnoseExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@
use PHPStan\Command\Output;
use PHPStan\ExtensionInstaller\GeneratedConfig;
use PHPStan\File\FileHelper;
use PHPStan\File\RelativePathHelper;
use PHPStan\Internal\ComposerHelper;
use PHPStan\Php\ComposerPhpVersionFactory;
use PHPStan\Php\PhpVersion;
use ReflectionClass;
use function array_count_values;
use function array_key_exists;
use function array_slice;
use function arsort;
use function class_exists;
use function count;
use function dirname;
Expand All @@ -27,7 +30,7 @@
use function substr;
use const PHP_VERSION_ID;

final class PHPStanDiagnoseExtension implements DiagnoseExtension
final class PHPStanDiagnoseExtension
{

/**
Expand All @@ -42,11 +45,15 @@ public function __construct(
private array $composerAutoloaderProjectPaths,
private array $allConfigFiles,
private ComposerPhpVersionFactory $composerPhpVersionFactory,
private RelativePathHelper $simpleRelativePathHelper,
)
{
}

public function print(Output $output): void
/**
* @param list<string> $processedFiles
*/
public function print(Output $output, array $processedFiles): void
{
$phpRuntimeVersion = new PhpVersion(PHP_VERSION_ID);
$output->writeLineFormatted(sprintf(
Expand Down Expand Up @@ -203,6 +210,45 @@ public function print(Output $output): void
$output->writeLineFormatted($composerAutoloaderProjectPath);
}
$output->writeLineFormatted('');

$topFiles = $this->getTopMostAnalysedFiles($processedFiles, 5);
if (count($topFiles) <= 0) {
return;
}

$output->writeLineFormatted('<info>Most often analysed files:</info>');
foreach ($topFiles as $file => $count) {
$output->writeLineFormatted(sprintf(
' %s: %d times',
$this->simpleRelativePathHelper->getRelativePath($file),
$count,
));
}
$output->writeLineFormatted('');
}

/**
* @param list<string> $processedFiles
* @return array<string, int<2, max>>
*/
private function getTopMostAnalysedFiles(array $processedFiles, int $limit): array
{
if ($processedFiles === []) {
return [];
}

$counts = array_count_values($processedFiles);
arsort($counts);

$result = [];
foreach (array_slice($counts, 0, $limit, true) as $file => $count) {
if ($count <= 1) {
continue;
}
$result[$file] = $count;
}

return $result;
}

}
Loading
Loading