Skip to content
Open
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
14 changes: 14 additions & 0 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,20 @@ jobs:
composer install
../../bin/phpstan -vvv
../../bin/phpstan -vvv
- script: |
cd e2e/result-cache-ci-notification
# https://github.com/phpstan/phpstan/issues/14881
# A slow full analysis in CI without a pre-existing result cache should hint
# the user to persist the result cache directory between runs.
../../bin/phpstan clear-result-cache
OUTPUT=$(../../bin/phpstan analyse 2>&1)
echo "$OUTPUT"
../bashunit -a contains 'Persist PHPStan' "$OUTPUT"
../bashunit -a contains 'to make your pipeline dramatically faster' "$OUTPUT"
# The second run finds the cache present, so the hint is not shown again.
OUTPUT=$(../../bin/phpstan analyse 2>&1)
echo "$OUTPUT"
../bashunit -a not_contains 'to make your pipeline dramatically faster' "$OUTPUT"
- script: |
cd e2e/bug-12606
export CONFIGTEST=test
Expand Down
1 change: 1 addition & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ parameters:
resultCachePath: %tmpDir%/resultCache.php
resultCacheSkipIfOlderThanDays: 7
resultCacheChecksProjectExtensionFilesDependencies: false
resultCacheCiNotificationSeconds: 60
dynamicConstantNames:
- ICONV_IMPL
- LIBXML_VERSION
Expand Down
1 change: 1 addition & 0 deletions conf/parametersSchema.neon
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ parametersSchema:
resultCachePath: string()
resultCacheSkipIfOlderThanDays: int()
resultCacheChecksProjectExtensionFilesDependencies: bool()
resultCacheCiNotificationSeconds: int()
dynamicConstantNames: arrayOf(string())
customRulesetUsed: schema(bool(), nullable())
rootDir: string()
Expand Down
6 changes: 6 additions & 0 deletions e2e/result-cache-ci-notification/phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
parameters:
level: 0
paths:
- src
# Notify already after 0 seconds so the slow-analysis condition is always met in the test.
resultCacheCiNotificationSeconds: 0
13 changes: 13 additions & 0 deletions e2e/result-cache-ci-notification/src/Foo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php declare(strict_types = 1);

namespace ResultCacheCiNotificationE2E;

class Foo
{

public function bar(): int
{
return 1;
}

}
9 changes: 9 additions & 0 deletions src/Analyser/ResultCache/ResultCacheManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,15 @@ public function __construct(
{
}

/**
* Whether the result cache file was present on disk when PHPStan started.
* Distinguishes "the cache never existed" from "the cache existed but was invalid".
*/
public function resultCacheExists(): bool
{
return is_file($this->cacheFilePath);
}

/**
* @param string[] $allAnalysedFiles
* @param mixed[]|null $projectConfigArray
Expand Down
2 changes: 2 additions & 0 deletions src/Command/AnalyseApplication.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public function analyse(
$fileReplacements = [$insteadOfFile => $tmpFile];
}
$resultCacheManager = $this->resultCacheManagerFactory->create($fileReplacements);
$resultCacheExisted = $resultCacheManager->resultCacheExists();

$ignoredErrorHelperResult = $this->ignoredErrorHelper->initialize();
$fileSpecificErrors = [];
Expand Down Expand Up @@ -190,6 +191,7 @@ public function analyse(
$isResultCacheUsed,
$changedProjectExtensionFilesOutsideOfAnalysedPaths,
$processedFiles,
$resultCacheExisted,
);
}

Expand Down
34 changes: 34 additions & 0 deletions src/Command/AnalyseCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
use function is_bool;
use function is_file;
use function is_string;
use function microtime;
use function pathinfo;
use function rewind;
use function sprintf;
Expand Down Expand Up @@ -494,6 +495,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$analysisResult->isResultCacheUsed(),
$analysisResult->getChangedProjectExtensionFilesOutsideOfAnalysedPaths(),
$analysisResult->getProcessedFiles(),
$analysisResult->resultCacheExisted(),
);

$exitCode = $errorFormatter->formatErrors($analysisResult, $inceptionResult->getStdOutput());
Expand Down Expand Up @@ -650,6 +652,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
}

$this->reportMissingResultCacheInCi(
$errorOutput,
$analysisResult,
$onlyFiles,
$container->getParameter('resultCacheCiNotificationSeconds'),
);

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

return $inceptionResult->handleReturn(
Expand All @@ -659,6 +668,31 @@ protected function execute(InputInterface $input, OutputInterface $output): int
);
}

private function reportMissingResultCacheInCi(Output $errorOutput, AnalysisResult $analysisResult, bool $onlyFiles, int $resultCacheCiNotificationSeconds): void
{
if (
$onlyFiles
|| $analysisResult->isResultCacheUsed()
|| $analysisResult->resultCacheExisted()
) {
return;
}

if (microtime(true) - $this->analysisStartTime < $resultCacheCiNotificationSeconds) {
return;
}

if (!(new CiDetector())->isCiDetected()) {
return;
}

$errorOutput->writeLineFormatted('<comment>Tip: This CI run analysed your whole project from scratch, which is why it was slow.</comment>');
$errorOutput->writeLineFormatted('Persist PHPStan\'s result cache directory between CI runs to make your pipeline dramatically faster.');
$errorOutput->writeLineFormatted('Only changed files and their dependencies are re-analysed, so most runs finish in a fraction of the time.');
$errorOutput->writeLineFormatted('Learn how to set it up: https://phpstan.org/user-guide/result-cache');
$errorOutput->writeLineFormatted('');
}

private function createStreamOutput(): StreamOutput
{
$resource = fopen('php://memory', 'w', false);
Expand Down
12 changes: 12 additions & 0 deletions src/Command/AnalysisResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public function __construct(
private bool $isResultCacheUsed,
private array $changedProjectExtensionFilesOutsideOfAnalysedPaths,
private array $processedFiles = [],
private bool $resultCacheExisted = true,
)
{
usort(
Expand Down Expand Up @@ -142,6 +143,16 @@ public function isResultCacheUsed(): bool
return $this->isResultCacheUsed;
}

/**
* Whether the result cache file existed when PHPStan started.
* False means the cache was never created; a stale/invalid cache that
* triggered a full re-analysis still counts as existing.
*/
public function resultCacheExisted(): bool
{
return $this->resultCacheExisted;
}

/**
* @return array<string, string>
*/
Expand Down Expand Up @@ -177,6 +188,7 @@ public function withFileSpecificErrors(array $fileSpecificErrors): self
$this->isResultCacheUsed,
$this->changedProjectExtensionFilesOutsideOfAnalysedPaths,
$this->processedFiles,
$this->resultCacheExisted,
);
}

Expand Down
Loading