Skip to content
Snippets Groups Projects
Commit ad53cc81 authored by Carsten Logemann's avatar Carsten Logemann
Browse files

Issue #3324435 by C-Logemann: Create cache (file) invalidation logic

parent 22d6c30a
Branches
No related tags found
1 merge request!14Issue #3324435 by C-Logemann: Create cache (file) invalidation logic
......@@ -4,13 +4,30 @@
* @file
* Contains boost.module.
*
* @todo, complete the nginx boost conf.
* @todo Complete the nginx boost conf.
* https://github.com/perusio/drupal-with-nginx/blob/D7/apps/drupal/drupal_boost.conf
* @todo, complete the example apache conf.
* @todo Complete the example apache conf.
*/
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Implements hook_cron().
*/
function boost_cron() {
$boost_config = \Drupal::config('boost.settings');
$purge_maximum_age = $boost_config->get('cron_purge_maximum_age') ?? 0;
if ($purge_maximum_age == 1) {
$batch_success = \Drupal::service('boost.purge')->purgeCacheFiles('max_age');
if ($batch_success) {
if (function_exists('drush_backend_batch_process')) {
// For drush.
drush_backend_batch_process();
}
}
}
}
/**
* Implements hook_help().
*/
......
......@@ -18,4 +18,4 @@ services:
arguments: ['@config.factory', '@entity_type.manager', '@boost.file']
boost.purge:
class: Drupal\boost\BoostCachePurge
arguments: ['@config.factory', '@file_system', '@boost.file']
arguments: ['@config.factory', '@datetime.time', '@file_system', '@boost.file']
......@@ -97,13 +97,13 @@ class BoostCacheFile implements BoostCacheFileInterface {
/**
* {@inheritdoc}
*/
public function getCachedFiles(): array {
public function getCachedFiles(array $options = []): array {
$files = [];
$scheme = $scheme ?? BoostCacheFileInterface::DEFAULT_SCHEME;
$dir = $dir ?? BoostCacheFileInterface::DEFAULT_DIRECTORY;
$cache_folder = $scheme . $dir;
try {
$files = $this->scanDirectory($cache_folder, '/.*/');
$files = $this->scanDirectory($cache_folder, '/.*/', $options);
}
catch (NotRegularDirectoryException $e) {
\Drupal::messenger()->addWarning($e->getMessage());
......@@ -142,7 +142,8 @@ class BoostCacheFile implements BoostCacheFileInterface {
// example, node_modules and bower_components. Ignoring irrelevant
// directories is a performance boost.
if (!isset($options['nomask'])) {
// $ignore_directories = $this->settings->get('file_scan_ignore_directories', []);
// $ignore_directories =
// $this->settings->get('file_scan_ignore_directories', []);
$ignore_directories = [];
array_walk($ignore_directories, function (&$value) {
$value = preg_quote($value, '/');
......@@ -203,6 +204,9 @@ class BoostCacheFile implements BoostCacheFileInterface {
$file->uri = $uri;
$file->filename = $filename;
$file->name = pathinfo($filename, PATHINFO_FILENAME);
if (isset($options['filemtime']) && $options['filemtime'] == TRUE) {
$file->filemtime = filemtime($uri);
}
$key = $options['key'];
$files[$file->{$key}] = $file;
if ($options['callback']) {
......
......@@ -4,12 +4,11 @@ declare(strict_types=1);
namespace Drupal\boost;
use Drupal\boost\BoostCacheFileInterface;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Batch\BatchBuilder;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\File\Exception\NotRegularDirectoryException;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
......@@ -18,6 +17,13 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
class BoostCachePurge {
use DependencySerializationTrait;
/**
* The time service.
*
* @var \Drupal\Component\Datetime\TimeInterface
*/
protected $time;
/**
* FileSystem object.
*
......@@ -37,6 +43,8 @@ class BoostCachePurge {
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* Config factory to get boost settings.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
* @param \Drupal\Core\File\FileSystemInterface $file_system
* The file system service.
* @param \Drupal\boost\BoostCacheFileInterface $boost_file
......@@ -46,10 +54,12 @@ class BoostCachePurge {
*/
public function __construct(
ConfigFactoryInterface $config_factory,
TimeInterface $time,
FileSystemInterface $file_system,
BoostCacheFileInterface $boost_file
) {
$this->config = $config_factory->get('boost.settings');
$this->config = $config_factory;
$this->time = $time;
$this->fileSystem = $file_system;
$this->boostCacheFile = $boost_file;
}
......@@ -60,6 +70,7 @@ class BoostCachePurge {
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.factory'),
$container->get('datetime.time'),
$container->get('file_system'),
$container->get('boost_file')
);
......@@ -68,30 +79,85 @@ class BoostCachePurge {
/**
* Purge boost cache files.
*/
public function purgeCacheFiles($files = []) {
public function purgeCacheFiles($opt = 'all') {
// Define a feedback boolean for drush;.
$batch_success = FALSE;
$files = $this->boostCacheFile->getCachedFiles();
$files_count = count($files);
if ($files_count == 0) {
\Drupal::messenger()->addMessage('No files found to delete.');
return $batch_success;
$config = $this->config->get('boost.settings');
$exec = $config->get('exec') ?? 0;
$max_age = 0;
if ($opt == 'max_age') {
$max_age = $this->config->get('system.performance')->get('cache.page.max_age');
}
if ($exec == 0) {
$files = [];
if ($opt == 'max_age') {
if ($max_age == 0) {
$files = $files = $this->boostCacheFile->getCachedFiles();
}
else {
$options = ['filemtime' => TRUE];
$files_all = $this->boostCacheFile->getCachedFiles($options);
$request_time = $this->time->getCurrentTime();
$time_limit = $request_time - $max_age;
foreach ($files_all as $file_uri => $file) {
$filemtime = $file->filemtime;
if ($filemtime < $time_limit) {
$files[$file_uri] = $file;
}
}
}
}
else {
$files = $this->boostCacheFile->getCachedFiles();
}
$files_count = count($files);
if ($files_count == 0) {
\Drupal::messenger()->addMessage('Boost: No files found to delete');
return $batch_success;
}
else {
\Drupal::messenger()->addMessage('Boost: files to delete: ' . $files_count);
}
$config = $this->config->get('boost.settings');
$chunk_size = $config->get('chunk_purge') ?? 30;
$batch_builder = (new BatchBuilder())
->setTitle(t('Processing Purge of Boost Cache Files Batch'))
->setFinishCallback([$this, 'batchFinished'])
->setInitMessage(t('Batch is starting'))
->setProgressMessage(t('Processed @current out of @total.'))
->setErrorMessage(t('Batch has encountered an error'));
foreach (array_chunk($files, $chunk_size) as $chunk) {
$batch_builder->addOperation([$this, 'batchProcess'], [$chunk]);
}
$batch_success = TRUE;
batch_set($batch_builder->toArray());
}
$config = $this->config;
$chunk_size = $config->get('chunk_purge') ?? 30;
$batch_builder = (new BatchBuilder())
->setTitle(t('Processing Purge of Boost Cache Files Batch'))
->setFinishCallback([$this, 'batchFinished'])
->setInitMessage(t('Batch is starting'))
->setProgressMessage(t('Processed @current out of @total.'))
->setErrorMessage(t('Batch has encountered an error'));
foreach (array_chunk($files, $chunk_size) as $chunk) {
$batch_builder->addOperation([$this, 'batchProcess'], [$chunk]);
else {
$scheme = $scheme ?? BoostCacheFileInterface::DEFAULT_SCHEME;
$dir = $dir ?? BoostCacheFileInterface::DEFAULT_DIRECTORY;
$cache_folder_raw = $this->fileSystem->realpath($scheme . $dir);
$cache_folder = escapeshellcmd($cache_folder_raw);
echo $shell_output;
if ($max_age == 0) {
$shell_command = 'rm -Rf ' . $cache_folder . '/*';
}
else {
$mmin_raw = $max_age / 60;
// Just in case the value list change or there is an override:
$mmin = round($mmin_raw);
$shell_command = 'find ' . $cache_folder . ' -maxdepth 12 ';
$shell_command .= '-type f -mmin +' . $mmin . ' | xargs rm -rf';
}
exec($shell_command);
\Drupal::messenger()->addMessage('Boost: Executed shell command for purging.');
}
$batch_success = TRUE;
batch_set($batch_builder->toArray());
return $batch_success;
}
......
......@@ -158,6 +158,20 @@ class BoostCommands extends DrushCommands {
}
}
/**
* Purges (delete) boost cache files (system: page cache maximum age).
*
* @command boost:purge-max-age
*
* @aliases boost-purge-max-age
*/
public function purgeMaxAgeCacheFiles($batch_success = FALSE) {
$batch_success = $this->boostCachePurge->purgeCacheFiles('max_age');
if ($batch_success) {
drush_backend_batch_process();
}
}
/**
* Checks that the site URI is set.
*
......
......@@ -5,9 +5,11 @@ namespace Drupal\boost\Form;
use Drupal\boost\BoostCacheFileInterface;
use Drupal\Component\Plugin\Factory\FactoryInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\system\Plugin\Condition\RequestPath;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -23,6 +25,13 @@ class BoostSettingsForm extends ConfigFormBase {
*/
protected BoostCacheFileInterface $boostCacheFile;
/**
* The date formatter service.
*
* @var \Drupal\Core\Datetime\DateFormatterInterface
*/
protected $dateFormatter;
/**
* The request path condition plugin.
*
......@@ -42,6 +51,8 @@ class BoostSettingsForm extends ConfigFormBase {
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The factory for configuration objects.
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date formatter service.
* @param \Drupal\Component\Plugin\Factory\FactoryInterface $plugin_factory
* The factory interface to create request path condition plugin.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
......@@ -53,11 +64,13 @@ class BoostSettingsForm extends ConfigFormBase {
*/
public function __construct(
ConfigFactoryInterface $config_factory,
DateFormatterInterface $date_formatter,
FactoryInterface $plugin_factory,
EntityTypeManagerInterface $entity_type_manager,
BoostCacheFileInterface $boost_file
) {
parent::__construct($config_factory);
$this->dateFormatter = $date_formatter;
$this->condition = $plugin_factory->createInstance('request_path');
$this->entityTypeManager = $entity_type_manager;
$this->boostCacheFile = $boost_file;
......@@ -69,6 +82,7 @@ class BoostSettingsForm extends ConfigFormBase {
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.factory'),
$container->get('date.formatter'),
$container->get('plugin.manager.condition'),
$container->get('entity_type.manager'),
$container->get('boost.file')
......@@ -94,10 +108,18 @@ class BoostSettingsForm extends ConfigFormBase {
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('boost.settings');
$config_performance = $this->config('system.performance');
$cacheability_pages_open = FALSE;
$path_entity_open = FALSE;
$xmlsitemap_crawl_open = FALSE;
// Copied from core/modules/system/src/Form/PerformanceForm.php.
$period = [0, 60, 180, 300, 600, 900, 1800, 2700, 3600, 10800, 21600, 32400, 43200, 86400];
$period = array_map([$this->dateFormatter, 'formatInterval'], array_combine($period, $period));
$period[0] = '<' . $this->t('no caching') . '>';
$system_cache_page_max_age = $period[$config_performance->get('cache.page.max_age')];
if ($cacheability_pages = $config->get('cacheability_pages')) {
if (trim($cacheability_pages['pages']) != "") {
$cacheability_pages_open = TRUE;
......@@ -130,6 +152,31 @@ class BoostSettingsForm extends ConfigFormBase {
$xmlsitemap_crawl_open = TRUE;
}
$form['boost_main'] = [
'#type' => 'details',
'#title' => $this->t('Boost Main Settings'),
'#open' => TRUE,
];
$form['boost_main']['cron_purge_maximum_age'] = [
'#type' => 'checkbox',
'#title' => $this->t('Cron purge (delete) cached files: cache maximum age'),
'#default_value' => $config->get('cron_purge_maximum_age'),
'#description' => $this->t('If enabled on cron a purge will we done based on file modification date. This follows the system config <a href=":performance_site">":performance_title" > ":performance_max_age" (Current value: :period_current)</a>. As an alternative you can directly use drush command "boost:purge-max-age".', [
':performance_title' => $this->t('Performance'),
':performance_site' => Url::fromRoute('system.performance_settings')->toString(),
':performance_max_age' => $this->t('Browser and proxy cache maximum age'),
':period_current' => $system_cache_page_max_age,
]),
'#disabled' => FALSE,
];
$form['boost_main']['exec'] = [
'#type' => 'checkbox',
'#title' => $this->t('User php exec/shell_exec with os commands "find" and "rm".'),
'#default_value' => $config->get('exec'),
'#description' => $this->t("Warning: There is no UI feedback. Check cli commands and file system if this is working for you. But if enabled and it's working the purge (delete) process of cache files is extreme fast."),
'#disabled' => FALSE,
];
$form['boost_cacheability'] = [
'#type' => 'details',
'#title' => $this->t('Boost Path Cacheability Settings'),
......@@ -265,7 +312,9 @@ class BoostSettingsForm extends ConfigFormBase {
}
$this->config('boost.settings')
->set('cron_purge_maximum_age', $form_values['cron_purge_maximum_age'])
->set('cacheability_pages', $this->condition->getConfiguration())
->set('exec', $form_values['exec'])
->set('path_entity_set', $pe_set)
->set('xmlsitemap_crawl', $xsc_config_array)
->save();
......@@ -282,10 +331,7 @@ class BoostSettingsForm extends ConfigFormBase {
*/
public function countBoostCachedFiles(array &$form, FormStateInterface $form_state) {
$files = $this->boostCacheFile->getCachedFiles();
$message = \Drupal::translation()->formatPlural(
count($files),
'No cache file found.', '@count cache files found.'
);
$message = $this->t('@count cache files found.', ['@count' => count($files)]);
\Drupal::messenger()->addMessage($message);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment