Skip to content

Commit 14bdc78

Browse files
xvilobarryvdh
andauthored
Draft: Support twig namespaces (php-debugbar#479)
* Add Twig 1, 2 and 3 as required dependency - Latest safe Twig 1 version - Latest safe Twig 2 version - Latest safe Twig 3 version * Add ext-json as a requirement * Add namespacedTwigProfileCollector * Deprecated old class * Update docs for PHP/Twig * Remove incompatibility * Update namespace * Update composer.json Co-authored-by: Barry vd. Heuvel <[email protected]>
1 parent e8ac349 commit 14bdc78

File tree

5 files changed

+245
-12
lines changed

5 files changed

+245
-12
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"symfony/var-dumper": "^2.6|^3|^4|^5"
2323
},
2424
"require-dev": {
25-
"phpunit/phpunit": "^7.5.20 || ^9.4.2"
25+
"phpunit/phpunit": "^7.5.20 || ^9.4.2",
26+
"twig/twig": "^1.38|^2.7|^3.0"
2627
},
2728
"autoload": {
2829
"psr-4": {

docs/bridge_collectors.md

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -90,22 +90,48 @@ Display log messages and sent mail using `DebugBar\Bridge\SwiftMailer\SwiftLogCo
9090

9191
http://twig.sensiolabs.org/
9292

93+
### Version 1 and 2
94+
9395
This collector uses the class `Twig_Extension_Profiler` to collect info about rendered
9496
templates, blocks and macros.
9597
You need to inject the root `Twig_Profiler_Profile` into the collector:
9698

97-
$loader = new Twig_Loader_Filesystem('.');
98-
$env = new Twig_Environment($loader);
99-
$profile = new Twig_Profiler_Profile();
100-
$env->addExtension(new Twig_Extension_Profiler($profile));
101-
$debugbar->addCollector(new DebugBar\Bridge\TwigProfileCollector($profile));
99+
```php
100+
$loader = new Twig_Loader_Filesystem('.');
101+
$env = new Twig_Environment($loader);
102+
$profile = new Twig_Profiler_Profile();
103+
$env->addExtension(new Twig_Extension_Profiler($profile));
104+
$debugbar->addCollector(new DebugBar\Bridge\TwigProfileCollector($profile));
105+
```
102106

103107
You can optionally use `DebugBar\Bridge\Twig\TimeableTwigExtensionProfiler` in place of
104108
`Twig_Extension_Profiler` so render operation can be measured.
105109

106-
$loader = new Twig_Loader_Filesystem('.');
107-
$env = new Twig_Environment($loader);
108-
$profile = new Twig_Profiler_Profile();
109-
$env->addExtension(new DebugBar\Bridge\Twig\TimeableTwigExtensionProfiler($profile, $debugbar['time']));
110-
$debugbar->addCollector(new DebugBar\Bridge\TwigProfileCollector($profile));
110+
```php
111+
$loader = new Twig_Loader_Filesystem('.');
112+
$env = new Twig_Environment($loader);
113+
$profile = new Twig_Profiler_Profile();
114+
$env->addExtension(new DebugBar\Bridge\Twig\TimeableTwigExtensionProfiler($profile, $debugbar['time']));
115+
$debugbar->addCollector(new DebugBar\Bridge\TwigProfileCollector($profile));
116+
```
117+
118+
### Version 2 and 3
119+
120+
This collector uses the class `Twig\Extension\ProfilerExtension` to collect info about rendered
121+
templates, blocks and macros.
122+
You need to inject the root `Twig\Profiler\Profile` into the collector:
123+
124+
```php
125+
use DebugBar\Bridge\NamespacedTwigProfileCollector;
126+
use Twig\Environment;
127+
use Twig\Extension\ProfilerExtension;
128+
use Twig\Loader\FilesystemLoader;
129+
use Twig\Profiler\Profile;
130+
131+
$loader = new FilesystemLoader('.');
132+
$env = new Environment($loader);
133+
$profile = new Profile();
134+
$env->addExtension(new ProfilerExtension($profile));
135+
$debugbar->addCollector(new NamespacedTwigProfileCollector($profile));
136+
```
111137

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DebugBar\Bridge;
6+
7+
use DebugBar\DataCollector\AssetProvider;
8+
use DebugBar\DataCollector\DataCollector;
9+
use DebugBar\DataCollector\Renderable;
10+
use Twig\Environment;
11+
use Twig\Loader\LoaderInterface;
12+
use Twig\Profiler\Dumper\HtmlDumper;
13+
use Twig\Profiler\Profile;
14+
15+
/**
16+
* Collects data about rendered templates
17+
*
18+
* http://twig.sensiolabs.org/
19+
*
20+
* A \Twig\Profiler\Profile should be added to your \Twig\Environment
21+
* The root-Twig\Profiler\Profile-object should then be injected into this collector
22+
*
23+
* you can optionally provide the \Twig\Environment or the \Twig\Loader to also create
24+
* debug-links.
25+
*
26+
* @see \Twig\Extension\ProfilerExtension, \Twig\Profiler\Profile
27+
*
28+
* <code>
29+
* $env = new \Twig\Environment($loader); // Or from a PSR11-container
30+
* $profile = new \Twig\Profiler\Profile();
31+
* $env->addExtension(new \Twig\Extension\ProfilerExtension($profile));
32+
* $debugbar->addCollector(new ModernTwigProfileCollector($profile, $env));
33+
* // or: $debugbar->addCollector(new ModernTwigProfileCollector($profile, $loader));
34+
* </code>
35+
*/
36+
class NamespacedTwigProfileCollector extends DataCollector implements Renderable, AssetProvider
37+
{
38+
/**
39+
* @var Profile
40+
*/
41+
private $profile;
42+
43+
/**
44+
* @var LoaderInterface|Environment|null
45+
*/
46+
private $loader;
47+
48+
/**
49+
* @var int
50+
*/
51+
private $templateCount;
52+
53+
/**
54+
* @var int
55+
*/
56+
private $blockCount;
57+
58+
/**
59+
* @var int
60+
*/
61+
private $macroCount;
62+
/**
63+
* @var array[] {
64+
* @var string $name
65+
* @var int $render_time
66+
* @var string $render_time_str
67+
* @var string $memory_str
68+
* @var string $xdebug_link
69+
* }
70+
*/
71+
private $templates;
72+
73+
/**
74+
* TwigProfileCollector constructor.
75+
*
76+
* @param Profile $profile
77+
* @param LoaderInterface|Environment $loaderOrEnv
78+
*/
79+
public function __construct(Profile $profile, $loaderOrEnv = null)
80+
{
81+
$this->profile = $profile;
82+
if ($loaderOrEnv instanceof Environment) {
83+
$loaderOrEnv = $loaderOrEnv->getLoader();
84+
}
85+
$this->loader = $loaderOrEnv;
86+
}
87+
88+
/**
89+
* Returns a hash where keys are control names and their values
90+
* an array of options as defined in {@see \DebugBar\JavascriptRenderer::addControl()}
91+
*
92+
* @return array
93+
*/
94+
public function getWidgets()
95+
{
96+
return [
97+
'twig' => [
98+
'icon' => 'leaf',
99+
'widget' => 'PhpDebugBar.Widgets.TemplatesWidget',
100+
'map' => 'twig',
101+
'default' => json_encode(['templates' => []]),
102+
],
103+
'twig:badge' => [
104+
'map' => 'twig.badge',
105+
'default' => 0,
106+
],
107+
];
108+
}
109+
110+
/**
111+
* @return array
112+
*/
113+
public function getAssets()
114+
{
115+
return [
116+
'css' => 'widgets/templates/widget.css',
117+
'js' => 'widgets/templates/widget.js',
118+
];
119+
}
120+
121+
/**
122+
* Called by the DebugBar when data needs to be collected
123+
*
124+
* @return array Collected data
125+
*/
126+
public function collect()
127+
{
128+
$this->templateCount = $this->blockCount = $this->macroCount = 0;
129+
$this->templates = [];
130+
$this->computeData($this->profile);
131+
132+
return [
133+
'nb_templates' => $this->templateCount,
134+
'nb_blocks' => $this->blockCount,
135+
'nb_macros' => $this->macroCount,
136+
'templates' => $this->templates,
137+
'accumulated_render_time' => $this->profile->getDuration(),
138+
'accumulated_render_time_str' => $this->getDataFormatter()->formatDuration($this->profile->getDuration()),
139+
'memory_usage_str' => $this->getDataFormatter()->formatBytes($this->profile->getMemoryUsage()),
140+
'callgraph' => $this->getHtmlCallGraph(),
141+
'badge' => implode(
142+
'/',
143+
[
144+
$this->templateCount,
145+
$this->blockCount,
146+
$this->macroCount,
147+
]
148+
),
149+
];
150+
}
151+
152+
/**
153+
* Returns the unique name of the collector
154+
*
155+
* @return string
156+
*/
157+
public function getName()
158+
{
159+
return 'twig';
160+
}
161+
162+
public function getHtmlCallGraph()
163+
{
164+
$dumper = new HtmlDumper();
165+
return $dumper->dump($this->profile);
166+
}
167+
168+
/**
169+
* Get an Xdebug Link to a file
170+
*
171+
* @return array {
172+
* @var string url
173+
* @var bool ajax
174+
* }
175+
*/
176+
public function getXdebugLink($template, $line = 1)
177+
{
178+
if (is_null($this->loader)) {
179+
return null;
180+
}
181+
$file = $this->loader->getSourceContext($template)->getPath();
182+
183+
return parent::getXdebugLink($file, $line);
184+
}
185+
186+
private function computeData(Profile $profile)
187+
{
188+
$this->templateCount += ($profile->isTemplate() ? 1 : 0);
189+
$this->blockCount += ($profile->isBlock() ? 1 : 0);
190+
$this->macroCount += ($profile->isMacro() ? 1 : 0);
191+
if ($profile->isTemplate()) {
192+
$this->templates[] = [
193+
'name' => $profile->getName(),
194+
'render_time' => $profile->getDuration(),
195+
'render_time_str' => $this->getDataFormatter()->formatDuration($profile->getDuration()),
196+
'memory_str' => $this->getDataFormatter()->formatBytes($profile->getMemoryUsage()),
197+
'xdebug_link' => $this->getXdebugLink($profile->getTemplate()),
198+
];
199+
}
200+
foreach ($profile as $p) {
201+
$this->computeData($p);
202+
}
203+
}
204+
}

src/DebugBar/Bridge/Twig/TimeableTwigExtensionProfiler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,4 @@ public function leave(Twig_Profiler_Profile $profile)
5757
$this->timeDataCollector->stopMeasure($profile->getName());
5858
}
5959
}
60-
}
60+
}

src/DebugBar/Bridge/TwigProfileCollector.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
* $debugbar->addCollector(new TwigProfileCollector($profile, $env));
3535
* // or: $debugbar->addCollector(new TwigProfileCollector($profile, $loader));
3636
* </code>
37+
*
38+
* @deprecated Use `\Debugbar\Bridge\NamespacedTwigProfileCollector` instead for Twig 2.x and 3.x
3739
*/
3840
class TwigProfileCollector extends DataCollector implements Renderable, AssetProvider
3941
{

0 commit comments

Comments
 (0)