Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: cache.gz #9889

Merged
merged 13 commits into from Jun 12, 2023
8 changes: 8 additions & 0 deletions src/Psalm/Config.php
Expand Up @@ -68,6 +68,7 @@
use function file_get_contents;
use function flock;
use function fopen;
use function function_exists;
use function get_class;
use function get_defined_constants;
use function get_defined_functions;
Expand Down Expand Up @@ -331,6 +332,9 @@ class Config
/** @var bool */
public $use_igbinary = false;

/** @var bool */
public $use_gzip = true;

/**
* @var bool
*/
Expand Down Expand Up @@ -1192,6 +1196,10 @@ private static function fromXmlAndPaths(
$config->use_igbinary = version_compare($igbinary_version, '2.0.5') >= 0;
}

if ($config->use_gzip) {
$config->use_gzip = function_exists('gzinflate');
}

if (!isset($config_xml['findUnusedBaselineEntry'])) {
$config->config_warnings[] = '"findUnusedBaselineEntry" will be defaulted to "true" in Psalm 6.'
. ' You should explicitly enable or disable this setting.';
Expand Down
92 changes: 92 additions & 0 deletions src/Psalm/Internal/Cache.php
@@ -0,0 +1,92 @@
<?php

namespace Psalm\Internal;

use Psalm\Config;
use Psalm\Internal\Provider\Providers;

use function file_exists;
use function file_put_contents;
use function gzdeflate;
use function gzinflate;
use function igbinary_serialize;
use function igbinary_unserialize;
use function serialize;
use function unlink;
use function unserialize;

use const LOCK_EX;

/**
* @internal
*/
class Cache
{
private Config $config;

public bool $use_igbinary;

public function __construct(Config $config)
{
$this->config = $config;
$this->use_igbinary = $config->use_igbinary;
}

/**
* @return array|object|string|null
*/
public function getItem(string $path)
{
if (!file_exists($path)) {
dkarlovi marked this conversation as resolved.
Show resolved Hide resolved
return null;
}

$cache = Providers::safeFileGetContents($path);
if ($this->config->use_gzip) {
$inflated = @gzinflate($cache);
if ($inflated !== false) {
$cache = $inflated;
}
}

if ($this->config->use_igbinary) {
/** @var object|false $unserialized */
$unserialized = igbinary_unserialize($cache);
} else {
/** @var object|false $unserialized */
$unserialized = @unserialize($cache);
}

return $unserialized !== false ? $unserialized : null;
}

public function deleteItem(string $path): void
{
if (file_exists($path)) {
unlink($path);
}
}

/**
* @param array|object|string $item
*/
public function saveItem(string $path, $item): void
{
if ($this->config->use_igbinary) {
$serialized = igbinary_serialize($item);
} else {
$serialized = serialize($item);
}

if ($this->config->use_gzip) {
$serialized = gzdeflate($serialized);
}

file_put_contents($path, $serialized, LOCK_EX);
}

public function getCacheDirectory(): ?string
{
return $this->config->getCacheDirectory();
}
}
Expand Up @@ -62,7 +62,7 @@ class FileReferenceCacheProvider extends InternalFileReferenceCacheProvider

public function __construct(Config $config)
{
$this->config = $config;
parent::__construct($config);
}

public function getCachedFileReferences(): ?array
Expand Down
49 changes: 10 additions & 39 deletions src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php
Expand Up @@ -3,45 +3,40 @@
namespace Psalm\Internal\Provider;

use Psalm\Config;
use Psalm\Internal\Cache;
use Psalm\Storage\ClassLikeStorage;
use RuntimeException;
use UnexpectedValueException;

use function array_merge;
use function dirname;
use function file_exists;
use function file_put_contents;
use function filemtime;
use function get_class;
use function hash;
use function igbinary_serialize;
use function igbinary_unserialize;
use function is_dir;
use function is_null;
use function mkdir;
use function serialize;
use function strtolower;
use function unlink;
use function unserialize;

use const DIRECTORY_SEPARATOR;
use const LOCK_EX;
use const PHP_VERSION_ID;

/**
* @internal
*/
class ClassLikeStorageCacheProvider
{
private Config $config;
private Cache $cache;

private string $modified_timestamps = '';

private const CLASS_CACHE_DIRECTORY = 'class_cache';

public function __construct(Config $config)
{
$this->config = $config;
$this->cache = new Cache($config);

$storage_dir = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'Storage' . DIRECTORY_SEPARATOR;

Expand All @@ -64,7 +59,7 @@ public function __construct(Config $config)
$this->modified_timestamps .= ' ' . filemtime($dependent_file_path);
}

$this->modified_timestamps .= $this->config->computeHash();
$this->modified_timestamps .= $config->computeHash();
}

public function writeToCache(ClassLikeStorage $storage, string $file_path, string $file_contents): void
Expand All @@ -80,11 +75,7 @@ public function writeToCache(ClassLikeStorage $storage, string $file_path, strin
}

$cache_location = $this->getCacheLocationForClass($fq_classlike_name_lc, $file_path, true);
if ($this->config->use_igbinary) {
file_put_contents($cache_location, igbinary_serialize($storage), LOCK_EX);
} else {
file_put_contents($cache_location, serialize($storage), LOCK_EX);
}
$this->cache->saveItem($cache_location, $storage);
}

public function getLatestFromCache(
Expand Down Expand Up @@ -118,31 +109,11 @@ private function getCacheHash(?string $_unused_file_path, ?string $file_contents
return PHP_VERSION_ID >= 8_01_00 ? hash('xxh128', $data) : hash('md4', $data);
}

/**
* @psalm-suppress MixedAssignment
*/
private function loadFromCache(string $fq_classlike_name_lc, ?string $file_path): ?ClassLikeStorage
{
$cache_location = $this->getCacheLocationForClass($fq_classlike_name_lc, $file_path);

if (file_exists($cache_location)) {
if ($this->config->use_igbinary) {
$storage = igbinary_unserialize(Providers::safeFileGetContents($cache_location));

if ($storage instanceof ClassLikeStorage) {
return $storage;
}

return null;
}

$storage = unserialize(Providers::safeFileGetContents($cache_location));

if ($storage instanceof ClassLikeStorage) {
return $storage;
}

return null;
$storage = $this->cache->getItem($this->getCacheLocationForClass($fq_classlike_name_lc, $file_path));
if ($storage instanceof ClassLikeStorage) {
return $storage;
}

return null;
Expand All @@ -153,7 +124,7 @@ private function getCacheLocationForClass(
?string $file_path,
bool $create_directory = false
): string {
$root_cache_directory = $this->config->getCacheDirectory();
$root_cache_directory = $this->cache->getCacheDirectory();

if (!$root_cache_directory) {
throw new UnexpectedValueException('No cache directory defined');
Expand Down Expand Up @@ -186,6 +157,6 @@ private function getCacheLocationForClass(
return $parser_cache_directory
. DIRECTORY_SEPARATOR
. $file_path_sha
. ($this->config->use_igbinary ? '-igbinary' : '');
. ($this->cache->use_igbinary ? '-igbinary' : '');
}
}