View source
<?php
namespace Drupal\purge\Plugin\Purge\Purger;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Lock\LockBackendInterface;
use Drupal\purge\Logger\LoggerServiceInterface;
use Drupal\purge\Plugin\Purge\DiagnosticCheck\DiagnosticsServiceInterface;
use Drupal\purge\Plugin\Purge\Invalidation\InvalidationInterface;
use Drupal\purge\Plugin\Purge\Processor\ProcessorInterface;
use Drupal\purge\Plugin\Purge\Purger\Exception\BadBehaviorException;
use Drupal\purge\Plugin\Purge\Purger\Exception\CapacityException;
use Drupal\purge\Plugin\Purge\Purger\Exception\DiagnosticsException;
use Drupal\purge\Plugin\Purge\Purger\Exception\LockException;
use Drupal\purge\ServiceBase;
class PurgersService extends ServiceBase implements PurgersServiceInterface {
const LOCKNAME = 'purge_purgers_service';
const LOGGER_PURGERS_FORMAT = 'purger_%s_%s';
protected $capacityTracker;
protected $configFactory;
protected $labels = NULL;
protected $lock;
protected $logger;
protected $purgers;
protected $purgeDiagnostics;
protected $purgeLogger;
protected $runtimeMeasurementTracker;
protected $types = NULL;
protected $typesByPurger = NULL;
public function __construct(PluginManagerInterface $pluginManager, LoggerServiceInterface $purge_logger, CapacityTrackerInterface $capacityTracker, RuntimeMeasurementTrackerInterface $runtimeMeasurementTracker, ConfigFactoryInterface $config_factory, LockBackendInterface $lock, DiagnosticsServiceInterface $purge_diagnostics) {
$this->pluginManager = $pluginManager;
$this->purgeLogger = $purge_logger;
$this->capacityTracker = $capacityTracker;
$this->runtimeMeasurementTracker = $runtimeMeasurementTracker;
$this->configFactory = $config_factory;
$this->purgeDiagnostics = $purge_diagnostics;
$this->lock = $lock;
$this->logger = $this->purgeLogger
->get('purgers');
}
public function capacityTracker() {
$this
->initializePurgers();
return $this->capacityTracker;
}
protected function checksBeforeTakeoff(array $invalidations) {
if ($fire = $this->purgeDiagnostics
->isSystemOnFire()) {
throw new DiagnosticsException($fire
->getRecommendation());
}
if (empty($invalidations)) {
$this->logger
->debug("no invalidation objects given.");
return FALSE;
}
foreach ($invalidations as $i => $invalidation) {
if (!$invalidation instanceof InvalidationInterface) {
throw new BadBehaviorException("Item {$i} is not a \\Drupal\\purge\\Plugin\\Purge\\Invalidation\\InvalidationInterface derivative.");
}
}
$inv_limit = $this
->capacityTracker()
->getRemainingInvalidationsLimit();
if (!$inv_limit) {
$this->logger
->debug("capacity limits exceeded.");
throw new CapacityException('Capacity limits exceeded.');
}
if (($count = count($invalidations)) > $inv_limit) {
$this->logger
->debug("capacity limit allows @limit invalidations during this request, @no given.", [
'@limit' => $inv_limit,
'@no' => $count,
]);
throw new CapacityException("Capacity limit allows {$inv_limit} invalidations during this request, {$count} given.");
}
$lease = $this
->capacityTracker()
->getLeaseTimeHint(count($invalidations));
if (!$this->lock
->acquire(self::LOCKNAME, (double) $lease)) {
$this->logger
->debug("could not acquire processing lock.");
throw new LockException("Could not acquire processing lock.");
}
return TRUE;
}
public function createId() {
return substr(sha1(microtime()), 0, 10);
}
public function getLabels() {
if (is_null($this->labels)) {
$this
->initializePurgers();
$this->labels = [];
foreach ($this
->getPluginsEnabled() as $id => $plugin_id) {
$this->labels[$id] = $this->purgers[$id]
->getLabel();
}
}
return $this->labels;
}
public function getPluginsEnabled() {
if (is_null($this->pluginsEnabled)) {
$this->pluginsEnabled = [];
$plugins = $this->configFactory
->get('purge.plugins')
->get('purgers');
$plugin_ids = array_keys($this
->getPlugins());
if (!is_null($plugins)) {
$setting = [];
foreach ($plugins as $inst) {
if (!in_array($inst['plugin_id'], $plugin_ids)) {
continue;
}
else {
$setting[$inst['order_index']] = $inst;
}
}
ksort($setting);
foreach ($setting as $inst) {
$this->pluginsEnabled[$inst['instance_id']] = $inst['plugin_id'];
}
}
}
return $this->pluginsEnabled;
}
public function getPluginsAvailable() {
$enabled = $this
->getPluginsEnabled();
$available = [];
foreach ($this
->getPlugins() as $plugin_id => $definition) {
if ($definition['multi_instance']) {
$available[] = $plugin_id;
}
else {
if (!in_array($plugin_id, $enabled)) {
$available[] = $plugin_id;
}
}
}
return $available;
}
public function getTypes() {
if (is_null($this->types)) {
$this
->initializePurgers();
$this->types = [];
foreach ($this->purgers as $purger) {
foreach ($purger
->getTypes() as $type) {
if (!in_array($type, $this->types)) {
$this->types[] = $type;
}
}
}
}
return $this->types;
}
public function getTypesByPurger() {
if (is_null($this->typesByPurger)) {
$this
->initializePurgers();
$this->typesByPurger = [];
foreach ($this
->getPluginsEnabled(FALSE) as $id => $plugin_id) {
$this->typesByPurger[$id] = $this->purgers[$id]
->getTypes();
}
}
return $this->typesByPurger;
}
public function setPluginsEnabled(array $plugin_ids) {
$this
->initializePurgers();
$definitions = $this->pluginManager
->getDefinitions();
foreach ($plugin_ids as $instance_id => $plugin_id) {
if (!is_string($instance_id) || empty($instance_id)) {
throw new \LogicException('Invalid instance ID (key).');
}
if (!isset($definitions[$plugin_id])) {
throw new \LogicException('Invalid plugin_id.');
}
}
foreach ($this
->getPluginsEnabled() as $instance_id => $plugin_id) {
if (!isset($plugin_ids[$instance_id])) {
$this->purgers[$instance_id]
->delete();
$this->purgeLogger
->deleteChannel(sprintf(self::LOGGER_PURGERS_FORMAT, $plugin_id, $instance_id));
}
}
$order_index = 1;
$setting = [];
foreach ($plugin_ids as $instance_id => $plugin_id) {
$order_index = $order_index + 1;
$setting[] = [
'order_index' => $order_index,
'instance_id' => $instance_id,
'plugin_id' => $plugin_id,
];
}
$this->configFactory
->getEditable('purge.plugins')
->set('purgers', $setting)
->save();
$this
->reload();
}
public function reload() {
parent::reload();
$this->configFactory = \Drupal::configFactory();
if (php_sapi_name() === 'cli') {
\Drupal::cache('config')
->deleteAll();
}
$this->purgers = NULL;
$this->labels = NULL;
$this->types = NULL;
$this->typesByPurger = NULL;
}
protected function initializePurgers() {
if (!is_null($this->purgers)) {
return;
}
$this->purgers = [];
foreach ($this
->getPluginsEnabled() as $id => $plugin_id) {
$this->purgers[$id] = $this->pluginManager
->createInstance($plugin_id, [
'id' => $id,
]);
$this->purgers[$id]
->setLogger($this->purgeLogger
->get(sprintf(self::LOGGER_PURGERS_FORMAT, $plugin_id, $id)));
$this->logger
->debug("loading purger @id (@plugin_id).", [
'@id' => $id,
'@plugin_id' => $plugin_id,
]);
}
$this->capacityTracker
->setPurgers($this->purgers);
$this->runtimeMeasurementTracker
->setPurgers($this->purgers);
}
public function invalidate(ProcessorInterface $processor, array $invalidations) {
$execution_time_start = microtime(TRUE);
if (!$this
->checksBeforeTakeoff($invalidations)) {
return;
}
$purger_ids = array_keys($this
->getPluginsEnabled());
foreach ($invalidations as $invalidation) {
foreach ($invalidation
->getStateContexts() as $purger_id) {
if (!in_array($purger_id, $purger_ids)) {
$invalidation
->removeStateContext($purger_id);
}
}
}
$types = [];
foreach ($invalidations as $i => $invalidation) {
$types[$i] = $invalidation
->getType();
$invalidation
->setStateContext(NULL);
}
$typesByPurger = $this
->getTypesByPurger();
foreach ($this->purgers as $id => $purger) {
$groups = [];
foreach ($invalidations as $i => $invalidation) {
$invalidation
->setStateContext($id);
if ($invalidation
->getState() == InvalidationInterface::SUCCEEDED) {
continue;
}
if (!in_array($types[$i], $typesByPurger[$id])) {
$invalidation
->setState(InvalidationInterface::NOT_SUPPORTED);
continue;
}
}
foreach ($typesByPurger[$id] as $type) {
$method = $purger
->routeTypeToMethod($type);
if (!isset($groups[$method])) {
$groups[$method] = [];
}
foreach ($types as $i => $invalidation_type) {
if ($invalidation_type === $type) {
$groups[$method][$i] = $invalidations[$i];
}
}
}
foreach ($groups as $method => $offers) {
if (!count($offers)) {
continue;
}
$this->logger
->debug("offering @no items to @plugin's @id::@method()", [
'@no' => count($offers),
'@id' => $id,
'@plugin' => $purger
->getPluginId(),
'@method' => $method,
]);
$has_runtime_measurement = $purger
->hasRuntimeMeasurement();
if ($has_runtime_measurement) {
$instrument = $purger
->getRuntimeMeasurement();
$instrument
->start();
$purger
->{$method}($offers);
$instrument
->stop($offers);
}
else {
$purger
->{$method}($offers);
}
}
if (count($groups)) {
$this
->capacityTracker()
->waitCooldownTime($id);
}
}
foreach ($invalidations as $i => $invalidation) {
$invalidation
->setStateContext(NULL);
}
$this
->capacityTracker()
->spentInvalidations()
->increment(count($invalidations));
$this
->capacityTracker()
->spentExecutionTime()
->increment(microtime(TRUE) - $execution_time_start);
$this->lock
->release(self::LOCKNAME);
}
public function movePurgerDown($purger_instance_id) {
$enabled = $this
->getPluginsEnabled();
if (!isset($enabled[$purger_instance_id])) {
throw new BadBehaviorException("Instance id '{$purger_instance_id}' is not enabled!");
}
$ordered = [];
$index = 0;
foreach ($enabled as $instance_id => $plugin_id) {
$index = $index + 2;
if ($instance_id === $purger_instance_id) {
$ordered[$index + 3] = [
$instance_id,
$plugin_id,
];
}
else {
$ordered[$index] = [
$instance_id,
$plugin_id,
];
}
}
ksort($ordered);
$enabled = [];
foreach ($ordered as $inst) {
$enabled[$inst[0]] = $inst[1];
}
$this
->setPluginsEnabled($enabled);
}
public function movePurgerUp($purger_instance_id) {
$enabled = $this
->getPluginsEnabled();
if (!isset($enabled[$purger_instance_id])) {
throw new BadBehaviorException("Instance id '{$purger_instance_id}' is not enabled!");
}
$ordered = [];
$index = 0;
foreach ($enabled as $instance_id => $plugin_id) {
$index = $index + 2;
if ($instance_id === $purger_instance_id) {
$ordered[$index - 3] = [
$instance_id,
$plugin_id,
];
}
else {
$ordered[$index] = [
$instance_id,
$plugin_id,
];
}
}
ksort($ordered);
$enabled = [];
foreach ($ordered as $inst) {
$enabled[$inst[0]] = $inst[1];
}
$this
->setPluginsEnabled($enabled);
}
}