2018-09-28 22:18:45 +02:00
|
|
|
<?php
|
2018-11-06 03:57:36 +01:00
|
|
|
namespace Psalm\Internal\Provider;
|
2018-09-28 22:18:45 +02:00
|
|
|
|
|
|
|
use Psalm\Config;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @psalm-type IssueData = array{
|
|
|
|
* severity: string,
|
|
|
|
* line_from: int,
|
|
|
|
* line_to: int,
|
|
|
|
* type: string,
|
|
|
|
* message: string,
|
|
|
|
* file_name: string,
|
|
|
|
* file_path: string,
|
|
|
|
* snippet: string,
|
|
|
|
* from: int,
|
|
|
|
* to: int,
|
|
|
|
* snippet_from: int,
|
|
|
|
* snippet_to: int,
|
|
|
|
* column_from: int,
|
|
|
|
* column_to: int
|
|
|
|
* }
|
2018-10-17 21:52:26 +02:00
|
|
|
*
|
|
|
|
* @psalm-type TaggedCodeType = array<int, array{0: int, 1: string}>
|
2018-09-28 22:18:45 +02:00
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Used to determine which files reference other files, necessary for using the --diff
|
|
|
|
* option from the command line.
|
|
|
|
*/
|
|
|
|
class FileReferenceCacheProvider
|
|
|
|
{
|
|
|
|
const REFERENCE_CACHE_NAME = 'references';
|
2018-11-02 02:52:39 +01:00
|
|
|
const ANALYZED_METHODS_CACHE_NAME = 'analyzed_methods';
|
2018-09-28 22:18:45 +02:00
|
|
|
const CLASS_METHOD_CACHE_NAME = 'class_method_references';
|
2019-04-16 22:07:48 +02:00
|
|
|
const FILE_CLASS_MEMBER_CACHE_NAME = 'file_class_member_references';
|
2018-09-28 22:18:45 +02:00
|
|
|
const ISSUES_CACHE_NAME = 'issues';
|
2018-10-17 21:52:26 +02:00
|
|
|
const FILE_MAPS_CACHE_NAME = 'file_maps';
|
2019-03-23 14:50:47 +01:00
|
|
|
const TYPE_COVERAGE_CACHE_NAME = 'type_coverage';
|
2018-11-01 22:42:48 +01:00
|
|
|
const CONFIG_HASH_CACHE_NAME = 'config';
|
2019-04-16 22:07:48 +02:00
|
|
|
const METHOD_MISSING_MEMBER_CACHE_NAME = 'method_missing_member';
|
|
|
|
const FILE_MISSING_MEMBER_CACHE_NAME = 'file_missing_member';
|
2018-09-28 22:18:45 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @var Config
|
|
|
|
*/
|
|
|
|
private $config;
|
|
|
|
|
2018-11-01 22:42:48 +01:00
|
|
|
/**
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
public $config_changed;
|
|
|
|
|
2018-09-28 22:18:45 +02:00
|
|
|
public function __construct(Config $config)
|
|
|
|
{
|
|
|
|
$this->config = $config;
|
2018-11-01 22:42:48 +01:00
|
|
|
$this->config_changed = $config->hash !== $this->getConfigHashCache();
|
|
|
|
$this->setConfigHashCache($config->hash);
|
2018-09-28 22:18:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return ?array
|
|
|
|
*
|
|
|
|
* @psalm-suppress MixedAssignment
|
|
|
|
* @psalm-suppress MixedTypeCoercion
|
|
|
|
*/
|
|
|
|
public function getCachedFileReferences()
|
|
|
|
{
|
|
|
|
$cache_directory = $this->config->getCacheDirectory();
|
|
|
|
|
|
|
|
if (!$cache_directory) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$reference_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::REFERENCE_CACHE_NAME;
|
|
|
|
|
|
|
|
if (!is_readable($reference_cache_location)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$reference_cache = unserialize((string) file_get_contents($reference_cache_location));
|
|
|
|
|
|
|
|
if (!is_array($reference_cache)) {
|
|
|
|
throw new \UnexpectedValueException('The reference cache must be an array');
|
|
|
|
}
|
|
|
|
|
|
|
|
return $reference_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return ?array
|
|
|
|
*
|
|
|
|
* @psalm-suppress MixedAssignment
|
|
|
|
* @psalm-suppress MixedTypeCoercion
|
|
|
|
*/
|
2019-04-16 22:07:48 +02:00
|
|
|
public function getCachedMethodMemberReferences()
|
2018-09-28 22:18:45 +02:00
|
|
|
{
|
|
|
|
$cache_directory = $this->config->getCacheDirectory();
|
|
|
|
|
|
|
|
if (!$cache_directory) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2019-04-13 00:28:07 +02:00
|
|
|
$class_member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::CLASS_METHOD_CACHE_NAME;
|
2018-09-28 22:18:45 +02:00
|
|
|
|
2019-04-13 00:28:07 +02:00
|
|
|
if (!is_readable($class_member_cache_location)) {
|
2018-09-28 22:18:45 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2019-04-13 00:28:07 +02:00
|
|
|
$class_member_reference_cache = unserialize((string) file_get_contents($class_member_cache_location));
|
2018-09-28 22:18:45 +02:00
|
|
|
|
2019-04-13 00:28:07 +02:00
|
|
|
if (!is_array($class_member_reference_cache)) {
|
2018-09-28 22:18:45 +02:00
|
|
|
throw new \UnexpectedValueException('The reference cache must be an array');
|
|
|
|
}
|
|
|
|
|
2019-04-13 00:28:07 +02:00
|
|
|
return $class_member_reference_cache;
|
2018-09-28 22:18:45 +02:00
|
|
|
}
|
|
|
|
|
2019-04-16 22:07:48 +02:00
|
|
|
/**
|
|
|
|
* @return ?array
|
|
|
|
*
|
|
|
|
* @psalm-suppress MixedAssignment
|
|
|
|
* @psalm-suppress MixedTypeCoercion
|
|
|
|
*/
|
|
|
|
public function getCachedMethodMissingMemberReferences()
|
|
|
|
{
|
|
|
|
$cache_directory = $this->config->getCacheDirectory();
|
|
|
|
|
|
|
|
if (!$cache_directory) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$class_member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::METHOD_MISSING_MEMBER_CACHE_NAME;
|
|
|
|
|
|
|
|
if (!is_readable($class_member_cache_location)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$class_member_reference_cache = unserialize((string) file_get_contents($class_member_cache_location));
|
|
|
|
|
|
|
|
if (!is_array($class_member_reference_cache)) {
|
|
|
|
throw new \UnexpectedValueException('The reference cache must be an array');
|
|
|
|
}
|
|
|
|
|
|
|
|
return $class_member_reference_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return ?array
|
|
|
|
*
|
|
|
|
* @psalm-suppress MixedAssignment
|
|
|
|
* @psalm-suppress MixedTypeCoercion
|
|
|
|
*/
|
|
|
|
public function getCachedFileMemberReferences()
|
|
|
|
{
|
|
|
|
$cache_directory = $this->config->getCacheDirectory();
|
|
|
|
|
|
|
|
if (!$cache_directory) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$file_class_member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::FILE_CLASS_MEMBER_CACHE_NAME;
|
|
|
|
|
|
|
|
if (!is_readable($file_class_member_cache_location)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$file_class_member_reference_cache = unserialize((string) file_get_contents($file_class_member_cache_location));
|
|
|
|
|
|
|
|
if (!is_array($file_class_member_reference_cache)) {
|
|
|
|
throw new \UnexpectedValueException('The reference cache must be an array');
|
|
|
|
}
|
|
|
|
|
|
|
|
return $file_class_member_reference_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return ?array
|
|
|
|
*
|
|
|
|
* @psalm-suppress MixedAssignment
|
|
|
|
* @psalm-suppress MixedTypeCoercion
|
|
|
|
*/
|
|
|
|
public function getCachedFileMissingMemberReferences()
|
|
|
|
{
|
|
|
|
$cache_directory = $this->config->getCacheDirectory();
|
|
|
|
|
|
|
|
if (!$cache_directory) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$file_class_member_cache_location
|
|
|
|
= $cache_directory . DIRECTORY_SEPARATOR . self::FILE_MISSING_MEMBER_CACHE_NAME;
|
|
|
|
|
|
|
|
if (!is_readable($file_class_member_cache_location)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$file_class_member_reference_cache = unserialize((string) file_get_contents($file_class_member_cache_location));
|
|
|
|
|
|
|
|
if (!is_array($file_class_member_reference_cache)) {
|
|
|
|
throw new \UnexpectedValueException('The reference cache must be an array');
|
|
|
|
}
|
|
|
|
|
|
|
|
return $file_class_member_reference_cache;
|
|
|
|
}
|
|
|
|
|
2018-09-28 22:18:45 +02:00
|
|
|
/**
|
|
|
|
* @return ?array
|
|
|
|
*
|
|
|
|
* @psalm-suppress MixedAssignment
|
|
|
|
* @psalm-suppress MixedTypeCoercion
|
|
|
|
*/
|
|
|
|
public function getCachedIssues()
|
|
|
|
{
|
|
|
|
$cache_directory = $this->config->getCacheDirectory();
|
|
|
|
|
|
|
|
if (!$cache_directory) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$issues_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::ISSUES_CACHE_NAME;
|
|
|
|
|
|
|
|
if (!is_readable($issues_cache_location)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$issues_cache = unserialize((string) file_get_contents($issues_cache_location));
|
|
|
|
|
|
|
|
if (!is_array($issues_cache)) {
|
|
|
|
throw new \UnexpectedValueException('The reference cache must be an array');
|
|
|
|
}
|
|
|
|
|
|
|
|
return $issues_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setCachedFileReferences(array $file_references)
|
|
|
|
{
|
|
|
|
$cache_directory = $this->config->getCacheDirectory();
|
|
|
|
|
|
|
|
if (!$cache_directory) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$reference_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::REFERENCE_CACHE_NAME;
|
|
|
|
|
|
|
|
file_put_contents($reference_cache_location, serialize($file_references));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return void
|
|
|
|
*/
|
2019-04-16 22:07:48 +02:00
|
|
|
public function setCachedMethodMemberReferences(array $member_references)
|
2018-09-28 22:18:45 +02:00
|
|
|
{
|
|
|
|
$cache_directory = $this->config->getCacheDirectory();
|
|
|
|
|
|
|
|
if (!$cache_directory) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-04-13 00:28:07 +02:00
|
|
|
$member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::CLASS_METHOD_CACHE_NAME;
|
2018-09-28 22:18:45 +02:00
|
|
|
|
2019-04-16 22:07:48 +02:00
|
|
|
file_put_contents($member_cache_location, serialize($member_references));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setCachedMethodMissingMemberReferences(array $member_references)
|
|
|
|
{
|
|
|
|
$cache_directory = $this->config->getCacheDirectory();
|
|
|
|
|
|
|
|
if (!$cache_directory) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::METHOD_MISSING_MEMBER_CACHE_NAME;
|
|
|
|
|
|
|
|
file_put_contents($member_cache_location, serialize($member_references));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setCachedFileMemberReferences(array $member_references)
|
|
|
|
{
|
|
|
|
$cache_directory = $this->config->getCacheDirectory();
|
|
|
|
|
|
|
|
if (!$cache_directory) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::FILE_CLASS_MEMBER_CACHE_NAME;
|
|
|
|
|
|
|
|
file_put_contents($member_cache_location, serialize($member_references));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setCachedFileMissingMemberReferences(array $member_references)
|
|
|
|
{
|
|
|
|
$cache_directory = $this->config->getCacheDirectory();
|
|
|
|
|
|
|
|
if (!$cache_directory) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::FILE_MISSING_MEMBER_CACHE_NAME;
|
|
|
|
|
2019-04-13 00:28:07 +02:00
|
|
|
file_put_contents($member_cache_location, serialize($member_references));
|
2018-09-28 22:18:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setCachedIssues(array $issues)
|
|
|
|
{
|
|
|
|
$cache_directory = $this->config->getCacheDirectory();
|
|
|
|
|
|
|
|
if (!$cache_directory) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$issues_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::ISSUES_CACHE_NAME;
|
|
|
|
|
|
|
|
file_put_contents($issues_cache_location, serialize($issues));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-10-07 04:58:21 +02:00
|
|
|
* @return array<string, array<string, int>>|false
|
2018-09-28 22:18:45 +02:00
|
|
|
*/
|
2018-11-02 02:52:39 +01:00
|
|
|
public function getAnalyzedMethodCache()
|
2018-09-28 22:18:45 +02:00
|
|
|
{
|
2018-10-07 02:11:19 +02:00
|
|
|
$cache_directory = $this->config->getCacheDirectory();
|
2018-09-28 22:18:45 +02:00
|
|
|
|
2018-11-02 02:52:39 +01:00
|
|
|
$analyzed_methods_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::ANALYZED_METHODS_CACHE_NAME;
|
2018-09-28 22:18:45 +02:00
|
|
|
|
|
|
|
if ($cache_directory
|
2018-11-02 02:52:39 +01:00
|
|
|
&& file_exists($analyzed_methods_cache_location)
|
2018-11-01 22:42:48 +01:00
|
|
|
&& !$this->config_changed
|
2018-09-28 22:18:45 +02:00
|
|
|
) {
|
2018-10-07 04:58:21 +02:00
|
|
|
/** @var array<string, array<string, int>> */
|
2018-11-02 02:52:39 +01:00
|
|
|
return unserialize(file_get_contents($analyzed_methods_cache_location));
|
2018-09-28 22:18:45 +02:00
|
|
|
}
|
|
|
|
|
2018-10-07 02:11:19 +02:00
|
|
|
return false;
|
2018-09-28 22:18:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-11-02 02:52:39 +01:00
|
|
|
* @param array<string, array<string, int>> $analyzed_methods
|
2018-09-28 22:18:45 +02:00
|
|
|
* @return void
|
|
|
|
*/
|
2018-11-02 02:52:39 +01:00
|
|
|
public function setAnalyzedMethodCache(array $analyzed_methods)
|
2018-09-28 22:18:45 +02:00
|
|
|
{
|
|
|
|
$cache_directory = Config::getInstance()->getCacheDirectory();
|
|
|
|
|
|
|
|
if ($cache_directory) {
|
2018-11-02 02:52:39 +01:00
|
|
|
$analyzed_methods_cache_location = $cache_directory
|
|
|
|
. DIRECTORY_SEPARATOR
|
|
|
|
. self::ANALYZED_METHODS_CACHE_NAME;
|
2018-09-28 22:18:45 +02:00
|
|
|
|
|
|
|
file_put_contents(
|
2018-11-02 02:52:39 +01:00
|
|
|
$analyzed_methods_cache_location,
|
|
|
|
serialize($analyzed_methods)
|
2018-09-28 22:18:45 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2018-10-26 22:17:15 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return array<string, array{0: TaggedCodeType, 1: TaggedCodeType}>|false
|
|
|
|
*/
|
|
|
|
public function getFileMapCache()
|
|
|
|
{
|
|
|
|
$cache_directory = $this->config->getCacheDirectory();
|
|
|
|
|
|
|
|
$file_maps_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::FILE_MAPS_CACHE_NAME;
|
|
|
|
|
|
|
|
if ($cache_directory
|
|
|
|
&& file_exists($file_maps_cache_location)
|
2018-11-01 22:42:48 +01:00
|
|
|
&& !$this->config_changed
|
2018-10-26 22:17:15 +02:00
|
|
|
) {
|
|
|
|
/** @var array<string, array{0: TaggedCodeType, 1: TaggedCodeType}> */
|
|
|
|
$file_maps_cache = unserialize(file_get_contents($file_maps_cache_location));
|
|
|
|
return $file_maps_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array<string, array{0: TaggedCodeType, 1: TaggedCodeType}> $file_maps
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setFileMapCache(array $file_maps)
|
|
|
|
{
|
|
|
|
$cache_directory = Config::getInstance()->getCacheDirectory();
|
|
|
|
|
|
|
|
if ($cache_directory) {
|
|
|
|
$file_maps_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::FILE_MAPS_CACHE_NAME;
|
|
|
|
|
|
|
|
file_put_contents(
|
|
|
|
$file_maps_cache_location,
|
|
|
|
serialize($file_maps)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2019-03-23 14:50:47 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return array<string, array{int, int}>|false
|
|
|
|
*/
|
|
|
|
public function getTypeCoverage()
|
|
|
|
{
|
|
|
|
$cache_directory = Config::getInstance()->getCacheDirectory();
|
|
|
|
|
|
|
|
$type_coverage_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::TYPE_COVERAGE_CACHE_NAME;
|
|
|
|
|
|
|
|
if ($cache_directory
|
|
|
|
&& file_exists($type_coverage_cache_location)
|
|
|
|
&& !$this->config_changed
|
|
|
|
) {
|
|
|
|
/** @var array<string, array{int, int}> */
|
|
|
|
$type_coverage_cache = unserialize(file_get_contents($type_coverage_cache_location));
|
|
|
|
return $type_coverage_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array<string, array{int, int}> $mixed_counts
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setTypeCoverage(array $mixed_counts)
|
|
|
|
{
|
|
|
|
$cache_directory = Config::getInstance()->getCacheDirectory();
|
|
|
|
|
|
|
|
if ($cache_directory) {
|
|
|
|
$type_coverage_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::TYPE_COVERAGE_CACHE_NAME;
|
|
|
|
|
|
|
|
file_put_contents(
|
|
|
|
$type_coverage_cache_location,
|
|
|
|
serialize($mixed_counts)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2018-11-01 22:42:48 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return string|false
|
|
|
|
*/
|
|
|
|
public function getConfigHashCache()
|
|
|
|
{
|
|
|
|
$cache_directory = $this->config->getCacheDirectory();
|
|
|
|
|
|
|
|
$config_hash_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::CONFIG_HASH_CACHE_NAME;
|
|
|
|
|
|
|
|
if ($cache_directory
|
|
|
|
&& file_exists($config_hash_cache_location)
|
|
|
|
) {
|
|
|
|
/** @var string */
|
|
|
|
$file_maps_cache = unserialize(file_get_contents($config_hash_cache_location));
|
|
|
|
return $file_maps_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setConfigHashCache(string $hash)
|
|
|
|
{
|
|
|
|
$cache_directory = Config::getInstance()->getCacheDirectory();
|
|
|
|
|
|
|
|
if ($cache_directory) {
|
|
|
|
$config_hash_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::CONFIG_HASH_CACHE_NAME;
|
|
|
|
|
|
|
|
file_put_contents(
|
|
|
|
$config_hash_cache_location,
|
|
|
|
serialize($hash)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2018-09-28 22:18:45 +02:00
|
|
|
}
|