/** * @param CodeBase $code_base * @param Map|array $element_list * @param string $issue_type * @param int $total_count * @param int $i * * @return void */ private static function analyzeElementListReferenceCounts(CodeBase $code_base, $element_list, string $issue_type, int $total_count, int &$i) { foreach ($element_list as $element) { CLI::progress('dead code', ++$i / $total_count); self::analyzeElementReferenceCounts($code_base, $element, $issue_type); } }
/** * 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 functions verifying various * states. * * @return null */ public static function analyzeFunctions(CodeBase $code_base) { $function_count = count($code_base->getFunctionAndMethodSet()); $i = 0; foreach ($code_base->getFunctionAndMethodSet() as $function_or_method) { CLI::progress('method', ++$i / $function_count); if ($function_or_method->isInternal()) { continue; } DuplicateFunctionAnalyzer::analyzeDuplicateFunction($code_base, $function_or_method); ParameterTypesAnalyzer::analyzeParameterTypes($code_base, $function_or_method); // Let any plugins analyze the methods or functions if ($function_or_method instanceof Func) { ConfigPluginSet::instance()->analyzeFunction($code_base, $function_or_method); } else { if ($function_or_method instanceof Method) { ConfigPluginSet::instance()->analyzeMethod($code_base, $function_or_method); } } } }
/** * Take a pass over all functions verifying various * states. * * @return null */ public static function analyzeFunctions(CodeBase $code_base) { $function_count = count($code_base->getMethodMap(), COUNT_RECURSIVE); $i = 0; foreach ($code_base->getMethodMap() as $fqsen_string => $method_map) { foreach ($method_map as $name => $method) { CLI::progress('method', ++$i / $function_count); if ($method->getContext()->isInternal()) { continue; } DuplicateFunctionAnalyzer::analyzeDuplicateFunction($code_base, $method); ParameterTypesAnalyzer::analyzeParameterTypes($code_base, $method); } } }
/** * @param CodeBase $code_base * A code base needs to be passed in because we require * it to be initialized before any classes or files are * loaded. * * @param string[] $file_path_list * A set of files to expand with the set of dependencies * on those files. * * @return string[] * Get an expanded list of files and dependencies for * the given file list */ public static function expandedFileList(CodeBase $code_base, array $file_path_list) : array { $file_count = count($file_path_list); // We'll construct a set of files that we'll // want to run an analysis on $dependency_file_path_list = []; foreach ($file_path_list as $i => $file_path) { CLI::progress('dependencies', ($i + 1) / $file_count); // Add the file itself to the list $dependency_file_path_list[] = $file_path; // Add any files that depend on this file $dependency_file_path_list = array_merge($dependency_file_path_list, $code_base->dependencyListForFile($file_path)); } return array_unique($dependency_file_path_list); }
declare (strict_types=1); // Phan does a ton of GC and this offers a major speed // improvment if your system can handle it (which it // should be able to) gc_disable(); // Check the environment to make sure Phan can run successfully require_once __DIR__ . '/requirements.php'; // Build a code base based on PHP internally defined // functions, methods and classes before loading our // own $code_base = (require_once __DIR__ . '/codebase.php'); require_once __DIR__ . '/Phan/Bootstrap.php'; use Phan\CLI; use Phan\CodeBase; use Phan\Config; use Phan\Phan; // Create our CLI interface and load arguments $cli = new CLI(); $file_list = $cli->getFileList(); // If requested, expand the file list to a set of // all files that should be re-analyzed if (Config::get()->expand_file_list) { assert((bool) Config::get()->stored_state_file_path, 'Requesting an expanded dependency list can only ' . ' be done if a state-file is defined'); // Analyze the file list provided via the CLI $file_list = Phan::expandedFileList($code_base, $file_list); } // Analyze the file list provided via the CLI $is_issue_found = Phan::analyzeFileList($code_base, $file_list); // Provide an exit status code based on if // issues were found exit($is_issue_found ? EXIT_ISSUES_FOUND : EXIT_SUCCESS);
/** * Take a pass over all functions verifying various * states. * * @return null */ public static function analyzeFunctions(CodeBase $code_base) { $function_count = count($code_base->getFunctionAndMethodSet()); $i = 0; foreach ($code_base->getFunctionAndMethodSet() as $function_or_method) { CLI::progress('method', ++$i / $function_count); if ($function_or_method->isInternal()) { continue; } DuplicateFunctionAnalyzer::analyzeDuplicateFunction($code_base, $function_or_method); ParameterTypesAnalyzer::analyzeParameterTypes($code_base, $function_or_method); } }
<?php declare (strict_types=1); assert(extension_loaded('ast'), "The php-ast extension must be loaded in order for Phan to work. See https://github.com/etsy/phan#getting-it-running for more details."); assert((int) phpversion()[0] >= 7, "Phan requires PHP version 7 or greater. See https://github.com/etsy/phan#getting-it-running for more details."); // Grab these before we define our own classes $internal_class_name_list = get_declared_classes(); $internal_interface_name_list = get_declared_interfaces(); $internal_trait_name_list = get_declared_traits(); $internal_function_name_list = get_defined_functions()['internal']; require_once __DIR__ . '/Phan/Bootstrap.php'; use Phan\CLI; use Phan\CodeBase; use Phan\Config; use Phan\Log; use Phan\Phan; // Create our CLI interface and load arguments $cli = new CLI(); $code_base = new CodeBase($internal_class_name_list, $internal_interface_name_list, $internal_trait_name_list, $internal_function_name_list); // Analyze the file list provided via the CLI (new Phan())->analyzeFileList($code_base, $cli->getFileList());
/** * Take a pass over all functions verifying various * states. * * @return null */ private function analyzeFunctions(CodeBase $code_base) { $function_count = count($code_base->getMethodMap()); $i = 0; foreach ($code_base->getMethodMap() as $fqsen_string => $method_map) { foreach ($method_map as $name => $method) { CLI::progress('method', ++$i / $function_count); if ($method->getContext()->isInternal()) { continue; } self::analyzeDuplicateFunction($code_base, $method); self::analyzeParameterTypes($code_base, $method); } } }
/** * 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); }
<?php declare (strict_types=1); assert(extension_loaded('ast'), "The php-ast extension must be loaded in order for Phan to work. See https://github.com/etsy/phan#getting-it-running for more details."); assert((int) phpversion()[0] >= 7, "Phan requires PHP version 7 or greater. See https://github.com/etsy/phan#getting-it-running for more details."); // Grab these before we define our own classes $internal_class_name_list = get_declared_classes(); $internal_interface_name_list = get_declared_interfaces(); $internal_trait_name_list = get_declared_traits(); $internal_function_name_list = get_defined_functions()['internal']; require_once __DIR__ . '/Phan/Bootstrap.php'; use Phan\CLI; use Phan\CodeBase; use Phan\Config; use Phan\Phan; // Create our CLI interface and load arguments $cli = new CLI(); $code_base = new CodeBase($internal_class_name_list, $internal_interface_name_list, $internal_trait_name_list, $internal_function_name_list); // If requested, expand the file list to a set of // all files that should be re-analyzed if (Config::get()->expanded_dependency_list) { assert((bool) Config::get()->stored_state_file_path, 'Requesting an expanded dependency list can only ' . ' be done if a state-file is defined'); // Analyze the file list provided via the CLI $dependency_file_list = Phan::dependencyFileList($code_base, $cli->getFileList()); // Emit the expanded file list print implode("\n", $dependency_file_list) . "\n"; exit(1); } // Analyze the file list provided via the CLI Phan::analyzeFileList($code_base, $cli->getFileList());