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
|
|
|
|
|
|
|
use Psalm\Config;
|
|
|
|
use Psalm\Storage\FileStorage;
|
|
|
|
|
|
|
|
class FileStorageCacheProvider
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
private $modified_timestamps = '';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var Config
|
|
|
|
*/
|
|
|
|
private $config;
|
|
|
|
|
2018-09-26 00:37:24 +02:00
|
|
|
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',
|
2018-03-13 17:52:00 +01:00
|
|
|
$storage_dir . 'ClassLikeStorage.php',
|
|
|
|
$storage_dir . 'MethodStorage.php',
|
2018-05-12 00:35:02 +02:00
|
|
|
$storage_dir . 'FunctionLikeParameter.php',
|
2018-02-19 06:27:39 +01:00
|
|
|
];
|
|
|
|
|
2018-09-04 21:28:03 +02:00
|
|
|
if ($config->after_visit_classlikes) {
|
|
|
|
$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)) {
|
|
|
|
throw new \UnexpectedValueException($dependent_file_path . ' must exist');
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->modified_timestamps .= ' ' . filemtime($dependent_file_path);
|
|
|
|
}
|
2018-02-25 17:35:43 +01:00
|
|
|
|
2018-11-01 22:42:48 +01:00
|
|
|
$this->modified_timestamps .= PSALM_VERSION . $this->config->hash;
|
2018-02-19 06:27:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string $file_path
|
|
|
|
* @param string $file_contents
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function writeToCache(FileStorage $storage, $file_contents)
|
|
|
|
{
|
|
|
|
$file_path = strtolower($storage->file_path);
|
|
|
|
$cache_location = $this->getCacheLocationForPath($file_path, true);
|
|
|
|
$storage->hash = $this->getCacheHash($file_path, $file_contents);
|
|
|
|
|
2018-02-19 17:14:07 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string $file_path
|
|
|
|
* @param string $file_contents
|
|
|
|
*
|
|
|
|
* @return FileStorage|null
|
|
|
|
*/
|
|
|
|
public function getLatestFromCache($file_path, $file_contents)
|
|
|
|
{
|
2018-10-17 21:52:26 +02: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 */
|
2018-03-12 05:01:21 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-03-12 05:01:21 +01:00
|
|
|
/**
|
|
|
|
* @param string $file_path
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function removeCacheForFile($file_path)
|
|
|
|
{
|
|
|
|
$cache_path = $this->getCacheLocationForPath($file_path);
|
|
|
|
|
|
|
|
if (file_exists($cache_path)) {
|
|
|
|
unlink($cache_path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-19 06:27:39 +01:00
|
|
|
/**
|
2018-02-19 18:06:13 +01:00
|
|
|
* @param string $file_path
|
|
|
|
* @param string $file_contents
|
2018-02-19 06:27:39 +01:00
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
private function getCacheHash($file_path, $file_contents)
|
|
|
|
{
|
2018-02-19 18:06:13 +01:00
|
|
|
return sha1(strtolower($file_path) . ' ' . $file_contents . $this->modified_timestamps);
|
2018-02-19 06:27:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string $file_path
|
|
|
|
*
|
|
|
|
* @return FileStorage|null
|
|
|
|
*/
|
|
|
|
private function loadFromCache($file_path)
|
|
|
|
{
|
|
|
|
$cache_location = $this->getCacheLocationForPath($file_path);
|
|
|
|
|
|
|
|
if (file_exists($cache_location)) {
|
2018-02-19 17:14:07 +01:00
|
|
|
if ($this->config->use_igbinary) {
|
|
|
|
/** @var FileStorage */
|
|
|
|
return igbinary_unserialize((string)file_get_contents($cache_location)) ?: null;
|
|
|
|
}
|
2018-02-19 17:53:30 +01:00
|
|
|
/** @var FileStorage */
|
|
|
|
return unserialize((string)file_get_contents($cache_location)) ?: null;
|
2018-02-19 06:27:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string $file_path
|
|
|
|
* @param bool $create_directory
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
private function getCacheLocationForPath($file_path, $create_directory = false)
|
|
|
|
{
|
|
|
|
$root_cache_directory = $this->config->getCacheDirectory();
|
|
|
|
|
|
|
|
if (!$root_cache_directory) {
|
|
|
|
throw new \UnexpectedValueException('No cache directory defined');
|
|
|
|
}
|
|
|
|
|
2018-09-26 00:37:24 +02: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);
|
|
|
|
}
|
|
|
|
|
2018-02-19 17:14:07 +01:00
|
|
|
return $parser_cache_directory
|
|
|
|
. DIRECTORY_SEPARATOR
|
|
|
|
. sha1($file_path)
|
|
|
|
. ($this->config->use_igbinary ? '-igbinary' : '');
|
2018-02-19 06:27:39 +01:00
|
|
|
}
|
|
|
|
}
|