1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-11 07:31:45 +01:00
psalm/src/Psalm/Internal/Provider/FileStorageCacheProvider.php

182 lines
5.1 KiB
PHP
Raw Normal View History

2018-02-19 06:27:39 +01:00
<?php
2018-11-06 03:57:36 +01:00
namespace Psalm\Internal\Provider;
2018-02-19 06:27:39 +01:00
2021-06-08 04:55:21 +02:00
use Psalm\Config;
use Psalm\Storage\FileStorage;
2021-12-03 21:40:18 +01:00
use UnexpectedValueException;
2021-06-08 04:55:21 +02:00
use function array_merge;
2019-07-05 22:24:00 +02:00
use function dirname;
use function file_exists;
2019-07-05 22:24:00 +02:00
use function file_get_contents;
use function file_put_contents;
2019-07-05 22:24:00 +02:00
use function filemtime;
use function get_class;
use function hash;
2019-07-05 22:24:00 +02:00
use function igbinary_serialize;
use function igbinary_unserialize;
use function is_dir;
use function mkdir;
2019-07-05 22:24:00 +02:00
use function serialize;
use function strtolower;
use function unlink;
use function unserialize;
2018-02-19 06:27:39 +01:00
2021-06-08 04:55:21 +02:00
use const DIRECTORY_SEPARATOR;
use const PHP_VERSION_ID;
2021-06-08 04:55:21 +02:00
/**
* @internal
*/
2018-02-19 06:27:39 +01:00
class FileStorageCacheProvider
{
/**
* @var string
*/
private $modified_timestamps = '';
/**
* @var Config
*/
private $config;
2020-09-20 18:54:46 +02:00
private const FILE_STORAGE_CACHE_DIRECTORY = 'file_cache';
2018-02-19 06:27:39 +01:00
public function __construct(Config $config)
{
$this->config = $config;
2018-11-06 03:57:36 +01:00
$storage_dir = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'Storage' . DIRECTORY_SEPARATOR;
2018-02-19 06:27:39 +01:00
$dependent_files = [
$storage_dir . 'FileStorage.php',
$storage_dir . 'FunctionLikeStorage.php',
$storage_dir . 'ClassLikeStorage.php',
$storage_dir . 'MethodStorage.php',
$storage_dir . 'FunctionLikeParameter.php',
2018-02-19 06:27:39 +01:00
];
if ($config->eventDispatcher->hasAfterClassLikeVisitHandlers()) {
$dependent_files = array_merge($dependent_files, $config->plugin_paths);
}
2018-02-19 06:27:39 +01:00
foreach ($dependent_files as $dependent_file_path) {
if (!file_exists($dependent_file_path)) {
2021-12-03 21:40:18 +01:00
throw new UnexpectedValueException($dependent_file_path . ' must exist');
2018-02-19 06:27:39 +01:00
}
$this->modified_timestamps .= ' ' . filemtime($dependent_file_path);
}
$this->modified_timestamps .= $this->config->computeHash();
2018-02-19 06:27:39 +01:00
}
public function writeToCache(FileStorage $storage, string $file_contents): void
2018-02-19 06:27:39 +01:00
{
$file_path = strtolower($storage->file_path);
$cache_location = $this->getCacheLocationForPath($file_path, true);
$storage->hash = $this->getCacheHash($file_path, $file_contents);
if ($this->config->use_igbinary) {
file_put_contents($cache_location, igbinary_serialize($storage));
} else {
file_put_contents($cache_location, serialize($storage));
}
2018-02-19 06:27:39 +01:00
}
public function getLatestFromCache(string $file_path, string $file_contents): ?FileStorage
2018-02-19 06:27:39 +01:00
{
$file_path = strtolower($file_path);
2018-02-19 06:27:39 +01:00
$cached_value = $this->loadFromCache($file_path);
if (!$cached_value) {
return null;
}
$cache_hash = $this->getCacheHash($file_path, $file_contents);
2018-09-04 20:37:33 +02:00
/** @psalm-suppress TypeDoesNotContainType */
if (@get_class($cached_value) === '__PHP_Incomplete_Class'
|| $cache_hash !== $cached_value->hash
) {
$this->removeCacheForFile($file_path);
2018-02-19 06:27:39 +01:00
return null;
}
return $cached_value;
}
public function removeCacheForFile(string $file_path): void
{
$cache_path = $this->getCacheLocationForPath($file_path);
if (file_exists($cache_path)) {
unlink($cache_path);
}
}
private function getCacheHash(string $file_path, string $file_contents): string
2018-02-19 06:27:39 +01:00
{
$data = ($file_path ? $file_contents : '') . $this->modified_timestamps;
2022-05-28 22:26:08 +02:00
return PHP_VERSION_ID >= 8_01_00 ? hash('xxh128', $data) : hash('md4', $data);
2018-02-19 06:27:39 +01:00
}
/**
* @psalm-suppress MixedAssignment
2018-02-19 06:27:39 +01:00
*/
private function loadFromCache(string $file_path): ?FileStorage
2018-02-19 06:27:39 +01:00
{
$cache_location = $this->getCacheLocationForPath($file_path);
if (file_exists($cache_location)) {
if ($this->config->use_igbinary) {
$storage = igbinary_unserialize((string)file_get_contents($cache_location));
if ($storage instanceof FileStorage) {
return $storage;
}
return null;
}
$storage = unserialize((string)file_get_contents($cache_location));
if ($storage instanceof FileStorage) {
return $storage;
}
return null;
2018-02-19 06:27:39 +01:00
}
return null;
}
private function getCacheLocationForPath(string $file_path, bool $create_directory = false): string
2018-02-19 06:27:39 +01:00
{
$root_cache_directory = $this->config->getCacheDirectory();
if (!$root_cache_directory) {
2021-12-03 21:40:18 +01:00
throw new UnexpectedValueException('No cache directory defined');
2018-02-19 06:27:39 +01:00
}
$parser_cache_directory = $root_cache_directory . DIRECTORY_SEPARATOR . self::FILE_STORAGE_CACHE_DIRECTORY;
2018-02-19 06:27:39 +01:00
if ($create_directory && !is_dir($parser_cache_directory)) {
mkdir($parser_cache_directory, 0777, true);
}
2022-05-28 22:26:08 +02:00
if (PHP_VERSION_ID >= 8_01_00) {
2022-05-24 10:00:54 +02:00
$hash = hash('xxh128', $file_path);
} else {
$hash = hash('md4', $file_path);
}
return $parser_cache_directory
. DIRECTORY_SEPARATOR
2022-05-24 10:00:54 +02:00
. $hash
. ($this->config->use_igbinary ? '-igbinary' : '');
2018-02-19 06:27:39 +01:00
}
}