You are here

varnish_focal_point_purge.module in Varnish purger 8

File

modules/varnish_focal_point_purge/varnish_focal_point_purge.module
View source
<?php

/**
 * @file
 * Contains varnish_focal_point_purge.module.
 */
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\file\Entity\File;
use Drupal\image\Entity\ImageStyle;
use Drupal\Core\Entity\EntityInterface;
use GuzzleHttp\Client;
use Drupal\file\FileInterface;
use Drupal\varnish_purger\Plugin\Purge\Purger\VarnishPurger;

/**
 * Implements hook_help().
 */
function varnish_focal_point_purge_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    case 'help.page.varnish_focal_point_purge':
      $output = '';
      $output .= '<h2>' . t('About') . '</h2>';
      $output .= '<p>' . t('Purge focal point entities after they are updated') . '</p>';
      $output .= '<p>' . t('You need to setup Varnish to listen to the request method URIBAN. Example:') . '</p>';
      $output .= '<pre><code>if (req.method == "URIBAN") {<br />';
      $output .= '  ban("req.http.host == " + req.http.host + " && req.url == " + req.url);<br />';
      $output .= '  # Throw a synthetic page so the request won\'t go to the backend.<br />';
      $output .= '  return (synth(200, "Ban added."));<br />';
      $output .= '}</code></pre>';
      return $output;
    default:
  }
}

/**
 * Implements hook_entity_update().
 */
function varnish_focal_point_purge_entity_update(EntityInterface $entity) {
  if ($entity
    ->bundle() !== 'focal_point') {
    return;
  }

  // Get the name(s) of the varnish purge configs, they get random suffix.
  // This is ugly, should be done better I guess.
  $query = \Drupal::database()
    ->select('config', 'c');
  $query
    ->fields('c', [
    'name',
  ]);
  $query
    ->condition('c.name', $query
    ->escapeLike('varnish_purger.settings') . '%', 'LIKE');
  $varnish_purgers = $query
    ->execute()
    ->fetchAllKeyed(0, 0);

  // Set the array to use for varnish purgers.
  $purgers = [];
  foreach ($varnish_purgers as $key => $value) {
    $config_purge = \Drupal::config($key);
    $purgers[$key]['hostname'] = $config_purge
      ->get('hostname');
    $purgers[$key]['port'] = $config_purge
      ->get('port');
  }
  if (empty($varnish_purgers)) {
    return;
  }
  $file = File::load($entity->entity_id->value);
  if (!$file instanceof FileInterface) {
    return;
  }

  // TODO: Consider are we not doing duplicated work related to varnish_image purge...
  // Do the actual purging...

  /* @var $client Client */
  $client = \Drupal::service('http_client');

  // Generator for purge requests to be sent out.
  $requests = function () use ($client, $file) {
    $styles = ImageStyle::loadMultiple();

    /* @var $style Drupal\image\Entity\ImageStyle; */
    foreach ($styles as $style) {
      $url = $style
        ->buildUrl($file
        ->getFileUri());
      (yield function () use ($client, $url) {
        return $client
          ->requestAsync('URIBAN', $url)
          ->then(function () {
        }, function ($reason) use ($url) {
          $message = $reason instanceof \Exception ? $reason
            ->getMessage() : (string) $reason;
          $logger = \Drupal::logger('varnish_focal_point_purge');
          $logger
            ->error("URL not purged {$url} {$message}");
        });
      });
    }
  };

  // Prepare a POOL that will make the requests with a given concurrency.
  // TODO: Have the concurrency exposed as configuration somewhere.
  $concurrency = VarnishPurger::VARNISH_PURGE_CONCURRENCY;
  (new \GuzzleHttp\Pool($client, $requests(), [
    'concurrency' => $concurrency,
  ]))
    ->promise()
    ->wait();
}