diff --git a/system/blueprints/config/backups.yaml b/system/blueprints/config/backups.yaml index fd584352a..2d054d049 100644 --- a/system/blueprints/config/backups.yaml +++ b/system/blueprints/config/backups.yaml @@ -119,7 +119,7 @@ form: .schedule_at: type: cron label: Run Scheduled Job - default: '* * * * *' + default: '* 3 * * *' validate: required: true diff --git a/system/blueprints/config/system.yaml b/system/blueprints/config/system.yaml index 5bc2e4970..6e5316027 100644 --- a/system/blueprints/config/system.yaml +++ b/system/blueprints/config/system.yaml @@ -514,6 +514,12 @@ form: help: PLUGIN_ADMIN.CACHE_PREFIX_HELP placeholder: PLUGIN_ADMIN.CACHE_PREFIX_PLACEHOLDER + cache.purge_at: + type: cron + label: PLUGIN_ADMIN.CACHE_PURGE_JOB + help: PLUGIN_ADMIN.CACHE_PURGE_JOB_HELP + default: '* 3 * * *' + cache.clear_images_by_default: type: toggle label: PLUGIN_ADMIN.CLEAR_IMAGES_BY_DEFAULT diff --git a/system/config/system.yaml b/system/config/system.yaml index 065e059f2..fad5a1c20 100644 --- a/system/config/system.yaml +++ b/system/config/system.yaml @@ -76,6 +76,7 @@ cache: method: file # Method to check for updates in pages: file|folder|hash|none driver: auto # One of: auto|file|apc|xcache|memcache|wincache prefix: 'g' # Cache prefix string (prevents cache conflicts) + purge_at: '0 4 * * *' # How often to purge old cache (using new scheduler) clear_images_by_default: true # By default grav will include processed images in cache clear, this can be disabled cli_compatibility: false # Ensures only non-volatile drivers are used (file, redis, memcache, etc.) lifetime: 604800 # Lifetime of cached data in seconds (0 = infinite) diff --git a/system/src/Grav/Common/Cache.php b/system/src/Grav/Common/Cache.php index 71eb29ff7..f458c9ca6 100644 --- a/system/src/Grav/Common/Cache.php +++ b/system/src/Grav/Common/Cache.php @@ -117,27 +117,45 @@ class Cache extends Getters $this->config = $grav['config']; $this->now = time(); - $this->cache_dir = $grav['locator']->findResource('cache://doctrine', true, true); + if (is_null($this->enabled)) { + $this->enabled = (bool)$this->config->get('system.cache.enabled'); + } /** @var Uri $uri */ $uri = $grav['uri']; $prefix = $this->config->get('system.cache.prefix'); - - if (is_null($this->enabled)) { - $this->enabled = (bool)$this->config->get('system.cache.enabled'); - } + $uniqueness = substr(md5($uri->rootUrl(true) . $this->config->key() . GRAV_VERSION), 2, 8); // Cache key allows us to invalidate all cache on configuration changes. - $this->key = ($prefix ? $prefix : 'g') . '-' . substr(md5($uri->rootUrl(true) . $this->config->key() . GRAV_VERSION), - 2, 8); - + $this->key = ($prefix ? $prefix : 'g') . '-' . $uniqueness; + $this->cache_dir = $grav['locator']->findResource('cache://doctrine/' . $uniqueness, true, true); $this->driver_setting = $this->config->get('system.cache.driver'); - $this->driver = $this->getCacheDriver(); - - // Set the cache namespace to our unique key $this->driver->setNamespace($this->key); + + /** @var EventDispatcher $dispatcher */ + $dispatcher = Grav::instance()['events']; + $dispatcher->addListener('onSchedulerInitialized', [$this, 'onSchedulerInitialized']); + } + + public function purgeOldCache() + { + $cache_dir = dirname($this->cache_dir); + $current = basename($this->cache_dir); + $count = 0; + + foreach (new \DirectoryIterator($cache_dir) as $file) { + $dir = $file->getBasename(); + if ($file->isDot() || $file->isFile() || $dir === $current) { + continue; + } + + Folder::delete($file->getPathname()); + $count++; + } + + return $count; } /** @@ -507,4 +525,29 @@ class Cache extends Getters return false; } } + + public static function purgeJob() + { + $cache = Grav::instance()['cache']; + $deleted_folders = $cache->purgeOldCache(); + echo 'Deleted ' . $deleted_folders . ' folders...'; + } + + public function onSchedulerInitialized(Event $event) + { + /** @var Scheduler $scheduler */ + $scheduler = $event['scheduler']; + $config = Grav::instance()['config']; + + $at = $config->get('system.cache.purge_at'); + $name = 'cache-purge'; + $logs = 'logs/' . $name . '.out'; + + $job = $scheduler->addFunction('Grav\Common\Cache::purgeJob', null, $name ); + $job->at($at); + $job->output($logs); + + } + + } diff --git a/system/src/Grav/Console/Cli/ClearCacheCommand.php b/system/src/Grav/Console/Cli/ClearCacheCommand.php index cb5ffe84f..294142d32 100644 --- a/system/src/Grav/Console/Cli/ClearCacheCommand.php +++ b/system/src/Grav/Console/Cli/ClearCacheCommand.php @@ -9,6 +9,7 @@ namespace Grav\Console\Cli; use Grav\Common\Cache; +use Grav\Common\Grav; use Grav\Console\ConsoleCommand; use Symfony\Component\Console\Input\InputOption; @@ -23,11 +24,14 @@ class ClearCacheCommand extends ConsoleCommand ->setName('clear-cache') ->setAliases(['clearcache']) ->setDescription('Clears Grav cache') + ->addOption('purge', null, InputOption::VALUE_NONE, 'If set purge old caches') ->addOption('all', null, InputOption::VALUE_NONE, 'If set will remove all including compiled, twig, doctrine caches') ->addOption('assets-only', null, InputOption::VALUE_NONE, 'If set will remove only assets/*') ->addOption('images-only', null, InputOption::VALUE_NONE, 'If set will remove only images/*') ->addOption('cache-only', null, InputOption::VALUE_NONE, 'If set will remove only cache/*') ->addOption('tmp-only', null, InputOption::VALUE_NONE, 'If set will remove only tmp/*') + ->addOption('tmp-only', null, InputOption::VALUE_NONE, 'If set will remove only tmp/*') + ->setHelp('The clear-cache deletes all cache files'); } @@ -45,25 +49,35 @@ class ClearCacheCommand extends ConsoleCommand private function cleanPaths() { $this->output->writeln(''); - $this->output->writeln('Clearing cache'); - $this->output->writeln(''); - if ($this->input->getOption('all')) { - $remove = 'all'; - } elseif ($this->input->getOption('assets-only')) { - $remove = 'assets-only'; - } elseif ($this->input->getOption('images-only')) { - $remove = 'images-only'; - } elseif ($this->input->getOption('cache-only')) { - $remove = 'cache-only'; - } elseif ($this->input->getOption('tmp-only')) { - $remove = 'tmp-only'; + + if ($this->input->getOption('purge')) { + $this->output->writeln('Purging old cache'); + $this->output->writeln(''); + + $cache = Grav::instance()['cache']; + $cache->purgeOldCache(); } else { - $remove = 'standard'; - } + $this->output->writeln('Clearing cache'); + $this->output->writeln(''); - foreach (Cache::clearCache($remove) as $result) { - $this->output->writeln($result); + if ($this->input->getOption('all')) { + $remove = 'all'; + } elseif ($this->input->getOption('assets-only')) { + $remove = 'assets-only'; + } elseif ($this->input->getOption('images-only')) { + $remove = 'images-only'; + } elseif ($this->input->getOption('cache-only')) { + $remove = 'cache-only'; + } elseif ($this->input->getOption('tmp-only')) { + $remove = 'tmp-only'; + } else { + $remove = 'standard'; + } + + foreach (Cache::clearCache($remove) as $result) { + $this->output->writeln($result); + } } } }