From 351f48e364e294860eb0d037b354a3a000055025 Mon Sep 17 00:00:00 2001 From: Michele Orselli Date: Sun, 29 Mar 2026 21:05:46 +0200 Subject: [PATCH 1/6] Introduce FilesToParse and ParsedFiles to separate parsing from rule application Runner::check now explicitly builds a FilesToParse collection from the ClassSet, iterates it to produce a ParsedFiles structure, then applies rules to the parsed class descriptions. The two data structures act as cache points between pipeline stages. Co-Authored-By: Claude Sonnet 4.6 --- src/Analyzer/FilesToParse.php | 26 ++++++++++++++++++++++++ src/Analyzer/ParsedFiles.php | 37 +++++++++++++++++++++++++++++++++++ src/CLI/Runner.php | 33 +++++++++++++++++++------------ 3 files changed, 83 insertions(+), 13 deletions(-) create mode 100644 src/Analyzer/FilesToParse.php create mode 100644 src/Analyzer/ParsedFiles.php diff --git a/src/Analyzer/FilesToParse.php b/src/Analyzer/FilesToParse.php new file mode 100644 index 00000000..34b0fdd3 --- /dev/null +++ b/src/Analyzer/FilesToParse.php @@ -0,0 +1,26 @@ + + */ +class FilesToParse implements \IteratorAggregate +{ + /** @var array */ + private array $files = []; + + public function add(SplFileInfo $file): void + { + $this->files[] = $file; + } + + public function getIterator(): \Traversable + { + return new \ArrayIterator($this->files); + } +} diff --git a/src/Analyzer/ParsedFiles.php b/src/Analyzer/ParsedFiles.php new file mode 100644 index 00000000..2ce15e57 --- /dev/null +++ b/src/Analyzer/ParsedFiles.php @@ -0,0 +1,37 @@ +classDescriptions = new ClassDescriptions(); + $this->parsingErrors = new ParsingErrors(); + } + + public function add(ParserResult $result): void + { + foreach ($result->classDescriptions() as $classDescription) { + $this->classDescriptions[] = $classDescription; + } + + $this->parsingErrors->merge($result->parsingErrors()); + } + + public function classDescriptions(): ClassDescriptions + { + return $this->classDescriptions; + } + + public function parsingErrors(): ParsingErrors + { + return $this->parsingErrors; + } +} diff --git a/src/CLI/Runner.php b/src/CLI/Runner.php index 910f0ca7..b14a66f5 100644 --- a/src/CLI/Runner.php +++ b/src/CLI/Runner.php @@ -6,6 +6,8 @@ use Arkitect\Analyzer\ClassDescription; use Arkitect\Analyzer\FileParserFactory; +use Arkitect\Analyzer\FilesToParse; +use Arkitect\Analyzer\ParsedFiles; use Arkitect\Analyzer\Parser; use Arkitect\Analyzer\ParsingErrors; use Arkitect\ClassSetRules; @@ -46,32 +48,37 @@ public function check( ParsingErrors $parsingErrors, bool $stopOnFailure, ): void { + $filesToParse = new FilesToParse(); /** @var SplFileInfo $file */ foreach ($classSetRule->getClassSet() as $file) { - $fileViolations = new Violations(); + $filesToParse->add($file); + } + $parsedFiles = new ParsedFiles(); + /** @var SplFileInfo $file */ + foreach ($filesToParse as $file) { $progress->startParsingFile($file->getRelativePathname()); + $parsedFiles->add($fileParser->parse($file->getContents(), $file->getRelativePathname())); + $progress->endParsingFile($file->getRelativePathname()); + } - $result = $fileParser->parse($file->getContents(), $file->getRelativePathname()); + $parsingErrors->merge($parsedFiles->parsingErrors()); - $parsingErrors->merge($result->parsingErrors()); + /** @var ClassDescription $classDescription */ + foreach ($parsedFiles->classDescriptions() as $classDescription) { + $fileViolations = new Violations(); - /** @var ClassDescription $classDescription */ - foreach ($result->classDescriptions() as $classDescription) { - foreach ($classSetRule->getRules() as $rule) { - $rule->check($classDescription, $fileViolations); + foreach ($classSetRule->getRules() as $rule) { + $rule->check($classDescription, $fileViolations); - if ($stopOnFailure && $fileViolations->count() > 0) { - $violations->merge($fileViolations); + if ($stopOnFailure && $fileViolations->count() > 0) { + $violations->merge($fileViolations); - throw new FailOnFirstViolationException(); - } + throw new FailOnFirstViolationException(); } } $violations->merge($fileViolations); - - $progress->endParsingFile($file->getRelativePathname()); } } From 425d4b38546186a01b2dd8c7ac04bbed8051d5a0 Mon Sep 17 00:00:00 2001 From: Michele Orselli Date: Sun, 29 Mar 2026 21:23:06 +0200 Subject: [PATCH 2/6] Add file-based parse result cache to skip re-parsing unchanged files Introduces ParseResultCache interface and FileParseResultCache which persists parse results to disk keyed by (filename, md5(content)). Runner accepts an optional cache via constructor; when provided, files whose content hash matches the cached entry are not re-parsed. The cache flushes to disk on destruction only when entries were written. Co-Authored-By: Claude Sonnet 4.6 --- src/Analyzer/FileParseResultCache.php | 53 +++++++++++++++++++++++++++ src/Analyzer/ParseResultCache.php | 12 ++++++ src/CLI/Runner.php | 25 ++++++++++++- 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/Analyzer/FileParseResultCache.php create mode 100644 src/Analyzer/ParseResultCache.php diff --git a/src/Analyzer/FileParseResultCache.php b/src/Analyzer/FileParseResultCache.php new file mode 100644 index 00000000..29bafd08 --- /dev/null +++ b/src/Analyzer/FileParseResultCache.php @@ -0,0 +1,53 @@ + */ + private array $entries = []; + + private bool $dirty = false; + + private string $filePath; + + public function __construct(string $filePath) + { + $this->filePath = $filePath; + + if (file_exists($filePath)) { + $data = unserialize((string) file_get_contents($filePath)); + if (is_array($data)) { + $this->entries = $data; + } + } + } + + public function get(string $filename, string $contentHash): ?ParserResult + { + if (!isset($this->entries[$filename])) { + return null; + } + + if ($this->entries[$filename]['hash'] !== $contentHash) { + return null; + } + + return $this->entries[$filename]['result']; + } + + public function set(string $filename, string $contentHash, ParserResult $result): void + { + $this->entries[$filename] = ['hash' => $contentHash, 'result' => $result]; + $this->dirty = true; + } + + public function __destruct() + { + if ($this->dirty) { + file_put_contents($this->filePath, serialize($this->entries)); + } + } +} diff --git a/src/Analyzer/ParseResultCache.php b/src/Analyzer/ParseResultCache.php new file mode 100644 index 00000000..ffd205eb --- /dev/null +++ b/src/Analyzer/ParseResultCache.php @@ -0,0 +1,12 @@ +cache = $cache; + } public function run(Config $config, Baseline $baseline, Progress $progress): AnalysisResult { [$violations, $parsingErrors] = $this->doRun($config, $progress); @@ -58,7 +65,23 @@ public function check( /** @var SplFileInfo $file */ foreach ($filesToParse as $file) { $progress->startParsingFile($file->getRelativePathname()); - $parsedFiles->add($fileParser->parse($file->getContents(), $file->getRelativePathname())); + + $filename = $file->getRelativePathname(); + $contents = $file->getContents(); + $contentHash = md5($contents); + + $cached = $this->cache !== null ? $this->cache->get($filename, $contentHash) : null; + + if ($cached !== null) { + $parsedFiles->add($cached); + } else { + $result = $fileParser->parse($contents, $filename); + if ($this->cache !== null) { + $this->cache->set($filename, $contentHash, $result); + } + $parsedFiles->add($result); + } + $progress->endParsingFile($file->getRelativePathname()); } From 55dc303785669bccf01502cadee3ced71b12cef0 Mon Sep 17 00:00:00 2001 From: Michele Orselli Date: Sun, 29 Mar 2026 22:56:43 +0200 Subject: [PATCH 3/6] wip --- src/Analyzer/FileParseResultCache.php | 16 ++++----- src/Analyzer/ParsedFiles.php | 27 +++------------ src/CLI/Runner.php | 50 +++++++++++---------------- 3 files changed, 34 insertions(+), 59 deletions(-) diff --git a/src/Analyzer/FileParseResultCache.php b/src/Analyzer/FileParseResultCache.php index 29bafd08..bb87ebea 100644 --- a/src/Analyzer/FileParseResultCache.php +++ b/src/Analyzer/FileParseResultCache.php @@ -19,12 +19,19 @@ public function __construct(string $filePath) if (file_exists($filePath)) { $data = unserialize((string) file_get_contents($filePath)); - if (is_array($data)) { + if (\is_array($data)) { $this->entries = $data; } } } + public function __destruct() + { + if ($this->dirty) { + file_put_contents($this->filePath, serialize($this->entries)); + } + } + public function get(string $filename, string $contentHash): ?ParserResult { if (!isset($this->entries[$filename])) { @@ -43,11 +50,4 @@ public function set(string $filename, string $contentHash, ParserResult $result) $this->entries[$filename] = ['hash' => $contentHash, 'result' => $result]; $this->dirty = true; } - - public function __destruct() - { - if ($this->dirty) { - file_put_contents($this->filePath, serialize($this->entries)); - } - } } diff --git a/src/Analyzer/ParsedFiles.php b/src/Analyzer/ParsedFiles.php index 2ce15e57..abf96d18 100644 --- a/src/Analyzer/ParsedFiles.php +++ b/src/Analyzer/ParsedFiles.php @@ -6,32 +6,15 @@ class ParsedFiles { - private ClassDescriptions $classDescriptions; + private array $data = []; - private ParsingErrors $parsingErrors; - - public function __construct() - { - $this->classDescriptions = new ClassDescriptions(); - $this->parsingErrors = new ParsingErrors(); - } - - public function add(ParserResult $result): void - { - foreach ($result->classDescriptions() as $classDescription) { - $this->classDescriptions[] = $classDescription; - } - - $this->parsingErrors->merge($result->parsingErrors()); - } - - public function classDescriptions(): ClassDescriptions + public function add(string $relativeFilePath, ParserResult $result): void { - return $this->classDescriptions; + $this->data[$relativeFilePath] = $result; } - public function parsingErrors(): ParsingErrors + public function get(string $relativeFilePath): ?ParserResult { - return $this->parsingErrors; + return $this->data[$relativeFilePath] ?? null; } } diff --git a/src/CLI/Runner.php b/src/CLI/Runner.php index ea4b6076..7acce529 100644 --- a/src/CLI/Runner.php +++ b/src/CLI/Runner.php @@ -8,7 +8,6 @@ use Arkitect\Analyzer\FileParserFactory; use Arkitect\Analyzer\FilesToParse; use Arkitect\Analyzer\ParsedFiles; -use Arkitect\Analyzer\ParseResultCache; use Arkitect\Analyzer\Parser; use Arkitect\Analyzer\ParsingErrors; use Arkitect\ClassSetRules; @@ -19,12 +18,6 @@ class Runner { - private ?ParseResultCache $cache; - - public function __construct(?ParseResultCache $cache = null) - { - $this->cache = $cache; - } public function run(Config $config, Baseline $baseline, Progress $progress): AnalysisResult { [$violations, $parsingErrors] = $this->doRun($config, $progress); @@ -55,49 +48,48 @@ public function check( ParsingErrors $parsingErrors, bool $stopOnFailure, ): void { + // first steo: collect all files to parse $filesToParse = new FilesToParse(); /** @var SplFileInfo $file */ foreach ($classSetRule->getClassSet() as $file) { $filesToParse->add($file); } + // second step: parse all files and collect results $parsedFiles = new ParsedFiles(); /** @var SplFileInfo $file */ foreach ($filesToParse as $file) { $progress->startParsingFile($file->getRelativePathname()); - $filename = $file->getRelativePathname(); - $contents = $file->getContents(); - $contentHash = md5($contents); + $result = $fileParser->parse($file->getContents(), $file->getRelativePathname()); - $cached = $this->cache !== null ? $this->cache->get($filename, $contentHash) : null; - - if ($cached !== null) { - $parsedFiles->add($cached); - } else { - $result = $fileParser->parse($contents, $filename); - if ($this->cache !== null) { - $this->cache->set($filename, $contentHash, $result); - } - $parsedFiles->add($result); - } + $parsedFiles->add($file->getRelativePathname(), $result); $progress->endParsingFile($file->getRelativePathname()); } - $parsingErrors->merge($parsedFiles->parsingErrors()); + /** @var SplFileInfo $file */ + foreach ($classSetRule->getClassSet() as $file) { + $result = $parsedFiles->get($file->getRelativePathname()); + + if (null === $result) { + continue; // this should not happen + } + + $parsingErrors->merge($result->parsingErrors()); - /** @var ClassDescription $classDescription */ - foreach ($parsedFiles->classDescriptions() as $classDescription) { $fileViolations = new Violations(); - foreach ($classSetRule->getRules() as $rule) { - $rule->check($classDescription, $fileViolations); + /** @var ClassDescription $classDescription */ + foreach ($result->classDescriptions() as $classDescription) { + foreach ($classSetRule->getRules() as $rule) { + $rule->check($classDescription, $fileViolations); - if ($stopOnFailure && $fileViolations->count() > 0) { - $violations->merge($fileViolations); + if ($stopOnFailure && $fileViolations->count() > 0) { + $violations->merge($fileViolations); - throw new FailOnFirstViolationException(); + throw new FailOnFirstViolationException(); + } } } From f3c5a9645f94740c4f26e4ac53538fd6ee0df794 Mon Sep 17 00:00:00 2001 From: Michele Orselli Date: Sun, 29 Mar 2026 23:23:03 +0200 Subject: [PATCH 4/6] wip --- src/CLI/Runner.php | 76 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 20 deletions(-) diff --git a/src/CLI/Runner.php b/src/CLI/Runner.php index 7acce529..7e3b196e 100644 --- a/src/CLI/Runner.php +++ b/src/CLI/Runner.php @@ -48,26 +48,15 @@ public function check( ParsingErrors $parsingErrors, bool $stopOnFailure, ): void { - // first steo: collect all files to parse - $filesToParse = new FilesToParse(); - /** @var SplFileInfo $file */ - foreach ($classSetRule->getClassSet() as $file) { - $filesToParse->add($file); - } - - // second step: parse all files and collect results - $parsedFiles = new ParsedFiles(); - /** @var SplFileInfo $file */ - foreach ($filesToParse as $file) { - $progress->startParsingFile($file->getRelativePathname()); - - $result = $fileParser->parse($file->getContents(), $file->getRelativePathname()); - - $parsedFiles->add($file->getRelativePathname(), $result); - - $progress->endParsingFile($file->getRelativePathname()); - } + } + public function checkRulesOnParsedFiles( + ClassSetRules $classSetRule, + ParsedFiles $parsedFiles, + Violations $violations, + ParsingErrors $parsingErrors, + bool $stopOnFailure, + ): void { /** @var SplFileInfo $file */ foreach ($classSetRule->getClassSet() as $file) { $result = $parsedFiles->get($file->getRelativePathname()); @@ -97,6 +86,36 @@ public function check( } } + protected function collectFilesToParse(ClassSetRules $classSetRule): FilesToParse + { + $filesToParse = new FilesToParse(); + + /** @var SplFileInfo $file */ + foreach ($classSetRule->getClassSet() as $file) { + $filesToParse->add($file); + } + + return $filesToParse; + } + + protected function collectParsedFiles(FilesToParse $filesToParse, Parser $fileParser, Progress $progress): ParsedFiles + { + $parsedFiles = new ParsedFiles(); + + /** @var SplFileInfo $file */ + foreach ($filesToParse as $file) { + $progress->startParsingFile($file->getRelativePathname()); + + $result = $fileParser->parse($file->getContents(), $file->getRelativePathname()); + + $parsedFiles->add($file->getRelativePathname(), $result); + + $progress->endParsingFile($file->getRelativePathname()); + } + + return $parsedFiles; + } + protected function doRun(Config $config, Progress $progress): array { $violations = new Violations(); @@ -112,7 +131,24 @@ protected function doRun(Config $config, Progress $progress): array $progress->startFileSetAnalysis($classSetRule->getClassSet()); try { - $this->check($classSetRule, $progress, $fileParser, $violations, $parsingErrors, $config->isStopOnFailure()); + // first step: collect all files to parse + $filesToParse = $this->collectFilesToParse($classSetRule); + + // second step: parse all files and collect results + $parsedFiles = $this->collectParsedFiles( + $filesToParse, + $fileParser, + $progress + ); + + // third step: check all rules on all files + $this->checkRulesOnParsedFiles( + $classSetRule, + $parsedFiles, + $violations, + $parsingErrors, + $config->isStopOnFailure() + ); } catch (FailOnFirstViolationException $e) { break; } finally { From 47cbc86540e81a0cc0ecdd93a4e5c2bc984faaab Mon Sep 17 00:00:00 2001 From: Michele Orselli Date: Mon, 30 Mar 2026 00:16:39 +0200 Subject: [PATCH 5/6] wip --- .gitignore | 3 +- ...seResultCache.php => CachedFileParser.php} | 28 +++++++++++--- src/Analyzer/FileParserFactory.php | 23 +++++++++--- src/Analyzer/ParseResultCache.php | 12 ------ src/CLI/Command/Check.php | 11 +++++- src/CLI/Command/DebugExpression.php | 4 +- src/CLI/Config.php | 17 +++++++++ src/CLI/Runner.php | 37 ++++++++++++------- .../ArchRuleCheckerConstraintAdapter.php | 6 +-- tests/E2E/Smoke/RunArkitectBinTest.php | 4 +- .../FileParser/CanParseDocblocksTest.php | 4 +- tests/Utils/TestRunner.php | 6 ++- 12 files changed, 108 insertions(+), 47 deletions(-) rename src/Analyzer/{FileParseResultCache.php => CachedFileParser.php} (58%) delete mode 100644 src/Analyzer/ParseResultCache.php diff --git a/.gitignore b/.gitignore index 275a2ff2..cb18e9bf 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ composer.lock .php-version composer.phar .phpunit.cache/ -.vscode \ No newline at end of file +.vscode +.phparkitect.cache \ No newline at end of file diff --git a/src/Analyzer/FileParseResultCache.php b/src/Analyzer/CachedFileParser.php similarity index 58% rename from src/Analyzer/FileParseResultCache.php rename to src/Analyzer/CachedFileParser.php index bb87ebea..9ef61320 100644 --- a/src/Analyzer/FileParseResultCache.php +++ b/src/Analyzer/CachedFileParser.php @@ -4,7 +4,7 @@ namespace Arkitect\Analyzer; -class FileParseResultCache implements ParseResultCache +class CachedFileParser implements Parser { /** @var array */ private array $entries = []; @@ -13,12 +13,15 @@ class FileParseResultCache implements ParseResultCache private string $filePath; - public function __construct(string $filePath) + private Parser $innerParser; + + public function __construct(Parser $innerParser, string $cacheFilePath) { - $this->filePath = $filePath; + $this->filePath = $cacheFilePath; + $this->innerParser = $innerParser; - if (file_exists($filePath)) { - $data = unserialize((string) file_get_contents($filePath)); + if (file_exists($cacheFilePath)) { + $data = unserialize((string) file_get_contents($cacheFilePath)); if (\is_array($data)) { $this->entries = $data; } @@ -32,6 +35,21 @@ public function __destruct() } } + public function parse(string $fileContent, string $filename): ParserResult + { + $cachedResult = $this->get($filename, md5($fileContent)); + + if (null !== $cachedResult) { + return $cachedResult; + } + + $result = $this->innerParser->parse($fileContent, $filename); + + $this->set($filename, md5($fileContent), $result); + + return $result; + } + public function get(string $filename, string $contentHash): ?ParserResult { if (!isset($this->entries[$filename])) { diff --git a/src/Analyzer/FileParserFactory.php b/src/Analyzer/FileParserFactory.php index 5c11da1f..c3c05399 100644 --- a/src/Analyzer/FileParserFactory.php +++ b/src/Analyzer/FileParserFactory.php @@ -10,19 +10,32 @@ class FileParserFactory { - public static function createFileParser(TargetPhpVersion $targetPhpVersion, bool $parseCustomAnnotations = true): FileParser - { - return new FileParser( + public static function createFileParser( + TargetPhpVersion $targetPhpVersion, + bool $parseCustomAnnotations = true, + ?string $cacheFilePath = null, + ): Parser { + $fp = new FileParser( new NodeTraverser(), new FileVisitor(new ClassDescriptionBuilder()), new NameResolver(), new DocblockTypesResolver($parseCustomAnnotations), $targetPhpVersion ); + + if (null !== $cacheFilePath) { + $fp = new CachedFileParser($fp, $cacheFilePath); + } + + return $fp; } - public static function forPhpVersion(string $targetPhpVersion): FileParser + public static function forPhpVersion(string $targetPhpVersion): Parser { - return self::createFileParser(TargetPhpVersion::create($targetPhpVersion), true); + return self::createFileParser( + TargetPhpVersion::create($targetPhpVersion), + true, + null + ); } } diff --git a/src/Analyzer/ParseResultCache.php b/src/Analyzer/ParseResultCache.php deleted file mode 100644 index ffd205eb..00000000 --- a/src/Analyzer/ParseResultCache.php +++ /dev/null @@ -1,12 +0,0 @@ -addOption( + self::NO_CACHE_PARAM, + 'o', + InputOption::VALUE_NONE, + 'Disable cache' ); } @@ -124,6 +131,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $generateBaseline = $input->getOption(self::GENERATE_BASELINE_PARAM); $phpVersion = $input->getOption('target-php-version'); $format = $input->getOption(self::FORMAT_PARAM); + $noCache = (bool) $input->getOption(self::NO_CACHE_PARAM); // we write everything on STDERR apart from the list of violations which goes on STDOUT // this allows to pipe the output of this command to a file while showing output on the terminal @@ -139,7 +147,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int ->baselineFilePath(Baseline::resolveFilePath($useBaseline, self::DEFAULT_BASELINE_FILENAME)) ->ignoreBaselineLinenumbers($ignoreBaselineLinenumbers) ->skipBaseline($skipBaseline) - ->format($format); + ->format($format) + ->noCache($noCache); $this->requireAutoload($output, $config->getAutoloadFilePath()); $printer = $this->createPrinter($output, $config->getFormat()); diff --git a/src/CLI/Command/DebugExpression.php b/src/CLI/Command/DebugExpression.php index c9ddf15f..6fa582a3 100644 --- a/src/CLI/Command/DebugExpression.php +++ b/src/CLI/Command/DebugExpression.php @@ -92,11 +92,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int /** * @throws \Arkitect\Exceptions\PhpVersionNotValidException */ - private function getParser(InputInterface $input): \Arkitect\Analyzer\FileParser + private function getParser(InputInterface $input): \Arkitect\Analyzer\Parser { $phpVersion = $input->getOption('target-php-version'); $targetPhpVersion = TargetPhpVersion::create($phpVersion); - $fileParser = FileParserFactory::createFileParser($targetPhpVersion); + $fileParser = FileParserFactory::createFileParser($targetPhpVersion, true, null); return $fileParser; } diff --git a/src/CLI/Config.php b/src/CLI/Config.php index 361a16aa..df16ffab 100644 --- a/src/CLI/Config.php +++ b/src/CLI/Config.php @@ -31,6 +31,8 @@ class Config private TargetPhpVersion $targetPhpVersion; + private ?string $cacheFilePath = null; + public function __construct() { $this->classSetRules = []; @@ -43,6 +45,7 @@ public function __construct() $this->format = PrinterFactory::default(); $this->autoloadFilePath = null; $this->targetPhpVersion = TargetPhpVersion::latest(); + $this->cacheFilePath = '.phparkitect.cache'; } public function add(ClassSet $classSet, ArchRule ...$rules): self @@ -167,4 +170,18 @@ public function getAutoloadFilePath(): ?string { return $this->autoloadFilePath; } + + public function noCache(bool $noCache): self + { + if ($noCache) { + $this->cacheFilePath = null; + } + + return $this; + } + + public function getCacheFilePath(): ?string + { + return $this->cacheFilePath; + } } diff --git a/src/CLI/Runner.php b/src/CLI/Runner.php index 7e3b196e..c6c0bd16 100644 --- a/src/CLI/Runner.php +++ b/src/CLI/Runner.php @@ -48,6 +48,24 @@ public function check( ParsingErrors $parsingErrors, bool $stopOnFailure, ): void { + // first step: collect all files to parse + $filesToParse = $this->collectFilesToParse($classSetRule); + + // second step: parse all files and collect results + $parsedFiles = $this->collectParsedFiles( + $filesToParse, + $fileParser, + $progress + ); + + // third step: check all rules on all files + $this->checkRulesOnParsedFiles( + $classSetRule, + $parsedFiles, + $violations, + $parsingErrors, + $stopOnFailure + ); } public function checkRulesOnParsedFiles( @@ -123,7 +141,8 @@ protected function doRun(Config $config, Progress $progress): array $fileParser = FileParserFactory::createFileParser( $config->getTargetPhpVersion(), - $config->isParseCustomAnnotationsEnabled() + $config->isParseCustomAnnotationsEnabled(), + $config->getCacheFilePath() ); /** @var ClassSetRules $classSetRule */ @@ -131,20 +150,10 @@ protected function doRun(Config $config, Progress $progress): array $progress->startFileSetAnalysis($classSetRule->getClassSet()); try { - // first step: collect all files to parse - $filesToParse = $this->collectFilesToParse($classSetRule); - - // second step: parse all files and collect results - $parsedFiles = $this->collectParsedFiles( - $filesToParse, - $fileParser, - $progress - ); - - // third step: check all rules on all files - $this->checkRulesOnParsedFiles( + $this->check( $classSetRule, - $parsedFiles, + $progress, + $fileParser, $violations, $parsingErrors, $config->isStopOnFailure() diff --git a/src/PHPUnit/ArchRuleCheckerConstraintAdapter.php b/src/PHPUnit/ArchRuleCheckerConstraintAdapter.php index 641509d6..6faf9832 100644 --- a/src/PHPUnit/ArchRuleCheckerConstraintAdapter.php +++ b/src/PHPUnit/ArchRuleCheckerConstraintAdapter.php @@ -4,7 +4,7 @@ namespace Arkitect\PHPUnit; -use Arkitect\Analyzer\FileParser; +use Arkitect\Analyzer\Parser; use Arkitect\Analyzer\FileParserFactory; use Arkitect\Analyzer\ParsingErrors; use Arkitect\ClassSet; @@ -32,7 +32,7 @@ class ArchRuleCheckerConstraintAdapter extends Constraint private Runner $runner; - private FileParser $fileparser; + private Parser $fileparser; private ParsingErrors $parsingErrors; @@ -42,7 +42,7 @@ public function __construct(ClassSet $classSet) { $targetPhpVersion = TargetPhpVersion::create(null); $this->runner = new Runner(); - $this->fileparser = FileParserFactory::createFileParser($targetPhpVersion); + $this->fileparser = FileParserFactory::createFileParser($targetPhpVersion, true, null); $this->classSet = $classSet; $this->violations = new Violations(); $this->parsingErrors = new ParsingErrors(); diff --git a/tests/E2E/Smoke/RunArkitectBinTest.php b/tests/E2E/Smoke/RunArkitectBinTest.php index eeb10da0..428f0025 100644 --- a/tests/E2E/Smoke/RunArkitectBinTest.php +++ b/tests/E2E/Smoke/RunArkitectBinTest.php @@ -113,7 +113,7 @@ public function test_only_violations_are_printed_on_stdout(): void protected function runArkitectPassingConfigFilePath($configFilePath): Process { - $process = new Process([$this->phparkitect, 'check', '--config='.$configFilePath], __DIR__); + $process = new Process([$this->phparkitect, 'check', '--no-cache', '--config='.$configFilePath], __DIR__); $process->run(); return $process; @@ -121,7 +121,7 @@ protected function runArkitectPassingConfigFilePath($configFilePath): Process protected function runArkitect(): Process { - $process = new Process([$this->phparkitect, 'check'], __DIR__); + $process = new Process([$this->phparkitect, 'check', '--no-cache'], __DIR__); $process->run(); return $process; diff --git a/tests/Unit/Analyzer/FileParser/CanParseDocblocksTest.php b/tests/Unit/Analyzer/FileParser/CanParseDocblocksTest.php index f4634317..ce7e5f4c 100644 --- a/tests/Unit/Analyzer/FileParser/CanParseDocblocksTest.php +++ b/tests/Unit/Analyzer/FileParser/CanParseDocblocksTest.php @@ -418,8 +418,10 @@ class ApplicationLevelDto $fp = FileParserFactory::createFileParser( TargetPhpVersion::create(TargetPhpVersion::PHP_8_1), - false + false, + null ); + $result = $fp->parse($code, 'relativePathName'); $cd = $result->classDescriptions(); diff --git a/tests/Utils/TestRunner.php b/tests/Utils/TestRunner.php index d09a5277..cc6603a2 100644 --- a/tests/Utils/TestRunner.php +++ b/tests/Utils/TestRunner.php @@ -29,7 +29,11 @@ private function __construct(?string $version = null) { $this->violations = new Violations(); $this->parsingErrors = new ParsingErrors(); - $this->fileParser = FileParserFactory::createFileParser(TargetPhpVersion::create($version)); + $this->fileParser = FileParserFactory::createFileParser( + TargetPhpVersion::create($version), + true, + null + ); } public static function create(?string $version = null): self From 58a6dd20d33610f0e352837aec8ff1da2ee50d9d Mon Sep 17 00:00:00 2001 From: Michele Orselli Date: Mon, 30 Mar 2026 00:26:51 +0200 Subject: [PATCH 6/6] wip --- src/PHPUnit/ArchRuleCheckerConstraintAdapter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PHPUnit/ArchRuleCheckerConstraintAdapter.php b/src/PHPUnit/ArchRuleCheckerConstraintAdapter.php index 6faf9832..6de962af 100644 --- a/src/PHPUnit/ArchRuleCheckerConstraintAdapter.php +++ b/src/PHPUnit/ArchRuleCheckerConstraintAdapter.php @@ -4,8 +4,8 @@ namespace Arkitect\PHPUnit; -use Arkitect\Analyzer\Parser; use Arkitect\Analyzer\FileParserFactory; +use Arkitect\Analyzer\Parser; use Arkitect\Analyzer\ParsingErrors; use Arkitect\ClassSet; use Arkitect\ClassSetRules;