mirror of
https://github.com/maximebf/php-debugbar.git
synced 2025-01-16 13:00:42 +01:00
New debugbar twig extensions (#632)
This commit is contained in:
parent
3168cb9adf
commit
3adb1e45f6
@ -1,2 +1,7 @@
|
||||
Hello {{ name }}
|
||||
{% include "foobar.html" %}
|
||||
|
||||
{% measure 'Measure Debugs' %}
|
||||
{{ debug('Hello ' ~ name) }}
|
||||
{{ dump({'name' : name}) }}
|
||||
{% endmeasure %}
|
||||
|
@ -8,8 +8,19 @@ $debugbarRenderer->setBaseUrl('../../../src/DebugBar/Resources');
|
||||
$loader = new Twig\Loader\FilesystemLoader('.');
|
||||
$twig = new Twig\Environment($loader);
|
||||
$profile = new Twig\Profiler\Profile();
|
||||
|
||||
// enable template measure on timeline
|
||||
$twig->addExtension(new DebugBar\Bridge\Twig\TimeableTwigExtensionProfiler($profile, $debugbar['time']));
|
||||
|
||||
// enable {% measure 'foo' %} {% endmeasure %} tags for time measure on templates
|
||||
$twig->addExtension(new DebugBar\Bridge\Twig\MeasureTwigExtension($debugbar['time']));
|
||||
|
||||
$twig->enableDebug();
|
||||
// enable {{ dump('foo') }} function on templates
|
||||
$twig->addExtension(new DebugBar\Bridge\Twig\DumpTwigExtension());
|
||||
// enable {{ debug('foo') }} function on templates
|
||||
$twig->addExtension(new DebugBar\Bridge\Twig\DebugTwigExtension($debugbar['messages']));
|
||||
|
||||
$debugbar->addCollector(new DebugBar\Bridge\NamespacedTwigProfileCollector($profile, $twig));
|
||||
|
||||
render_demo_page(function() use ($twig) {
|
||||
|
@ -118,17 +118,6 @@ $env->addExtension(new Twig_Extension_Profiler($profile));
|
||||
$debugbar->addCollector(new DebugBar\Bridge\TwigProfileCollector($profile));
|
||||
```
|
||||
|
||||
You can optionally use `DebugBar\Bridge\Twig\TimeableTwigExtensionProfiler` in place of
|
||||
`Twig_Extension_Profiler` so render operation can be measured.
|
||||
|
||||
```php
|
||||
$loader = new Twig_Loader_Filesystem('.');
|
||||
$env = new Twig_Environment($loader);
|
||||
$profile = new Twig_Profiler_Profile();
|
||||
$env->addExtension(new DebugBar\Bridge\Twig\TimeableTwigExtensionProfiler($profile, $debugbar['time']));
|
||||
$debugbar->addCollector(new DebugBar\Bridge\TwigProfileCollector($profile));
|
||||
```
|
||||
|
||||
### Version 2 and 3
|
||||
|
||||
This collector uses the class `Twig\Extension\ProfilerExtension` to collect info about rendered
|
||||
@ -149,3 +138,49 @@ $env->addExtension(new ProfilerExtension($profile));
|
||||
$debugbar->addCollector(new NamespacedTwigProfileCollector($profile));
|
||||
```
|
||||
|
||||
### Optional debugbar twig extensions
|
||||
|
||||
You can optionally use `DebugBar\Bridge\Twig\TimeableTwigExtensionProfiler` in place of
|
||||
`Twig\Profiler\Profile` so render operation can be measured.
|
||||
|
||||
```php
|
||||
use Twig\Environment;
|
||||
use Twig\Loader\FilesystemLoader;
|
||||
use Twig\Profiler\Profile;
|
||||
|
||||
$loader = new FilesystemLoader('.');
|
||||
$env = new Environment($loader);
|
||||
$profile = new Profile();
|
||||
|
||||
$env->addExtension(new DebugBar\Bridge\Twig\TimeableTwigExtensionProfiler($profile, $debugbar['time']));
|
||||
$debugbar->addCollector(new DebugBar\Bridge\TwigProfileCollector($profile));
|
||||
```
|
||||
|
||||
Other optional extensions add functions and tags for debugbar integration into templates.
|
||||
|
||||
```php
|
||||
use Twig\Environment;
|
||||
use Twig\Loader\FilesystemLoader;
|
||||
use Twig\Profiler\Profile;
|
||||
|
||||
$loader = new FilesystemLoader('.');
|
||||
$env = new Environment($loader);
|
||||
$profile = new Profile();
|
||||
|
||||
// enable {% measure 'foo' %} {% endmeasure %} tags for time measure on templates
|
||||
// this extension adds timeline items to TimeDataCollector
|
||||
$twig->addExtension(new DebugBar\Bridge\Twig\MeasureTwigExtension($debugbar['time']));
|
||||
|
||||
$twig->enableDebug(); // if Twig\Environment debug is disabled, dump/debug are ignored
|
||||
|
||||
// enable {{ dump('foo') }} function on templates
|
||||
// this extension allows dumping data using debugbar DataFormatter
|
||||
$twig->addExtension(new DebugBar\Bridge\Twig\DumpTwigExtension());
|
||||
|
||||
// enable {{ debug('foo') }} function on templates
|
||||
// this extension allows debugging in MessageCollector
|
||||
$twig->addExtension(new DebugBar\Bridge\Twig\DebugTwigExtension($debugbar['messages']));
|
||||
|
||||
$debugbar->addCollector(new DebugBar\Bridge\TwigProfileCollector($profile));
|
||||
```
|
||||
|
||||
|
94
src/DebugBar/Bridge/Twig/DebugTwigExtension.php
Normal file
94
src/DebugBar/Bridge/Twig/DebugTwigExtension.php
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace DebugBar\Bridge\Twig;
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
/**
|
||||
* Debug messages to debugbar in your Twig templates.
|
||||
*
|
||||
* @package DebugBar\Bridge\Twig
|
||||
*/
|
||||
class DebugTwigExtension extends AbstractExtension
|
||||
{
|
||||
/**
|
||||
* @var \DebugBar\DataCollector\MessagesCollector|null
|
||||
*/
|
||||
protected $messagesCollector;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $functionName;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param \DebugBar\DataCollector\MessagesCollector|null $app
|
||||
* @param string $functionName
|
||||
*/
|
||||
public function __construct($messagesCollector, $functionName = 'debug')
|
||||
{
|
||||
$this->messagesCollector = $messagesCollector;
|
||||
$this->functionName = $functionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return static::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new TwigFunction(
|
||||
$this->functionName,
|
||||
[$this, 'debug'],
|
||||
['needs_context' => true, 'needs_environment' => true]
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on Twig_Extension_Debug / twig_var_dump
|
||||
*
|
||||
* @param Environment $env
|
||||
* @param $context
|
||||
*/
|
||||
public function debug(Environment $env, $context)
|
||||
{
|
||||
if (!$env->isDebug() || !$this->messagesCollector) {
|
||||
return;
|
||||
}
|
||||
|
||||
$count = func_num_args();
|
||||
if (2 === $count) {
|
||||
$data = [];
|
||||
foreach ($context as $key => $value) {
|
||||
if (is_object($value)) {
|
||||
if (method_exists($value, 'toArray')) {
|
||||
$data[$key] = $value->toArray();
|
||||
} else {
|
||||
$data[$key] = "Object (" . get_class($value) . ")";
|
||||
}
|
||||
} else {
|
||||
$data[$key] = $value;
|
||||
}
|
||||
}
|
||||
$this->messagesCollector->addMessage($data, 'debug');
|
||||
} else {
|
||||
for ($i = 2; $i < $count; $i++) {
|
||||
$this->messagesCollector->addMessage(func_get_arg($i), 'debug');
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
99
src/DebugBar/Bridge/Twig/DumpTwigExtension.php
Normal file
99
src/DebugBar/Bridge/Twig/DumpTwigExtension.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace DebugBar\Bridge\Twig;
|
||||
|
||||
use DebugBar\DataFormatter\HasDataFormatter;
|
||||
use Twig\Environment;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
/**
|
||||
* Dump variables using debugbar DataFormatter
|
||||
*
|
||||
* @package DebugBar\Bridge\Twig
|
||||
*/
|
||||
class DumpTwigExtension extends AbstractExtension
|
||||
{
|
||||
use HasDataFormatter;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $functionName;
|
||||
|
||||
/**
|
||||
* Create a new auth extension.
|
||||
*
|
||||
* @param string $functionName
|
||||
*/
|
||||
public function __construct($functionName = 'dump')
|
||||
{
|
||||
$this->functionName = $functionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return static::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new TwigFunction(
|
||||
$this->functionName,
|
||||
[$this, 'dump'],
|
||||
['is_safe' => ['html'], 'needs_context' => true, 'needs_environment' => true]
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on Twig_Extension_Debug / twig_var_dump
|
||||
*
|
||||
* @param Environment $env
|
||||
* @param $context
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function dump(Environment $env, $context)
|
||||
{
|
||||
if (!$env->isDebug()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$output = '';
|
||||
|
||||
$count = func_num_args();
|
||||
if (2 === $count) {
|
||||
$data = [];
|
||||
foreach ($context as $key => $value) {
|
||||
if (is_object($value)) {
|
||||
if (method_exists($value, 'toArray')) {
|
||||
$data[$key] = $value->toArray();
|
||||
} else {
|
||||
$data[$key] = "Object (" . get_class($value) . ")";
|
||||
}
|
||||
} else {
|
||||
$data[$key] = $value;
|
||||
}
|
||||
}
|
||||
$output .= $this->formatVar($data);
|
||||
} else {
|
||||
for ($i = 2; $i < $count; $i++) {
|
||||
$output .= $this->formatVar(func_get_arg($i));
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->isHtmlVarDumperUsed()) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
return '<pre>' . $output . '</pre>';
|
||||
}
|
||||
}
|
77
src/DebugBar/Bridge/Twig/MeasureTwigExtension.php
Normal file
77
src/DebugBar/Bridge/Twig/MeasureTwigExtension.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace DebugBar\Bridge\Twig;
|
||||
|
||||
use Twig\Extension\AbstractExtension;
|
||||
|
||||
/**
|
||||
* Access debugbar timeline measure in your Twig templates.
|
||||
* Based on Symfony\Bridge\Twig\Extension\StopwatchExtension
|
||||
*
|
||||
* @package DebugBar\Bridge\Twig
|
||||
*/
|
||||
class MeasureTwigExtension extends AbstractExtension
|
||||
{
|
||||
/**
|
||||
* @var \DebugBar\DataCollector\TimeDataCollector|null
|
||||
*/
|
||||
protected $timeCollector;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $tagName;
|
||||
|
||||
/**
|
||||
* Create a new auth extension.
|
||||
*
|
||||
* @param \DebugBar\DataCollector\TimeDataCollector|null $debugbar
|
||||
* @param string $tagName
|
||||
*/
|
||||
public function __construct($timeCollector, $tagName = 'measure')
|
||||
{
|
||||
$this->timeCollector = $timeCollector;
|
||||
$this->tagName = $tagName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return static::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Twig\TokenParser\TokenParserInterface[]
|
||||
*/
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return [
|
||||
/*
|
||||
* {% measure foo %}
|
||||
* Some stuff which will be recorded on the timeline
|
||||
* {% endmeasure %}
|
||||
*/
|
||||
new MeasureTwigTokenParser(!is_null($this->timeCollector), $this->tagName, $this->getName()),
|
||||
];
|
||||
}
|
||||
|
||||
public function startMeasure(...$arg)
|
||||
{
|
||||
if (!$this->timeCollector) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->timeCollector->startMeasure(...$arg);
|
||||
}
|
||||
|
||||
public function stopMeasure(...$arg)
|
||||
{
|
||||
if (!$this->timeCollector) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->timeCollector->stopMeasure(...$arg);
|
||||
}
|
||||
}
|
50
src/DebugBar/Bridge/Twig/MeasureTwigNode.php
Normal file
50
src/DebugBar/Bridge/Twig/MeasureTwigNode.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace DebugBar\Bridge\Twig;
|
||||
|
||||
use DebugBar\Bridge\Twig\MeasureTwigExtension;
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\AssignNameExpression;
|
||||
use Twig\Node\Node;
|
||||
|
||||
/**
|
||||
* Represents a measure node.
|
||||
* Based on Symfony\Bridge\Twig\Node\StopwatchNode
|
||||
*/
|
||||
class MeasureTwigNode extends Node
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $extName;
|
||||
|
||||
public function __construct(
|
||||
Node $name,
|
||||
$body,
|
||||
AssignNameExpression $var,
|
||||
$lineno = 0,
|
||||
$tag = null,
|
||||
$extName = null
|
||||
) {
|
||||
parent::__construct(['body' => $body, 'name' => $name, 'var' => $var], [], $lineno, $tag);
|
||||
$this->extName = $extName ?: MeasureTwigExtension::class;
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('')
|
||||
->subcompile($this->getNode('var'))
|
||||
->raw(' = ')
|
||||
->subcompile($this->getNode('name'))
|
||||
->write(";\n")
|
||||
->write("\$this->env->getExtension('".$this->extName."')->startMeasure(")
|
||||
->subcompile($this->getNode('var'))
|
||||
->raw(");\n")
|
||||
->subcompile($this->getNode('body'))
|
||||
->write("\$this->env->getExtension('".$this->extName."')->stopMeasure(")
|
||||
->subcompile($this->getNode('var'))
|
||||
->raw(");\n");
|
||||
}
|
||||
}
|
78
src/DebugBar/Bridge/Twig/MeasureTwigTokenParser.php
Normal file
78
src/DebugBar/Bridge/Twig/MeasureTwigTokenParser.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace DebugBar\Bridge\Twig;
|
||||
|
||||
use Twig\Node\Expression\AssignNameExpression;
|
||||
use Twig\Token;
|
||||
use Twig\TokenParser\AbstractTokenParser;
|
||||
|
||||
/**
|
||||
* Token Parser for the measure tag.
|
||||
* Based on Symfony\Bridge\Twig\TokenParser\StopwatchTokenParser;
|
||||
*/
|
||||
class MeasureTwigTokenParser extends AbstractTokenParser
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $extName;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $tagName;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $enabled;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $tagName
|
||||
*/
|
||||
public function __construct($enabled, $tagName, $extName = null)
|
||||
{
|
||||
$this->enabled = $enabled;
|
||||
$this->tagName = $tagName;
|
||||
$this->extName = $extName;
|
||||
}
|
||||
|
||||
public function parse(Token $token)
|
||||
{
|
||||
$lineno = $token->getLine();
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
// {% measure 'bar' %}
|
||||
$name = $this->parser->getExpressionParser()->parseExpression();
|
||||
|
||||
$stream->expect(Token::BLOCK_END_TYPE);
|
||||
|
||||
// {% endmeasure %}
|
||||
$body = $this->parser->subparse([$this, 'decideMeasureEnd'], true);
|
||||
$stream->expect(Token::BLOCK_END_TYPE);
|
||||
|
||||
if ($this->enabled) {
|
||||
return new MeasureTwigNode(
|
||||
$name,
|
||||
$body,
|
||||
new AssignNameExpression($this->parser->getVarName(), $token->getLine()),
|
||||
$lineno,
|
||||
$this->getTag(),
|
||||
$this->extName
|
||||
);
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
public function getTag()
|
||||
{
|
||||
return $this->tagName;
|
||||
}
|
||||
|
||||
public function decideMeasureEnd(Token $token)
|
||||
{
|
||||
return $token->test('end'.$this->getTag());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user