diff --git a/src/Cache/AnalysisResultCache.php b/src/Cache/AnalysisResultCache.php index 424fa8bb..2d5d79fb 100644 --- a/src/Cache/AnalysisResultCache.php +++ b/src/Cache/AnalysisResultCache.php @@ -197,26 +197,7 @@ public function loadClassNodes(string $file, string $namespace): ?array return null; } - $classNodes = $this->classNodesFromPayload($payload); - $anonymousClassNodes = $this->anonymousClassNodesFromPayload($payload); - $fileReferences = $this->fileReferencesFromPayload($payload); - $fileInstantiations = $this->fileInstantiationsFromPayload($payload); - - if ( - $classNodes === null - || $anonymousClassNodes === null - || $fileReferences === null - || $fileInstantiations === null - ) { - return null; - } - - return [ - 'classNodes' => $classNodes, - 'anonymousClassNodes' => $anonymousClassNodes, - 'fileReferences' => $fileReferences, - 'fileInstantiations' => $fileInstantiations, - ]; + return $this->classNodeResultFromPayload($payload); } /** @@ -236,20 +217,46 @@ public function loadClassNodesWithFileAnalysis(string $file, string $namespace): return null; } + $fileAnalysis = is_array($payload['fileAnalysis'] ?? null) + ? $this->fileAnalysisFromArray($payload['fileAnalysis']) + : null; + + if (! $fileAnalysis instanceof FileAnalysis) { + return null; + } + + $result = $this->classNodeResultFromPayload($payload); + + if ($result === null) { + return null; + } + + $result['fileAnalysis'] = $fileAnalysis; + + return $result; + } + + /** + * @param array $payload + * @return array{ + * classNodes: list, + * anonymousClassNodes: list, + * fileReferences: list, + * fileInstantiations: list + * }|null + */ + private function classNodeResultFromPayload(array $payload): ?array + { $classNodes = $this->classNodesFromPayload($payload); $anonymousClassNodes = $this->anonymousClassNodesFromPayload($payload); $fileReferences = $this->fileReferencesFromPayload($payload); $fileInstantiations = $this->fileInstantiationsFromPayload($payload); - $fileAnalysis = is_array($payload['fileAnalysis'] ?? null) - ? $this->fileAnalysisFromArray($payload['fileAnalysis']) - : null; if ( $classNodes === null || $anonymousClassNodes === null || $fileReferences === null || $fileInstantiations === null - || ! $fileAnalysis instanceof FileAnalysis ) { return null; } @@ -259,7 +266,6 @@ public function loadClassNodesWithFileAnalysis(string $file, string $namespace): 'anonymousClassNodes' => $anonymousClassNodes, 'fileReferences' => $fileReferences, 'fileInstantiations' => $fileInstantiations, - 'fileAnalysis' => $fileAnalysis, ]; } @@ -367,10 +373,6 @@ private function readPath(string $path): ?array */ private function ruleViolationFromArray(array $violation): ?RuleViolation { - if (! $this->hasOnlyStringKeys($violation)) { - return null; - } - $ruleKey = $violation['rule'] ?? null; $message = $violation['message'] ?? null; $file = $violation['file'] ?? null; @@ -462,7 +464,7 @@ private function anonymousClassNodesFromPayload(array $payload): ?array $anonymousClassNodes = []; foreach ($rawNodes as $rawNode) { - if (! is_array($rawNode) || ! $this->hasOnlyStringKeys($rawNode)) { + if (! is_array($rawNode)) { return null; } @@ -532,10 +534,6 @@ private function classNodeToArray(ClassNode $classNode): array */ private function classNodeFromArray(array $node): ?ClassNode { - if (! $this->hasOnlyStringKeys($node)) { - return null; - } - $className = $node['className'] ?? null; $file = $node['file'] ?? null; $line = $node['line'] ?? null; @@ -752,10 +750,6 @@ private function enumCaseNodeToArray(EnumCaseNode $enumCaseNode): array */ private function methodNodeFromArray(array $method): ?MethodNode { - if (! $this->hasOnlyStringKeys($method)) { - return null; - } - if ( ! is_string($method['name'] ?? null) || ! is_string($method['visibility'] ?? null) @@ -790,10 +784,6 @@ private function methodNodeFromArray(array $method): ?MethodNode */ private function constantNodeFromArray(array $constant): ?ConstantNode { - if (! $this->hasOnlyStringKeys($constant)) { - return null; - } - if ( ! is_string($constant['name'] ?? null) || ! is_string($constant['visibility'] ?? null) @@ -816,10 +806,6 @@ private function constantNodeFromArray(array $constant): ?ConstantNode */ private function propertyNodeFromArray(array $property): ?PropertyNode { - if (! $this->hasOnlyStringKeys($property)) { - return null; - } - if ( ! is_string($property['name'] ?? null) || ! is_string($property['visibility'] ?? null) @@ -842,10 +828,6 @@ private function propertyNodeFromArray(array $property): ?PropertyNode */ private function enumCaseNodeFromArray(array $enumCase): ?EnumCaseNode { - if (! $this->hasOnlyStringKeys($enumCase)) { - return null; - } - $value = $enumCase['value'] ?? null; if ( @@ -882,8 +864,7 @@ private function fileAnalysisToArray(FileAnalysis $fileAnalysis): array private function fileAnalysisFromArray(array $analysis): ?FileAnalysis { if ( - ! $this->hasOnlyStringKeys($analysis) - || ! is_string($analysis['file'] ?? null) + ! is_string($analysis['file'] ?? null) || ! is_bool($analysis['hasUtf8Bom'] ?? null) || ! is_bool($analysis['hasValidUtf8'] ?? null) || ! array_key_exists('invalidPhpTagLine', $analysis) diff --git a/tests/Cache/AnalysisResultCacheTest.php b/tests/Cache/AnalysisResultCacheTest.php index 28fd6554..5ec96665 100644 --- a/tests/Cache/AnalysisResultCacheTest.php +++ b/tests/Cache/AnalysisResultCacheTest.php @@ -1291,6 +1291,35 @@ public function testClassNodesWithFileAnalysisMissesMalformedFacts(array $fileAn } } + public function testClassNodesWithFileAnalysisMissesMalformedNodesWhenFactsAreValid(): void + { + $cacheDirectory = $this->createTempDirectory(); + $sourceFile = $cacheDirectory . '/Foo.php'; + $analysisResultCache = new AnalysisResultCache(__DIR__, new FileHashProvider(), $cacheDirectory); + + file_put_contents($sourceFile, 'storeClassNodes( + $sourceFile, + 'config', + [$this->makeClassNode($sourceFile)], + new FileAnalysis($sourceFile, false, true, null, true, true, false, 1), + ); + + $cacheFile = $this->firstJsonFile($cacheDirectory); + $payload = json_decode((string) file_get_contents($cacheFile), true, 512, JSON_THROW_ON_ERROR); + $this->assertIsArray($payload); + $payload['nodes'] = 'invalid'; + $this->writeCachePayload($cacheDirectory, $payload, $cacheFile); + + $this->assertNull($analysisResultCache->loadClassNodesWithFileAnalysis($sourceFile, 'config')); + } finally { + unlink($sourceFile); + $this->removeTempDirectory($cacheDirectory); + } + } + public function testClassNodesMissWhenFileMetadataChanges(): void { $cacheDirectory = $this->createTempDirectory();