Skip to content

Commit

Permalink
Draft: Support twig namespaces (#479)
Browse files Browse the repository at this point in the history
* 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 <barryvdh@gmail.com>
  • Loading branch information
xvilo and barryvdh committed Dec 12, 2021
1 parent e8ac349 commit 14bdc78
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 12 deletions.
3 changes: 2 additions & 1 deletion composer.json
Expand Up @@ -22,7 +22,8 @@
"symfony/var-dumper": "^2.6|^3|^4|^5"
},
"require-dev": {
"phpunit/phpunit": "^7.5.20 || ^9.4.2"
"phpunit/phpunit": "^7.5.20 || ^9.4.2",
"twig/twig": "^1.38|^2.7|^3.0"
},
"autoload": {
"psr-4": {
Expand Down
46 changes: 36 additions & 10 deletions docs/bridge_collectors.md
Expand Up @@ -90,22 +90,48 @@ Display log messages and sent mail using `DebugBar\Bridge\SwiftMailer\SwiftLogCo

http://twig.sensiolabs.org/

### Version 1 and 2

This collector uses the class `Twig_Extension_Profiler` to collect info about rendered
templates, blocks and macros.
You need to inject the root `Twig_Profiler_Profile` into the collector:

$loader = new Twig_Loader_Filesystem('.');
$env = new Twig_Environment($loader);
$profile = new Twig_Profiler_Profile();
$env->addExtension(new Twig_Extension_Profiler($profile));
$debugbar->addCollector(new DebugBar\Bridge\TwigProfileCollector($profile));
```php
$loader = new Twig_Loader_Filesystem('.');
$env = new Twig_Environment($loader);
$profile = new Twig_Profiler_Profile();
$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.

$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));
```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
templates, blocks and macros.
You need to inject the root `Twig\Profiler\Profile` into the collector:

```php
use DebugBar\Bridge\NamespacedTwigProfileCollector;
use Twig\Environment;
use Twig\Extension\ProfilerExtension;
use Twig\Loader\FilesystemLoader;
use Twig\Profiler\Profile;

