2018-01-21 19:38:51 +01:00
|
|
|
<?php
|
2021-12-15 04:58:32 +01:00
|
|
|
|
2018-01-21 19:38:51 +01:00
|
|
|
namespace Psalm;
|
|
|
|
|
2022-02-15 01:18:49 +01:00
|
|
|
use InvalidArgumentException;
|
2018-01-21 19:38:51 +01:00
|
|
|
use PhpParser;
|
2021-12-04 03:37:19 +01:00
|
|
|
use PhpParser\Node\Arg;
|
|
|
|
use Psalm\CodeLocation;
|
|
|
|
use Psalm\Exception\UnpopulatedClasslikeException;
|
2022-02-15 01:18:49 +01:00
|
|
|
use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
|
|
|
|
use Psalm\Internal\Analyzer\ProjectAnalyzer;
|
2019-07-05 22:24:00 +02:00
|
|
|
use Psalm\Internal\Analyzer\Statements\Block\ForeachAnalyzer;
|
2021-06-08 04:55:21 +02:00
|
|
|
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
2021-12-04 03:37:19 +01:00
|
|
|
use Psalm\Internal\Codebase\Analyzer;
|
|
|
|
use Psalm\Internal\Codebase\ClassLikes;
|
|
|
|
use Psalm\Internal\Codebase\Functions;
|
|
|
|
use Psalm\Internal\Codebase\Methods;
|
|
|
|
use Psalm\Internal\Codebase\Populator;
|
|
|
|
use Psalm\Internal\Codebase\Properties;
|
2021-12-03 20:11:20 +01:00
|
|
|
use Psalm\Internal\Codebase\Reflection;
|
2021-12-04 03:37:19 +01:00
|
|
|
use Psalm\Internal\Codebase\Scanner;
|
|
|
|
use Psalm\Internal\Codebase\TaintFlowGraph;
|
2021-12-03 20:11:20 +01:00
|
|
|
use Psalm\Internal\DataFlow\TaintSink;
|
|
|
|
use Psalm\Internal\DataFlow\TaintSource;
|
|
|
|
use Psalm\Internal\MethodIdentifier;
|
2018-11-06 03:57:36 +01:00
|
|
|
use Psalm\Internal\Provider\ClassLikeStorageProvider;
|
|
|
|
use Psalm\Internal\Provider\FileProvider;
|
|
|
|
use Psalm\Internal\Provider\FileReferenceProvider;
|
|
|
|
use Psalm\Internal\Provider\FileStorageProvider;
|
|
|
|
use Psalm\Internal\Provider\Providers;
|
|
|
|
use Psalm\Internal\Provider\StatementsProvider;
|
2021-06-08 04:55:21 +02:00
|
|
|
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
|
2019-05-30 16:30:41 +02:00
|
|
|
use Psalm\Progress\Progress;
|
|
|
|
use Psalm\Progress\VoidProgress;
|
2018-01-21 19:38:51 +01:00
|
|
|
use Psalm\Storage\ClassLikeStorage;
|
|
|
|
use Psalm\Storage\FileStorage;
|
2021-12-04 03:37:19 +01:00
|
|
|
use Psalm\Storage\FunctionLikeParameter;
|
2018-01-21 19:38:51 +01:00
|
|
|
use Psalm\Storage\FunctionLikeStorage;
|
2021-12-14 00:45:01 +01:00
|
|
|
use Psalm\Storage\FunctionStorage;
|
|
|
|
use Psalm\Storage\MethodStorage;
|
2021-12-13 16:28:14 +01:00
|
|
|
use Psalm\Type\Atomic;
|
2021-12-03 20:11:20 +01:00
|
|
|
use Psalm\Type\TaintKindGroup;
|
2021-12-13 16:28:14 +01:00
|
|
|
use Psalm\Type\Union;
|
2021-12-03 21:40:18 +01:00
|
|
|
use ReflectionType;
|
|
|
|
use UnexpectedValueException;
|
2021-06-08 04:55:21 +02:00
|
|
|
|
2022-02-15 01:18:49 +01:00
|
|
|
use function array_combine;
|
2021-06-08 04:55:21 +02:00
|
|
|
use function array_merge;
|
|
|
|
use function explode;
|
2022-02-15 01:18:49 +01:00
|
|
|
use function in_array;
|
2020-02-15 02:54:26 +01:00
|
|
|
use function is_string;
|
2019-06-26 22:52:29 +02:00
|
|
|
use function strpos;
|
|
|
|
use function strtolower;
|
|
|
|
use function substr;
|
2021-06-08 04:55:21 +02:00
|
|
|
|
|
|
|
use const PHP_MAJOR_VERSION;
|
|
|
|
use const PHP_MINOR_VERSION;
|
2022-01-02 12:22:37 +01:00
|
|
|
use const PHP_VERSION_ID;
|
2018-01-21 19:38:51 +01:00
|
|
|
|
|
|
|
class Codebase
|
|
|
|
{
|
|
|
|
/**
|
2018-04-05 20:40:41 +02:00
|
|
|
* @var Config
|
2018-01-21 19:38:51 +01:00
|
|
|
*/
|
|
|
|
public $config;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A map of fully-qualified use declarations to the files
|
|
|
|
* that reference them (keyed by filename)
|
|
|
|
*
|
2021-12-04 03:37:19 +01:00
|
|
|
* @var array<lowercase-string, array<int, CodeLocation>>
|
2018-01-21 19:38:51 +01:00
|
|
|
*/
|
|
|
|
public $use_referencing_locations = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var FileStorageProvider
|
|
|
|
*/
|
|
|
|
public $file_storage_provider;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var ClassLikeStorageProvider
|
|
|
|
*/
|
|
|
|
public $classlike_storage_provider;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
public $collect_references = false;
|
|
|
|
|
2019-04-13 00:28:07 +02:00
|
|
|
/**
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
public $collect_locations = false;
|
|
|
|
|
2019-04-17 19:15:06 +02:00
|
|
|
/**
|
|
|
|
* @var null|'always'|'auto'
|
|
|
|
*/
|
2021-09-26 23:24:07 +02:00
|
|
|
public $find_unused_code;
|
2019-04-17 19:15:06 +02:00
|
|
|
|
2018-01-21 19:38:51 +01:00
|
|
|
/**
|
|
|
|
* @var FileProvider
|
|
|
|
*/
|
2018-10-26 06:59:14 +02:00
|
|
|
public $file_provider;
|
2018-01-21 19:38:51 +01:00
|
|
|
|
2018-10-07 04:58:21 +02:00
|
|
|
/**
|
|
|
|
* @var FileReferenceProvider
|
|
|
|
*/
|
|
|
|
public $file_reference_provider;
|
|
|
|
|
2018-01-21 19:38:51 +01:00
|
|
|
/**
|
|
|
|
* @var StatementsProvider
|
|
|
|
*/
|
2018-02-19 17:53:30 +01:00
|
|
|
public $statements_provider;
|
2018-01-21 19:38:51 +01:00
|
|
|
|
|
|
|
/**
|
2019-05-30 16:30:41 +02:00
|
|
|
* @var Progress
|
2018-01-21 19:38:51 +01:00
|
|
|
*/
|
2019-05-30 16:30:41 +02:00
|
|
|
private $progress;
|
2018-01-21 19:38:51 +01:00
|
|
|
|
|
|
|
/**
|
2021-12-13 16:28:14 +01:00
|
|
|
* @var array<string, Union>
|
2018-01-21 19:38:51 +01:00
|
|
|
*/
|
2018-02-04 00:52:35 +01:00
|
|
|
private static $stubbed_constants = [];
|
2018-01-21 19:38:51 +01:00
|
|
|
|
2018-06-30 21:29:37 +02:00
|
|
|
/**
|
|
|
|
* Whether to register autoloaded information
|
|
|
|
*
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
public $register_autoload_files = false;
|
|
|
|
|
2018-01-21 19:38:51 +01:00
|
|
|
/**
|
2018-02-04 00:52:35 +01:00
|
|
|
* Whether to log functions just at the file level or globally (for stubs)
|
|
|
|
*
|
|
|
|
* @var bool
|
2018-01-21 19:38:51 +01:00
|
|
|
*/
|
2018-06-30 21:29:37 +02:00
|
|
|
public $register_stub_files = false;
|
2018-01-21 19:38:51 +01:00
|
|
|
|
2019-03-05 21:45:09 +01:00
|
|
|
/**
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
public $find_unused_variables = false;
|
|
|
|
|
2018-01-21 19:38:51 +01:00
|
|
|
/**
|
2021-12-04 03:37:19 +01:00
|
|
|
* @var Scanner
|
2018-01-21 19:38:51 +01:00
|
|
|
*/
|
2018-02-04 00:52:35 +01:00
|
|
|
public $scanner;
|
2018-01-21 19:38:51 +01:00
|
|
|
|
|
|
|
/**
|
2021-12-04 03:37:19 +01:00
|
|
|
* @var Analyzer
|
2022-02-15 01:18:49 +01:00
|
|
|
* @psalm-suppress PropertyNotSetInConstructor
|
2018-01-21 19:38:51 +01:00
|
|
|
*/
|
2018-02-04 00:52:35 +01:00
|
|
|
public $analyzer;
|
2018-01-21 19:38:51 +01:00
|
|
|
|
|
|
|
/**
|
2021-12-04 03:37:19 +01:00
|
|
|
* @var Functions
|
2018-01-21 19:38:51 +01:00
|
|
|
*/
|
2018-02-04 00:52:35 +01:00
|
|
|
public $functions;
|
2018-01-21 19:38:51 +01:00
|
|
|
|
|
|
|
/**
|
2021-12-04 03:37:19 +01:00
|
|
|
* @var ClassLikes
|
2018-01-21 19:38:51 +01:00
|
|
|
*/
|
2018-02-04 00:52:35 +01:00
|
|
|
public $classlikes;
|
2018-01-21 19:38:51 +01:00
|
|
|
|
2018-01-31 22:08:52 +01:00
|
|
|
/**
|
2021-12-04 03:37:19 +01:00
|
|
|
* @var Methods
|
2018-01-31 22:08:52 +01:00
|
|
|
*/
|
2018-02-04 00:52:35 +01:00
|
|
|
public $methods;
|
2018-01-31 22:08:52 +01:00
|
|
|
|
2018-02-09 00:14:28 +01:00
|
|
|
/**
|
2021-12-04 03:37:19 +01:00
|
|
|
* @var Properties
|
2018-02-09 00:14:28 +01:00
|
|
|
*/
|
|
|
|
public $properties;
|
|
|
|
|
2018-01-31 22:08:52 +01:00
|
|
|
/**
|
2021-12-04 03:37:19 +01:00
|
|
|
* @var Populator
|
2018-01-31 22:08:52 +01:00
|
|
|
*/
|
2018-02-04 00:52:35 +01:00
|
|
|
public $populator;
|
2018-01-31 22:08:52 +01:00
|
|
|
|
2019-08-04 16:37:36 +02:00
|
|
|
/**
|
2021-12-04 03:37:19 +01:00
|
|
|
* @var ?TaintFlowGraph
|
2019-08-04 16:37:36 +02:00
|
|
|
*/
|
2021-09-26 23:24:07 +02:00
|
|
|
public $taint_flow_graph;
|
2019-08-04 16:37:36 +02:00
|
|
|
|
2018-10-17 21:52:26 +02:00
|
|
|
/**
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
public $server_mode = false;
|
|
|
|
|
2019-02-24 07:33:25 +01:00
|
|
|
/**
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
public $store_node_types = false;
|
|
|
|
|
2018-11-06 03:57:36 +01:00
|
|
|
/**
|
|
|
|
* Whether or not to infer types from usage. Computationally expensive, so turned off by default
|
|
|
|
*
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
public $infer_types_from_usage = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
public $alter_code = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
public $diff_methods = false;
|
|
|
|
|
2019-06-01 06:56:54 +02:00
|
|
|
/**
|
2020-02-15 02:54:26 +01:00
|
|
|
* @var array<lowercase-string, string>
|
2019-06-01 06:56:54 +02:00
|
|
|
*/
|
2019-06-02 18:02:32 +02:00
|
|
|
public $methods_to_move = [];
|
|
|
|
|
|
|
|
/**
|
2020-02-15 02:54:26 +01:00
|
|
|
* @var array<lowercase-string, string>
|
2019-06-02 18:02:32 +02:00
|
|
|
*/
|
|
|
|
public $methods_to_rename = [];
|
2019-06-01 06:56:54 +02:00
|
|
|
|
2019-06-04 06:32:19 +02:00
|
|
|
/**
|
|
|
|
* @var array<string, string>
|
|
|
|
*/
|
|
|
|
public $properties_to_move = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array<string, string>
|
|
|
|
*/
|
|
|
|
public $properties_to_rename = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array<string, string>
|
|
|
|
*/
|
|
|
|
public $class_constants_to_move = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array<string, string>
|
|
|
|
*/
|
|
|
|
public $class_constants_to_rename = [];
|
|
|
|
|
2019-06-04 22:36:32 +02:00
|
|
|
/**
|
2020-02-15 02:54:26 +01:00
|
|
|
* @var array<lowercase-string, string>
|
2019-06-04 22:36:32 +02:00
|
|
|
*/
|
|
|
|
public $classes_to_move = [];
|
|
|
|
|
2019-06-01 06:56:54 +02:00
|
|
|
/**
|
2020-12-29 12:42:12 +01:00
|
|
|
* @var array<lowercase-string, string>
|
2019-06-01 06:56:54 +02:00
|
|
|
*/
|
|
|
|
public $call_transforms = [];
|
|
|
|
|
2019-06-04 06:32:19 +02:00
|
|
|
/**
|
|
|
|
* @var array<string, string>
|
|
|
|
*/
|
|
|
|
public $property_transforms = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array<string, string>
|
|
|
|
*/
|
|
|
|
public $class_constant_transforms = [];
|
|
|
|
|
2019-06-04 22:36:32 +02:00
|
|
|
/**
|
2020-03-15 04:54:42 +01:00
|
|
|
* @var array<lowercase-string, string>
|
2019-06-04 22:36:32 +02:00
|
|
|
*/
|
|
|
|
public $class_transforms = [];
|
|
|
|
|
2019-05-13 20:38:18 +02:00
|
|
|
/**
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
public $allow_backwards_incompatible_changes = true;
|
|
|
|
|
2019-02-07 18:25:57 +01:00
|
|
|
/**
|
|
|
|
* @var int
|
2022-01-02 12:22:37 +01:00
|
|
|
* @deprecated Removed in Psalm 5, use Codebase::$analysis_php_version_id
|
2019-02-07 18:25:57 +01:00
|
|
|
*/
|
|
|
|
public $php_major_version = PHP_MAJOR_VERSION;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var int
|
2022-01-02 12:22:37 +01:00
|
|
|
* @deprecated Removed in Psalm 5, use Codebase::$analysis_php_version_id
|
2019-02-07 18:25:57 +01:00
|
|
|
*/
|
|
|
|
public $php_minor_version = PHP_MINOR_VERSION;
|
|
|
|
|
2022-01-02 12:22:37 +01:00
|
|
|
/** @var int */
|
|
|
|
public $analysis_php_version_id = PHP_VERSION_ID;
|
|
|
|
|
2021-11-27 01:06:33 +01:00
|
|
|
/** @var 'cli'|'config'|'composer'|'tests'|'runtime' */
|
|
|
|
public $php_version_source = 'runtime';
|
|
|
|
|
2019-08-18 20:27:50 +02:00
|
|
|
/**
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
public $track_unused_suppressions = false;
|
2019-08-04 16:37:36 +02:00
|
|
|
|
2018-01-21 19:38:51 +01:00
|
|
|
public function __construct(
|
|
|
|
Config $config,
|
2018-09-28 22:18:45 +02:00
|
|
|
Providers $providers,
|
2020-09-07 01:36:47 +02:00
|
|
|
?Progress $progress = null
|
2018-01-21 19:38:51 +01:00
|
|
|
) {
|
2019-05-30 16:30:41 +02:00
|
|
|
if ($progress === null) {
|
|
|
|
$progress = new VoidProgress();
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:38:51 +01:00
|
|
|
$this->config = $config;
|
2018-09-28 22:18:45 +02:00
|
|
|
$this->file_storage_provider = $providers->file_storage_provider;
|
|
|
|
$this->classlike_storage_provider = $providers->classlike_storage_provider;
|
2019-05-30 16:30:41 +02:00
|
|
|
$this->progress = $progress;
|
2018-09-28 22:18:45 +02:00
|
|
|
$this->file_provider = $providers->file_provider;
|
2018-10-07 04:58:21 +02:00
|
|
|
$this->file_reference_provider = $providers->file_reference_provider;
|
2018-09-28 22:18:45 +02:00
|
|
|
$this->statements_provider = $providers->statements_provider;
|
2018-01-21 19:38:51 +01:00
|
|
|
|
|
|
|
self::$stubbed_constants = [];
|
|
|
|
|
2021-12-14 01:54:11 +01:00
|
|
|
$reflection = new Reflection($providers->classlike_storage_provider, $this);
|
2018-01-21 19:38:51 +01:00
|
|
|
|
2021-12-04 03:37:19 +01:00
|
|
|
$this->scanner = new Scanner(
|
2018-02-04 00:52:35 +01:00
|
|
|
$this,
|
|
|
|
$config,
|
2018-09-28 22:18:45 +02:00
|
|
|
$providers->file_storage_provider,
|
|
|
|
$providers->file_provider,
|
2020-01-03 19:10:24 +01:00
|
|
|
$reflection,
|
2018-09-28 22:18:45 +02:00
|
|
|
$providers->file_reference_provider,
|
2019-05-30 16:30:41 +02:00
|
|
|
$progress
|
2018-02-04 00:52:35 +01:00
|
|
|
);
|
2018-01-21 19:38:51 +01:00
|
|
|
|
2018-09-28 22:18:45 +02:00
|
|
|
$this->loadAnalyzer();
|
2018-01-21 19:38:51 +01:00
|
|
|
|
2021-12-04 03:37:19 +01:00
|
|
|
$this->functions = new Functions($providers->file_storage_provider, $reflection);
|
2018-12-21 17:32:44 +01:00
|
|
|
|
2021-12-04 03:37:19 +01:00
|
|
|
$this->classlikes = new ClassLikes(
|
2018-10-07 02:11:19 +02:00
|
|
|
$this->config,
|
2018-09-28 22:18:45 +02:00
|
|
|
$providers->classlike_storage_provider,
|
2019-04-13 00:28:07 +02:00
|
|
|
$providers->file_reference_provider,
|
2020-03-03 04:27:54 +01:00
|
|
|
$providers->statements_provider,
|
2018-12-21 17:32:44 +01:00
|
|
|
$this->scanner
|
2018-02-04 00:52:35 +01:00
|
|
|
);
|
2018-12-21 17:32:44 +01:00
|
|
|
|
2021-12-04 03:37:19 +01:00
|
|
|
$this->properties = new Properties(
|
2021-01-22 01:38:17 +01:00
|
|
|
$providers->classlike_storage_provider,
|
|
|
|
$providers->file_reference_provider,
|
|
|
|
$this->classlikes
|
|
|
|
);
|
|
|
|
|
2021-12-04 03:37:19 +01:00
|
|
|
$this->methods = new Methods(
|
2018-12-21 17:32:44 +01:00
|
|
|
$providers->classlike_storage_provider,
|
|
|
|
$providers->file_reference_provider,
|
|
|
|
$this->classlikes
|
|
|
|
);
|
|
|
|
|
2021-12-04 03:37:19 +01:00
|
|
|
$this->populator = new Populator(
|
2018-02-04 00:52:35 +01:00
|
|
|
$config,
|
2018-09-28 22:18:45 +02:00
|
|
|
$providers->classlike_storage_provider,
|
|
|
|
$providers->file_storage_provider,
|
2018-02-04 00:52:35 +01:00
|
|
|
$this->classlikes,
|
2018-09-28 22:18:45 +02:00
|
|
|
$providers->file_reference_provider,
|
2019-05-30 16:30:41 +02:00
|
|
|
$progress
|
2018-02-04 00:52:35 +01:00
|
|
|
);
|
2018-10-07 02:11:19 +02:00
|
|
|
|
|
|
|
$this->loadAnalyzer();
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
|
|
|
|
2022-02-14 22:02:05 +01:00
|
|
|
protected function loadAnalyzer(): void
|
2018-09-28 22:18:45 +02:00
|
|
|
{
|
2021-12-04 03:37:19 +01:00
|
|
|
$this->analyzer = new Analyzer(
|
2018-09-28 22:18:45 +02:00
|
|
|
$this->config,
|
|
|
|
$this->file_provider,
|
|
|
|
$this->file_storage_provider,
|
2019-05-30 16:30:41 +02:00
|
|
|
$this->progress
|
2018-09-28 22:18:45 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-09-12 17:24:05 +02:00
|
|
|
public function enterServerMode(): void
|
2018-10-17 21:52:26 +02:00
|
|
|
{
|
|
|
|
$this->server_mode = true;
|
2019-02-24 07:33:25 +01:00
|
|
|
$this->store_node_types = true;
|
2018-10-17 21:52:26 +02:00
|
|
|
}
|
|
|
|
|
2020-09-12 17:24:05 +02:00
|
|
|
public function collectLocations(): void
|
2019-04-13 00:28:07 +02:00
|
|
|
{
|
|
|
|
$this->collect_locations = true;
|
|
|
|
$this->classlikes->collect_locations = true;
|
|
|
|
$this->methods->collect_locations = true;
|
|
|
|
$this->properties->collect_locations = true;
|
|
|
|
}
|
|
|
|
|
2018-02-17 23:45:30 +01:00
|
|
|
/**
|
2019-04-17 19:15:06 +02:00
|
|
|
* @param 'always'|'auto' $find_unused_code
|
|
|
|
*
|
2018-02-17 23:45:30 +01:00
|
|
|
*/
|
2020-09-12 17:24:05 +02:00
|
|
|
public function reportUnusedCode(string $find_unused_code = 'auto'): void
|
2018-02-17 23:45:30 +01:00
|
|
|
{
|
2019-04-17 19:15:06 +02:00
|
|
|
$this->collect_references = true;
|
|
|
|
$this->classlikes->collect_references = true;
|
|
|
|
$this->find_unused_code = $find_unused_code;
|
2019-03-05 21:45:09 +01:00
|
|
|
$this->find_unused_variables = true;
|
|
|
|
}
|
|
|
|
|
2020-09-12 17:24:05 +02:00
|
|
|
public function reportUnusedVariables(): void
|
2019-03-05 21:45:09 +01:00
|
|
|
{
|
|
|
|
$this->collect_references = true;
|
|
|
|
$this->find_unused_variables = true;
|
2018-02-17 23:45:30 +01:00
|
|
|
}
|
|
|
|
|
2018-01-21 19:38:51 +01:00
|
|
|
/**
|
2018-02-04 00:52:35 +01:00
|
|
|
* @param array<string, string> $files_to_analyze
|
2018-01-21 19:38:51 +01:00
|
|
|
*
|
|
|
|
*/
|
2020-09-12 17:24:05 +02:00
|
|
|
public function addFilesToAnalyze(array $files_to_analyze): void
|
2018-01-21 19:38:51 +01:00
|
|
|
{
|
2018-02-04 00:52:35 +01:00
|
|
|
$this->scanner->addFilesToDeepScan($files_to_analyze);
|
2020-03-26 19:22:06 +01:00
|
|
|
$this->analyzer->addFilesToAnalyze($files_to_analyze);
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
|
|
|
|
2018-02-01 07:10:27 +01:00
|
|
|
/**
|
2018-02-04 00:52:35 +01:00
|
|
|
* Scans all files their related files
|
2018-02-01 07:10:27 +01:00
|
|
|
*
|
|
|
|
*/
|
2020-09-12 17:24:05 +02:00
|
|
|
public function scanFiles(int $threads = 1): void
|
2018-02-01 07:10:27 +01:00
|
|
|
{
|
2018-10-11 19:58:39 +02:00
|
|
|
$has_changes = $this->scanner->scanFiles($this->classlikes, $threads);
|
2018-02-01 07:10:27 +01:00
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
if ($has_changes) {
|
2019-09-14 20:26:31 +02:00
|
|
|
$this->populator->populateCodebase();
|
2018-02-01 07:10:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-07 01:36:47 +02:00
|
|
|
public function getFileContents(string $file_path): string
|
2018-01-21 19:38:51 +01:00
|
|
|
{
|
2018-02-04 00:52:35 +01:00
|
|
|
return $this->file_provider->getContents($file_path);
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-10-09 01:06:16 +02:00
|
|
|
* @return list<PhpParser\Node\Stmt>
|
2018-01-21 19:38:51 +01:00
|
|
|
*/
|
2020-09-12 17:24:05 +02:00
|
|
|
public function getStatementsForFile(string $file_path): array
|
2018-01-21 19:38:51 +01:00
|
|
|
{
|
2018-02-04 00:52:35 +01:00
|
|
|
return $this->statements_provider->getStatementsForFile(
|
|
|
|
$file_path,
|
2020-08-09 22:23:43 +02:00
|
|
|
$this->php_major_version . '.' . $this->php_minor_version,
|
2019-05-30 16:30:41 +02:00
|
|
|
$this->progress
|
2018-02-04 00:52:35 +01:00
|
|
|
);
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
|
|
|
|
2020-09-07 01:36:47 +02:00
|
|
|
public function createClassLikeStorage(string $fq_classlike_name): ClassLikeStorage
|
2018-01-21 19:38:51 +01:00
|
|
|
{
|
2018-02-04 00:52:35 +01:00
|
|
|
return $this->classlike_storage_provider->create($fq_classlike_name);
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
|
|
|
|
2020-09-12 17:24:05 +02:00
|
|
|
public function cacheClassLikeStorage(ClassLikeStorage $classlike_storage, string $file_path): void
|
2018-02-19 06:27:39 +01:00
|
|
|
{
|
|
|
|
$file_contents = $this->file_provider->getContents($file_path);
|
2018-09-28 22:18:45 +02:00
|
|
|
|
|
|
|
if ($this->classlike_storage_provider->cache) {
|
|
|
|
$this->classlike_storage_provider->cache->writeToCache($classlike_storage, $file_path, $file_contents);
|
|
|
|
}
|
2018-02-19 06:27:39 +01:00
|
|
|
}
|
|
|
|
|
2020-09-12 17:24:05 +02:00
|
|
|
public function exhumeClassLikeStorage(string $fq_classlike_name, string $file_path): void
|
2018-02-19 06:27:39 +01:00
|
|
|
{
|
|
|
|
$file_contents = $this->file_provider->getContents($file_path);
|
2020-02-15 02:54:26 +01:00
|
|
|
$storage = $this->classlike_storage_provider->exhume(
|
|
|
|
$fq_classlike_name,
|
|
|
|
$file_path,
|
|
|
|
$file_contents
|
|
|
|
);
|
2018-02-19 06:27:39 +01:00
|
|
|
|
|
|
|
if ($storage->is_trait) {
|
2020-02-15 02:54:26 +01:00
|
|
|
$this->classlikes->addFullyQualifiedTraitName($storage->name, $file_path);
|
2018-02-19 06:27:39 +01:00
|
|
|
} elseif ($storage->is_interface) {
|
2020-02-15 02:54:26 +01:00
|
|
|
$this->classlikes->addFullyQualifiedInterfaceName($storage->name, $file_path);
|
2018-02-19 06:27:39 +01:00
|
|
|
} else {
|
2020-02-15 02:54:26 +01:00
|
|
|
$this->classlikes->addFullyQualifiedClassName($storage->name, $file_path);
|
2018-02-19 06:27:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-13 16:28:14 +01:00
|
|
|
public static function getPsalmTypeFromReflection(?ReflectionType $type): Union
|
2019-01-27 23:26:32 +01:00
|
|
|
{
|
2021-12-03 20:11:20 +01:00
|
|
|
return Reflection::getPsalmTypeFromReflectionType($type);
|
2019-01-27 23:26:32 +01:00
|
|
|
}
|
|
|
|
|
2020-09-07 01:36:47 +02:00
|
|
|
public function createFileStorageForPath(string $file_path): FileStorage
|
2018-01-21 19:38:51 +01:00
|
|
|
{
|
2018-02-04 00:52:35 +01:00
|
|
|
return $this->file_storage_provider->create($file_path);
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-17 18:36:44 +02:00
|
|
|
* @return array<int, CodeLocation>
|
2018-01-21 19:38:51 +01:00
|
|
|
*/
|
2020-09-07 01:36:47 +02:00
|
|
|
public function findReferencesToSymbol(string $symbol): array
|
2018-01-21 19:38:51 +01:00
|
|
|
{
|
2019-04-13 00:28:07 +02:00
|
|
|
if (!$this->collect_locations) {
|
2021-12-03 21:40:18 +01:00
|
|
|
throw new UnexpectedValueException('Should not be checking references');
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
2019-01-22 17:10:37 +01:00
|
|
|
if (strpos($symbol, '::$') !== false) {
|
|
|
|
return $this->findReferencesToProperty($symbol);
|
|
|
|
}
|
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
if (strpos($symbol, '::') !== false) {
|
|
|
|
return $this->findReferencesToMethod($symbol);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->findReferencesToClassLike($symbol);
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-17 18:36:44 +02:00
|
|
|
* @return array<int, CodeLocation>
|
2018-01-21 19:38:51 +01:00
|
|
|
*/
|
2020-09-07 01:36:47 +02:00
|
|
|
public function findReferencesToMethod(string $method_id): array
|
2018-01-21 19:38:51 +01:00
|
|
|
{
|
2019-04-13 00:28:07 +02:00
|
|
|
return $this->file_reference_provider->getClassMethodLocations(strtolower($method_id));
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
|
|
|
|
2019-01-22 17:10:37 +01:00
|
|
|
/**
|
2020-10-17 18:36:44 +02:00
|
|
|
* @return array<int, CodeLocation>
|
2019-01-22 17:10:37 +01:00
|
|
|
*/
|
2020-09-04 22:26:33 +02:00
|
|
|
public function findReferencesToProperty(string $property_id): array
|
2019-01-22 17:10:37 +01:00
|
|
|
{
|
2020-09-02 06:17:41 +02:00
|
|
|
[$fq_class_name, $property_name] = explode('::', $property_id);
|
2019-07-05 22:24:00 +02:00
|
|
|
|
2019-04-13 00:28:07 +02:00
|
|
|
return $this->file_reference_provider->getClassPropertyLocations(
|
|
|
|
strtolower($fq_class_name) . '::' . $property_name
|
|
|
|
);
|
2019-01-22 17:10:37 +01:00
|
|
|
}
|
|
|
|
|
2018-01-21 19:38:51 +01:00
|
|
|
/**
|
2020-10-17 18:36:44 +02:00
|
|
|
* @return CodeLocation[]
|
|
|
|
*
|
|
|
|
* @psalm-return array<int, CodeLocation>
|
2018-01-21 19:38:51 +01:00
|
|
|
*/
|
2020-09-07 01:36:47 +02:00
|
|
|
public function findReferencesToClassLike(string $fq_class_name): array
|
2018-01-21 19:38:51 +01:00
|
|
|
{
|
2018-02-04 00:52:35 +01:00
|
|
|
$fq_class_name_lc = strtolower($fq_class_name);
|
2019-04-13 00:28:07 +02:00
|
|
|
$locations = $this->file_reference_provider->getClassLocations($fq_class_name_lc);
|
2018-01-21 19:38:51 +01:00
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
if (isset($this->use_referencing_locations[$fq_class_name_lc])) {
|
2019-04-13 00:28:07 +02:00
|
|
|
$locations = array_merge($locations, $this->use_referencing_locations[$fq_class_name_lc]);
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
|
|
|
|
2019-04-13 00:28:07 +02:00
|
|
|
return $locations;
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
|
|
|
|
2021-12-14 00:45:01 +01:00
|
|
|
public function getClosureStorage(string $file_path, string $closure_id): FunctionStorage
|
2018-01-31 23:09:09 +01:00
|
|
|
{
|
|
|
|
$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];
|
|
|
|
}
|
|
|
|
|
2021-12-03 21:40:18 +01:00
|
|
|
throw new UnexpectedValueException(
|
2018-01-31 23:09:09 +01:00
|
|
|
'Expecting ' . $closure_id . ' to have storage in ' . $file_path
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-12-13 16:28:14 +01:00
|
|
|
public function addGlobalConstantType(string $const_id, Union $type): void
|
2018-01-21 19:38:51 +01:00
|
|
|
{
|
|
|
|
self::$stubbed_constants[$const_id] = $type;
|
|
|
|
}
|
|
|
|
|
2021-12-13 16:28:14 +01:00
|
|
|
public function getStubbedConstantType(string $const_id): ?Union
|
2018-01-21 19:38:51 +01:00
|
|
|
{
|
2021-09-26 22:06:24 +02:00
|
|
|
return self::$stubbed_constants[$const_id] ?? null;
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
|
|
|
|
2020-01-09 05:48:42 +01:00
|
|
|
/**
|
2021-12-13 16:28:14 +01:00
|
|
|
* @return array<string, Union>
|
2020-01-09 05:48:42 +01:00
|
|
|
*/
|
2020-09-04 22:26:33 +02:00
|
|
|
public function getAllStubbedConstants(): array
|
2020-01-09 05:48:42 +01:00
|
|
|
{
|
|
|
|
return self::$stubbed_constants;
|
|
|
|
}
|
|
|
|
|
2020-09-07 01:36:47 +02:00
|
|
|
public function fileExists(string $file_path): bool
|
2018-01-21 19:38:51 +01:00
|
|
|
{
|
2018-02-04 00:52:35 +01:00
|
|
|
return $this->file_provider->fileExists($file_path);
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-02-04 00:52:35 +01:00
|
|
|
* Check whether a class/interface exists
|
2018-01-21 19:38:51 +01:00
|
|
|
*/
|
2020-03-26 17:35:27 +01:00
|
|
|
public function classOrInterfaceExists(
|
2020-09-07 01:36:47 +02:00
|
|
|
string $fq_class_name,
|
|
|
|
?CodeLocation $code_location = null,
|
2020-03-29 03:39:44 +02:00
|
|
|
?string $calling_fq_class_name = null,
|
2020-03-26 17:35:27 +01:00
|
|
|
?string $calling_method_id = null
|
2020-09-04 22:26:33 +02:00
|
|
|
): bool {
|
2020-03-29 03:39:44 +02:00
|
|
|
return $this->classlikes->classOrInterfaceExists(
|
|
|
|
$fq_class_name,
|
|
|
|
$code_location,
|
|
|
|
$calling_fq_class_name,
|
|
|
|
$calling_method_id
|
|
|
|
);
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
|
|
|
|
2021-05-03 23:54:09 +02:00
|
|
|
/**
|
|
|
|
* Check whether a class/interface exists
|
|
|
|
*/
|
|
|
|
public function classOrInterfaceOrEnumExists(
|
|
|
|
string $fq_class_name,
|
|
|
|
?CodeLocation $code_location = null,
|
|
|
|
?string $calling_fq_class_name = null,
|
|
|
|
?string $calling_method_id = null
|
|
|
|
): bool {
|
|
|
|
return $this->classlikes->classOrInterfaceOrEnumExists(
|
|
|
|
$fq_class_name,
|
|
|
|
$code_location,
|
|
|
|
$calling_fq_class_name,
|
|
|
|
$calling_method_id
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-09-07 01:36:47 +02:00
|
|
|
public function classExtendsOrImplements(string $fq_class_name, string $possible_parent): bool
|
2018-01-21 19:38:51 +01:00
|
|
|
{
|
2018-02-04 00:52:35 +01:00
|
|
|
return $this->classlikes->classExtends($fq_class_name, $possible_parent)
|
|
|
|
|| $this->classlikes->classImplements($fq_class_name, $possible_parent);
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-02-04 00:52:35 +01:00
|
|
|
* Determine whether or not a given class exists
|
2018-01-21 19:38:51 +01:00
|
|
|
*/
|
2020-03-29 03:39:44 +02:00
|
|
|
public function classExists(
|
2020-09-07 01:36:47 +02:00
|
|
|
string $fq_class_name,
|
|
|
|
?CodeLocation $code_location = null,
|
2020-03-29 03:39:44 +02:00
|
|
|
?string $calling_fq_class_name = null,
|
|
|
|
?string $calling_method_id = null
|
2020-09-04 22:26:33 +02:00
|
|
|
): bool {
|
2020-03-29 03:39:44 +02:00
|
|
|
return $this->classlikes->classExists(
|
|
|
|
$fq_class_name,
|
|
|
|
$code_location,
|
|
|
|
$calling_fq_class_name,
|
|
|
|
$calling_method_id
|
|
|
|
);
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-02-04 00:52:35 +01:00
|
|
|
* Determine whether or not a class extends a parent
|
2018-01-21 19:38:51 +01:00
|
|
|
*
|
2021-12-04 03:37:19 +01:00
|
|
|
* @throws UnpopulatedClasslikeException when called on unpopulated class
|
|
|
|
* @throws InvalidArgumentException when class does not exist
|
2018-01-21 19:38:51 +01:00
|
|
|
*/
|
2020-09-07 01:36:47 +02:00
|
|
|
public function classExtends(string $fq_class_name, string $possible_parent): bool
|
2018-01-21 19:38:51 +01:00
|
|
|
{
|
2019-03-17 22:11:04 +01:00
|
|
|
return $this->classlikes->classExtends($fq_class_name, $possible_parent, true);
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-02-04 00:52:35 +01:00
|
|
|
* Check whether a class implements an interface
|
2018-01-21 19:38:51 +01:00
|
|
|
*/
|
2020-09-07 01:36:47 +02:00
|
|
|
public function classImplements(string $fq_class_name, string $interface): bool
|
2018-01-21 19:38:51 +01:00
|
|
|
{
|
2018-02-04 00:52:35 +01:00
|
|
|
return $this->classlikes->classImplements($fq_class_name, $interface);
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
2020-09-21 00:27:02 +02:00
|
|
|
|
2020-03-26 17:35:27 +01:00
|
|
|
public function interfaceExists(
|
2020-09-07 01:36:47 +02:00
|
|
|
string $fq_interface_name,
|
|
|
|
?CodeLocation $code_location = null,
|
2020-03-29 03:39:44 +02:00
|
|
|
?string $calling_fq_class_name = null,
|
2020-03-26 17:35:27 +01:00
|
|
|
?string $calling_method_id = null
|
2020-09-04 22:26:33 +02:00
|
|
|
): bool {
|
2020-03-29 03:39:44 +02:00
|
|
|
return $this->classlikes->interfaceExists(
|
|
|
|
$fq_interface_name,
|
|
|
|
$code_location,
|
|
|
|
$calling_fq_class_name,
|
|
|
|
$calling_method_id
|
|
|
|
);
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|
2018-01-31 22:08:52 +01:00
|
|
|
|
2020-09-07 01:36:47 +02:00
|
|
|
public function interfaceExtends(string $interface_name, string $possible_parent): bool
|
2018-01-31 22:08:52 +01:00
|
|
|
{
|
2018-02-04 00:52:35 +01:00
|
|
|
return $this->classlikes->interfaceExtends($interface_name, $possible_parent);
|
|
|
|
}
|
2018-01-31 22:08:52 +01:00
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
/**
|
2020-10-17 18:36:44 +02:00
|
|
|
* @return array<string, string> all interfaces extended by $interface_name
|
2018-02-04 00:52:35 +01:00
|
|
|
*/
|
2020-09-07 01:36:47 +02:00
|
|
|
public function getParentInterfaces(string $fq_interface_name): array
|
2018-02-04 00:52:35 +01:00
|
|
|
{
|
2020-02-15 02:54:26 +01:00
|
|
|
return $this->classlikes->getParentInterfaces(
|
|
|
|
$this->classlikes->getUnAliasedName($fq_interface_name)
|
|
|
|
);
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
2018-01-31 22:08:52 +01:00
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
/**
|
|
|
|
* Determine whether or not a class has the correct casing
|
|
|
|
*/
|
2020-09-07 01:36:47 +02:00
|
|
|
public function classHasCorrectCasing(string $fq_class_name): bool
|
2018-02-04 00:52:35 +01:00
|
|
|
{
|
|
|
|
return $this->classlikes->classHasCorrectCasing($fq_class_name);
|
2018-01-31 22:08:52 +01:00
|
|
|
}
|
|
|
|
|
2020-09-07 01:36:47 +02:00
|
|
|
public function interfaceHasCorrectCasing(string $fq_interface_name): bool
|
2018-01-31 22:08:52 +01:00
|
|
|
{
|
2018-02-04 00:52:35 +01:00
|
|
|
return $this->classlikes->interfaceHasCorrectCasing($fq_interface_name);
|
|
|
|
}
|
2018-01-31 22:08:52 +01:00
|
|
|
|
2022-09-08 14:33:20 +02:00
|
|
|
public function traitHasCorrectCasing(string $fq_trait_name): bool
|
2018-02-04 00:52:35 +01:00
|
|
|
{
|
2022-09-08 14:33:20 +02:00
|
|
|
return $this->classlikes->traitHasCorrectCasing($fq_trait_name);
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
2018-01-31 22:08:52 +01:00
|
|
|
|
2020-03-14 19:51:43 +01:00
|
|
|
/**
|
|
|
|
* Given a function id, return the function like storage for
|
|
|
|
* a method, closure, or function.
|
2020-05-15 16:18:05 +02:00
|
|
|
*
|
|
|
|
* @param non-empty-string $function_id
|
2020-10-17 18:36:44 +02:00
|
|
|
*
|
2021-12-14 00:45:01 +01:00
|
|
|
* @return FunctionStorage|MethodStorage
|
2020-03-14 19:51:43 +01:00
|
|
|
*/
|
|
|
|
public function getFunctionLikeStorage(
|
|
|
|
StatementsAnalyzer $statements_analyzer,
|
|
|
|
string $function_id
|
|
|
|
): FunctionLikeStorage {
|
|
|
|
$doesMethodExist =
|
2021-12-03 20:11:20 +01:00
|
|
|
MethodIdentifier::isValidMethodIdReference($function_id)
|
2020-03-14 19:51:43 +01:00
|
|
|
&& $this->methodExists($function_id);
|
2020-03-16 16:45:49 +01:00
|
|
|
|
2020-03-14 19:51:43 +01:00
|
|
|
if ($doesMethodExist) {
|
2021-12-03 20:11:20 +01:00
|
|
|
$method_id = MethodIdentifier::wrap($function_id);
|
2020-03-16 16:45:49 +01:00
|
|
|
|
|
|
|
$declaring_method_id = $this->methods->getDeclaringMethodId($method_id);
|
|
|
|
|
|
|
|
if (!$declaring_method_id) {
|
2021-12-03 21:40:18 +01:00
|
|
|
throw new UnexpectedValueException('Declaring method for ' . $method_id . ' cannot be found');
|
2020-03-16 16:45:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return $this->methods->getStorage($declaring_method_id);
|
2020-03-14 19:51:43 +01:00
|
|
|
}
|
|
|
|
|
2020-04-21 03:36:44 +02:00
|
|
|
return $this->functions->getStorage($statements_analyzer, strtolower($function_id));
|
2020-03-14 19:51:43 +01:00
|
|
|
}
|
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
/**
|
|
|
|
* Whether or not a given method exists
|
|
|
|
*
|
2021-12-04 03:37:19 +01:00
|
|
|
* @param string|MethodIdentifier $method_id
|
|
|
|
* @param string|MethodIdentifier|null $calling_method_id
|
2018-02-04 00:52:35 +01:00
|
|
|
*/
|
2019-04-17 06:17:28 +02:00
|
|
|
public function methodExists(
|
2020-02-15 02:54:26 +01:00
|
|
|
$method_id,
|
2020-03-26 17:35:27 +01:00
|
|
|
?CodeLocation $code_location = null,
|
|
|
|
$calling_method_id = null,
|
2021-06-10 20:18:15 +02:00
|
|
|
?string $file_path = null,
|
|
|
|
bool $is_used = true
|
2020-09-04 22:26:33 +02:00
|
|
|
): bool {
|
2019-04-17 06:17:28 +02:00
|
|
|
return $this->methods->methodExists(
|
2021-12-14 01:54:11 +01:00
|
|
|
MethodIdentifier::wrap($method_id),
|
2020-03-26 17:35:27 +01:00
|
|
|
is_string($calling_method_id) ? strtolower($calling_method_id) : strtolower((string) $calling_method_id),
|
2019-04-17 06:17:28 +02:00
|
|
|
$code_location,
|
|
|
|
null,
|
2021-06-10 20:18:15 +02:00
|
|
|
$file_path,
|
|
|
|
true,
|
|
|
|
$is_used
|
2019-04-17 06:17:28 +02:00
|
|
|
);
|
2018-01-31 22:08:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-12-04 03:37:19 +01:00
|
|
|
* @param string|MethodIdentifier $method_id
|
2018-02-04 00:52:35 +01:00
|
|
|
*
|
2021-12-04 03:37:19 +01:00
|
|
|
* @return array<int, FunctionLikeParameter>
|
2018-01-31 22:08:52 +01:00
|
|
|
*/
|
2020-09-04 22:26:33 +02:00
|
|
|
public function getMethodParams($method_id): array
|
2018-01-31 22:08:52 +01:00
|
|
|
{
|
2021-12-14 01:54:11 +01:00
|
|
|
return $this->methods->getMethodParams(MethodIdentifier::wrap($method_id));
|
2018-01-31 22:08:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-12-04 03:37:19 +01:00
|
|
|
* @param string|MethodIdentifier $method_id
|
2018-02-04 00:52:35 +01:00
|
|
|
*
|
2018-01-31 22:08:52 +01:00
|
|
|
*/
|
2020-09-04 22:26:33 +02:00
|
|
|
public function isVariadic($method_id): bool
|
2018-01-31 22:08:52 +01:00
|
|
|
{
|
2021-12-14 01:54:11 +01:00
|
|
|
return $this->methods->isVariadic(MethodIdentifier::wrap($method_id));
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
2018-01-31 22:08:52 +01:00
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
/**
|
2021-12-04 03:37:19 +01:00
|
|
|
* @param string|MethodIdentifier $method_id
|
|
|
|
* @param list<Arg> $call_args
|
2018-02-04 00:52:35 +01:00
|
|
|
*
|
|
|
|
*/
|
2021-12-13 16:28:14 +01:00
|
|
|
public function getMethodReturnType($method_id, ?string &$self_class, array $call_args = []): ?Union
|
2018-02-04 00:52:35 +01:00
|
|
|
{
|
2020-02-15 02:54:26 +01:00
|
|
|
return $this->methods->getMethodReturnType(
|
2021-12-14 01:54:11 +01:00
|
|
|
MethodIdentifier::wrap($method_id),
|
2020-02-15 02:54:26 +01:00
|
|
|
$self_class,
|
|
|
|
null,
|
|
|
|
$call_args
|
|
|
|
);
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
2018-01-31 22:08:52 +01:00
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
/**
|
2021-12-04 03:37:19 +01:00
|
|
|
* @param string|MethodIdentifier $method_id
|
2018-02-04 00:52:35 +01:00
|
|
|
*
|
|
|
|
*/
|
2020-09-04 22:26:33 +02:00
|
|
|
public function getMethodReturnsByRef($method_id): bool
|
2018-02-04 00:52:35 +01:00
|
|
|
{
|
2021-12-14 01:54:11 +01:00
|
|
|
return $this->methods->getMethodReturnsByRef(MethodIdentifier::wrap($method_id));
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
2018-01-31 22:08:52 +01:00
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
/**
|
2021-12-04 03:37:19 +01:00
|
|
|
* @param string|MethodIdentifier $method_id
|
|
|
|
* @param CodeLocation|null $defined_location
|
2018-02-04 00:52:35 +01:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
public function getMethodReturnTypeLocation(
|
|
|
|
$method_id,
|
|
|
|
CodeLocation &$defined_location = null
|
2020-09-04 22:26:33 +02:00
|
|
|
): ?CodeLocation {
|
2020-02-15 02:54:26 +01:00
|
|
|
return $this->methods->getMethodReturnTypeLocation(
|
2021-12-14 01:54:11 +01:00
|
|
|
MethodIdentifier::wrap($method_id),
|
2020-02-15 02:54:26 +01:00
|
|
|
$defined_location
|
|
|
|
);
|
2018-01-31 22:08:52 +01:00
|
|
|
}
|
|
|
|
|
2018-01-31 23:09:09 +01:00
|
|
|
/**
|
2021-12-04 03:37:19 +01:00
|
|
|
* @param string|MethodIdentifier $method_id
|
2018-02-04 00:52:35 +01:00
|
|
|
*
|
2018-01-31 23:09:09 +01:00
|
|
|
*/
|
2020-09-04 22:26:33 +02:00
|
|
|
public function getDeclaringMethodId($method_id): ?string
|
2018-01-31 23:09:09 +01:00
|
|
|
{
|
2021-12-14 01:54:11 +01:00
|
|
|
$new_method_id = $this->methods->getDeclaringMethodId(MethodIdentifier::wrap($method_id));
|
2020-11-24 06:18:24 +01:00
|
|
|
|
|
|
|
return $new_method_id ? (string) $new_method_id : null;
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
2018-01-31 23:09:09 +01:00
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
/**
|
|
|
|
* Get the class this method appears in (vs is declared in, which could give a trait)
|
|
|
|
*
|
2021-12-04 03:37:19 +01:00
|
|
|
* @param string|MethodIdentifier $method_id
|
2018-02-04 00:52:35 +01:00
|
|
|
*
|
|
|
|
*/
|
2020-09-04 22:26:33 +02:00
|
|
|
public function getAppearingMethodId($method_id): ?string
|
2018-02-04 00:52:35 +01:00
|
|
|
{
|
2021-12-14 01:54:11 +01:00
|
|
|
$new_method_id = $this->methods->getAppearingMethodId(MethodIdentifier::wrap($method_id));
|
2020-11-24 06:18:24 +01:00
|
|
|
|
|
|
|
return $new_method_id ? (string) $new_method_id : null;
|
2018-01-31 23:09:09 +01:00
|
|
|
}
|
|
|
|
|
2018-01-31 22:08:52 +01:00
|
|
|
/**
|
2021-12-04 03:37:19 +01:00
|
|
|
* @param string|MethodIdentifier $method_id
|
2018-02-04 00:52:35 +01:00
|
|
|
*
|
2021-12-14 01:54:11 +01:00
|
|
|
* @return array<string, MethodIdentifier>
|
2018-01-31 22:08:52 +01:00
|
|
|
*/
|
2020-09-04 22:26:33 +02:00
|
|
|
public function getOverriddenMethodIds($method_id): array
|
2018-01-31 22:08:52 +01:00
|
|
|
{
|
2021-12-14 01:54:11 +01:00
|
|
|
return $this->methods->getOverriddenMethodIds(MethodIdentifier::wrap($method_id));
|
2018-01-31 22:08:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-12-04 03:37:19 +01:00
|
|
|
* @param string|MethodIdentifier $method_id
|
2018-02-04 00:52:35 +01:00
|
|
|
*
|
2018-01-31 22:08:52 +01:00
|
|
|
*/
|
2020-09-04 22:26:33 +02:00
|
|
|
public function getCasedMethodId($method_id): string
|
2018-01-31 22:08:52 +01:00
|
|
|
{
|
2021-12-14 01:54:11 +01:00
|
|
|
return $this->methods->getCasedMethodId(MethodIdentifier::wrap($method_id));
|
2018-01-31 22:08:52 +01:00
|
|
|
}
|
2018-09-28 22:18:45 +02:00
|
|
|
|
2020-10-12 21:02:52 +02:00
|
|
|
public function invalidateInformationForFile(string $file_path): void
|
2018-09-28 22:18:45 +02:00
|
|
|
{
|
|
|
|
$this->scanner->removeFile($file_path);
|
|
|
|
|
|
|
|
try {
|
|
|
|
$file_storage = $this->file_storage_provider->get($file_path);
|
2021-12-03 21:40:18 +01:00
|
|
|
} catch (InvalidArgumentException $e) {
|
2018-09-28 22:18:45 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($file_storage->classlikes_in_file as $fq_classlike_name) {
|
|
|
|
$this->classlike_storage_provider->remove($fq_classlike_name);
|
|
|
|
$this->classlikes->removeClassLike($fq_classlike_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->file_storage_provider->remove($file_path);
|
|
|
|
}
|
2020-09-21 00:27:02 +02:00
|
|
|
|
2021-01-27 03:34:46 +01:00
|
|
|
public function getFunctionStorageForSymbol(string $file_path, string $symbol): ?FunctionLikeStorage
|
|
|
|
{
|
|
|
|
if (strpos($symbol, '::')) {
|
|
|
|
$symbol = substr($symbol, 0, -2);
|
|
|
|
/** @psalm-suppress ArgumentTypeCoercion */
|
2021-12-03 20:11:20 +01:00
|
|
|
$method_id = new MethodIdentifier(...explode('::', $symbol));
|
2021-01-27 03:34:46 +01:00
|
|
|
|
|
|
|
$declaring_method_id = $this->methods->getDeclaringMethodId($method_id);
|
|
|
|
|
|
|
|
if (!$declaring_method_id) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2021-09-25 02:34:21 +02:00
|
|
|
return $this->methods->getStorage($declaring_method_id);
|
2021-01-27 03:34:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$function_id = strtolower(substr($symbol, 0, -2));
|
|
|
|
$file_storage = $this->file_storage_provider->get($file_path);
|
|
|
|
|
|
|
|
if (isset($file_storage->functions[$function_id])) {
|
2021-09-25 02:34:21 +02:00
|
|
|
return $file_storage->functions[$function_id];
|
2021-01-27 03:34:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!$function_id) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2021-09-25 02:34:21 +02:00
|
|
|
return $this->functions->getStorage(null, $function_id);
|
2021-01-27 03:34:46 +01:00
|
|
|
}
|
|
|
|
|
2019-02-18 21:54:23 +01:00
|
|
|
/**
|
2019-02-20 16:59:33 +01:00
|
|
|
* Checks if type is a subtype of other
|
|
|
|
*
|
|
|
|
* Given two types, checks if `$input_type` is a subtype of `$container_type`.
|
2021-12-13 16:28:14 +01:00
|
|
|
* If you consider `Union` as a set of types, this will tell you
|
2019-02-20 16:59:33 +01:00
|
|
|
* if `$input_type` is fully contained in `$container_type`,
|
|
|
|
*
|
|
|
|
* $input_type ⊆ $container_type
|
|
|
|
*
|
|
|
|
* Useful for emitting issues like InvalidArgument, where argument at the call site
|
|
|
|
* should be a subset of the function parameter type.
|
2019-02-18 21:54:23 +01:00
|
|
|
*/
|
2019-02-18 21:40:47 +01:00
|
|
|
public function isTypeContainedByType(
|
2021-12-13 16:28:14 +01:00
|
|
|
Union $input_type,
|
|
|
|
Union $container_type
|
2019-02-18 21:40:47 +01:00
|
|
|
): bool {
|
2020-07-22 01:40:35 +02:00
|
|
|
return UnionTypeComparator::isContainedBy($this, $input_type, $container_type);
|
2019-02-18 21:40:47 +01:00
|
|
|
}
|
2019-02-20 12:54:01 +01:00
|
|
|
|
|
|
|
/**
|
2019-02-20 16:59:33 +01:00
|
|
|
* Checks if type has any part that is a subtype of other
|
|
|
|
*
|
|
|
|
* Given two types, checks if *any part* of `$input_type` is a subtype of `$container_type`.
|
2021-12-13 16:28:14 +01:00
|
|
|
* If you consider `Union` as a set of types, this will tell you if intersection
|
2019-02-20 16:59:33 +01:00
|
|
|
* of `$input_type` with `$container_type` is not empty.
|
|
|
|
*
|
2019-02-20 17:06:51 +01:00
|
|
|
* $input_type ∩ $container_type ≠ ∅ , e.g. they are not disjoint.
|
2019-02-20 16:59:33 +01:00
|
|
|
*
|
|
|
|
* Useful for emitting issues like PossiblyInvalidArgument, where argument at the call
|
|
|
|
* site should be a subtype of the function parameter type, but it's has some types that are
|
|
|
|
* not a subtype of the required type.
|
2019-02-20 12:54:01 +01:00
|
|
|
*/
|
|
|
|
public function canTypeBeContainedByType(
|
2021-12-13 16:28:14 +01:00
|
|
|
Union $input_type,
|
|
|
|
Union $container_type
|
2019-02-20 12:54:01 +01:00
|
|
|
): bool {
|
2020-07-22 01:40:35 +02:00
|
|
|
return UnionTypeComparator::canBeContainedBy($this, $input_type, $container_type);
|
2019-02-20 12:54:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-02-20 16:59:33 +01:00
|
|
|
* Extracts key and value types from a traversable object (or iterable)
|
|
|
|
*
|
|
|
|
* Given an iterable type (*but not TArray*) returns a tuple of it's key/value types.
|
|
|
|
* First element of the tuple holds key type, second has the value type.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* ```php
|
|
|
|
* $codebase->getKeyValueParamsForTraversableObject(Type::parseString('iterable<int,string>'))
|
|
|
|
* // returns [Union(TInt), Union(TString)]
|
|
|
|
* ```
|
|
|
|
*
|
2021-12-13 16:28:14 +01:00
|
|
|
* @return array{Union, Union}
|
2019-02-20 12:54:01 +01:00
|
|
|
*/
|
2021-12-13 16:28:14 +01:00
|
|
|
public function getKeyValueParamsForTraversableObject(Atomic $type): array
|
2019-02-20 12:54:01 +01:00
|
|
|
{
|
|
|
|
$key_type = null;
|
|
|
|
$value_type = null;
|
|
|
|
|
|
|
|
ForeachAnalyzer::getKeyValueParamsForTraversableObject($type, $this, $key_type, $value_type);
|
|
|
|
|
|
|
|
return [
|
|
|
|
$key_type ?? Type::getMixed(),
|
|
|
|
$value_type ?? Type::getMixed(),
|
|
|
|
];
|
|
|
|
}
|
2020-04-05 23:25:47 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array<string, mixed> $phantom_classes
|
|
|
|
* @psalm-suppress PossiblyUnusedMethod part of the public API
|
|
|
|
*/
|
|
|
|
public function queueClassLikeForScanning(
|
|
|
|
string $fq_classlike_name,
|
|
|
|
bool $analyze_too = false,
|
|
|
|
bool $store_failure = true,
|
|
|
|
array $phantom_classes = []
|
|
|
|
): void {
|
|
|
|
$this->scanner->queueClassLikeForScanning($fq_classlike_name, $analyze_too, $store_failure, $phantom_classes);
|
|
|
|
}
|
2020-06-18 06:16:19 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array<string> $taints
|
|
|
|
*
|
|
|
|
* @psalm-suppress PossiblyUnusedMethod
|
|
|
|
*/
|
|
|
|
public function addTaintSource(
|
2021-12-13 16:28:14 +01:00
|
|
|
Union $expr_type,
|
2020-06-18 06:16:19 +02:00
|
|
|
string $taint_id,
|
2021-12-03 20:11:20 +01:00
|
|
|
array $taints = TaintKindGroup::ALL_INPUT,
|
2020-06-18 06:16:19 +02:00
|
|
|
?CodeLocation $code_location = null
|
2021-12-05 18:51:26 +01:00
|
|
|
): void {
|
2020-09-25 06:37:40 +02:00
|
|
|
if (!$this->taint_flow_graph) {
|
2020-06-18 06:16:19 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-12-03 20:11:20 +01:00
|
|
|
$source = new TaintSource(
|
2020-06-18 06:16:19 +02:00
|
|
|
$taint_id,
|
|
|
|
$taint_id,
|
|
|
|
$code_location,
|
|
|
|
null,
|
|
|
|
$taints
|
|
|
|
);
|
|
|
|
|
2020-09-25 06:37:40 +02:00
|
|
|
$this->taint_flow_graph->addSource($source);
|
2020-06-18 06:16:19 +02:00
|
|
|
|
|
|
|
$expr_type->parent_nodes = [
|
2020-09-25 06:37:40 +02:00
|
|
|
$source->id => $source,
|
2020-06-18 06:16:19 +02:00
|
|
|
];
|
|
|
|
}
|
2020-07-16 22:02:26 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array<string> $taints
|
|
|
|
*
|
|
|
|
* @psalm-suppress PossiblyUnusedMethod
|
|
|
|
*/
|
|
|
|
public function addTaintSink(
|
|
|
|
string $taint_id,
|
2021-12-03 20:11:20 +01:00
|
|
|
array $taints = TaintKindGroup::ALL_INPUT,
|
2020-07-16 22:02:26 +02:00
|
|
|
?CodeLocation $code_location = null
|
2021-12-05 18:51:26 +01:00
|
|
|
): void {
|
2020-09-25 06:37:40 +02:00
|
|
|
if (!$this->taint_flow_graph) {
|
2020-07-16 22:02:26 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-12-03 20:11:20 +01:00
|
|
|
$sink = new TaintSink(
|
2020-07-16 22:02:26 +02:00
|
|
|
$taint_id,
|
|
|
|
$taint_id,
|
|
|
|
$code_location,
|
|
|
|
null,
|
|
|
|
$taints
|
|
|
|
);
|
|
|
|
|
2020-09-25 06:37:40 +02:00
|
|
|
$this->taint_flow_graph->addSink($sink);
|
2020-07-16 22:02:26 +02:00
|
|
|
}
|
2022-02-15 01:18:49 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array<string> $candidate_files
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public function reloadFiles(ProjectAnalyzer $project_analyzer, array $candidate_files, bool $force = false): void
|
|
|
|
{
|
|
|
|
$this->loadAnalyzer();
|
|
|
|
|
|
|
|
if ($force) {
|
|
|
|
FileReferenceProvider::clearCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->file_reference_provider->loadReferenceCache($force);
|
|
|
|
|
|
|
|
FunctionLikeAnalyzer::clearCache();
|
|
|
|
|
|
|
|
if ($force || !$this->statements_provider->parser_cache_provider) {
|
|
|
|
$diff_files = $candidate_files;
|
|
|
|
} else {
|
|
|
|
$diff_files = [];
|
|
|
|
|
|
|
|
$parser_cache_provider = $this->statements_provider->parser_cache_provider;
|
|
|
|
|
|
|
|
foreach ($candidate_files as $candidate_file_path) {
|
|
|
|
if ($parser_cache_provider->loadExistingFileContentsFromCache($candidate_file_path)
|
|
|
|
!== $this->file_provider->getContents($candidate_file_path)
|
|
|
|
) {
|
|
|
|
$diff_files[] = $candidate_file_path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$referenced_files = $project_analyzer->getReferencedFilesFromDiff($diff_files, false);
|
|
|
|
|
|
|
|
foreach ($diff_files as $diff_file_path) {
|
|
|
|
$this->invalidateInformationForFile($diff_file_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($referenced_files as $referenced_file_path) {
|
|
|
|
if (in_array($referenced_file_path, $diff_files, true)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$file_storage = $this->file_storage_provider->get($referenced_file_path);
|
|
|
|
|
|
|
|
foreach ($file_storage->classlikes_in_file as $fq_classlike_name) {
|
|
|
|
$this->classlike_storage_provider->remove($fq_classlike_name);
|
|
|
|
$this->classlikes->removeClassLike($fq_classlike_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->file_storage_provider->remove($referenced_file_path);
|
|
|
|
$this->scanner->removeFile($referenced_file_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
$referenced_files = array_combine($referenced_files, $referenced_files);
|
|
|
|
|
|
|
|
$this->scanner->addFilesToDeepScan($referenced_files);
|
|
|
|
$this->addFilesToAnalyze(array_combine($candidate_files, $candidate_files));
|
|
|
|
|
|
|
|
$this->scanner->scanFiles($this->classlikes);
|
|
|
|
|
|
|
|
$this->file_reference_provider->updateReferenceCache($this, $referenced_files);
|
|
|
|
|
|
|
|
$this->populator->populateCodebase();
|
|
|
|
}
|
2018-01-21 19:38:51 +01:00
|
|
|
}
|