/** * Take a look at all globally accessible elements and see if * we can find any dead code that is never referenced * * @return void */ public static function analyzeReferenceCounts(CodeBase $code_base) { // Check to see if dead code detection is enabled. Keep // in mind that the results here are just a guess and // we can't tell with certainty that anything is // definitely unreferenced. if (!Config::get()->dead_code_detection) { return; } // Get the count of all known elements $total_count = count($code_base->getMethodMap(), COUNT_RECURSIVE) + count($code_base->getPropertyMap(), COUNT_RECURSIVE) + count($code_base->getConstantMap(), COUNT_RECURSIVE) + count($code_base->getClassMap(), COUNT_RECURSIVE); $i = 0; $analyze_list = function ($list) use($code_base, &$i, $total_count) { foreach ($list as $name => $element) { CLI::progress('dead code', ++$i / $total_count); self::analyzeElementReferenceCounts($code_base, $element); } }; $analyze_map = function ($map) use($code_base, &$i, $total_count) { foreach ($map as $fqsen_string => $list) { foreach ($list as $name => $element) { CLI::progress('dead code', ++$i / $total_count); // Don't worry about internal elements if ($element->getContext()->isInternal()) { continue; } $element_fqsen = $element->getFQSEN(); if ($element_fqsen instanceof FullyQualifiedClassElement) { $class_fqsen = $element->getDefiningClassFQSEN(); // Don't analyze elements defined in a parent // class if ((string) $class_fqsen !== $fqsen_string) { continue; } $defining_class = $element->getDefiningClass($code_base); // Don't analyze elements on interfaces or on // abstract classes, as they're uncallable. if ($defining_class->isInterface() || $defining_class->isAbstract() || $defining_class->isTrait()) { continue; } // Ignore magic methods if ($element instanceof Method && $element->getIsMagic()) { continue; } } self::analyzeElementReferenceCounts($code_base, $element); } } }; $analyze_map($code_base->getMethodMap()); $analyze_map($code_base->getPropertyMap()); $analyze_map($code_base->getConstantMap()); $analyze_list($code_base->getClassMap()); }
/** * Take a pass over all classes verifying various * states. * * @return null */ public static function analyzeClasses(CodeBase $code_base) { $class_count = 2 * count($code_base->getClassMap()); // Take a pass to import all details from ancestors $i = 0; foreach ($code_base->getClassMap() as $fqsen_string => $clazz) { CLI::progress('classes', ++$i / $class_count); // Make sure the parent classes exist ParentClassExistsAnalyzer::analyzeParentClassExists($code_base, $clazz); // Then import them $clazz->importAncestorClasses($code_base); // Then figure out which methods are overrides of // ancestor methods $clazz->analyzeMethodOverrides($code_base); } // Run a few checks on all of the classes foreach ($code_base->getClassMap() as $fqsen_string => $clazz) { CLI::progress('classes', ++$i / $class_count); if ($clazz->getContext()->isInternal()) { continue; } DuplicateClassAnalyzer::analyzeDuplicateClass($code_base, $clazz); ParentConstructorCalledAnalyzer::analyzeParentConstructorCalled($code_base, $clazz); PropertyTypesAnalyzer::analyzePropertyTypes($code_base, $clazz); } }
/** * Take a pass over all classes verifying various * states. * * @return null */ public static function analyzeClasses(CodeBase $code_base) { $class_count = count($code_base->getClassMap()); // Take a pass to import all details from ancestors $i = 0; foreach ($code_base->getClassMap() as $fqsen => $class) { CLI::progress('classes', ++$i / $class_count); if ($class->isInternal()) { continue; } // Make sure the parent classes exist ParentClassExistsAnalyzer::analyzeParentClassExists($code_base, $class); DuplicateClassAnalyzer::analyzeDuplicateClass($code_base, $class); ParentConstructorCalledAnalyzer::analyzeParentConstructorCalled($code_base, $class); PropertyTypesAnalyzer::analyzePropertyTypes($code_base, $class); } }
/** * Take a pass over all classes verifying various * states. * * @return null */ private function analyzeClasses(CodeBase $code_base) { $class_count = 2 * count($code_base->getClassMap()); // Take a pass to import all details from ancestors $i = 0; foreach ($code_base->getClassMap() as $fqsen_string => $clazz) { CLI::progress('classes', ++$i / $class_count); // Make sure the parent classes exist self::analyzeParentClassExists($code_base, $clazz); // The import them $clazz->importAncestorClasses($code_base); } // Run a few checks on all of the classes foreach ($code_base->getClassMap() as $fqsen_string => $clazz) { CLI::progress('classes', ++$i / $class_count); if ($clazz->getContext()->isInternal()) { continue; } self::analyzeDuplicateClass($code_base, $clazz); self::analyzeParentConstructorCalled($code_base, $clazz); self::analyzePropertyTypes($code_base, $clazz); } }
/** * Take a look at all globally accessible elements and see if * we can find any dead code that is never referenced * * @return void */ public static function analyzeReferenceCounts(CodeBase $code_base) { // Check to see if dead code detection is enabled. Keep // in mind that the results here are just a guess and // we can't tell with certainty that anything is // definitely unreferenced. if (!Config::get()->dead_code_detection) { return; } // Get the count of all known elements $total_count = count($code_base->getMethodMap(), COUNT_RECURSIVE) + count($code_base->getPropertyMap(), COUNT_RECURSIVE) + count($code_base->getConstantMap(), COUNT_RECURSIVE) + count($code_base->getClassMap(), COUNT_RECURSIVE); $i = 0; $analyze_list = function ($list, string $issue_type) use($code_base, &$i, $total_count) { foreach ($list as $name => $element) { CLI::progress('dead code', ++$i / $total_count); self::analyzeElementReferenceCounts($code_base, $element, $issue_type); } }; $analyze_map = function ($map, string $issue_type) use($code_base, &$i, $total_count) { foreach ($map as $fqsen_string => $list) { foreach ($list as $name => $element) { CLI::progress('dead code', ++$i / $total_count); // Don't worry about internal elements if ($element->isInternal()) { continue; } $element_fqsen = $element->getFQSEN(); if (0 !== strpos((string) $element_fqsen, $fqsen_string)) { continue; } // Skip methods that are overrides of other methods if ($element_fqsen instanceof FullyQualifiedMethodName) { if ($element->getIsOverride()) { continue; } } // Skip properties on classes that have a magic // __get or __set method given that we can't track // their access if ($element instanceof Property) { $defining_class = $element->getDefiningClass($code_base); if ($defining_class->hasMethodWithName($code_base, '__set') || $defining_class->hasMethodWithName($code_base, '__get')) { continue; } } if ($element_fqsen instanceof FullyQualifiedClassElement) { $class_fqsen = $element->getDefiningClassFQSEN(); // Don't analyze elements defined in a parent // class if ((string) $class_fqsen !== $fqsen_string) { continue; } $defining_class = $element->getDefiningClass($code_base); // Don't analyze elements on interfaces or on // abstract classes, as they're uncallable. if ($defining_class->isInterface() || $defining_class->isAbstract() || $defining_class->isTrait()) { continue; } // Ignore magic methods if ($element instanceof Method) { // Doubly nested so that `$element` shows // up as Method in Phan. if ($element->getIsMagic()) { continue; } } } self::analyzeElementReferenceCounts($code_base, $element, $issue_type); } } }; $analyze_map($code_base->getMethodMap(), Issue::UnreferencedMethod); $analyze_map($code_base->getPropertyMap(), Issue::UnreferencedProperty); $analyze_map($code_base->getConstantMap(), Issue::UnreferencedConstant); $analyze_list($code_base->getClassMap(), Issue::UnreferencedClass); }