$loader = new FilesystemLoader('.');
$env = new Environment($loader);
$profile = new Profile();
$env->addExtension(new ProfilerExtension($profile));
$debugbar->addCollector(new NamespacedTwigProfileCollector($profile));
```

204 changes: 204 additions & 0 deletions src/DebugBar/Bridge/NamespacedTwigProfileCollector.php
@@ -0,0 +1,204 @@
<?php

declare(strict_types=1);

namespace DebugBar\Bridge;

use DebugBar\DataCollector\AssetProvider;
use DebugBar\DataCollector\DataCollector;
use DebugBar\DataCollector\Renderable;
use Twig\Environment;
use Twig\Loader\LoaderInterface;
use Twig\Profiler\Dumper\HtmlDumper;
use Twig\Profiler\Profile;

/**
* Collects data about rendered templates
*
* http://twig.sensiolabs.org/
*
* A \Twig\Profiler\Profile should be added to your \Twig\Environment
* The root-Twig\Profiler\Profile-object should then be injected into this collector
*
* you can optionally provide the \Twig\Environment or the \Twig\Loader to also create
* debug-links.
*
* @see \Twig\Extension\ProfilerExtension, \Twig\Profiler\Profile
*
* <code>
* $env = new \Twig\Environment($loader); // Or from a PSR11-container
* $profile = new \Twig\Profiler\Profile();
* $env->addExtension(new \Twig\Extension\ProfilerExtension($profile));
* $debugbar->addCollector(new ModernTwigProfileCollector($profile, $env));
* // or: $debugbar->addCollector(new ModernTwigProfileCollector($profile, $loader));
* </code>
*/
class NamespacedTwigProfileCollector extends DataCollector implements Renderable, AssetProvider
{
/**
* @var Profile
*/
private $profile;

/**
* @var LoaderInterface|Environment|null
*/
private $loader;

/**
* @var int
*/
private $templateCount;

/**
* @var int
*/
private $blockCount;

/**
* @var int
*/
private $macroCount;
/**
* @var array[] {
* @var string $name
* @var int $render_time
* @var string $render_time_str
* @var string $memory_str
* @var string $xdebug_link
* }
*/
private $templates;

/**
* TwigProfileCollector constructor.
*
* @param Profile $profile
* @param LoaderInterface|Environment $loaderOrEnv
*/
public function __construct(Profile $profile, $loaderOrEnv = null)
{
$this->profile = $profile;
if ($loaderOrEnv instanceof Environment) {
$loaderOrEnv = $loaderOrEnv->getLoader();
}
$this->loader = $loaderOrEnv;
}

/**
* Returns a hash where keys are control names and their values
* an array of options as defined in {@see \DebugBar\JavascriptRenderer::addControl()}
*
* @return array
*/
public function getWidgets()
{
return [
'twig' => [
'icon' => 'leaf',
'widget' => 'PhpDebugBar.Widgets.TemplatesWidget',
'map' => 'twig',
'default' => json_encode(['templates' => []]),
],
'twig:badge' => [
'map' => 'twig.badge',
'default' => 0,
],
];
}

/**
* @return array
*/
public function getAssets()
{
return [
'css' => 'widgets/templates/widget.css',
'js' => 'widgets/templates/widget.js',
];
}

/**
* Called by the DebugBar when data needs to be collected
*
* @return array Collected data
*/
public function collect()
{
$this->templateCount = $this->blockCount = $this->macroCount = 0;
$this->templates = [];
$this->computeData($this->profile);

return [
'nb_templates' => $this->templateCount,
'nb_blocks' => $this->blockCount,
'nb_macros' => $this->macroCount,
'templates' => $this->templates,
'accumulated_render_time' => $this->profile->getDuration(),
'accumulated_render_time_str' => $this->getDataFormatter()->formatDuration($this->profile->getDuration()),
'memory_usage_str' => $this->getDataFormatter()->formatBytes($this->profile->getMemoryUsage()),
'callgraph' => $this->getHtmlCallGraph(),
'badge' => implode(
'/',
[
$this->templateCount,
$this->blockCount,
$this->macroCount,
]
),
];
}

/**
* Returns the unique name of the collector
*
* @return string
*/
public function getName()
{
return 'twig';
}

public function getHtmlCallGraph()
{
$dumper = new HtmlDumper();
return $dumper->dump($this->profile);
}

/**
* Get an Xdebug Link to a file
*
* @return array {
* @var string url
* @var bool ajax
* }
*/
public function getXdebugLink($template, $line = 1)
{
if (is_null($this->loader)) {
return null;
}
$file = $this->loader->getSourceContext($template)->getPath();

return parent::getXdebugLink($file, $line);
}

private function computeData(Profile $profile)
{
$this->templateCount += ($profile->isTemplate() ? 1 : 0);
$this->blockCount += ($profile->isBlock() ? 1 : 0);
$this->macroCount += ($profile->isMacro() ? 1 : 0);
if ($profile->isTemplate()) {
$this->templates[] = [
'name' => $profile->getName(),
'render_time' => $profile->getDuration(),
'render_time_str' => $this->getDataFormatter()->formatDuration($profile->getDuration()),
'memory_str' => $this->getDataFormatter()->formatBytes($profile->getMemoryUsage()),
'xdebug_link' => $this->getXdebugLink($profile->getTemplate()),
];
}
foreach ($profile as $p) {
$this->computeData($p);
}
}
}
2 changes: 1 addition & 1 deletion src/DebugBar/Bridge/Twig/TimeableTwigExtensionProfiler.php
Expand Up @@ -57,4 +57,4 @@ public function leave(Twig_Profiler_Profile $profile)
$this->timeDataCollector->stopMeasure($profile->getName());
}
}
}
}
2 changes: 2 additions & 0 deletions src/DebugBar/Bridge/TwigProfileCollector.php
Expand Up @@ -34,6 +34,8 @@
* $debugbar->addCollector(new TwigProfileCollector($profile, $env));
* // or: $debugbar->addCollector(new TwigProfileCollector($profile, $loader));
* </code>
*
* @deprecated Use `\Debugbar\Bridge\NamespacedTwigProfileCollector` instead for Twig 2.x and 3.x
*/
class TwigProfileCollector extends DataCollector implements Renderable, AssetProvider
{
Expand Down

0 comments on commit 14bdc78

Please sign in to comment.