Merge remote-tracking branch 'galymzhan/add-cache-files-maxsize'
commit
6ce285b70c
|
@ -114,16 +114,27 @@ class Cache
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function gc($ttl)
|
public function gc($ttl, $cacheMaxSize)
|
||||||
{
|
{
|
||||||
$expire = new \DateTime();
|
$expire = new \DateTime();
|
||||||
$expire->modify('-'.$ttl.' seconds');
|
$expire->modify('-'.$ttl.' seconds');
|
||||||
|
|
||||||
$finder = Finder::create()->files()->in($this->root)->date('until '.$expire->format('Y-m-d H:i:s'));
|
$finder = $this->getFinder()->date('until '.$expire->format('Y-m-d H:i:s'));
|
||||||
foreach ($finder as $file) {
|
foreach ($finder as $file) {
|
||||||
unlink($file->getRealPath());
|
unlink($file->getRealPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$totalCacheSize = $this->filesystem->size($this->root);
|
||||||
|
if ($totalCacheSize > $cacheMaxSize) {
|
||||||
|
$iterator = $this->getFinder()->sortByAccessedTime()->getIterator();
|
||||||
|
while ($totalCacheSize > $cacheMaxSize && $iterator->valid()) {
|
||||||
|
$filepath = $iterator->current()->getRealPath();
|
||||||
|
$totalCacheSize -= $this->filesystem->size($filepath);
|
||||||
|
unlink($filepath);
|
||||||
|
$iterator->next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,4 +157,9 @@ class Cache
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getFinder()
|
||||||
|
{
|
||||||
|
return Finder::create()->in($this->root)->files();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ class Config
|
||||||
public static $defaultConfig = array(
|
public static $defaultConfig = array(
|
||||||
'process-timeout' => 300,
|
'process-timeout' => 300,
|
||||||
'cache-ttl' => 15552000, // 6 months
|
'cache-ttl' => 15552000, // 6 months
|
||||||
|
'cache-files-maxsize' => '300MiB',
|
||||||
'vendor-dir' => 'vendor',
|
'vendor-dir' => 'vendor',
|
||||||
'bin-dir' => '{$vendor-dir}/bin',
|
'bin-dir' => '{$vendor-dir}/bin',
|
||||||
'notify-on-install' => true,
|
'notify-on-install' => true,
|
||||||
|
@ -137,6 +138,25 @@ class Config
|
||||||
case 'cache-ttl':
|
case 'cache-ttl':
|
||||||
return (int) $this->config[$key];
|
return (int) $this->config[$key];
|
||||||
|
|
||||||
|
case 'cache-files-maxsize':
|
||||||
|
if (!preg_match('/^\s*(\d+)\s*([kmg]ib)?\s*$/i', $this->config[$key], $matches)) {
|
||||||
|
throw new \RuntimeException(
|
||||||
|
"composer.json contains invalid 'cache-files-maxsize' value: {$this->config[$key]}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$size = $matches[1];
|
||||||
|
if (isset($matches[2])) {
|
||||||
|
switch (strtolower($matches[2])) {
|
||||||
|
case 'gib':
|
||||||
|
$size *= 1024;
|
||||||
|
case 'mib':
|
||||||
|
$size *= 1024;
|
||||||
|
case 'kib':
|
||||||
|
$size *= 1024;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $size;
|
||||||
|
|
||||||
case 'cache-files-ttl':
|
case 'cache-files-ttl':
|
||||||
if (isset($this->config[$key])) {
|
if (isset($this->config[$key])) {
|
||||||
return (int) $this->config[$key];
|
return (int) $this->config[$key];
|
||||||
|
|
|
@ -56,7 +56,7 @@ class FileDownloader implements DownloaderInterface
|
||||||
$this->cache = $cache;
|
$this->cache = $cache;
|
||||||
|
|
||||||
if ($this->cache && !self::$cacheCollected && !rand(0, 50)) {
|
if ($this->cache && !self::$cacheCollected && !rand(0, 50)) {
|
||||||
$this->cache->gc($config->get('cache-ttl'));
|
$this->cache->gc($config->get('cache-ttl'), $config->get('cache-files-maxsize'));
|
||||||
}
|
}
|
||||||
self::$cacheCollected = true;
|
self::$cacheCollected = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,38 @@ class Filesystem
|
||||||
return substr($path, 0, 1) === '/' || substr($path, 1, 1) === ':';
|
return substr($path, 0, 1) === '/' || substr($path, 1, 1) === ':';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns size of a file or directory specified by path. If a directory is
|
||||||
|
* given, it's size will be computed recursively.
|
||||||
|
*
|
||||||
|
* @param string $path Path to the file or directory
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function size($path)
|
||||||
|
{
|
||||||
|
if (!file_exists($path)) {
|
||||||
|
throw new \RuntimeException("$path does not exist.");
|
||||||
|
}
|
||||||
|
if (is_dir($path)) {
|
||||||
|
return $this->directorySize($path);
|
||||||
|
}
|
||||||
|
return filesize($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function directorySize($directory)
|
||||||
|
{
|
||||||
|
$it = new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS);
|
||||||
|
$ri = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
|
||||||
|
|
||||||
|
$size = 0;
|
||||||
|
foreach ($ri as $file) {
|
||||||
|
if ($file->isFile()) {
|
||||||
|
$size += $file->getSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $size;
|
||||||
|
}
|
||||||
|
|
||||||
protected function getProcess()
|
protected function getProcess()
|
||||||
{
|
{
|
||||||
return new ProcessExecutor;
|
return new ProcessExecutor;
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Test;
|
||||||
|
|
||||||
|
use Composer\Cache;
|
||||||
|
|
||||||
|
class CacheTest extends TestCase
|
||||||
|
{
|
||||||
|
private $files, $root, $finder, $cache;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->root = sys_get_temp_dir() . '/composer_testdir';
|
||||||
|
$this->ensureDirectoryExistsAndClear($this->root);
|
||||||
|
|
||||||
|
$this->files = array();
|
||||||
|
$zeros = str_repeat('0', 1000);
|
||||||
|
for ($i = 0; $i < 4; $i++) {
|
||||||
|
file_put_contents("{$this->root}/cached.file{$i}.zip", $zeros);
|
||||||
|
$this->files[] = new \SplFileInfo("{$this->root}/cached.file{$i}.zip");
|
||||||
|
}
|
||||||
|
$this->finder = $this->getMock('Symfony\Component\Finder\Finder');
|
||||||
|
|
||||||
|
$io = $this->getMock('Composer\IO\IOInterface');
|
||||||
|
$this->cache = $this->getMock(
|
||||||
|
'Composer\Cache',
|
||||||
|
array('getFinder'),
|
||||||
|
array($io, $this->root)
|
||||||
|
);
|
||||||
|
$this->cache
|
||||||
|
->expects($this->any())
|
||||||
|
->method('getFinder')
|
||||||
|
->will($this->returnValue($this->finder));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRemoveOutdatedFiles()
|
||||||
|
{
|
||||||
|
$outdated = array_slice($this->files, 1);
|
||||||
|
$this->finder
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getIterator')
|
||||||
|
->will($this->returnValue(new \ArrayIterator($outdated)));
|
||||||
|
$this->finder
|
||||||
|
->expects($this->once())
|
||||||
|
->method('date')
|
||||||
|
->will($this->returnValue($this->finder));
|
||||||
|
|
||||||
|
$this->cache->gc(600, 1024 * 1024 * 1024);
|
||||||
|
|
||||||
|
for ($i = 1; $i < 4; $i++) {
|
||||||
|
$this->assertFileNotExists("{$this->root}/cached.file{$i}.zip");
|
||||||
|
}
|
||||||
|
$this->assertFileExists("{$this->root}/cached.file0.zip");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRemoveFilesWhenCacheIsTooLarge()
|
||||||
|
{
|
||||||
|
$emptyFinder = $this->getMock('Symfony\Component\Finder\Finder');
|
||||||
|
$emptyFinder
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getIterator')
|
||||||
|
->will($this->returnValue(new \EmptyIterator()));
|
||||||
|
|
||||||
|
$this->finder
|
||||||
|
->expects($this->once())
|
||||||
|
->method('date')
|
||||||
|
->will($this->returnValue($emptyFinder));
|
||||||
|
$this->finder
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getIterator')
|
||||||
|
->will($this->returnValue(new \ArrayIterator($this->files)));
|
||||||
|
$this->finder
|
||||||
|
->expects($this->once())
|
||||||
|
->method('sortByAccessedTime')
|
||||||
|
->will($this->returnValue($this->finder));
|
||||||
|
|
||||||
|
$this->cache->gc(600, 1500);
|
||||||
|
|
||||||
|
for ($i = 0; $i < 3; $i++) {
|
||||||
|
$this->assertFileNotExists("{$this->root}/cached.file{$i}.zip");
|
||||||
|
}
|
||||||
|
$this->assertFileExists("{$this->root}/cached.file3.zip");
|
||||||
|
}
|
||||||
|
}
|
|
@ -107,5 +107,25 @@ class FilesystemTest extends TestCase
|
||||||
$this->assertTrue($fs->removeDirectoryPhp($tmp . "/composer_testdir"));
|
$this->assertTrue($fs->removeDirectoryPhp($tmp . "/composer_testdir"));
|
||||||
$this->assertFalse(file_exists($tmp . "/composer_testdir/level1/level2/hello.txt"));
|
$this->assertFalse(file_exists($tmp . "/composer_testdir/level1/level2/hello.txt"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testFileSize()
|
||||||
|
{
|
||||||
|
$tmp = sys_get_temp_dir();
|
||||||
|
file_put_contents("$tmp/composer_test_file", 'Hello');
|
||||||
|
|
||||||
|
$fs = new Filesystem;
|
||||||
|
$this->assertGreaterThanOrEqual(5, $fs->size("$tmp/composer_test_file"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDirectorySize()
|
||||||
|
{
|
||||||
|
$tmp = sys_get_temp_dir();
|
||||||
|
@mkdir("$tmp/composer_testdir", 0777, true);
|
||||||
|
file_put_contents("$tmp/composer_testdir/file1.txt", 'Hello');
|
||||||
|
file_put_contents("$tmp/composer_testdir/file2.txt", 'World');
|
||||||
|
|
||||||
|
$fs = new Filesystem;
|
||||||
|
$this->assertGreaterThanOrEqual(10, $fs->size("$tmp/composer_testdir"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue