added a Twig panel to the WebProfiler

This commit is contained in:
Fabien Potencier 2015-01-15 07:13:06 +01:00
parent ef0c9679cb
commit daad64fa54
10 changed files with 244 additions and 34 deletions

View File

@ -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": {

View File

@ -0,0 +1,143 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 <fabien@symfony.com>
*/
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';
}
}

View File

@ -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]);
}

View File

@ -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",

View File

@ -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')) {

View File

@ -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']);
}

View File

@ -1,16 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="debug.templating.engine.twig.class">Symfony\Bundle\TwigBundle\Debug\TimedTwigEngine</parameter>
</parameters>
<services>
<service id="twig.extension.debug" class="Twig_Extension_Debug" public="false">
<tag name="twig.extension" />
</service>
</services>
</container>

View File

@ -72,11 +72,16 @@
<service id="twig.extension.profiler" class="Symfony\Bridge\Twig\Extension\ProfilerExtension" public="false">
<argument type="service" id="twig.profile" />
<argument type="service" id="debug.stopwatch" on-invalid="ignore" />
<argument type="service" id="debug.stopwatch" on-invalid="null" />
</service>
<service id="twig.profile" class="Twig_Profiler_Profile" />
<service id="data_collector.twig" class="Symfony\Bridge\Twig\DataCollector\TwigDataCollector" public="false">
<tag name="data_collector" template="@WebProfiler/Collector/twig.html.twig" id="twig" priority="255" />
<argument type="service" id="twig.profile" />
</service>
<service id="twig.extension.trans" class="%twig.extension.trans.class%" public="false">
<argument type="service" id="translator" />
</service>
@ -128,6 +133,8 @@
<argument type="service" id="twig.form.renderer" />
</service>
<service id="twig.extension.debug" class="Twig_Extension_Debug" public="false" />
<service id="twig.form.engine" class="%twig.form.engine.class%" public="false">
<argument>%twig.form.resources%</argument>
</service>

View File

@ -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 %}
<img height="28" alt="Twig" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAcCAYAAACOGPReAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAbElEQVRIx2NgGAXUBowMDAwMaWlp/6ll4KxZsxhZYJy0tDRqGMjAwMDAwEQL77OgCxSXlJBsSG9PDwqfJi6lj/fRvTJ4XYocUTBXE4q8oRtRRBnKwsw8RFw6fA0lKkd1dnYOIpfCCthRMIIAAI0IFu9Hxh7ZAAAAAElFTkSuQmCC" />
<span class="sf-toolbar-status">{{ time }}</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Render Time</b>
<span>{{ time }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Template Calls</b>
<span>{{ collector.templatecount }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Block Calls</b>
<span>{{ collector.blockcount }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Macro Calls</b>
<span>{{ collector.macrocount }}</span>
</div>
{% endset %}
{% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url } %}
{% endblock %}
{% block menu %}
<span class="label">
<span class="icon"><img alt="Twig" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAgCAYAAAABtRhCAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAbklEQVRIx2NgGAVDHTDCGGlpaf9pZcmsWbPg9rAgS6SlpdHCMhQ+E72DlAWbYHFJCcUG9/b0YBWnuw9HLaRPosEV4cPHh9iyBczXxGaZ0WxBfBwwM4/mw1ELRy0c4MK7s7NzCPsQvYU1CkYBNgAAV5UW+fU+ZL4AAAAASUVORK5CYII="></span>
<strong>Twig</strong>
<span class="count">
<span>{{ collector.templatecount }}</span>
<span>{{ '%0.0f ms'|format(collector.time) }}</span>
</span>
</span>
{% endblock %}
{% block panel %}
{% if collector.templatecount %}
<h2>Twig Stats</h2>
<table>
<tr>
<th>Total Render Time<br /><small>including sub-requests rendering time</small></th>
<td><pre>{{ '%0.0f ms'|format(collector.time) }}</pre></td>
</tr>
<tr>
<th scope="col" style="width: 30%">Template Calls</th>
<td scope="col" style="width: 60%"><pre>{{ collector.templatecount }}</pre></td>
</tr>
<tr>
<th>Block Calls</th>
<td><pre>{{ collector.blockcount }}</pre></td>
</tr>
<tr>
<th>Macro Calls</th>
<td><pre>{{ collector.macrocount }}</pre></td>
</tr>
</table>
<h2>Rendered Templates</h2>
<table>
<tr>
<th scope="col">Template Name</th>
<th scope="col">Render Count</th>
</tr>
{% for template, count in collector.templates %}
<tr>
<td><code>{{ template }}</code></td>
<td><pre>{{ count }}</pre></td>
</tr>
{% endfor %}
</table>
<h2>Rendering Call Graph</h2>
{{ collector.htmlcallgraph }}
{% else %}
<p><em>No Twig templates were rendered for this request.</em></p>
{% endif %}
{% endblock %}

View File

@ -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",