2021-06-11 11:03:31 +00:00
< ? php
declare ( strict_types = 1 );
2022-06-06 17:12:56 +00:00
namespace Rector\Caching\ValueObject\Storage ;
2021-06-11 11:03:31 +00:00
2021-10-18 17:44:46 +00:00
use FilesystemIterator ;
2022-06-07 06:40:10 +00:00
use RectorPrefix20220607\Nette\Utils\FileSystem ;
use RectorPrefix20220607\Nette\Utils\Random ;
2022-06-06 17:12:56 +00:00
use Rector\Caching\Contract\ValueObject\Storage\CacheStorageInterface ;
use Rector\Caching\ValueObject\CacheFilePaths ;
use Rector\Caching\ValueObject\CacheItem ;
use Rector\Core\Exception\Cache\CachingException ;
2022-06-07 06:40:10 +00:00
use RectorPrefix20220607\Symplify\SmartFileSystem\SmartFileSystem ;
2021-06-11 11:03:31 +00:00
/**
2021-07-26 13:28:02 +00:00
* Inspired by https :// github . com / phpstan / phpstan - src / blob / 1e7 ceae933f07e5a250b61ed94799e6c2ea8daa2 / src / Cache / FileCacheStorage . php
2021-10-18 17:44:46 +00:00
* @ see \Rector\Tests\Caching\ValueObject\Storage\FileCacheStorageTest
2021-06-11 11:03:31 +00:00
*/
2022-06-06 17:12:56 +00:00
final class FileCacheStorage implements \Rector\Caching\Contract\ValueObject\Storage\CacheStorageInterface
2021-06-11 11:03:31 +00:00
{
/**
* @ var string
*/
private $directory ;
/**
2021-08-23 00:20:32 +00:00
* @ var \Symplify\SmartFileSystem\SmartFileSystem
2021-06-11 11:03:31 +00:00
*/
private $smartFileSystem ;
2022-06-07 06:40:10 +00:00
public function __construct ( string $directory , \RectorPrefix20220607\Symplify\SmartFileSystem\SmartFileSystem $smartFileSystem )
2021-06-11 11:03:31 +00:00
{
$this -> directory = $directory ;
$this -> smartFileSystem = $smartFileSystem ;
}
2021-12-10 10:22:23 +00:00
public function load ( string $key , string $variableKey )
2021-06-11 11:03:31 +00:00
{
2021-07-26 13:28:02 +00:00
return ( function ( string $key , string $variableKey ) {
$cacheFilePaths = $this -> getCacheFilePaths ( $key );
$filePath = $cacheFilePaths -> getFilePath ();
if ( ! \is_file ( $filePath )) {
return null ;
}
$cacheItem = ( require $filePath );
2022-06-06 17:12:56 +00:00
if ( ! $cacheItem instanceof \Rector\Caching\ValueObject\CacheItem ) {
2021-07-26 13:28:02 +00:00
return null ;
}
if ( ! $cacheItem -> isVariableKeyValid ( $variableKey )) {
return null ;
}
return $cacheItem -> getData ();
})( $key , $variableKey );
2021-06-11 11:03:31 +00:00
}
2021-12-10 10:22:23 +00:00
public function save ( string $key , string $variableKey , $data ) : void
2021-06-11 11:03:31 +00:00
{
$cacheFilePaths = $this -> getCacheFilePaths ( $key );
$this -> smartFileSystem -> mkdir ( $cacheFilePaths -> getFirstDirectory ());
$this -> smartFileSystem -> mkdir ( $cacheFilePaths -> getSecondDirectory ());
2021-07-26 13:28:02 +00:00
$path = $cacheFilePaths -> getFilePath ();
2022-06-07 06:40:10 +00:00
$tmpPath = \sprintf ( '%s/%s.tmp' , $this -> directory , \RectorPrefix20220607\Nette\Utils\Random :: generate ());
2021-06-11 11:03:31 +00:00
$errorBefore = \error_get_last ();
2022-06-06 17:12:56 +00:00
$exported = @ \var_export ( new \Rector\Caching\ValueObject\CacheItem ( $variableKey , $data ), \true );
2021-06-11 11:03:31 +00:00
$errorAfter = \error_get_last ();
if ( $errorAfter !== null && $errorBefore !== $errorAfter ) {
2022-06-06 17:12:56 +00:00
throw new \Rector\Core\Exception\Cache\CachingException ( \sprintf ( 'Error occurred while saving item %s (%s) to cache: %s' , $key , $variableKey , $errorAfter [ 'message' ]));
2021-07-26 13:28:02 +00:00
}
// for performance reasons we don't use SmartFileSystem
2022-06-07 06:40:10 +00:00
\RectorPrefix20220607\Nette\Utils\FileSystem :: write ( $tmpPath , \sprintf ( " <?php declare(strict_types = 1); \n \n return %s; " , $exported ));
2021-07-26 13:28:02 +00:00
$renameSuccess = @ \rename ( $tmpPath , $path );
if ( $renameSuccess ) {
return ;
}
@ \unlink ( $tmpPath );
if ( \DIRECTORY_SEPARATOR === '/' || ! \file_exists ( $path )) {
2022-06-06 17:12:56 +00:00
throw new \Rector\Core\Exception\Cache\CachingException ( \sprintf ( 'Could not write data to cache file %s.' , $path ));
2021-06-11 11:03:31 +00:00
}
}
2021-12-10 10:22:23 +00:00
public function clean ( string $key ) : void
2021-06-11 11:03:31 +00:00
{
2021-07-28 07:05:44 +00:00
$cacheFilePaths = $this -> getCacheFilePaths ( $key );
2021-10-18 17:44:46 +00:00
$this -> processRemoveCacheFilePath ( $cacheFilePaths );
$this -> processRemoveEmptyDirectory ( $cacheFilePaths -> getSecondDirectory ());
$this -> processRemoveEmptyDirectory ( $cacheFilePaths -> getFirstDirectory ());
}
public function clear () : void
{
$this -> smartFileSystem -> remove ( $this -> directory );
}
2022-06-06 17:12:56 +00:00
private function processRemoveCacheFilePath ( \Rector\Caching\ValueObject\CacheFilePaths $cacheFilePaths ) : void
2021-10-18 17:44:46 +00:00
{
$filePath = $cacheFilePaths -> getFilePath ();
if ( ! $this -> smartFileSystem -> exists ( $filePath )) {
2021-10-18 16:50:10 +00:00
return ;
}
2021-10-18 17:44:46 +00:00
$this -> smartFileSystem -> remove ( $filePath );
}
private function processRemoveEmptyDirectory ( string $directory ) : void
{
if ( ! $this -> smartFileSystem -> exists ( $directory )) {
2021-10-18 16:50:10 +00:00
return ;
}
2021-10-18 17:44:46 +00:00
if ( $this -> isNotEmptyDirectory ( $directory )) {
2021-10-18 16:50:10 +00:00
return ;
}
2021-10-18 17:44:46 +00:00
$this -> smartFileSystem -> remove ( $directory );
2021-06-11 11:03:31 +00:00
}
2021-10-18 17:44:46 +00:00
private function isNotEmptyDirectory ( string $directory ) : bool
2021-06-11 11:03:31 +00:00
{
2021-10-18 17:44:46 +00:00
// FilesystemIterator will initially point to the first file in the folder - if there are no files in the folder, valid() will return false
2022-06-06 17:12:56 +00:00
$filesystemIterator = new \FilesystemIterator ( $directory );
2021-10-18 17:44:46 +00:00
return $filesystemIterator -> valid ();
2021-06-11 11:03:31 +00:00
}
2022-06-06 17:12:56 +00:00
private function getCacheFilePaths ( string $key ) : \Rector\Caching\ValueObject\CacheFilePaths
2021-06-11 11:03:31 +00:00
{
$keyHash = \sha1 ( $key );
2021-07-26 13:28:02 +00:00
$firstDirectory = \sprintf ( '%s/%s' , $this -> directory , \substr ( $keyHash , 0 , 2 ));
$secondDirectory = \sprintf ( '%s/%s' , $firstDirectory , \substr ( $keyHash , 2 , 2 ));
2021-06-11 11:03:31 +00:00
$filePath = \sprintf ( '%s/%s.php' , $secondDirectory , $keyHash );
2022-06-06 17:12:56 +00:00
return new \Rector\Caching\ValueObject\CacheFilePaths ( $firstDirectory , $secondDirectory , $filePath );
2021-06-11 11:03:31 +00:00
}
}