Skip to content
Snippets Groups Projects
Verified Commit 07994b06 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3306864 by lauriii, mherchel, alexpott: Integrate Twig with Symfony...

Issue #3306864 by lauriii, mherchel, alexpott: Integrate Twig with Symfony VarDumper for improved debugging experience
parent f1e6390c
No related branches found
No related tags found
37 merge requests!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!54479.5.x SF update,!5014Issue #3071143: Table Render Array Example Is Incorrect,!4868Issue #1428520: Improve menu parent link selection,!4289Issue #1344552 by marcingy, Niklas Fiekas, Ravi.J, aleevas, Eduardo Morales...,!4114Issue #2707291: Disable body-level scrolling when a dialog is open as a modal,!4100Issue #3249600: Add support for PHP 8.1 Enums as allowed values for list_* data types,!3630Issue #2815301 by Chi, DanielVeza, kostyashupenko, smustgrave: Allow to create...,!3600Issue #3344629: Passing null to parameter #1 ($haystack) of type string is deprecated,!3291Issue #3336463: Rewrite rules for gzipped CSS and JavaScript aggregates never match,!3102Issue #3164428 by DonAtt, longwave, sahil.goyal, Anchal_gupta, alexpott: Use...,!2853#3274419 Makes BaseFieldOverride inherit the internal property from the base field.,!2378Issue #2875033: Optimize joins and table selection in SQL entity query implementation,!2334Issue #3228209: Add hasRole() method to AccountInterface,!2074Issue #2707689: NodeForm::actions() checks for delete access on new entities,!2062Issue #3246454: Add weekly granularity to views date sort,!1591Issue #3199697: Add JSON:API Translation experimental module,!1484Exposed filters get values from URL when Ajax is on,!1255Issue #3238922: Refactor (if feasible) uses of the jQuery serialize function to use vanillaJS,!1254Issue #3238915: Refactor (if feasible) uses of the jQuery ready function to use VanillaJS,!1162Issue #3100350: Unable to save '/' root path alias,!1105Issue #3025039: New non translatable field on translatable content throws error,!1073issue #3191727: Focus states on mobile second level navigation items fixed,!10223132456: Fix issue where views instances are emptied before an ajax request is complete,!957Added throwing of InvalidPluginDefinitionException from getDefinition().,!925Issue #2339235: Remove taxonomy hard dependency on node module,!877Issue #2708101: Default value for link text is not saved,!873Issue #2875228: Site install not using batch API service,!872Draft: Issue #3221319: Race condition when creating menu links and editing content deletes menu links,!844Resolve #3036010 "Updaters",!712Issue #2909128: Autocomplete intermittent on Chrome Android,!617Issue #3043725: Provide a Entity Handler for user cancelation,!579Issue #2230909: Simple decimals fail to pass validation,!560Move callback classRemove outside of the loop,!555Issue #3202493,!485Sets the autocomplete attribute for username/password input field on login form.,!30Issue #3182188: Updates composer usage to point at ./vendor/bin/composer
......@@ -1536,12 +1536,18 @@ services:
arguments: ['@renderer', '@url_generator', '@theme.manager', '@date.formatter', '@file_url_generator']
tags:
- { name: twig.extension, priority: 100 }
# @todo Figure out what to do about debugging functions.
# @see https://www.drupal.org/node/1804998
twig.extension.debug:
class: Twig\Extension\DebugExtension
tags:
- { name: twig.extension }
- { name: twig.extension, priority: 50 }
twig.extension.varDumper:
class: Drupal\Core\Template\DebugExtension
tags:
# This extension is loaded after the Twig Debug Extension because for Twig
# Extensions, last extension loaded takes precedent. This allows this
# extension to override the default Twig Debug Extension conditionally
# when Symfony VarDumper is available.
- { name: twig.extension, priority: 25 }
twig.loader:
class: Twig\Loader\ChainLoader
public: false
......
<?php
namespace Drupal\Core\Template;
use Twig\Environment;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
/**
* A class providing Drupal Twig Debug extension.
*/
final class DebugExtension extends AbstractExtension {
/**
* The Symfony VarDumper class.
*
* Defined as a string because the Symfony VarDumper does not always exist.
*
* @type string
*/
private const SYMFONY_VAR_DUMPER_CLASS = '\Symfony\Component\VarDumper\VarDumper';
/**
* {@inheritdoc}
*/
public function getFunctions(): array {
// Override Twig built in debugger when Symfony VarDumper is available to
// improve developer experience.
// @see \Twig\Extension\DebugExtension
// @see \Symfony\Component\VarDumper\VarDumper
if (class_exists(self::SYMFONY_VAR_DUMPER_CLASS)) {
return [
new TwigFunction('dump', [self::class, 'dump'], ['needs_context' => TRUE, 'needs_environment' => TRUE, 'is_variadic' => TRUE]),
];
}
return [];
}
/**
* Dumps information about variables using Symfony VarDumper.
*
* @param \Twig\Environment $env
* The Twig environment.
* @param array $context
* Variables from the Twig template.
* @param array $variables
* (optional) Variable(s) to dump.
*/
public static function dump(Environment $env, array $context, ...$variables): void {
if (!$env->isDebug()) {
return;
}
if (class_exists(self::SYMFONY_VAR_DUMPER_CLASS)) {
if (func_num_args() === 2) {
call_user_func(self::SYMFONY_VAR_DUMPER_CLASS . '::dump', $context);
}
else {
array_walk($variables, self::SYMFONY_VAR_DUMPER_CLASS . '::dump');
}
}
else {
throw new \LogicException('Could not dump the variable because symfony/var-dumper component is not installed.');
}
}
}
......@@ -111,4 +111,11 @@ public function embedTagRender() {
return ['#theme' => 'twig_theme_test_embed_tag'];
}
/**
* Renders for testing drupal_dump function.
*/
public function dump() {
return ['#theme' => 'twig_theme_test_dump'];
}
}
{% set foo = '💩' %}
{% set bar = '🐣' %}
{% set baz = '☄️' %}
{{ dump(foo) }}
{{ dump() }}
{{ dump(foo, baz) }}
......@@ -77,6 +77,10 @@ function twig_theme_test_theme($existing, $type, $theme, $path) {
'variables' => [],
'template' => 'twig_theme_test.embed_tag',
];
$items['twig_theme_test_dump'] = [
'variables' => [],
'template' => 'twig_theme_test.dump',
];
return $items;
}
......
......@@ -76,3 +76,10 @@ twig_theme_test_embed_tag:
_controller: '\Drupal\twig_theme_test\TwigThemeTestController::embedTagRender'
requirements:
_access: 'TRUE'
twig_theme_test_dump:
path: '/twig-theme-test/dump'
defaults:
_controller: '\Drupal\twig_theme_test\TwigThemeTestController::dump'
requirements:
_access: 'TRUE'
......@@ -17,7 +17,7 @@ class TwigExtensionTest extends BrowserTestBase {
*
* @var array
*/
protected static $modules = ['theme_test', 'twig_extension_test'];
protected static $modules = ['theme_test', 'twig_extension_test', 'twig_theme_test'];
/**
* {@inheritdoc}
......@@ -92,4 +92,35 @@ public function testsRenderZeroValue() {
$this->assertSame(0, $extension->renderVar(0.0), 'TwigExtension::renderVar() renders zero correctly when provided as a double.');
}
/**
* Tests the dump function.
*/
public function testDump() {
// Test Twig Debug disabled.
$this->drupalGet('/twig-theme-test/dump');
$this->assertSession()->elementsCount('css', '.sf-dump', 0);
// Test Twig Debug enabled.
$parameters = $this->container->getParameter('twig.config');
$parameters['debug'] = TRUE;
$this->setContainerParameter('twig.config', $parameters);
$this->resetAll();
$this->drupalGet('/twig-theme-test/dump');
$dumps = $this->getSession()->getPage()->findAll('css', '.sf-dump');
$this->assertEquals(4, count($dumps));
// Test dumping single variable.
$this->assertStringContainsString('💩', $dumps[0]->getText());
$this->assertStringNotContainsString('🐣', $dumps[0]->getText());
// Test dumping context.
$this->assertStringContainsString('"bar" => "🐣"', $dumps[1]->getText());
// Test dump as a variadic.
$this->assertStringContainsString('💩', $dumps[2]->getText());
$this->assertStringContainsString('☄️', $dumps[3]->getText());
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment