Issue #3306864 by lauriii, mherchel, alexpott: Integrate Twig with Symfony VarDumper for improved debugging experience
(cherry picked from commit 07994b06fb
)
merge-requests/2715/head
parent
bd29f2e041
commit
6425af2663
|
@ -1668,12 +1668,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
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
<?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 @@ class TwigThemeTestController {
|
|||
return ['#theme' => 'twig_theme_test_embed_tag'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders for testing drupal_dump function.
|
||||
*/
|
||||
public function dump() {
|
||||
return ['#theme' => 'twig_theme_test_dump'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{% 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 @@ class TwigExtensionTest extends BrowserTestBase {
|
|||
$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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue