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
27 changes: 23 additions & 4 deletions src/Caching/Detector/ChangedFilesDetector.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ final class ChangedFilesDetector
// scopes the per-file cache key to the active --only / --only-suffix / --filter selection (empty = full run)
private string $scopeSuffix = '';

private ?string $configurationCacheKey = null;

/**
* @var array{hash: string, rules: string[], sets: string[], skip: string[]}|null
*/
private ?array $configurationSnapshot = null;

public function __construct(
private readonly FileHashComputer $fileHashComputer,
private readonly Cache $cache,
Expand Down Expand Up @@ -95,6 +102,15 @@ public function invalidateFile(string $filePath): void
public function clear(): void
{
$this->cache->clear();

// keep the snapshot of the current run, so the next run can still detect a configuration change
if ($this->configurationCacheKey !== null && $this->configurationSnapshot !== null) {
$this->cache->save(
$this->configurationCacheKey,
CacheKey::CONFIGURATION_HASH_KEY,
$this->configurationSnapshot
);
}
}

public function setFirstResolvedConfigFileInfo(string $filePath): void
Expand Down Expand Up @@ -141,18 +157,21 @@ private function createConfigurationSnapshot(string $filePath): array
private function storeConfigurationDataHash(string $filePath, array $configurationSnapshot): void
{
$key = CacheKey::CONFIGURATION_HASH_KEY . '_' . $this->getFilePathCacheKey($filePath);
$this->invalidateCacheIfConfigurationChanged($key, $configurationSnapshot);
$oldCachedValue = $this->cache->load($key, CacheKey::CONFIGURATION_HASH_KEY);

$this->configurationCacheKey = $key;
$this->configurationSnapshot = $configurationSnapshot;

$this->invalidateCacheIfConfigurationChanged($oldCachedValue, $configurationSnapshot);

$this->cache->save($key, CacheKey::CONFIGURATION_HASH_KEY, $configurationSnapshot);
}

/**
* @param array{hash: string, rules: string[], sets: string[], skip: string[]} $configurationSnapshot
*/
private function invalidateCacheIfConfigurationChanged(string $key, array $configurationSnapshot): void
private function invalidateCacheIfConfigurationChanged(mixed $oldCachedValue, array $configurationSnapshot): void
{
$oldCachedValue = $this->cache->load($key, CacheKey::CONFIGURATION_HASH_KEY);

// first run, nothing to compare against
if ($oldCachedValue === null) {
return;
Expand Down
20 changes: 20 additions & 0 deletions tests/Caching/Detector/ChangedFilesDetectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,26 @@ public function testCacheClearedWhenSkipRemoved(): void
$this->assertTrue($this->changedFilesDetector->hasFileChanged($filePath));
}

public function testCacheClearedWhenSkipRemovedAfterClearCacheRun(): void
{
SimpleParameterProvider::setParameter(Option::SKIP, ['Rector\\RuleA']);
$this->applyRules(['Rector\\RuleA', 'Rector\\RuleB']);

// --clear-cache wipes the cache after the configuration snapshot was stored
$this->changedFilesDetector->clear();

$filePath = __DIR__ . '/Source/file.php';
$this->changedFilesDetector->addCacheableFile($filePath);
$this->changedFilesDetector->cacheFile($filePath);
$this->assertFalse($this->changedFilesDetector->hasFileChanged($filePath));

// the next run removes the skip, the snapshot of the cleared run must still be there to compare
SimpleParameterProvider::setParameter(Option::SKIP, []);
$this->snapshotConfiguration();

$this->assertTrue($this->changedFilesDetector->hasFileChanged($filePath));
}

public function testCacheClearedWhenOutputAffectingParameterChanged(): void
{
SimpleParameterProvider::setParameter(Option::FILE_EXTENSIONS, ['php']);
Expand Down
Loading