2018-02-04 00:52:35 +01:00
|
|
|
<?php
|
2018-11-12 16:46:55 +01:00
|
|
|
namespace Psalm\Internal\Codebase;
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2019-07-05 22:24:00 +02:00
|
|
|
use function array_keys;
|
|
|
|
use function array_merge;
|
|
|
|
use function count;
|
|
|
|
use function is_int;
|
|
|
|
use Psalm\Config;
|
2018-11-06 03:57:36 +01:00
|
|
|
use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
|
|
|
|
use Psalm\Internal\Provider\ClassLikeStorageProvider;
|
|
|
|
use Psalm\Internal\Provider\FileReferenceProvider;
|
|
|
|
use Psalm\Internal\Provider\FileStorageProvider;
|
2019-07-05 22:24:00 +02:00
|
|
|
use Psalm\Issue\CircularReference;
|
|
|
|
use Psalm\IssueBuffer;
|
2019-05-30 16:30:41 +02:00
|
|
|
use Psalm\Progress\Progress;
|
2018-02-04 00:52:35 +01:00
|
|
|
use Psalm\Storage\ClassLikeStorage;
|
|
|
|
use Psalm\Storage\FileStorage;
|
|
|
|
use Psalm\Type;
|
2019-06-26 22:52:29 +02:00
|
|
|
use function reset;
|
2019-07-05 22:24:00 +02:00
|
|
|
use function strpos;
|
|
|
|
use function strtolower;
|
2020-07-23 01:27:35 +02:00
|
|
|
use function strlen;
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2018-02-09 23:51:49 +01:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*
|
|
|
|
* Populates file and class information so that analysis can work properly
|
|
|
|
*/
|
2018-02-04 00:52:35 +01:00
|
|
|
class Populator
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @var ClassLikeStorageProvider
|
|
|
|
*/
|
|
|
|
private $classlike_storage_provider;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var FileStorageProvider
|
|
|
|
*/
|
|
|
|
private $file_storage_provider;
|
|
|
|
|
2020-03-29 06:13:01 +02:00
|
|
|
/**
|
|
|
|
* @var array<lowercase-string, list<ClassLikeStorage>>
|
|
|
|
*/
|
|
|
|
private $invalid_class_storages = [];
|
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
/**
|
2019-05-30 16:30:41 +02:00
|
|
|
* @var Progress
|
2018-02-04 00:52:35 +01:00
|
|
|
*/
|
2019-05-30 16:30:41 +02:00
|
|
|
private $progress;
|
2018-02-04 00:52:35 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @var ClassLikes
|
|
|
|
*/
|
|
|
|
private $classlikes;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var Config
|
|
|
|
*/
|
|
|
|
private $config;
|
|
|
|
|
2018-09-28 22:18:45 +02:00
|
|
|
/**
|
|
|
|
* @var FileReferenceProvider
|
|
|
|
*/
|
|
|
|
private $file_reference_provider;
|
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
public function __construct(
|
|
|
|
Config $config,
|
|
|
|
ClassLikeStorageProvider $classlike_storage_provider,
|
|
|
|
FileStorageProvider $file_storage_provider,
|
|
|
|
ClassLikes $classlikes,
|
2018-09-28 22:18:45 +02:00
|
|
|
FileReferenceProvider $file_reference_provider,
|
2019-05-30 16:30:41 +02:00
|
|
|
Progress $progress
|
2018-02-04 00:52:35 +01:00
|
|
|
) {
|
|
|
|
$this->classlike_storage_provider = $classlike_storage_provider;
|
|
|
|
$this->file_storage_provider = $file_storage_provider;
|
|
|
|
$this->classlikes = $classlikes;
|
2019-05-30 16:30:41 +02:00
|
|
|
$this->progress = $progress;
|
2018-02-04 00:52:35 +01:00
|
|
|
$this->config = $config;
|
2018-09-28 22:18:45 +02:00
|
|
|
$this->file_reference_provider = $file_reference_provider;
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
2020-09-12 17:24:05 +02:00
|
|
|
public function populateCodebase(): void
|
2018-02-04 00:52:35 +01:00
|
|
|
{
|
2019-05-30 16:30:41 +02:00
|
|
|
$this->progress->debug('ClassLikeStorage is populating' . "\n");
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2018-10-18 16:42:37 +02:00
|
|
|
foreach ($this->classlike_storage_provider->getNew() as $class_storage) {
|
2018-02-04 00:52:35 +01:00
|
|
|
$this->populateClassLikeStorage($class_storage);
|
|
|
|
}
|
|
|
|
|
2019-05-30 16:30:41 +02:00
|
|
|
$this->progress->debug('ClassLikeStorage is populated' . "\n");
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2019-05-30 16:30:41 +02:00
|
|
|
$this->progress->debug('FileStorage is populating' . "\n");
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2018-10-18 16:42:37 +02:00
|
|
|
$all_file_storage = $this->file_storage_provider->getNew();
|
2018-02-04 00:52:35 +01:00
|
|
|
|
|
|
|
foreach ($all_file_storage as $file_storage) {
|
|
|
|
$this->populateFileStorage($file_storage);
|
|
|
|
}
|
|
|
|
|
2018-10-18 16:42:37 +02:00
|
|
|
foreach ($this->classlike_storage_provider->getNew() as $class_storage) {
|
2018-06-28 03:53:25 +02:00
|
|
|
if ($this->config->allow_phpstorm_generics) {
|
2018-02-04 00:52:35 +01:00
|
|
|
foreach ($class_storage->properties as $property_storage) {
|
|
|
|
if ($property_storage->type) {
|
|
|
|
$this->convertPhpStormGenericToPsalmGeneric($property_storage->type, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($class_storage->methods as $method_storage) {
|
|
|
|
if ($method_storage->return_type) {
|
|
|
|
$this->convertPhpStormGenericToPsalmGeneric($method_storage->return_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($method_storage->params as $param_storage) {
|
|
|
|
if ($param_storage->type) {
|
|
|
|
$this->convertPhpStormGenericToPsalmGeneric($param_storage->type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-15 02:54:26 +01:00
|
|
|
foreach ($class_storage->dependent_classlikes as $dependent_classlike_lc => $_) {
|
|
|
|
$dependee_storage = $this->classlike_storage_provider->get($dependent_classlike_lc);
|
2019-07-12 00:23:02 +02:00
|
|
|
|
|
|
|
$class_storage->dependent_classlikes += $dependee_storage->dependent_classlikes;
|
|
|
|
}
|
2018-06-28 03:53:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->config->allow_phpstorm_generics) {
|
2018-02-04 00:52:35 +01:00
|
|
|
foreach ($all_file_storage as $file_storage) {
|
|
|
|
foreach ($file_storage->functions as $function_storage) {
|
|
|
|
if ($function_storage->return_type) {
|
|
|
|
$this->convertPhpStormGenericToPsalmGeneric($function_storage->return_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($function_storage->params as $param_storage) {
|
|
|
|
if ($param_storage->type) {
|
|
|
|
$this->convertPhpStormGenericToPsalmGeneric($param_storage->type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-30 16:30:41 +02:00
|
|
|
$this->progress->debug('FileStorage is populated' . "\n");
|
2018-10-18 16:42:37 +02:00
|
|
|
|
|
|
|
$this->classlike_storage_provider->populated();
|
|
|
|
$this->file_storage_provider->populated();
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
2020-10-12 21:02:52 +02:00
|
|
|
private function populateClassLikeStorage(ClassLikeStorage $storage, array $dependent_classlikes = []): void
|
2018-02-04 00:52:35 +01:00
|
|
|
{
|
|
|
|
if ($storage->populated) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$fq_classlike_name_lc = strtolower($storage->name);
|
|
|
|
|
|
|
|
if (isset($dependent_classlikes[$fq_classlike_name_lc])) {
|
|
|
|
if ($storage->location && IssueBuffer::accepts(
|
|
|
|
new CircularReference(
|
|
|
|
'Circular reference discovered when loading ' . $storage->name,
|
|
|
|
$storage->location
|
|
|
|
)
|
|
|
|
)) {
|
|
|
|
// fall through
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$storage_provider = $this->classlike_storage_provider;
|
|
|
|
|
2020-01-07 23:06:44 +01:00
|
|
|
$dependent_classlikes[$fq_classlike_name_lc] = true;
|
|
|
|
|
2018-02-04 02:03:31 +01:00
|
|
|
$this->populateDataFromTraits($storage, $storage_provider, $dependent_classlikes);
|
|
|
|
|
|
|
|
if ($storage->parent_classes) {
|
|
|
|
$this->populateDataFromParentClass($storage, $storage_provider, $dependent_classlikes);
|
|
|
|
}
|
|
|
|
|
2018-09-25 23:23:19 +02:00
|
|
|
if (!strpos($fq_classlike_name_lc, '\\')
|
|
|
|
&& !isset($storage->methods['__construct'])
|
|
|
|
&& isset($storage->methods[$fq_classlike_name_lc])
|
|
|
|
&& !$storage->is_interface
|
|
|
|
&& !$storage->is_trait
|
|
|
|
) {
|
|
|
|
$storage->methods['__construct'] = $storage->methods[$fq_classlike_name_lc];
|
|
|
|
}
|
|
|
|
|
2018-02-04 02:03:31 +01:00
|
|
|
$this->populateInterfaceDataFromParentInterfaces($storage, $storage_provider, $dependent_classlikes);
|
|
|
|
|
|
|
|
$this->populateDataFromImplementedInterfaces($storage, $storage_provider, $dependent_classlikes);
|
|
|
|
|
|
|
|
if ($storage->location) {
|
|
|
|
$file_path = $storage->location->file_path;
|
|
|
|
|
|
|
|
foreach ($storage->parent_interfaces as $parent_interface_lc) {
|
2018-09-28 22:18:45 +02:00
|
|
|
$this->file_reference_provider->addFileInheritanceToClass($file_path, $parent_interface_lc);
|
2018-02-04 02:03:31 +01:00
|
|
|
}
|
|
|
|
|
2019-03-08 23:35:09 +01:00
|
|
|
foreach ($storage->parent_classes as $parent_class_lc => $_) {
|
2018-09-28 22:18:45 +02:00
|
|
|
$this->file_reference_provider->addFileInheritanceToClass($file_path, $parent_class_lc);
|
2018-02-04 02:03:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($storage->class_implements as $implemented_interface) {
|
2018-09-28 22:18:45 +02:00
|
|
|
$this->file_reference_provider->addFileInheritanceToClass(
|
|
|
|
$file_path,
|
|
|
|
strtolower($implemented_interface)
|
|
|
|
);
|
2018-02-04 02:03:31 +01:00
|
|
|
}
|
|
|
|
|
2018-11-20 21:51:47 +01:00
|
|
|
foreach ($storage->used_traits as $used_trait_lc => $_) {
|
2018-09-28 22:18:45 +02:00
|
|
|
$this->file_reference_provider->addFileInheritanceToClass($file_path, $used_trait_lc);
|
2018-02-04 02:03:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:36:35 +02:00
|
|
|
if ($storage->mutation_free || $storage->external_mutation_free) {
|
|
|
|
foreach ($storage->methods as $method) {
|
2019-09-09 17:14:40 +02:00
|
|
|
if (!$method->is_static && !$method->external_mutation_free) {
|
2019-09-09 16:38:55 +02:00
|
|
|
$method->mutation_free = $storage->mutation_free;
|
|
|
|
$method->external_mutation_free = $storage->external_mutation_free;
|
|
|
|
}
|
2019-08-30 18:36:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($storage->mutation_free) {
|
|
|
|
foreach ($storage->properties as $property) {
|
2019-09-09 16:38:55 +02:00
|
|
|
if (!$property->is_static) {
|
|
|
|
$property->readonly = true;
|
|
|
|
}
|
2019-08-30 18:36:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-19 07:22:51 +02:00
|
|
|
if ($storage->specialize_instance) {
|
|
|
|
foreach ($storage->methods as $method) {
|
|
|
|
if (!$method->is_static) {
|
|
|
|
$method->specialize_call = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-24 15:32:54 +02:00
|
|
|
if (!$storage->is_interface && !$storage->is_trait) {
|
2018-12-02 00:37:49 +01:00
|
|
|
foreach ($storage->methods as $method) {
|
2020-07-24 15:32:54 +02:00
|
|
|
if (strlen($storage->internal) > strlen($method->internal)) {
|
|
|
|
$method->internal = $storage->internal;
|
2020-07-23 01:27:35 +02:00
|
|
|
}
|
2018-12-02 00:37:49 +01:00
|
|
|
}
|
|
|
|
|
2020-07-23 01:27:35 +02:00
|
|
|
|
2018-12-02 00:37:49 +01:00
|
|
|
foreach ($storage->properties as $property) {
|
2020-07-24 15:32:54 +02:00
|
|
|
if (strlen($storage->internal) > strlen($property->internal)) {
|
|
|
|
$property->internal = $storage->internal;
|
2020-07-23 01:27:35 +02:00
|
|
|
}
|
2018-12-02 00:37:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-28 21:53:07 +01:00
|
|
|
$this->populateOverriddenMethods($storage);
|
|
|
|
|
2019-05-30 16:30:41 +02:00
|
|
|
$this->progress->debug('Have populated ' . $storage->name . "\n");
|
2019-03-28 21:53:07 +01:00
|
|
|
|
|
|
|
$storage->populated = true;
|
2020-03-29 06:13:01 +02:00
|
|
|
|
|
|
|
if (isset($this->invalid_class_storages[$fq_classlike_name_lc])) {
|
|
|
|
foreach ($this->invalid_class_storages[$fq_classlike_name_lc] as $dependency) {
|
|
|
|
$dependency->populated = false;
|
|
|
|
$this->populateClassLikeStorage($dependency, $dependent_classlikes);
|
|
|
|
}
|
|
|
|
|
|
|
|
unset($this->invalid_class_storages[$fq_classlike_name_lc]);
|
|
|
|
}
|
2019-03-28 21:53:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private function populateOverriddenMethods(
|
|
|
|
ClassLikeStorage $storage
|
2020-09-12 17:24:05 +02:00
|
|
|
): void {
|
2019-03-25 16:25:43 +01:00
|
|
|
foreach ($storage->methods as $method_name => $method_storage) {
|
|
|
|
if (isset($storage->overridden_method_ids[$method_name])) {
|
2019-09-25 18:02:50 +02:00
|
|
|
$overridden_method_ids = $storage->overridden_method_ids[$method_name];
|
|
|
|
|
|
|
|
$candidate_overridden_ids = null;
|
|
|
|
|
|
|
|
$declaring_class_storages = [];
|
|
|
|
|
|
|
|
foreach ($overridden_method_ids as $declaring_method_id) {
|
2020-02-15 02:54:26 +01:00
|
|
|
$declaring_class = $declaring_method_id->fq_class_name;
|
2019-09-25 18:02:50 +02:00
|
|
|
$declaring_class_storage
|
|
|
|
= $declaring_class_storages[$declaring_class]
|
|
|
|
= $this->classlike_storage_provider->get($declaring_class);
|
|
|
|
|
|
|
|
if ($candidate_overridden_ids === null) {
|
2019-12-20 21:11:27 +01:00
|
|
|
$candidate_overridden_ids
|
|
|
|
= ($declaring_class_storage->overridden_method_ids[$method_name] ?? [])
|
2020-02-15 02:54:26 +01:00
|
|
|
+ [$declaring_method_id->fq_class_name => $declaring_method_id];
|
2019-09-25 18:02:50 +02:00
|
|
|
} else {
|
|
|
|
$candidate_overridden_ids = \array_intersect_key(
|
|
|
|
$candidate_overridden_ids,
|
2019-12-20 21:11:27 +01:00
|
|
|
($declaring_class_storage->overridden_method_ids[$method_name] ?? [])
|
2020-02-15 02:54:26 +01:00
|
|
|
+ [$declaring_method_id->fq_class_name => $declaring_method_id]
|
2019-09-25 18:02:50 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($overridden_method_ids as $declaring_method_id) {
|
2020-02-15 02:54:26 +01:00
|
|
|
$declaring_class = $declaring_method_id->fq_class_name;
|
|
|
|
$declaring_method_name = $declaring_method_id->method_name;
|
2019-09-25 18:02:50 +02:00
|
|
|
$declaring_class_storage = $declaring_class_storages[$declaring_class];
|
2019-03-25 16:25:43 +01:00
|
|
|
|
2020-02-13 23:52:23 +01:00
|
|
|
$declaring_method_storage = $declaring_class_storage->methods[$declaring_method_name];
|
2019-03-25 16:25:43 +01:00
|
|
|
|
2020-11-12 19:54:27 +01:00
|
|
|
if (($declaring_method_storage->has_docblock_param_types
|
|
|
|
|| $declaring_method_storage->return_type
|
|
|
|
!== $declaring_method_storage->signature_return_type)
|
2019-03-25 16:25:43 +01:00
|
|
|
&& !$method_storage->has_docblock_param_types
|
2020-11-12 19:54:27 +01:00
|
|
|
&& $method_storage->return_type === $method_storage->signature_return_type
|
|
|
|
&& (!$declaring_method_storage->signature_return_type
|
|
|
|
|| ($method_storage->signature_return_type
|
|
|
|
&& $method_storage->signature_return_type->getId()
|
|
|
|
=== $declaring_method_storage->signature_return_type->getId()))
|
|
|
|
&& $method_storage->inherited_return_type !== null
|
|
|
|
) {
|
2020-11-12 21:52:13 +01:00
|
|
|
if (!isset($storage->documenting_method_ids[$method_name])
|
|
|
|
|| (string) $storage->documenting_method_ids[$method_name]
|
|
|
|
=== (string) $declaring_method_id
|
|
|
|
) {
|
2020-11-12 19:54:27 +01:00
|
|
|
$storage->documenting_method_ids[$method_name] = $declaring_method_id;
|
|
|
|
$method_storage->inherited_return_type = true;
|
|
|
|
} else {
|
|
|
|
if (\in_array(
|
2020-11-12 01:22:23 +01:00
|
|
|
$storage->documenting_method_ids[$method_name]->fq_class_name,
|
|
|
|
$declaring_class_storage->parent_interfaces
|
2020-11-12 20:22:54 +01:00
|
|
|
)) {
|
2020-11-12 19:54:27 +01:00
|
|
|
$storage->documenting_method_ids[$method_name] = $declaring_method_id;
|
|
|
|
$method_storage->inherited_return_type = true;
|
|
|
|
} else {
|
|
|
|
$documenting_class_storage = $declaring_class_storages
|
|
|
|
[$storage->documenting_method_ids[$method_name]->fq_class_name];
|
|
|
|
|
|
|
|
if (!\in_array(
|
|
|
|
$declaring_class,
|
|
|
|
$documenting_class_storage->parent_interfaces
|
2020-11-12 20:22:54 +01:00
|
|
|
) && $documenting_class_storage->is_interface
|
|
|
|
) {
|
2020-11-12 19:54:27 +01:00
|
|
|
unset($storage->documenting_method_ids[$method_name]);
|
|
|
|
$method_storage->inherited_return_type = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-25 16:25:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// tell the declaring class it's overridden downstream
|
|
|
|
$declaring_method_storage->overridden_downstream = true;
|
|
|
|
$declaring_method_storage->overridden_somewhere = true;
|
|
|
|
|
2019-09-22 15:40:00 +02:00
|
|
|
if ($declaring_method_storage->mutation_free_inferred) {
|
|
|
|
$declaring_method_storage->mutation_free = false;
|
|
|
|
$declaring_method_storage->external_mutation_free = false;
|
|
|
|
$declaring_method_storage->mutation_free_inferred = false;
|
|
|
|
}
|
|
|
|
|
2019-07-14 23:29:04 +02:00
|
|
|
if ($declaring_method_storage->throws
|
|
|
|
&& (!$method_storage->throws || $method_storage->inheritdoc)
|
2019-03-25 16:25:43 +01:00
|
|
|
) {
|
2019-07-14 23:29:04 +02:00
|
|
|
$method_storage->throws += $declaring_method_storage->throws;
|
2019-03-25 16:25:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-02-04 02:03:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private function populateDataFromTraits(
|
|
|
|
ClassLikeStorage $storage,
|
|
|
|
ClassLikeStorageProvider $storage_provider,
|
|
|
|
array $dependent_classlikes
|
2020-09-12 17:24:05 +02:00
|
|
|
): void {
|
2018-02-04 00:52:35 +01:00
|
|
|
foreach ($storage->used_traits as $used_trait_lc => $_) {
|
|
|
|
try {
|
2020-02-15 02:54:26 +01:00
|
|
|
$used_trait_lc = strtolower(
|
|
|
|
$this->classlikes->getUnAliasedName(
|
|
|
|
$used_trait_lc
|
|
|
|
)
|
2018-12-21 15:29:23 +01:00
|
|
|
);
|
2018-02-04 00:52:35 +01:00
|
|
|
$trait_storage = $storage_provider->get($used_trait_lc);
|
|
|
|
} catch (\InvalidArgumentException $e) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->populateClassLikeStorage($trait_storage, $dependent_classlikes);
|
|
|
|
|
|
|
|
$this->inheritMethodsFromParent($storage, $trait_storage);
|
|
|
|
$this->inheritPropertiesFromParent($storage, $trait_storage);
|
2019-01-28 05:12:40 +01:00
|
|
|
|
|
|
|
if ($trait_storage->template_types) {
|
2019-06-25 05:31:06 +02:00
|
|
|
if (isset($storage->template_type_extends[$trait_storage->name])) {
|
|
|
|
foreach ($storage->template_type_extends[$trait_storage->name] as $i => $type) {
|
2019-01-28 05:12:40 +01:00
|
|
|
$trait_template_type_names = array_keys($trait_storage->template_types);
|
|
|
|
|
|
|
|
$mapped_name = $trait_template_type_names[$i] ?? null;
|
|
|
|
|
|
|
|
if ($mapped_name) {
|
2019-06-25 05:31:06 +02:00
|
|
|
$storage->template_type_extends[$trait_storage->name][$mapped_name] = $type;
|
2019-01-28 05:12:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($trait_storage->template_type_extends) {
|
|
|
|
foreach ($trait_storage->template_type_extends as $t_storage_class => $type_map) {
|
|
|
|
foreach ($type_map as $i => $type) {
|
2020-02-15 16:42:29 +01:00
|
|
|
if (is_int($i)) {
|
2019-01-28 05:12:40 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-03-16 16:15:25 +01:00
|
|
|
$storage->template_type_extends[$t_storage_class][$i] = self::extendType(
|
|
|
|
$type,
|
|
|
|
$storage
|
|
|
|
);
|
2019-01-28 05:12:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2019-06-25 05:31:06 +02:00
|
|
|
$storage->template_type_extends[$trait_storage->name] = [];
|
2019-01-28 05:12:40 +01:00
|
|
|
|
2019-03-22 20:59:10 +01:00
|
|
|
foreach ($trait_storage->template_types as $template_name => $template_type_map) {
|
|
|
|
foreach ($template_type_map as $template_type) {
|
2020-11-27 17:43:23 +01:00
|
|
|
$default_param = clone $template_type;
|
2019-11-14 18:12:09 +01:00
|
|
|
$default_param->from_docblock = false;
|
2019-06-25 05:31:06 +02:00
|
|
|
$storage->template_type_extends[$trait_storage->name][$template_name]
|
2019-11-14 18:12:09 +01:00
|
|
|
= $default_param;
|
2019-03-22 20:59:10 +01:00
|
|
|
}
|
2019-01-28 05:12:40 +01:00
|
|
|
}
|
|
|
|
}
|
2019-01-28 22:56:42 +01:00
|
|
|
} elseif ($trait_storage->template_type_extends) {
|
|
|
|
$storage->template_type_extends = array_merge(
|
|
|
|
$storage->template_type_extends ?: [],
|
|
|
|
$trait_storage->template_type_extends
|
|
|
|
);
|
2019-01-28 05:12:40 +01:00
|
|
|
}
|
2020-06-20 22:53:17 +02:00
|
|
|
|
|
|
|
$storage->pseudo_property_get_types += $trait_storage->pseudo_property_get_types;
|
|
|
|
$storage->pseudo_property_set_types += $trait_storage->pseudo_property_set_types;
|
|
|
|
|
|
|
|
$storage->pseudo_methods += $trait_storage->pseudo_methods;
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
2018-02-04 02:03:31 +01:00
|
|
|
}
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2019-03-16 16:15:25 +01:00
|
|
|
private static function extendType(
|
|
|
|
Type\Union $type,
|
|
|
|
ClassLikeStorage $storage
|
|
|
|
) : Type\Union {
|
|
|
|
$extended_types = [];
|
|
|
|
|
2020-01-04 18:20:26 +01:00
|
|
|
foreach ($type->getAtomicTypes() as $atomic_type) {
|
2019-12-30 13:25:27 +01:00
|
|
|
if ($atomic_type instanceof Type\Atomic\TTemplateParam) {
|
2019-03-16 16:15:25 +01:00
|
|
|
$referenced_type
|
2019-06-25 05:31:06 +02:00
|
|
|
= $storage->template_type_extends[$atomic_type->defining_class][$atomic_type->param_name]
|
2019-03-16 16:15:25 +01:00
|
|
|
?? null;
|
|
|
|
|
|
|
|
if ($referenced_type) {
|
2020-01-04 18:20:26 +01:00
|
|
|
foreach ($referenced_type->getAtomicTypes() as $atomic_referenced_type) {
|
2019-03-16 16:15:25 +01:00
|
|
|
if (!$atomic_referenced_type instanceof Type\Atomic\TTemplateParam) {
|
|
|
|
$extended_types[] = $atomic_referenced_type;
|
|
|
|
} else {
|
|
|
|
$extended_types[] = $atomic_type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$extended_types[] = $atomic_type;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$extended_types[] = $atomic_type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return new Type\Union($extended_types);
|
|
|
|
}
|
|
|
|
|
2018-02-04 02:03:31 +01:00
|
|
|
private function populateDataFromParentClass(
|
|
|
|
ClassLikeStorage $storage,
|
|
|
|
ClassLikeStorageProvider $storage_provider,
|
|
|
|
array $dependent_classlikes
|
2020-10-12 21:02:52 +02:00
|
|
|
): void {
|
2018-02-14 19:34:16 +01:00
|
|
|
$parent_storage_class = reset($storage->parent_classes);
|
|
|
|
|
2020-03-29 06:13:01 +02:00
|
|
|
$parent_storage_class = strtolower(
|
|
|
|
$this->classlikes->getUnAliasedName(
|
|
|
|
$parent_storage_class
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
2018-02-04 02:03:31 +01:00
|
|
|
try {
|
2018-02-14 19:34:16 +01:00
|
|
|
$parent_storage = $storage_provider->get($parent_storage_class);
|
2018-02-04 02:03:31 +01:00
|
|
|
} catch (\InvalidArgumentException $e) {
|
2019-05-30 16:30:41 +02:00
|
|
|
$this->progress->debug('Populator could not find dependency (' . __LINE__ . ")\n");
|
2019-01-02 12:58:49 +01:00
|
|
|
|
2018-02-14 19:34:16 +01:00
|
|
|
$storage->invalid_dependencies[] = $parent_storage_class;
|
2020-03-29 06:13:01 +02:00
|
|
|
|
|
|
|
$this->invalid_class_storages[strtolower($parent_storage_class)][] = $storage;
|
|
|
|
|
|
|
|
return;
|
2018-02-04 02:03:31 +01:00
|
|
|
}
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2020-03-29 06:13:01 +02:00
|
|
|
$this->populateClassLikeStorage($parent_storage, $dependent_classlikes);
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2020-03-29 06:13:01 +02:00
|
|
|
$storage->parent_classes = array_merge($storage->parent_classes, $parent_storage->parent_classes);
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2020-03-29 06:13:01 +02:00
|
|
|
if ($parent_storage->template_types) {
|
|
|
|
if (isset($storage->template_type_extends[$parent_storage->name])) {
|
|
|
|
foreach ($storage->template_type_extends[$parent_storage->name] as $i => $type) {
|
|
|
|
$parent_template_type_names = array_keys($parent_storage->template_types);
|
2019-01-10 22:59:44 +01:00
|
|
|
|
2020-03-29 06:13:01 +02:00
|
|
|
$mapped_name = $parent_template_type_names[$i] ?? null;
|
2019-01-10 22:59:44 +01:00
|
|
|
|
2020-03-29 06:13:01 +02:00
|
|
|
if ($mapped_name) {
|
|
|
|
$storage->template_type_extends[$parent_storage->name][$mapped_name] = $type;
|
2019-01-10 22:59:44 +01:00
|
|
|
}
|
2020-03-29 06:13:01 +02:00
|
|
|
}
|
2019-01-13 00:18:23 +01:00
|
|
|
|
2020-03-29 06:13:01 +02:00
|
|
|
if ($parent_storage->template_type_extends) {
|
|
|
|
foreach ($parent_storage->template_type_extends as $t_storage_class => $type_map) {
|
|
|
|
foreach ($type_map as $i => $type) {
|
|
|
|
if (is_int($i)) {
|
|
|
|
continue;
|
2019-01-16 18:07:30 +01:00
|
|
|
}
|
2019-01-24 22:09:04 +01:00
|
|
|
|
2020-03-29 06:13:01 +02:00
|
|
|
$storage->template_type_extends[$t_storage_class][$i] = self::extendType(
|
|
|
|
$type,
|
|
|
|
$storage
|
|
|
|
);
|
2019-03-22 20:59:10 +01:00
|
|
|
}
|
2019-01-24 22:09:04 +01:00
|
|
|
}
|
2020-03-29 06:13:01 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$storage->template_type_extends[$parent_storage->name] = [];
|
|
|
|
|
|
|
|
foreach ($parent_storage->template_types as $template_name => $template_type_map) {
|
|
|
|
foreach ($template_type_map as $template_type) {
|
2020-11-27 17:43:23 +01:00
|
|
|
$default_param = clone $template_type;
|
2020-03-29 06:13:01 +02:00
|
|
|
$default_param->from_docblock = false;
|
|
|
|
$storage->template_type_extends[$parent_storage->name][$template_name]
|
|
|
|
= $default_param;
|
2019-06-25 21:06:02 +02:00
|
|
|
}
|
2019-01-13 00:18:23 +01:00
|
|
|
}
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2020-03-29 06:13:01 +02:00
|
|
|
if ($parent_storage->template_type_extends) {
|
|
|
|
$storage->template_type_extends = array_merge(
|
|
|
|
$storage->template_type_extends,
|
|
|
|
$parent_storage->template_type_extends
|
|
|
|
);
|
|
|
|
}
|
2018-09-04 20:34:14 +02:00
|
|
|
}
|
2020-03-29 06:13:01 +02:00
|
|
|
} elseif ($parent_storage->template_type_extends) {
|
|
|
|
$storage->template_type_extends = array_merge(
|
|
|
|
$storage->template_type_extends ?: [],
|
|
|
|
$parent_storage->template_type_extends
|
2018-09-09 17:18:20 +02:00
|
|
|
);
|
2020-03-29 06:13:01 +02:00
|
|
|
}
|
2018-05-09 04:13:26 +02:00
|
|
|
|
2020-03-29 06:13:01 +02:00
|
|
|
$this->inheritMethodsFromParent($storage, $parent_storage);
|
|
|
|
$this->inheritPropertiesFromParent($storage, $parent_storage);
|
2019-01-02 19:46:46 +01:00
|
|
|
|
2020-03-29 06:13:01 +02:00
|
|
|
$storage->class_implements = array_merge($storage->class_implements, $parent_storage->class_implements);
|
|
|
|
$storage->invalid_dependencies = array_merge(
|
|
|
|
$storage->invalid_dependencies,
|
|
|
|
$parent_storage->invalid_dependencies
|
|
|
|
);
|
2019-01-02 19:46:46 +01:00
|
|
|
|
2020-03-29 06:13:01 +02:00
|
|
|
if ($parent_storage->has_visitor_issues) {
|
|
|
|
$storage->has_visitor_issues = true;
|
|
|
|
}
|
2018-11-30 21:13:25 +01:00
|
|
|
|
2020-10-05 15:50:32 +02:00
|
|
|
$storage->constants = array_merge(
|
|
|
|
\array_filter(
|
|
|
|
$parent_storage->constants,
|
|
|
|
function ($constant) {
|
|
|
|
return $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC
|
|
|
|
|| $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PROTECTED;
|
|
|
|
}
|
|
|
|
),
|
|
|
|
$storage->constants
|
2020-03-29 06:13:01 +02:00
|
|
|
);
|
|
|
|
|
2020-08-06 01:39:27 +02:00
|
|
|
if ($parent_storage->preserve_constructor_signature) {
|
|
|
|
$storage->preserve_constructor_signature = true;
|
|
|
|
}
|
|
|
|
|
2020-08-05 21:49:19 +02:00
|
|
|
if (($parent_storage->namedMixins || $parent_storage->templatedMixins)
|
|
|
|
&& (!$storage->namedMixins || !$storage->templatedMixins)) {
|
2020-05-27 05:29:37 +02:00
|
|
|
$storage->mixin_declaring_fqcln = $parent_storage->mixin_declaring_fqcln;
|
2020-08-05 21:49:19 +02:00
|
|
|
|
|
|
|
if (!$storage->namedMixins) {
|
|
|
|
$storage->namedMixins = $parent_storage->namedMixins;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$storage->templatedMixins) {
|
|
|
|
$storage->templatedMixins = $parent_storage->templatedMixins;
|
|
|
|
}
|
2020-05-10 15:04:30 +02:00
|
|
|
}
|
|
|
|
|
2020-03-29 06:13:01 +02:00
|
|
|
$storage->pseudo_property_get_types += $parent_storage->pseudo_property_get_types;
|
|
|
|
$storage->pseudo_property_set_types += $parent_storage->pseudo_property_set_types;
|
|
|
|
|
|
|
|
$parent_storage->dependent_classlikes[strtolower($storage->name)] = true;
|
|
|
|
|
|
|
|
$storage->pseudo_methods += $parent_storage->pseudo_methods;
|
2018-02-04 02:03:31 +01:00
|
|
|
}
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2018-02-04 02:03:31 +01:00
|
|
|
private function populateInterfaceDataFromParentInterfaces(
|
|
|
|
ClassLikeStorage $storage,
|
|
|
|
ClassLikeStorageProvider $storage_provider,
|
|
|
|
array $dependent_classlikes
|
2020-09-12 17:24:05 +02:00
|
|
|
): void {
|
2018-02-04 00:52:35 +01:00
|
|
|
$parent_interfaces = [];
|
|
|
|
|
|
|
|
foreach ($storage->parent_interfaces as $parent_interface_lc => $_) {
|
|
|
|
try {
|
2020-02-15 02:54:26 +01:00
|
|
|
$parent_interface_lc = strtolower(
|
|
|
|
$this->classlikes->getUnAliasedName(
|
|
|
|
$parent_interface_lc
|
|
|
|
)
|
2018-12-21 15:29:23 +01:00
|
|
|
);
|
2018-02-04 00:52:35 +01:00
|
|
|
$parent_interface_storage = $storage_provider->get($parent_interface_lc);
|
|
|
|
} catch (\InvalidArgumentException $e) {
|
2019-05-30 16:30:41 +02:00
|
|
|
$this->progress->debug('Populator could not find dependency (' . __LINE__ . ")\n");
|
2019-01-02 12:58:49 +01:00
|
|
|
|
2018-02-14 19:34:16 +01:00
|
|
|
$storage->invalid_dependencies[] = $parent_interface_lc;
|
2018-02-04 00:52:35 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->populateClassLikeStorage($parent_interface_storage, $dependent_classlikes);
|
|
|
|
|
|
|
|
// copy over any constants
|
2020-10-05 15:50:32 +02:00
|
|
|
$storage->constants = array_merge(
|
|
|
|
\array_filter(
|
|
|
|
$parent_interface_storage->constants,
|
|
|
|
function ($constant) {
|
|
|
|
return $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC;
|
|
|
|
}
|
|
|
|
),
|
|
|
|
$storage->constants
|
2018-02-04 00:52:35 +01:00
|
|
|
);
|
|
|
|
|
2018-02-14 19:34:16 +01:00
|
|
|
$storage->invalid_dependencies = array_merge(
|
|
|
|
$storage->invalid_dependencies,
|
|
|
|
$parent_interface_storage->invalid_dependencies
|
|
|
|
);
|
|
|
|
|
2019-01-26 22:58:49 +01:00
|
|
|
if ($parent_interface_storage->template_types) {
|
2019-06-25 05:31:06 +02:00
|
|
|
if (isset($storage->template_type_extends[$parent_interface_storage->name])) {
|
|
|
|
foreach ($storage->template_type_extends[$parent_interface_storage->name] as $i => $type) {
|
2019-01-26 22:58:49 +01:00
|
|
|
$parent_template_type_names = array_keys($parent_interface_storage->template_types);
|
|
|
|
|
|
|
|
$mapped_name = $parent_template_type_names[$i] ?? null;
|
|
|
|
|
|
|
|
if ($mapped_name) {
|
2019-06-25 05:31:06 +02:00
|
|
|
$storage->template_type_extends[$parent_interface_storage->name][$mapped_name] = $type;
|
2019-01-26 22:58:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($parent_interface_storage->template_type_extends) {
|
|
|
|
foreach ($parent_interface_storage->template_type_extends as $t_storage_class => $type_map) {
|
|
|
|
foreach ($type_map as $i => $type) {
|
2020-02-15 16:42:29 +01:00
|
|
|
if (is_int($i)) {
|
2019-01-26 22:58:49 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-03-16 16:15:25 +01:00
|
|
|
$storage->template_type_extends[$t_storage_class][$i] = self::extendType(
|
|
|
|
$type,
|
|
|
|
$storage
|
|
|
|
);
|
2019-01-26 22:58:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2019-06-25 05:31:06 +02:00
|
|
|
$storage->template_type_extends[$parent_interface_storage->name] = [];
|
2019-01-26 22:58:49 +01:00
|
|
|
|
2019-03-22 20:59:10 +01:00
|
|
|
foreach ($parent_interface_storage->template_types as $template_name => $template_type_map) {
|
|
|
|
foreach ($template_type_map as $template_type) {
|
2020-11-27 17:43:23 +01:00
|
|
|
$default_param = clone $template_type;
|
2019-11-14 18:12:09 +01:00
|
|
|
$default_param->from_docblock = false;
|
2019-06-25 05:31:06 +02:00
|
|
|
$storage->template_type_extends[$parent_interface_storage->name][$template_name]
|
2019-11-14 18:12:09 +01:00
|
|
|
= $default_param;
|
2019-03-22 20:59:10 +01:00
|
|
|
}
|
2019-01-26 22:58:49 +01:00
|
|
|
}
|
|
|
|
}
|
2020-07-24 17:29:36 +02:00
|
|
|
} elseif ($parent_interface_storage->template_type_extends) {
|
|
|
|
$storage->template_type_extends = array_merge(
|
|
|
|
$storage->template_type_extends ?: [],
|
|
|
|
$parent_interface_storage->template_type_extends
|
|
|
|
);
|
2019-01-26 22:58:49 +01:00
|
|
|
}
|
|
|
|
|
2019-06-25 15:06:23 +02:00
|
|
|
$parent_interface_storage->dependent_classlikes[strtolower($storage->name)] = true;
|
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
$parent_interfaces = array_merge($parent_interfaces, $parent_interface_storage->parent_interfaces);
|
|
|
|
|
|
|
|
$this->inheritMethodsFromParent($storage, $parent_interface_storage);
|
2019-07-05 04:39:28 +02:00
|
|
|
|
|
|
|
$storage->pseudo_methods += $parent_interface_storage->pseudo_methods;
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$storage->parent_interfaces = array_merge($parent_interfaces, $storage->parent_interfaces);
|
2018-02-04 02:03:31 +01:00
|
|
|
}
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2018-02-04 02:03:31 +01:00
|
|
|
private function populateDataFromImplementedInterfaces(
|
|
|
|
ClassLikeStorage $storage,
|
|
|
|
ClassLikeStorageProvider $storage_provider,
|
|
|
|
array $dependent_classlikes
|
2020-09-12 17:24:05 +02:00
|
|
|
): void {
|
2018-02-04 00:52:35 +01:00
|
|
|
$extra_interfaces = [];
|
|
|
|
|
|
|
|
foreach ($storage->class_implements as $implemented_interface_lc => $_) {
|
|
|
|
try {
|
2020-02-15 02:54:26 +01:00
|
|
|
$implemented_interface_lc = strtolower(
|
|
|
|
$this->classlikes->getUnAliasedName(
|
|
|
|
$implemented_interface_lc
|
|
|
|
)
|
2018-12-21 15:29:23 +01:00
|
|
|
);
|
2018-02-04 00:52:35 +01:00
|
|
|
$implemented_interface_storage = $storage_provider->get($implemented_interface_lc);
|
|
|
|
} catch (\InvalidArgumentException $e) {
|
2019-05-30 16:30:41 +02:00
|
|
|
$this->progress->debug('Populator could not find dependency (' . __LINE__ . ")\n");
|
2019-01-02 12:58:49 +01:00
|
|
|
|
2018-02-14 19:34:16 +01:00
|
|
|
$storage->invalid_dependencies[] = $implemented_interface_lc;
|
2018-02-04 00:52:35 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->populateClassLikeStorage($implemented_interface_storage, $dependent_classlikes);
|
|
|
|
|
|
|
|
// copy over any constants
|
2020-10-05 15:50:32 +02:00
|
|
|
$storage->constants = array_merge(
|
|
|
|
\array_filter(
|
|
|
|
$implemented_interface_storage->constants,
|
|
|
|
function ($constant) {
|
|
|
|
return $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC;
|
|
|
|
}
|
|
|
|
),
|
|
|
|
$storage->constants
|
2018-02-04 00:52:35 +01:00
|
|
|
);
|
|
|
|
|
2018-02-14 19:34:16 +01:00
|
|
|
$storage->invalid_dependencies = array_merge(
|
|
|
|
$storage->invalid_dependencies,
|
|
|
|
$implemented_interface_storage->invalid_dependencies
|
|
|
|
);
|
2019-01-10 22:59:44 +01:00
|
|
|
|
2019-01-24 22:09:04 +01:00
|
|
|
if ($implemented_interface_storage->template_types) {
|
2019-06-25 05:31:06 +02:00
|
|
|
if (isset($storage->template_type_extends[$implemented_interface_storage->name])) {
|
|
|
|
foreach ($storage->template_type_extends[$implemented_interface_storage->name] as $i => $type) {
|
2019-01-24 22:09:04 +01:00
|
|
|
$parent_template_type_names = array_keys($implemented_interface_storage->template_types);
|
|
|
|
|
|
|
|
$mapped_name = $parent_template_type_names[$i] ?? null;
|
2019-01-10 22:59:44 +01:00
|
|
|
|
2019-01-24 22:09:04 +01:00
|
|
|
if ($mapped_name) {
|
2019-06-25 05:31:06 +02:00
|
|
|
$storage->template_type_extends[$implemented_interface_storage->name][$mapped_name] = $type;
|
2019-01-24 22:09:04 +01:00
|
|
|
}
|
|
|
|
}
|
2019-01-26 22:58:49 +01:00
|
|
|
|
|
|
|
if ($implemented_interface_storage->template_type_extends) {
|
2020-02-24 23:58:44 +01:00
|
|
|
foreach ($implemented_interface_storage->template_type_extends as $e_i => $type_map) {
|
|
|
|
foreach ($type_map as $i => $type) {
|
|
|
|
if (is_int($i)) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-01-26 22:58:49 +01:00
|
|
|
|
2020-02-24 23:58:44 +01:00
|
|
|
$storage->template_type_extends[$e_i][$i] = self::extendType(
|
|
|
|
$type,
|
|
|
|
$storage
|
|
|
|
);
|
|
|
|
}
|
2019-01-26 22:58:49 +01:00
|
|
|
}
|
|
|
|
}
|
2019-01-24 22:09:04 +01:00
|
|
|
} else {
|
2019-06-25 05:31:06 +02:00
|
|
|
$storage->template_type_extends[$implemented_interface_storage->name] = [];
|
2019-01-10 22:59:44 +01:00
|
|
|
|
2019-03-22 20:59:10 +01:00
|
|
|
foreach ($implemented_interface_storage->template_types as $template_name => $template_type_map) {
|
|
|
|
foreach ($template_type_map as $template_type) {
|
2020-11-27 17:43:23 +01:00
|
|
|
$default_param = clone $template_type;
|
2019-11-14 18:12:09 +01:00
|
|
|
$default_param->from_docblock = false;
|
2019-06-25 05:31:06 +02:00
|
|
|
$storage->template_type_extends[$implemented_interface_storage->name][$template_name]
|
2019-11-14 18:12:09 +01:00
|
|
|
= $default_param;
|
2019-03-22 20:59:10 +01:00
|
|
|
}
|
2019-01-10 22:59:44 +01:00
|
|
|
}
|
|
|
|
}
|
2019-06-11 16:49:39 +02:00
|
|
|
} elseif ($implemented_interface_storage->template_type_extends) {
|
|
|
|
$storage->template_type_extends = array_merge(
|
|
|
|
$storage->template_type_extends ?: [],
|
|
|
|
$implemented_interface_storage->template_type_extends
|
|
|
|
);
|
2019-01-10 22:59:44 +01:00
|
|
|
}
|
2018-02-14 19:34:16 +01:00
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
$extra_interfaces = array_merge($extra_interfaces, $implemented_interface_storage->parent_interfaces);
|
|
|
|
}
|
|
|
|
|
2019-01-26 22:58:49 +01:00
|
|
|
$storage->class_implements = array_merge($storage->class_implements, $extra_interfaces);
|
2018-02-04 00:52:35 +01:00
|
|
|
|
|
|
|
$interface_method_implementers = [];
|
|
|
|
|
2019-01-24 21:03:13 +01:00
|
|
|
foreach ($storage->class_implements as $implemented_interface_lc => $_) {
|
2018-02-04 00:52:35 +01:00
|
|
|
try {
|
2020-02-15 02:54:26 +01:00
|
|
|
$implemented_interface = strtolower(
|
|
|
|
$this->classlikes->getUnAliasedName(
|
|
|
|
$implemented_interface_lc
|
|
|
|
)
|
2018-12-21 15:29:23 +01:00
|
|
|
);
|
2018-02-04 00:52:35 +01:00
|
|
|
$implemented_interface_storage = $storage_provider->get($implemented_interface);
|
|
|
|
} catch (\InvalidArgumentException $e) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-01-24 21:03:13 +01:00
|
|
|
$implemented_interface_storage->dependent_classlikes[strtolower($storage->name)] = true;
|
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
foreach ($implemented_interface_storage->methods as $method_name => $method) {
|
2018-11-06 03:57:36 +01:00
|
|
|
if ($method->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC) {
|
2020-02-15 02:54:26 +01:00
|
|
|
$interface_method_implementers[$method_name][] = new \Psalm\Internal\MethodIdentifier(
|
|
|
|
$implemented_interface_storage->name,
|
|
|
|
$method_name
|
|
|
|
);
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($interface_method_implementers as $method_name => $interface_method_ids) {
|
|
|
|
if (count($interface_method_ids) === 1) {
|
2019-01-24 17:55:59 +01:00
|
|
|
if (isset($storage->methods[$method_name])) {
|
|
|
|
$method_storage = $storage->methods[$method_name];
|
|
|
|
|
|
|
|
if ($method_storage->signature_return_type
|
|
|
|
&& !$method_storage->signature_return_type->isVoid()
|
|
|
|
&& $method_storage->return_type === $method_storage->signature_return_type
|
|
|
|
) {
|
2020-02-15 02:54:26 +01:00
|
|
|
$interface_fqcln = $interface_method_ids[0]->fq_class_name;
|
2019-01-24 17:55:59 +01:00
|
|
|
$interface_storage = $storage_provider->get($interface_fqcln);
|
|
|
|
|
|
|
|
if (isset($interface_storage->methods[$method_name])) {
|
|
|
|
$interface_method_storage = $interface_storage->methods[$method_name];
|
|
|
|
|
2019-07-14 23:29:04 +02:00
|
|
|
if ($interface_method_storage->throws
|
|
|
|
&& (!$method_storage->throws || $method_storage->inheritdoc)
|
2019-02-01 00:40:40 +01:00
|
|
|
) {
|
2019-07-14 23:29:04 +02:00
|
|
|
$method_storage->throws += $interface_method_storage->throws;
|
2019-02-01 00:40:40 +01:00
|
|
|
}
|
2019-01-24 17:55:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-12-20 02:42:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($interface_method_ids as $interface_method_id) {
|
2020-02-15 02:54:26 +01:00
|
|
|
$storage->overridden_method_ids[$method_name][$interface_method_id->fq_class_name]
|
|
|
|
= $interface_method_id;
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array<string, bool> $dependent_file_paths
|
|
|
|
*/
|
2020-10-12 21:02:52 +02:00
|
|
|
private function populateFileStorage(FileStorage $storage, array $dependent_file_paths = []): void
|
2018-02-04 00:52:35 +01:00
|
|
|
{
|
|
|
|
if ($storage->populated) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$file_path_lc = strtolower($storage->file_path);
|
|
|
|
|
|
|
|
if (isset($dependent_file_paths[$file_path_lc])) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$dependent_file_paths[$file_path_lc] = true;
|
|
|
|
|
2018-05-30 18:23:53 +02:00
|
|
|
$all_required_file_paths = $storage->required_file_paths;
|
2018-05-23 05:38:27 +02:00
|
|
|
|
2018-05-30 18:23:53 +02:00
|
|
|
foreach ($storage->required_file_paths as $included_file_path => $_) {
|
2018-02-04 00:52:35 +01:00
|
|
|
try {
|
|
|
|
$included_file_storage = $this->file_storage_provider->get($included_file_path);
|
|
|
|
} catch (\InvalidArgumentException $e) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->populateFileStorage($included_file_storage, $dependent_file_paths);
|
|
|
|
|
2018-05-30 18:23:53 +02:00
|
|
|
$all_required_file_paths = $all_required_file_paths + $included_file_storage->required_file_paths;
|
2018-05-23 05:38:27 +02:00
|
|
|
}
|
|
|
|
|
2018-05-30 18:23:53 +02:00
|
|
|
foreach ($all_required_file_paths as $included_file_path => $_) {
|
2018-05-23 05:38:27 +02:00
|
|
|
try {
|
|
|
|
$included_file_storage = $this->file_storage_provider->get($included_file_path);
|
|
|
|
} catch (\InvalidArgumentException $e) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
$storage->declaring_function_ids = array_merge(
|
|
|
|
$included_file_storage->declaring_function_ids,
|
|
|
|
$storage->declaring_function_ids
|
|
|
|
);
|
|
|
|
|
|
|
|
$storage->declaring_constants = array_merge(
|
|
|
|
$included_file_storage->declaring_constants,
|
|
|
|
$storage->declaring_constants
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-02-08 23:41:03 +01:00
|
|
|
foreach ($storage->referenced_classlikes as $fq_class_name) {
|
|
|
|
try {
|
|
|
|
$classlike_storage = $this->classlike_storage_provider->get($fq_class_name);
|
|
|
|
} catch (\InvalidArgumentException $e) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$classlike_storage->location) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
$included_file_storage = $this->file_storage_provider->get($classlike_storage->location->file_path);
|
|
|
|
} catch (\InvalidArgumentException $e) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-10-15 18:09:08 +02:00
|
|
|
foreach ($classlike_storage->used_traits as $used_trait) {
|
|
|
|
try {
|
|
|
|
$trait_storage = $this->classlike_storage_provider->get($used_trait);
|
|
|
|
} catch (\InvalidArgumentException $e) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$trait_storage->location) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
$included_trait_file_storage = $this->file_storage_provider->get(
|
|
|
|
$trait_storage->location->file_path
|
|
|
|
);
|
|
|
|
} catch (\InvalidArgumentException $e) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$storage->declaring_function_ids = array_merge(
|
|
|
|
$included_trait_file_storage->declaring_function_ids,
|
|
|
|
$storage->declaring_function_ids
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-02-08 23:41:03 +01:00
|
|
|
$storage->declaring_function_ids = array_merge(
|
|
|
|
$included_file_storage->declaring_function_ids,
|
|
|
|
$storage->declaring_function_ids
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-05-30 18:23:53 +02:00
|
|
|
$storage->required_file_paths = $all_required_file_paths;
|
|
|
|
|
|
|
|
foreach ($all_required_file_paths as $required_file_path) {
|
|
|
|
try {
|
|
|
|
$required_file_storage = $this->file_storage_provider->get($required_file_path);
|
|
|
|
} catch (\InvalidArgumentException $e) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$required_file_storage->required_by_file_paths += [$file_path_lc => $storage->file_path];
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($storage->required_classes as $required_classlike) {
|
|
|
|
try {
|
|
|
|
$classlike_storage = $this->classlike_storage_provider->get($required_classlike);
|
|
|
|
} catch (\InvalidArgumentException $e) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$classlike_storage->location) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
$required_file_storage = $this->file_storage_provider->get($classlike_storage->location->file_path);
|
|
|
|
} catch (\InvalidArgumentException $e) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$required_file_storage->required_by_file_paths += [$file_path_lc => $storage->file_path];
|
|
|
|
}
|
2018-05-23 05:38:27 +02:00
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
$storage->populated = true;
|
|
|
|
}
|
|
|
|
|
2020-10-12 21:46:47 +02:00
|
|
|
private function convertPhpStormGenericToPsalmGeneric(Type\Union $candidate, bool $is_property = false): void
|
2018-02-04 00:52:35 +01:00
|
|
|
{
|
2020-01-04 18:20:26 +01:00
|
|
|
$atomic_types = $candidate->getAtomicTypes();
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2019-05-06 22:38:08 +02:00
|
|
|
if (isset($atomic_types['array']) && count($atomic_types) > 1 && !isset($atomic_types['null'])) {
|
2018-02-04 00:52:35 +01:00
|
|
|
$iterator_name = null;
|
|
|
|
$generic_params = null;
|
2019-05-30 01:58:54 +02:00
|
|
|
$iterator_key = null;
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2019-03-25 15:44:40 +01:00
|
|
|
try {
|
2019-05-30 01:58:54 +02:00
|
|
|
foreach ($atomic_types as $type_key => $type) {
|
2019-03-25 15:44:40 +01:00
|
|
|
if ($type instanceof Type\Atomic\TIterable
|
|
|
|
|| ($type instanceof Type\Atomic\TNamedObject
|
|
|
|
&& (!$type->from_docblock || $is_property)
|
|
|
|
&& (
|
|
|
|
strtolower($type->value) === 'traversable'
|
|
|
|
|| $this->classlikes->interfaceExtends(
|
|
|
|
$type->value,
|
|
|
|
'Traversable'
|
|
|
|
)
|
|
|
|
|| $this->classlikes->classImplements(
|
|
|
|
$type->value,
|
|
|
|
'Traversable'
|
|
|
|
)
|
|
|
|
))
|
|
|
|
) {
|
|
|
|
$iterator_name = $type->value;
|
2019-05-30 01:58:54 +02:00
|
|
|
$iterator_key = $type_key;
|
2019-03-25 15:44:40 +01:00
|
|
|
} elseif ($type instanceof Type\Atomic\TArray) {
|
|
|
|
$generic_params = $type->type_params;
|
|
|
|
}
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
2019-03-25 15:44:40 +01:00
|
|
|
} catch (\InvalidArgumentException $e) {
|
|
|
|
// ignore class-not-found issues
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
2019-05-30 01:58:54 +02:00
|
|
|
if ($iterator_name && $iterator_key && $generic_params) {
|
2019-01-04 20:54:40 +01:00
|
|
|
if ($iterator_name === 'iterable') {
|
2019-01-23 05:42:54 +01:00
|
|
|
$generic_iterator = new Type\Atomic\TIterable($generic_params);
|
2019-01-04 20:54:40 +01:00
|
|
|
} else {
|
2019-02-20 17:13:33 +01:00
|
|
|
if (strtolower($iterator_name) === 'generator') {
|
|
|
|
$generic_params[] = Type::getMixed();
|
|
|
|
$generic_params[] = Type::getMixed();
|
|
|
|
}
|
2019-01-04 20:54:40 +01:00
|
|
|
$generic_iterator = new Type\Atomic\TGenericObject($iterator_name, $generic_params);
|
|
|
|
}
|
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
$candidate->removeType('array');
|
2019-05-30 01:58:54 +02:00
|
|
|
$candidate->removeType($iterator_key);
|
2018-02-04 00:52:35 +01:00
|
|
|
$candidate->addType($generic_iterator);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-03 05:50:19 +01:00
|
|
|
protected function inheritMethodsFromParent(
|
|
|
|
ClassLikeStorage $storage,
|
2020-06-11 17:28:41 +02:00
|
|
|
ClassLikeStorage $parent_storage
|
2020-09-12 17:24:05 +02:00
|
|
|
): void {
|
2018-02-04 00:52:35 +01:00
|
|
|
$fq_class_name = $storage->name;
|
2020-02-15 02:54:26 +01:00
|
|
|
$fq_class_name_lc = strtolower($fq_class_name);
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2020-06-11 17:28:41 +02:00
|
|
|
if ($parent_storage->sealed_methods) {
|
|
|
|
$storage->sealed_methods = true;
|
|
|
|
}
|
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
// register where they appear (can never be in a trait)
|
2020-02-15 02:54:26 +01:00
|
|
|
foreach ($parent_storage->appearing_method_ids as $method_name_lc => $appearing_method_id) {
|
|
|
|
$aliased_method_names = [$method_name_lc];
|
2018-03-21 21:55:31 +01:00
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
if ($parent_storage->is_trait
|
|
|
|
&& $storage->trait_alias_map
|
|
|
|
) {
|
2018-11-30 19:45:39 +01:00
|
|
|
$aliased_method_names = array_merge(
|
|
|
|
$aliased_method_names,
|
2020-02-15 02:54:26 +01:00
|
|
|
array_keys($storage->trait_alias_map, $method_name_lc, true)
|
2018-11-30 19:45:39 +01:00
|
|
|
);
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
2018-03-21 21:55:31 +01:00
|
|
|
foreach ($aliased_method_names as $aliased_method_name) {
|
|
|
|
if (isset($storage->appearing_method_ids[$aliased_method_name])) {
|
|
|
|
continue;
|
|
|
|
}
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2020-02-15 02:54:26 +01:00
|
|
|
$implemented_method_id = new \Psalm\Internal\MethodIdentifier(
|
|
|
|
$fq_class_name,
|
|
|
|
$aliased_method_name
|
|
|
|
);
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2018-03-21 21:55:31 +01:00
|
|
|
$storage->appearing_method_ids[$aliased_method_name] =
|
|
|
|
$parent_storage->is_trait ? $implemented_method_id : $appearing_method_id;
|
2018-09-26 00:37:24 +02:00
|
|
|
|
2020-02-15 02:54:26 +01:00
|
|
|
$this_method_id = $fq_class_name_lc . '::' . $method_name_lc;
|
2018-09-26 00:37:24 +02:00
|
|
|
|
|
|
|
if (isset($storage->methods[$aliased_method_name])) {
|
|
|
|
$storage->potential_declaring_method_ids[$aliased_method_name] = [$this_method_id => true];
|
|
|
|
} else {
|
|
|
|
if (isset($parent_storage->potential_declaring_method_ids[$aliased_method_name])) {
|
|
|
|
$storage->potential_declaring_method_ids[$aliased_method_name]
|
|
|
|
= $parent_storage->potential_declaring_method_ids[$aliased_method_name];
|
|
|
|
}
|
|
|
|
|
|
|
|
$storage->potential_declaring_method_ids[$aliased_method_name][$this_method_id] = true;
|
|
|
|
|
2020-02-15 02:54:26 +01:00
|
|
|
$parent_method_id = strtolower($parent_storage->name) . '::' . $method_name_lc;
|
2018-09-26 00:37:24 +02:00
|
|
|
$storage->potential_declaring_method_ids[$aliased_method_name][$parent_method_id] = true;
|
|
|
|
}
|
2018-03-21 21:55:31 +01:00
|
|
|
}
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// register where they're declared
|
2020-02-15 02:54:26 +01:00
|
|
|
foreach ($parent_storage->inheritable_method_ids as $method_name_lc => $declaring_method_id) {
|
2020-08-06 01:39:27 +02:00
|
|
|
if ($method_name_lc !== '__construct'
|
|
|
|
|| $parent_storage->preserve_constructor_signature
|
|
|
|
) {
|
2019-03-29 03:47:17 +01:00
|
|
|
if ($parent_storage->is_trait) {
|
2020-02-15 02:54:26 +01:00
|
|
|
$declaring_class = $declaring_method_id->fq_class_name;
|
2019-03-29 03:47:17 +01:00
|
|
|
$declaring_class_storage = $this->classlike_storage_provider->get($declaring_class);
|
|
|
|
|
2020-02-15 02:54:26 +01:00
|
|
|
if (isset($declaring_class_storage->methods[$method_name_lc])
|
|
|
|
&& $declaring_class_storage->methods[$method_name_lc]->abstract
|
2019-03-29 03:47:17 +01:00
|
|
|
) {
|
2020-02-15 02:54:26 +01:00
|
|
|
$storage->overridden_method_ids[$method_name_lc][$declaring_method_id->fq_class_name]
|
|
|
|
= $declaring_method_id;
|
2019-03-29 03:47:17 +01:00
|
|
|
}
|
|
|
|
} else {
|
2020-02-15 02:54:26 +01:00
|
|
|
$storage->overridden_method_ids[$method_name_lc][$declaring_method_id->fq_class_name]
|
|
|
|
= $declaring_method_id;
|
2019-03-29 03:47:17 +01:00
|
|
|
}
|
2019-09-25 18:02:50 +02:00
|
|
|
|
2020-02-15 02:54:26 +01:00
|
|
|
if (isset($parent_storage->overridden_method_ids[$method_name_lc])
|
|
|
|
&& isset($storage->overridden_method_ids[$method_name_lc])
|
2019-09-25 18:02:50 +02:00
|
|
|
) {
|
2020-02-15 02:54:26 +01:00
|
|
|
$storage->overridden_method_ids[$method_name_lc]
|
|
|
|
+= $parent_storage->overridden_method_ids[$method_name_lc];
|
2019-09-25 18:02:50 +02:00
|
|
|
}
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
2020-02-15 02:54:26 +01:00
|
|
|
$aliased_method_names = [$method_name_lc];
|
2018-03-21 21:55:31 +01:00
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
if ($parent_storage->is_trait
|
|
|
|
&& $storage->trait_alias_map
|
|
|
|
) {
|
2018-11-30 19:45:39 +01:00
|
|
|
$aliased_method_names = array_merge(
|
|
|
|
$aliased_method_names,
|
2020-02-15 02:54:26 +01:00
|
|
|
array_keys($storage->trait_alias_map, $method_name_lc, true)
|
2018-11-30 19:45:39 +01:00
|
|
|
);
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
2018-03-21 21:55:31 +01:00
|
|
|
foreach ($aliased_method_names as $aliased_method_name) {
|
|
|
|
if (isset($storage->declaring_method_ids[$aliased_method_name])) {
|
2020-02-15 02:54:26 +01:00
|
|
|
$implementing_method_id = $storage->declaring_method_ids[$aliased_method_name];
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2020-02-15 02:54:26 +01:00
|
|
|
$implementing_class_storage = $this->classlike_storage_provider->get(
|
|
|
|
$implementing_method_id->fq_class_name
|
|
|
|
);
|
2018-02-04 00:52:35 +01:00
|
|
|
|
2020-05-03 03:57:53 +02:00
|
|
|
if (!$implementing_class_storage->methods[$implementing_method_id->method_name]->abstract
|
|
|
|
|| !empty($storage->methods[$implementing_method_id->method_name]->abstract)
|
|
|
|
) {
|
2018-03-21 21:55:31 +01:00
|
|
|
continue;
|
|
|
|
}
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
2018-03-21 21:55:31 +01:00
|
|
|
$storage->declaring_method_ids[$aliased_method_name] = $declaring_method_id;
|
|
|
|
$storage->inheritable_method_ids[$aliased_method_name] = $declaring_method_id;
|
|
|
|
}
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-03 05:50:19 +01:00
|
|
|
private function inheritPropertiesFromParent(
|
|
|
|
ClassLikeStorage $storage,
|
2020-06-11 17:28:41 +02:00
|
|
|
ClassLikeStorage $parent_storage
|
2020-09-12 17:24:05 +02:00
|
|
|
): void {
|
2020-06-11 17:28:41 +02:00
|
|
|
if ($parent_storage->sealed_properties) {
|
|
|
|
$storage->sealed_properties = true;
|
|
|
|
}
|
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
// register where they appear (can never be in a trait)
|
|
|
|
foreach ($parent_storage->appearing_property_ids as $property_name => $appearing_property_id) {
|
|
|
|
if (isset($storage->appearing_property_ids[$property_name])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$parent_storage->is_trait
|
|
|
|
&& isset($parent_storage->properties[$property_name])
|
2020-06-11 17:28:41 +02:00
|
|
|
&& $parent_storage->properties[$property_name]->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE
|
2018-02-04 00:52:35 +01:00
|
|
|
) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$implemented_property_id = $storage->name . '::$' . $property_name;
|
|
|
|
|
|
|
|
$storage->appearing_property_ids[$property_name] =
|
|
|
|
$parent_storage->is_trait ? $implemented_property_id : $appearing_property_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
// register where they're declared
|
2018-07-22 01:56:26 +02:00
|
|
|
foreach ($parent_storage->declaring_property_ids as $property_name => $declaring_property_class) {
|
2018-02-04 00:52:35 +01:00
|
|
|
if (isset($storage->declaring_property_ids[$property_name])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$parent_storage->is_trait
|
|
|
|
&& isset($parent_storage->properties[$property_name])
|
2020-06-11 17:28:41 +02:00
|
|
|
&& $parent_storage->properties[$property_name]->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE
|
2018-02-04 00:52:35 +01:00
|
|
|
) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-07-22 01:56:26 +02:00
|
|
|
$storage->declaring_property_ids[$property_name] = $declaring_property_class;
|
2018-02-04 00:52:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// register where they're declared
|
|
|
|
foreach ($parent_storage->inheritable_property_ids as $property_name => $inheritable_property_id) {
|
|
|
|
if (!$parent_storage->is_trait
|
|
|
|
&& isset($parent_storage->properties[$property_name])
|
2020-06-11 17:28:41 +02:00
|
|
|
&& $parent_storage->properties[$property_name]->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE
|
2018-02-04 00:52:35 +01:00
|
|
|
) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-03-04 18:24:50 +01:00
|
|
|
if (!$parent_storage->is_trait) {
|
|
|
|
$storage->overridden_property_ids[$property_name][] = $inheritable_property_id;
|
|
|
|
}
|
|
|
|
|
2018-02-04 00:52:35 +01:00
|
|
|
$storage->inheritable_property_ids[$property_name] = $inheritable_property_id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|