2018-02-09 00:14:28 +01:00
|
|
|
<?php
|
2018-11-12 16:46:55 +01:00
|
|
|
namespace Psalm\Internal\Codebase;
|
2018-02-09 00:14:28 +01:00
|
|
|
|
|
|
|
use Psalm\CodeLocation;
|
2018-11-06 03:57:36 +01:00
|
|
|
use Psalm\Internal\Provider\ClassLikeStorageProvider;
|
|
|
|
use Psalm\Internal\Provider\FileReferenceProvider;
|
2018-02-09 00:14:28 +01:00
|
|
|
|
2018-02-09 23:51:49 +01:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*
|
|
|
|
* Handles information about class properties
|
|
|
|
*/
|
2018-02-09 00:14:28 +01:00
|
|
|
class Properties
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @var ClassLikeStorageProvider
|
|
|
|
*/
|
|
|
|
private $classlike_storage_provider;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
public $collect_references = false;
|
|
|
|
|
2018-09-28 22:18:45 +02:00
|
|
|
/**
|
|
|
|
* @var FileReferenceProvider
|
|
|
|
*/
|
|
|
|
public $file_reference_provider;
|
|
|
|
|
2018-02-09 00:14:28 +01:00
|
|
|
public function __construct(
|
2018-09-28 22:18:45 +02:00
|
|
|
ClassLikeStorageProvider $storage_provider,
|
|
|
|
FileReferenceProvider $file_reference_provider
|
2018-02-09 00:14:28 +01:00
|
|
|
) {
|
|
|
|
$this->classlike_storage_provider = $storage_provider;
|
2018-09-28 22:18:45 +02:00
|
|
|
$this->file_reference_provider = $file_reference_provider;
|
2018-02-09 00:14:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether or not a given property exists
|
|
|
|
*
|
|
|
|
* @param string $property_id
|
2018-09-27 19:32:08 +02:00
|
|
|
* @param ?string $calling_method_id
|
2018-09-26 00:37:24 +02:00
|
|
|
* @param string $calling_method_id
|
2018-02-09 00:14:28 +01:00
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function propertyExists(
|
|
|
|
$property_id,
|
2018-09-27 19:32:08 +02:00
|
|
|
$calling_method_id = null,
|
2018-02-09 00:14:28 +01:00
|
|
|
CodeLocation $code_location = null
|
|
|
|
) {
|
|
|
|
// remove trailing backslash if it exists
|
|
|
|
$property_id = preg_replace('/^\\\\/', '', $property_id);
|
|
|
|
|
|
|
|
list($fq_class_name, $property_name) = explode('::$', $property_id);
|
|
|
|
|
|
|
|
$class_storage = $this->classlike_storage_provider->get($fq_class_name);
|
|
|
|
|
|
|
|
if (isset($class_storage->declaring_property_ids[$property_name])) {
|
2018-09-26 00:37:24 +02:00
|
|
|
$declaring_property_class = $class_storage->declaring_property_ids[$property_name];
|
|
|
|
|
|
|
|
if ($calling_method_id) {
|
2018-09-28 22:18:45 +02:00
|
|
|
$this->file_reference_provider->addReferenceToClassMethod(
|
2018-09-26 00:37:24 +02:00
|
|
|
$calling_method_id,
|
|
|
|
strtolower($declaring_property_class) . '::$' . $property_name
|
|
|
|
);
|
|
|
|
}
|
2018-02-09 00:14:28 +01:00
|
|
|
|
2018-09-26 00:37:24 +02:00
|
|
|
if ($this->collect_references && $code_location) {
|
2018-02-09 00:14:28 +01:00
|
|
|
$declaring_class_storage = $this->classlike_storage_provider->get($declaring_property_class);
|
2018-07-22 01:56:26 +02:00
|
|
|
$declaring_property_storage = $declaring_class_storage->properties[$property_name];
|
2018-02-09 00:14:28 +01:00
|
|
|
|
|
|
|
if ($declaring_property_storage->referencing_locations === null) {
|
|
|
|
$declaring_property_storage->referencing_locations = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
$declaring_property_storage->referencing_locations[$code_location->file_path][] = $code_location;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string $property_id
|
|
|
|
*
|
|
|
|
* @return string|null
|
|
|
|
*/
|
|
|
|
public function getDeclaringClassForProperty($property_id)
|
|
|
|
{
|
|
|
|
list($fq_class_name, $property_name) = explode('::$', $property_id);
|
|
|
|
|
|
|
|
$fq_class_name = strtolower($fq_class_name);
|
|
|
|
|
|
|
|
$class_storage = $this->classlike_storage_provider->get($fq_class_name);
|
|
|
|
|
|
|
|
if (isset($class_storage->declaring_property_ids[$property_name])) {
|
2018-07-22 01:56:26 +02:00
|
|
|
return $class_storage->declaring_property_ids[$property_name];
|
2018-02-09 00:14:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the class this property appears in (vs is declared in, which could give a trait)
|
|
|
|
*
|
|
|
|
* @param string $property_id
|
|
|
|
*
|
|
|
|
* @return string|null
|
|
|
|
*/
|
|
|
|
public function getAppearingClassForProperty($property_id)
|
|
|
|
{
|
|
|
|
list($fq_class_name, $property_name) = explode('::$', $property_id);
|
|
|
|
|
|
|
|
$fq_class_name = strtolower($fq_class_name);
|
|
|
|
|
|
|
|
$class_storage = $this->classlike_storage_provider->get($fq_class_name);
|
|
|
|
|
|
|
|
if (isset($class_storage->appearing_property_ids[$property_name])) {
|
|
|
|
$appearing_property_id = $class_storage->appearing_property_ids[$property_name];
|
|
|
|
|
|
|
|
return explode('::$', $appearing_property_id)[0];
|
|
|
|
}
|
|
|
|
}
|
2018-10-26 22:17:15 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string $property_id
|
|
|
|
* @return \Psalm\Storage\PropertyStorage
|
|
|
|
*/
|
|
|
|
public function getStorage($property_id)
|
|
|
|
{
|
|
|
|
// remove trailing backslash if it exists
|
|
|
|
$property_id = preg_replace('/^\\\\/', '', $property_id);
|
|
|
|
|
|
|
|
list($fq_class_name, $property_name) = explode('::$', $property_id);
|
|
|
|
|
|
|
|
$class_storage = $this->classlike_storage_provider->get($fq_class_name);
|
|
|
|
|
|
|
|
if (isset($class_storage->declaring_property_ids[$property_name])) {
|
|
|
|
$declaring_property_class = $class_storage->declaring_property_ids[$property_name];
|
|
|
|
$declaring_class_storage = $this->classlike_storage_provider->get($declaring_property_class);
|
|
|
|
|
|
|
|
if (isset($declaring_class_storage->properties[$property_name])) {
|
|
|
|
return $declaring_class_storage->properties[$property_name];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new \UnexpectedValueException('Property ' . $property_id . ' should exist');
|
|
|
|
}
|
2018-02-09 00:14:28 +01:00
|
|
|
}
|