>> */ public $use_referencing_locations = []; /** * A map of file names to the classes that they contain explicit references to * used in collaboration with use_referencing_locations * * @var array> */ public $use_referencing_files = []; /** * @var FileStorageProvider */ public $file_storage_provider; /** * @var ClassLikeStorageProvider */ public $classlike_storage_provider; /** * @var bool */ public $collect_references = false; /** * @var FileProvider */ private $file_provider; /** * @var StatementsProvider */ private $statements_provider; /** * @var bool */ private $debug_output = false; /** * @var array */ private static $stubbed_constants = []; /** * Whether to log functions just at the file level or globally (for stubs) * * @var bool */ public $register_global_functions = false; /** * @var bool */ public $find_unused_code = false; /** * @var Codebase\Reflection */ private $reflection; /** * @var Codebase\Scanner */ public $scanner; /** * @var Codebase\Analyzer */ public $analyzer; /** * @var Codebase\Functions */ public $functions; /** * @var Codebase\ClassLikes */ public $classlikes; /** * @var Codebase\Methods */ public $methods; /** * @var Codebase\Properties */ public $properties; /** * @var Codebase\Populator */ public $populator; /** * @param bool $debug_output */ public function __construct( Config $config, FileStorageProvider $file_storage_provider, ClassLikeStorageProvider $classlike_storage_provider, FileProvider $file_provider, StatementsProvider $statements_provider, $debug_output = false ) { $this->config = $config; $this->file_storage_provider = $file_storage_provider; $this->classlike_storage_provider = $classlike_storage_provider; $this->debug_output = $debug_output; $this->file_provider = $file_provider; $this->statements_provider = $statements_provider; $this->debug_output = $debug_output; self::$stubbed_constants = []; $this->reflection = new Codebase\Reflection($classlike_storage_provider, $this); $this->scanner = new Codebase\Scanner( $this, $config, $file_storage_provider, $statements_provider, $this->reflection, $debug_output ); $this->analyzer = new Codebase\Analyzer($config, $file_provider, $debug_output); $this->functions = new Codebase\Functions($file_storage_provider, $this->reflection); $this->methods = new Codebase\Methods($classlike_storage_provider); $this->properties = new Codebase\Properties($classlike_storage_provider); $this->classlikes = new Codebase\ClassLikes( $config, $this, $classlike_storage_provider, $this->scanner, $this->methods, $debug_output ); $this->populator = new Codebase\Populator( $config, $classlike_storage_provider, $file_storage_provider, $this->classlikes, $this->methods, $debug_output ); } /** * @return void */ public function collectReferences() { $this->collect_references = true; $this->classlikes->collect_references = true; $this->methods->collect_references = true; $this->properties->collect_references = true; } /** * @return void */ public function reportUnusedCode() { $this->collectReferences(); $this->find_unused_code = true; } /** * @param array $files_to_analyze * * @return void */ public function addFilesToAnalyze(array $files_to_analyze) { $this->scanner->addFilesToDeepScan($files_to_analyze); $this->analyzer->addFiles($files_to_analyze); } /** * Scans all files their related files * * @return void */ public function scanFiles() { $has_changes = $this->scanner->scanFiles($this->classlikes); if ($has_changes) { $this->populator->populateCodebase(); } } /** * @param string $file_path * * @return string */ public function getFileContents($file_path) { return $this->file_provider->getContents($file_path); } /** * @param string $file_path * * @return array */ public function getStatementsForFile($file_path) { return $this->statements_provider->getStatementsForFile( $file_path, $this->debug_output ); } /** * @param string $fq_classlike_name * * @return ClassLikeStorage */ public function createClassLikeStorage($fq_classlike_name) { return $this->classlike_storage_provider->create($fq_classlike_name); } /** * @param string $file_path * * @return FileStorage */ public function createFileStorageForPath($file_path) { return $this->file_storage_provider->create($file_path); } /** * @param string $symbol * * @return array */ public function findReferencesToSymbol($symbol) { if (!$this->collect_references) { throw new \UnexpectedValueException('Should not be checking references'); } if (strpos($symbol, '::') !== false) { return $this->findReferencesToMethod($symbol); } return $this->findReferencesToClassLike($symbol); } /** * @param string $method_id * * @return array */ public function findReferencesToMethod($method_id) { list($fq_class_name, $method_name) = explode('::', $method_id); try { $class_storage = $this->classlike_storage_provider->get($fq_class_name); } catch (\InvalidArgumentException $e) { die('Class ' . $fq_class_name . ' cannot be found' . PHP_EOL); } $method_name_lc = strtolower($method_name); if (!isset($class_storage->methods[$method_name_lc])) { die('Method ' . $method_id . ' cannot be found' . PHP_EOL); } $method_storage = $class_storage->methods[$method_name_lc]; if ($method_storage->referencing_locations === null) { die('No references found for ' . $method_id . PHP_EOL); } return $method_storage->referencing_locations; } /** * @param string $fq_class_name * * @return array */ public function findReferencesToClassLike($fq_class_name) { try { $class_storage = $this->classlike_storage_provider->get($fq_class_name); } catch (\InvalidArgumentException $e) { die('Class ' . $fq_class_name . ' cannot be found' . PHP_EOL); } if ($class_storage->referencing_locations === null) { die('No references found for ' . $fq_class_name . PHP_EOL); } $classlike_references_by_file = $class_storage->referencing_locations; $fq_class_name_lc = strtolower($fq_class_name); if (isset($this->use_referencing_locations[$fq_class_name_lc])) { foreach ($this->use_referencing_locations[$fq_class_name_lc] as $file_path => $locations) { if (!isset($classlike_references_by_file[$file_path])) { $classlike_references_by_file[$file_path] = $locations; } else { $classlike_references_by_file[$file_path] = array_merge( $locations, $classlike_references_by_file[$file_path] ); } } } return $classlike_references_by_file; } /** * @param string $file_path * @param string $closure_id * * @return FunctionLikeStorage */ public function getClosureStorage($file_path, $closure_id) { $file_storage = $this->file_storage_provider->get($file_path); // closures can be returned here if (isset($file_storage->functions[$closure_id])) { return $file_storage->functions[$closure_id]; } throw new \UnexpectedValueException( 'Expecting ' . $closure_id . ' to have storage in ' . $file_path ); } /** * @param string $const_id * @param Type\Union $type * * @return void */ public function addStubbedConstantType($const_id, $type) { self::$stubbed_constants[$const_id] = $type; } /** * @param string $const_id * * @return Type\Union|null */ public function getStubbedConstantType($const_id) { return isset(self::$stubbed_constants[$const_id]) ? self::$stubbed_constants[$const_id] : null; } /** * @param string $file_path * * @return bool */ public function fileExists($file_path) { return $this->file_provider->fileExists($file_path); } /** * Check whether a class/interface exists * * @param string $fq_class_name * @param CodeLocation $code_location * * @return bool */ public function classOrInterfaceExists($fq_class_name, CodeLocation $code_location = null) { return $this->classlikes->classOrInterfaceExists($fq_class_name, $code_location); } /** * @param string $fq_class_name * @param string $possible_parent * * @return bool */ public function classExtendsOrImplements($fq_class_name, $possible_parent) { return $this->classlikes->classExtends($fq_class_name, $possible_parent) || $this->classlikes->classImplements($fq_class_name, $possible_parent); } /** * Determine whether or not a given class exists * * @param string $fq_class_name * * @return bool */ public function classExists($fq_class_name) { return $this->classlikes->classExists($fq_class_name); } /** * Determine whether or not a class extends a parent * * @param string $fq_class_name * @param string $possible_parent * * @return bool */ public function classExtends($fq_class_name, $possible_parent) { return $this->classlikes->classExtends($fq_class_name, $possible_parent); } /** * Check whether a class implements an interface * * @param string $fq_class_name * @param string $interface * * @return bool */ public function classImplements($fq_class_name, $interface) { return $this->classlikes->classImplements($fq_class_name, $interface); } /** * @param string $fq_interface_name * * @return bool */ public function interfaceExists($fq_interface_name) { return $this->classlikes->interfaceExists($fq_interface_name); } /** * @param string $interface_name * @param string $possible_parent * * @return bool */ public function interfaceExtends($interface_name, $possible_parent) { return $this->classlikes->interfaceExtends($interface_name, $possible_parent); } /** * @param string $fq_interface_name * * @return array all interfaces extended by $interface_name */ public function getParentInterfaces($fq_interface_name) { return $this->classlikes->getParentInterfaces($fq_interface_name); } /** * Determine whether or not a class has the correct casing * * @param string $fq_class_name * * @return bool */ public function classHasCorrectCasing($fq_class_name) { return $this->classlikes->classHasCorrectCasing($fq_class_name); } /** * @param string $fq_interface_name * * @return bool */ public function interfaceHasCorrectCasing($fq_interface_name) { return $this->classlikes->interfaceHasCorrectCasing($fq_interface_name); } /** * @param string $fq_trait_name * * @return bool */ public function traitHasCorrectCase($fq_trait_name) { return $this->classlikes->traitHasCorrectCase($fq_trait_name); } /** * Whether or not a given method exists * * @param string $method_id * @param CodeLocation|null $code_location * * @return bool */ public function methodExists($method_id, CodeLocation $code_location = null) { return $this->methods->methodExists($method_id, $code_location); } /** * @param string $method_id * * @return array */ public function getMethodParams($method_id) { return $this->methods->getMethodParams($method_id); } /** * @param string $method_id * * @return bool */ public function isVariadic($method_id) { return $this->methods->isVariadic($method_id); } /** * @param string $method_id * @param string $self_class * * @return Type\Union|null */ public function getMethodReturnType($method_id, &$self_class) { return $this->methods->getMethodReturnType($method_id, $self_class); } /** * @param string $method_id * * @return bool */ public function getMethodReturnsByRef($method_id) { return $this->methods->getMethodReturnsByRef($method_id); } /** * @param string $method_id * @param CodeLocation|null $defined_location * * @return CodeLocation|null */ public function getMethodReturnTypeLocation( $method_id, CodeLocation &$defined_location = null ) { return $this->methods->getMethodReturnTypeLocation($method_id, $defined_location); } /** * @param string $method_id * * @return string|null */ public function getDeclaringMethodId($method_id) { return $this->methods->getDeclaringMethodId($method_id); } /** * Get the class this method appears in (vs is declared in, which could give a trait) * * @param string $method_id * * @return string|null */ public function getAppearingMethodId($method_id) { return $this->methods->getAppearingMethodId($method_id); } /** * @param string $method_id * * @return array */ public function getOverriddenMethodIds($method_id) { return $this->methods->getOverriddenMethodIds($method_id); } /** * @param string $method_id * * @return string */ public function getCasedMethodId($method_id) { return $this->methods->getCasedMethodId($method_id); } }