> */ private static $file_references_to_class = []; /** * A lookup table used for getting all the files that reference any other file * * @var array> */ private static $referencing_files = []; /** * @var array> */ private static $files_inheriting_classes = []; /** * A list of all files deleted since the last successful run * * @var array|null */ private static $deleted_files = null; /** * A lookup table used for getting all the files referenced by a file * * @var array, i:array}> */ private static $file_references = []; /** * @var array> */ private static $class_method_references = []; /** * @var array> */ private static $correct_methods = []; /** * @var array> */ private static $issues = []; /** * @var bool */ private $loaded_correct_methods_from_cache = false; /** * @var ?FileReferenceCacheProvider */ public $cache; public function __construct(FileReferenceCacheProvider $cache = null) { $this->cache = $cache; } /** * @return array */ public function getDeletedReferencedFiles() { if (self::$deleted_files === null) { self::$deleted_files = array_filter( array_keys(self::$file_references), /** * @param string $file_name * * @return bool */ function ($file_name) { return !file_exists($file_name); } ); } return self::$deleted_files; } /** * @param string $source_file * @param string $fq_class_name_lc * * @return void */ public function addFileReferenceToClass($source_file, $fq_class_name_lc) { self::$referencing_files[$source_file] = true; self::$file_references_to_class[$fq_class_name_lc][$source_file] = true; } /** * @return array> */ public function getAllFileReferences() { return self::$file_references_to_class; } /** * @param array> $references * @psalm-suppress MixedTypeCoercion * * @return void */ public function addFileReferences(array $references) { self::$file_references_to_class = array_merge_recursive($references, self::$file_references_to_class); } /** * @param string $source_file * @param string $fq_class_name_lc * * @return void */ public function addFileInheritanceToClass($source_file, $fq_class_name_lc) { self::$files_inheriting_classes[$fq_class_name_lc][$source_file] = true; } /** * @param string $file * * @return array */ private function calculateFilesReferencingFile(ProjectChecker $project_checker, $file) { $referenced_files = []; $file_classes = ClassLikeChecker::getClassesForFile($project_checker, $file); foreach ($file_classes as $file_class_lc => $_) { if (isset(self::$file_references_to_class[$file_class_lc])) { $referenced_files = array_merge( $referenced_files, array_keys(self::$file_references_to_class[$file_class_lc]) ); } } return array_unique($referenced_files); } /** * @param string $file * * @return array */ private function calculateFilesInheritingFile(ProjectChecker $project_checker, $file) { $referenced_files = []; $file_classes = ClassLikeChecker::getClassesForFile($project_checker, $file); foreach ($file_classes as $file_class_lc => $_) { if (isset(self::$files_inheriting_classes[$file_class_lc])) { $referenced_files = array_merge( $referenced_files, array_keys(self::$files_inheriting_classes[$file_class_lc]) ); } } return array_unique($referenced_files); } /** * @return void */ public function removeDeletedFilesFromReferences() { $deleted_files = self::getDeletedReferencedFiles(); if ($deleted_files) { foreach ($deleted_files as $file) { unset(self::$file_references[$file]); } if ($this->cache) { $this->cache->setCachedFileReferences(self::$file_references); } } } /** * @param string $file * * @return array */ public function getFilesReferencingFile($file) { return isset(self::$file_references[$file]['a']) ? self::$file_references[$file]['a'] : []; } /** * @param string $file * * @return array */ public function getFilesInheritingFromFile($file) { return isset(self::$file_references[$file]['i']) ? self::$file_references[$file]['i'] : []; } /** * @return array> */ public function getMethodsReferencing() { return self::$class_method_references; } /** * @return bool * @psalm-suppress MixedAssignment * @psalm-suppress MixedTypeCoercion */ public function loadReferenceCache() { if ($this->cache && !$this->loaded_from_cache) { $this->loaded_from_cache = true; $file_references = $this->cache->getCachedFileReferences(); if ($file_references === null) { return false; } self::$file_references = $file_references; $class_method_references = $this->cache->getCachedMethodReferences(); if ($class_method_references === null) { return false; } self::$class_method_references = $class_method_references; $issues = $this->cache->getCachedIssues(); if ($issues === null) { return false; } self::$issues = $issues; return true; } return false; } /** * @param array $visited_files * * @return void */ public function updateReferenceCache(ProjectChecker $project_checker, array $visited_files) { foreach ($visited_files as $file => $_) { $all_file_references = array_unique( array_merge( isset(self::$file_references[$file]['a']) ? self::$file_references[$file]['a'] : [], $this->calculateFilesReferencingFile($project_checker, $file) ) ); $inheritance_references = array_unique( array_merge( isset(self::$file_references[$file]['i']) ? self::$file_references[$file]['i'] : [], $this->calculateFilesInheritingFile($project_checker, $file) ) ); self::$file_references[$file] = [ 'a' => $all_file_references, 'i' => $inheritance_references, ]; } if ($this->cache) { $this->cache->setCachedFileReferences(self::$file_references); $this->cache->setCachedMethodReferences(self::$class_method_references); $this->cache->setCachedIssues(self::$issues); } } /** * @param string $calling_method_id * @param string $referenced_member_id * @return void */ public function addReferenceToClassMethod($calling_method_id, $referenced_member_id) { if (!isset(self::$class_method_references[$referenced_member_id])) { self::$class_method_references[$referenced_member_id] = [$calling_method_id => true]; } else { self::$class_method_references[$referenced_member_id][$calling_method_id] = true; } } /** * @return array> */ public function getClassMethodReferences() : array { return self::$class_method_references; } /** * @param array> $references * @psalm-suppress MixedTypeCoercion * * @return void */ public function addClassMethodReferences(array $references) { foreach ($references as $referenced_member_id => $calling_method_ids) { if (isset(self::$class_method_references[$referenced_member_id])) { self::$class_method_references[$referenced_member_id] = array_merge( self::$class_method_references[$referenced_member_id], $calling_method_ids ); } else { self::$class_method_references[$referenced_member_id] = $calling_method_ids; } } } /** * @return array> */ public function getExistingIssues() : array { return self::$issues; } /** * @param string $file_path * @return void */ public function clearExistingIssuesForFile($file_path) { unset(self::$issues[$file_path]); } /** * @param string $file_path * @return void */ public function addIssue($file_path, array $issue) { if (!isset(self::$issues[$file_path])) { self::$issues[$file_path] = [$issue]; } else { self::$issues[$file_path][] = $issue; } } /** * @return array> */ public function getCorrectMethods(\Psalm\Config $config) { if (!$this->loaded_correct_methods_from_cache && $this->cache) { self::$correct_methods = $this->cache->getCorrectMethodCache($config) ?: []; $this->loaded_correct_methods_from_cache = true; } return self::$correct_methods; } /** * @param array> $correct_methods * @return void */ public function setCorrectMethods(array $correct_methods) { self::$correct_methods = $correct_methods; if ($this->cache) { $this->cache->setCorrectMethodCache($correct_methods); } } /** * @return void */ public static function clearCache() { self::$file_references_to_class = []; self::$referencing_files = []; self::$files_inheriting_classes = []; self::$deleted_files = null; self::$file_references = []; self::$class_method_references = []; self::$correct_methods = []; self::$issues = []; } }