From daad64fa543103d966d9942cbed0a10f78046d6f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 15 Jan 2015 07:13:06 +0100 Subject: [PATCH] added a Twig panel to the WebProfiler --- composer.json | 8 +- .../Twig/DataCollector/TwigDataCollector.php | 143 ++++++++++++++++++ .../Twig/Extension/ProfilerExtension.php | 6 +- src/Symfony/Bridge/Twig/composer.json | 2 +- .../Compiler/ExtensionPass.php | 3 +- .../DependencyInjection/TwigExtension.php | 4 - .../TwigBundle/Resources/config/debug.xml | 16 -- .../TwigBundle/Resources/config/twig.xml | 9 +- .../Resources/views/Collector/twig.html.twig | 85 +++++++++++ .../Bundle/WebProfilerBundle/composer.json | 2 +- 10 files changed, 244 insertions(+), 34 deletions(-) create mode 100644 src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php delete mode 100644 src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml create mode 100644 src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/twig.html.twig diff --git a/composer.json b/composer.json index 106e7f112e..b123e37c78 100644 --- a/composer.json +++ b/composer.json @@ -1,10 +1,4 @@ { - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/fabpot/Twig" - } - ], "name": "symfony/symfony", "type": "library", "description": "The Symfony PHP framework", @@ -24,7 +18,7 @@ "require": { "php": ">=5.3.9", "doctrine/common": "~2.3", - "twig/twig": "dev-profiler as 1.17.x-dev", + "twig/twig": "~1.18", "psr/log": "~1.0" }, "replace": { diff --git a/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php b/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php new file mode 100644 index 0000000000..a291290c80 --- /dev/null +++ b/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Twig\DataCollector; + +use Symfony\Component\HttpKernel\DataCollector\DataCollector; +use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * TwigDataCollector. + * + * @author Fabien Potencier + */ +class TwigDataCollector extends DataCollector implements LateDataCollectorInterface +{ + private $profile; + private $computed; + + public function __construct(\Twig_Profiler_Profile $profile) + { + $this->profile = $profile; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + } + + /** + * {@inheritdoc} + */ + public function lateCollect() + { + $this->data['profile'] = serialize($this->profile); + } + + public function getTime() + { + return $this->getProfile()->getDuration() * 1000; + } + + public function getTemplateCount() + { + return $this->getComputedData('template_count'); + } + + public function getTemplates() + { + return $this->getComputedData('templates'); + } + + public function getBlockCount() + { + return $this->getComputedData('block_count'); + } + + public function getMacroCount() + { + return $this->getComputedData('macro_count'); + } + + public function getHtmlCallGraph() + { + $dumper = new \Twig_Profiler_Dumper_Html(); + + return new \Twig_Markup($dumper->dump($this->getProfile()), 'UTF-8'); + } + + public function getProfile() + { + if (null === $this->profile) { + $this->profile = unserialize($this->data['profile']); + } + + return $this->profile; + } + + private function getComputedData($index) + { + if (null === $this->computed) { + $this->computed = $this->computeData($this->getProfile()); + } + + return $this->computed['index']; + } + + private function computeData(\Twig_Profiler_Profile $profile) + { + $data = array( + 'template_count' => 0, + 'block_count' => 0, + 'macro_count' => 0, + ); + + $templates = array(); + foreach ($profile as $p) { + $d = $this->computeData($p); + + $data['template_count'] += ($p->isTemplate() ? 1 : 0) + $d['template_count']; + $data['block_count'] += ($p->isBlock() ? 1 : 0) + $d['block_count']; + $data['macro_count'] += ($p->isMacro() ? 1 : 0) + $d['macro_count']; + + if ($p->isTemplate()) { + if (!isset($templates[$p->getTemplate()])) { + $templates[$p->getTemplate()] = 1; + } else { + $templates[$p->getTemplate()]++; + } + } + + foreach ($d['templates'] as $template => $count) { + if (!isset($templates[$template])) { + $templates[$template] = $count; + } else { + $templates[$template] += $count; + } + } + } + $data['templates'] = $templates; + + return $data; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'twig'; + } +} diff --git a/src/Symfony/Bridge/Twig/Extension/ProfilerExtension.php b/src/Symfony/Bridge/Twig/Extension/ProfilerExtension.php index 2ec5fc189c..648a6c8036 100644 --- a/src/Symfony/Bridge/Twig/Extension/ProfilerExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/ProfilerExtension.php @@ -21,7 +21,7 @@ class ProfilerExtension extends \Twig_Extension_Profiler private $stopwatch; private $events; - public function __construct(\Twig_Profiler_Profile $profile, Stopwatch $stopwatch) + public function __construct(\Twig_Profiler_Profile $profile, Stopwatch $stopwatch = null) { parent::__construct($profile); @@ -31,7 +31,7 @@ class ProfilerExtension extends \Twig_Extension_Profiler public function enter(\Twig_Profiler_Profile $profile) { - if ($profile->isTemplate()) { + if ($this->stopwatch && $profile->isTemplate()) { $this->events[$profile] = $this->stopwatch->start($profile->getName(), 'template'); } @@ -42,7 +42,7 @@ class ProfilerExtension extends \Twig_Extension_Profiler { parent::leave($profile); - if ($profile->isTemplate()) { + if ($this->stopwatch && $profile->isTemplate()) { $this->events[$profile]->stop(); unset($this->events[$profile]); } diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 803642831f..c534b8f6ac 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -18,7 +18,7 @@ "require": { "php": ">=5.3.9", "symfony/security-csrf": "~2.6|~3.0.0", - "twig/twig": "~1.17" + "twig/twig": "~1.18" }, "require-dev": { "symfony/finder": "~2.3|~3.0.0", diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php index fa92bbd9f1..86f1f39ca0 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php @@ -63,8 +63,9 @@ class ExtensionPass implements CompilerPassInterface $container->getDefinition('twig.extension.code')->replaceArgument(0, $container->getParameter('templating.helper.code.file_link_format')); } - if ($container->getParameter('kernel.debug') && $container->has('debug.stopwatch')) { + if ($container->getParameter('kernel.debug')) { $container->getDefinition('twig.extension.profiler')->addTag('twig.extension'); + $container->getDefinition('twig.extension.debug')->addTag('twig.extension'); } if ($container->has('templating')) { diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php index c9595ff219..0a570a8d43 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php @@ -101,10 +101,6 @@ class TwigExtension extends Extension $config['extensions'] ); - if ($container->getParameter('kernel.debug')) { - $loader->load('debug.xml'); - } - if (isset($config['autoescape_service']) && isset($config['autoescape_service_method'])) { $config['autoescape'] = array(new Reference($config['autoescape_service']), $config['autoescape_service_method']); } diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml deleted file mode 100644 index 3f3ff3260d..0000000000 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - Symfony\Bundle\TwigBundle\Debug\TimedTwigEngine - - - - - - - - diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml index 28b7483180..96cfea52c5 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml @@ -72,11 +72,16 @@ - + + + + + + @@ -128,6 +133,8 @@ + + %twig.form.resources% diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/twig.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/twig.html.twig new file mode 100644 index 0000000000..0ecd469bd0 --- /dev/null +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/twig.html.twig @@ -0,0 +1,85 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block toolbar %} + {% set time = collector.templatecount ? '%0.0f ms'|format(collector.time) : 'n/a' %} + {% set icon %} + Twig + {{ time }} + {% endset %} + {% set text %} +
+ Render Time + {{ time }} +
+
+ Template Calls + {{ collector.templatecount }} +
+
+ Block Calls + {{ collector.blockcount }} +
+
+ Macro Calls + {{ collector.macrocount }} +
+ {% endset %} + {% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url } %} +{% endblock %} + +{% block menu %} + + Twig + Twig + + {{ collector.templatecount }} + {{ '%0.0f ms'|format(collector.time) }} + + +{% endblock %} + +{% block panel %} + {% if collector.templatecount %} +

Twig Stats

+ + + + + + + + + + + + + + + + + + +
Total Render Time
including sub-requests rendering time
{{ '%0.0f ms'|format(collector.time) }}
Template Calls
{{ collector.templatecount }}
Block Calls
{{ collector.blockcount }}
Macro Calls
{{ collector.macrocount }}
+ +

Rendered Templates

+ + + + + + + {% for template, count in collector.templates %} + + + + + {% endfor %} +
Template NameRender Count
{{ template }}
{{ count }}
+ +

Rendering Call Graph

+ + {{ collector.htmlcallgraph }} + {% else %} +

No Twig templates were rendered for this request.

+ {% endif %} +{% endblock %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/composer.json b/src/Symfony/Bundle/WebProfilerBundle/composer.json index c1ab33973c..5c2051d430 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/composer.json +++ b/src/Symfony/Bundle/WebProfilerBundle/composer.json @@ -19,7 +19,7 @@ "php": ">=5.3.9", "symfony/http-kernel": "~2.4|~3.0.0", "symfony/routing": "~2.2|~3.0.0", - "symfony/twig-bridge": "~2.2|~3.0.0" + "symfony/twig-bridge": "~2.7|~3.0.0" }, "require-dev": { "symfony/config": "~2.2|~3.0.0",