Verified Commit da2eaec5 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3278493 by mglaman, rabbitlair, mherchel, Purencool, dww, benjifisher,...

Issue #3278493 by mglaman, rabbitlair, mherchel, Purencool, dww, benjifisher, lauriii, andy-blum, AdamPS, jwilson3, alexpott, smustgrave, rlahoda, akalata, ckrina, bnjmnm, crasx, mndonx, hbrokmeier, callinmullaney: Make it easier for theme builders to enable Twig debugging and disable render cache
parent 17c7f0a1
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
use Drupal\Core\DependencyInjection\Compiler\CorsCompilerPass;
use Drupal\Core\DependencyInjection\Compiler\DeprecatedServicePass;
use Drupal\Core\DependencyInjection\Compiler\ContextProvidersPass;
use Drupal\Core\DependencyInjection\Compiler\DevelopmentSettingsPass;
use Drupal\Core\DependencyInjection\Compiler\ProxyServicesPass;
use Drupal\Core\DependencyInjection\Compiler\StackedKernelPass;
use Drupal\Core\DependencyInjection\Compiler\StackedSessionHandlerPass;
@@ -58,6 +59,8 @@ public function register(ContainerBuilder $container) {
    // list-building passes are operating on the post-alter services list.
    $container->addCompilerPass(new ModifyServiceDefinitionsPass());

    $container->addCompilerPass(new DevelopmentSettingsPass());

    $container->addCompilerPass(new ProxyServicesPass());

    $container->addCompilerPass(new BackendCompilerPass());
@@ -93,6 +96,7 @@ public function register(ContainerBuilder $container) {
    $container->addCompilerPass(new PluginManagerPass());

    $container->addCompilerPass(new DeprecatedServicePass());

  }

  /**
+44 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\Core\DependencyInjection\Compiler;

use Drupal\Core\Cache\NullBackendFactory;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
 * Defines a compiler pass to register development settings.
 */
class DevelopmentSettingsPass implements CompilerPassInterface {

  /**
   * {@inheritdoc}
   */
  public function process(ContainerBuilder $container) {
    /** @var \Drupal\Core\State\StateInterface $state */
    $state = $container->get('state');
    $twig_debug = $state->get('twig_debug', FALSE);
    $twig_cache_disable = $state->get('twig_cache_disable', FALSE);
    if ($twig_debug || $twig_cache_disable) {
      $twig_config = $container->getParameter('twig.config');
      $twig_config['debug'] = $twig_debug;
      $twig_config['cache'] = !$twig_cache_disable;
      $container->setParameter('twig.config', $twig_config);
    }

    if ($state->get('disable_rendered_output_cache_bins', FALSE)) {
      $cache_bins = ['page', 'dynamic_page_cache', 'render'];
      if (!$container->hasDefinition('cache.backend.null')) {
        $container->register('cache.backend.null', NullBackendFactory::class);
      }
      foreach ($cache_bins as $cache_bin) {
        if ($container->has("cache.$cache_bin")) {
          $container->getDefinition("cache.$cache_bin")
            ->clearTag('cache.bin')
            ->addTag('cache.bin', ['default_backend' => 'cache.backend.null']);
        }
      }
    }
  }

}
+152 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\system\Form;

use Drupal\Core\DrupalKernelInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\State\StateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Configure development settings for this site.
 *
 * @internal
 */
class DevelopmentSettingsForm extends FormBase {

  /**
   * Constructs a new development settings form.
   *
   * @param \Drupal\Core\State\StateInterface $state
   *   The state service.
   * @param \Drupal\Core\DrupalKernelInterface $kernel
   *   The Drupal kernel.
   */
  public function __construct(
    protected StateInterface $state,
    protected DrupalKernelInterface $kernel
  ) {
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    $instance = new static(
      $container->get('state'),
      $container->get('kernel')
    );
    $instance->setMessenger($container->get('messenger'));
    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'development_settings_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['description'] = [
      '#plain_text' => $this->t('These settings should only be enabled on development environments and never on production.'),
    ];

    $twig_debug = $this->state->get('twig_debug', FALSE);
    $twig_cache_disable = $this->state->get('twig_cache_disable', FALSE);
    $twig_development_state_conditions = [
      'input[data-drupal-selector="edit-twig-development-mode"]' => [
        'checked' => TRUE,
      ],
    ];
    $form['twig_development_mode'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Twig development mode'),
      '#description' => $this->t('Exposes Twig development settings.'),
      '#default_value' => $twig_debug || $twig_cache_disable,
    ];
    $form['twig_development'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Twig development mode'),
      '#states' => [
        'visible' => $twig_development_state_conditions,
      ],
    ];
    $form['twig_development']['twig_debug'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Twig debug mode'),
      '#description' => $this->t("Provides Twig's <code>dump()</code> function for debugging, outputs template suggestions to HTML comments, and automatically recompile Twig templates after changes."),
      '#default_value' => $twig_debug,
    ];
    $form['twig_development']['twig_cache_disable'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Disable Twig cache'),
      '#description' => $this->t('Twig templates are not cached and are always compiled when rendered.'),
      '#default_value' => $twig_cache_disable,
    ];
    if (!$twig_debug && !$twig_cache_disable) {
      $form['twig_development']['twig_debug']['#states'] = [
        'checked' => $twig_development_state_conditions,
      ];
      $form['twig_development']['twig_cache_disable']['#states'] = [
        'checked' => $twig_development_state_conditions,
      ];
    }

    $form['disable_rendered_output_cache_bins'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Do not cache markup'),
      '#description' => $this->t('Disables render cache, dynamic page cache, and page cache.'),
      '#default_value' => $this->state->get('disable_rendered_output_cache_bins', FALSE),
    ];

    $form['actions']['#type'] = 'actions';
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Save settings'),
      '#button_type' => 'primary',
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $disable_rendered_output_cache_bins_previous = $this->state->get('disable_rendered_output_cache_bins', FALSE);
    $disable_rendered_output_cache_bins = (bool) $form_state->getValue('disable_rendered_output_cache_bins');
    if ($disable_rendered_output_cache_bins) {
      $this->state->set('disable_rendered_output_cache_bins', TRUE);
    }
    else {
      $this->state->delete('disable_rendered_output_cache_bins');
    }

    $twig_development_mode = (bool) $form_state->getValue('twig_development_mode');
    $twig_development_previous = $this->state->getMultiple(['twig_debug', 'twig_cache_disable']);
    $twig_development = [
      'twig_debug' => (bool) $form_state->getValue('twig_debug'),
      'twig_cache_disable' => (bool) $form_state->getValue('twig_cache_disable'),
    ];
    if ($twig_development_mode) {
      $invalidate_container = $twig_development_previous !== $twig_development;
      $this->state->setMultiple($twig_development);
    }
    else {
      $invalidate_container = TRUE;
      $this->state->deleteMultiple(array_keys($twig_development));
    }

    if ($invalidate_container || $disable_rendered_output_cache_bins_previous !== $disable_rendered_output_cache_bins) {
      $this->kernel->invalidateContainer();
    }

    $this->messenger()->addStatus($this->t('The settings have been saved.'));
  }

}
+31 −0
Original line number Diff line number Diff line
@@ -1532,6 +1532,37 @@ function (callable $hook, string $module) use (&$module_list, $update_registry,
    }
  }

  // Add warning when twig debug option is enabled.
  if ($phase === 'runtime') {
    $twig_debug = \Drupal::state()->get('twig_debug', FALSE);
    $twig_cache_disable = \Drupal::state()->get('twig_cache_disable', FALSE);
    if ($twig_debug || $twig_cache_disable) {
      $requirements['twig_debug_enabled'] = [
        'title' => t('Twig development mode'),
        'value' => t('Twig development mode settings are turned on. Go to @link to disable them.', [
          '@link' => Link::createFromRoute(
            'development settings page',
            'system.development_settings',
          )->toString(),
        ]),
        'severity' => REQUIREMENT_WARNING,
      ];
    }
    $render_cache_disabled = \Drupal::state()->get('disable_rendered_output_cache_bins', FALSE);
    if ($render_cache_disabled) {
      $requirements['render_cache_disabled'] = [
        'title' => t('Markup caching disabled'),
        'value' => t('Render cache, dynamic page cache, and page cache are bypassed. Go to @link to enable them.', [
          '@link' => Link::createFromRoute(
            'development settings page',
            'system.development_settings',
          )->toString(),
        ]),
        'severity' => REQUIREMENT_WARNING,
      ];
    }
  }

  return $requirements;
}

+6 −0
Original line number Diff line number Diff line
@@ -76,6 +76,12 @@ system.performance_settings:
  description: 'Configure caching and bandwidth optimization.'
  route_name: system.performance_settings
  weight: -20
system.development_settings:
  title: Development settings
  parent: system.admin_config_development
  description: 'Configure theme development settings'
  route_name: system.development_settings
  weight: -19
system.logging_settings:
  title: 'Logging and errors'
  parent: system.admin_config_development
Loading