8000 merged branch fabpot/twig-stopwatch-helper (PR #8719) · symfony/symfony@6b32c36 · GitHub
[go: up one dir, main page]

Skip to content

Commit 6b32c36

Browse files
committed
merged branch fabpot/twig-stopwatch-helper (PR #8719)
This PR was merged into the master branch. Discussion ---------- [TwigBundle] Created stopwatch tag for profiling templates | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #7953 | License | MIT | Doc PR | symfony/symfony-docs#2630 This PR is the continuation of #7953 This PR adds a new tag to Twig which you can use to time parts of a template and see it in the timing tab of the profiler. Usage: ````jinja {% stopwatch foo %} ... some things that gets timed {% endstopwatch %} ```` Commits ------- 29a58e7 change the stopwatch argument to be 8000 any valid expression 4590974 removed code that prevents the stopwatch to work properly 2f67776 removed unneeded safeguard as it's already done during compilation bbad387 fixed CS f39ed57 Created stopwatch tag
2 parents e587fa8 + 29a58e7 commit 6b32c36

File tree

6 files changed

+250
-0
lines changed

6 files changed

+250
-0
lines changed

src/Symfony/Bridge/Twig/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
2.4.0
5+
-----
6+
7+
* added stopwatch tag to time templates with the WebProfilerBundle
8+
49
2.3.0
510
-----
611

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Twig\Extension;
13+
14+
use Symfony\Component\Stopwatch\Stopwatch;
15+
use Symfony\Bridge\Twig\TokenParser\StopwatchTokenParser;
16+
17+
/**
18+
* Twig extension for the stopwatch helper.
19+
*
20+
* @author Wouter J <wouter@wouterj.nl>
21+
*/
22+
class StopwatchExtension extends \Twig_Extension
23+
{
24+
private $stopwatch;
25+
26+
public function __construct(Stopwatch $stopwatch = null)
27+
{
28+
$this->stopwatch = $stopwatch;
29+
}
30+
31+
public function getStopwatch()
32+
{
33+
return $this->stopwatch;
34+
}
35+
36+
public function getTokenParsers()
37+
{
38+
return array(
39+
/*
40+
* {% stopwatch foo %}
41+
* Some stuff which will be recorded on the timeline
42+
* {% endstopwatch %}
43+
*/
44+
new StopwatchTokenParser($this->stopwatch !== null),
45+
);
46+
}
47+
48+
public function getName()
49+
{
50+
return 'stopwatch';
51+
}
52+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Twig\Node;
13+
14+
/**
15+
* Represents a stopwatch node.
16+
*
17+
* @author Wouter J <wouter@wouterj.nl>
18+
*/
19+
class StopwatchNode extends \Twig_Node
20+
{
21+
public function __construct(\Twig_NodeInterface $name, $body, \Twig_Node_Expression_AssignName $var, $lineno = 0, $tag = null)
22+
{
23+
parent::__construct(array('body' => $body, 'name' => $name, 'var' => $var), array(), $lineno, $tag);
24+
}
25+
26+
public function compile(\Twig_Compiler $compiler)
27+
{
28+
$compiler
29+
->addDebugInfo($this)
30+
->write('')
31+
->subcompile($this->getNode('var'))
32+
->raw(' = ')
33+
->subcompile($this->getNode('name'))
34+
->write(";\n")
35+
->write("\$this->env->getExtension('stopwatch')->getStopwatch()->start(")
36+
->subcompile($this->getNode('var'))
37+
->raw(", 'template');\n")
38+
->subcompile($this->getNode('body'))
39+
->write("\$this->env->getExtension('stopwatch')->getStopwatch()->stop(")
40+
->subcompile($this->getNode('var'))
41+
->raw(");\n")
42+
;
43+
}
44+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Twig\Tests\Extension;
13+
14+
use Symfony\Bridge\Twig\Extension\StopwatchExtension;
15+
use Symfony\Component\Stopwatch\Stopwatch;
16+
use Symfony\Bridge\Twig\Tests\TestCase;
17+
18+
class StopwatchExtensionTest extends TestCase
19+
{
20+
protected function setUp()
21+
{
22+
parent::setUp();
23+
24+
if (!class_exists('Symfony\Component\Stopwatch\Stopwatch')) {
25+
$this->markTestSkipped('The "Stopwatch" component is not available');
26+
}
27+
}
28+
29+
/**
30+
* @expectedException \Twig_Error_Syntax
31+
*/
32+
public function testFailIfStoppingWrongEvent()
33+
{
34+
$this->testTiming('{% stopwatch "foo" %}{% endstopwatch "bar" %}', array());
35+
}
36+
37+
/**
38+
* @dataProvider getTimingTemplates
39+
*/
40+
public function testTiming($template, $events)
41+
{
42+
$twig = new \Twig_Environment(new \Twig_Loader_String(), array('debug' => true, 'cache' => false, 'autoescape' => true, 'optimizations' => 0));
43+
$twig->addExtension(new StopwatchExtension($this->getStopwatch($events)));
44+
45+
try {
46+
$nodes = $twig->render($template);
47+
} catch (\Twig_Error_Runtime $e) {
48+
throw $e->getPrevious();
49+
}
50+
}
51+
52+
public function getTimingTemplates()
53+
{
54+
return array(
55+
array('{% stopwatch "foo" %}something{% endstopwatch %}', 'foo'),
56+
array('{% stopwatch "foo" %}symfony2 is fun{% endstopwatch %}{% stopwatch "bar" %}something{% endstopwatch %}', array('foo', 'bar')),
57+
array('{% set foo = "foo" %}{% stopwatch foo %}something{% endstopwatch %}', 'foo'),
58+
array('{% set foo = "foo" %}{% stopwatch foo %}something {% set foo = "bar" %}{% endstopwatch %}', 'foo'),
59+
array('{% stopwatch "foo.bar" %}something{% endstopwatch %}', 'foo.bar'),
60+
array('{% stopwatch "foo" %}something{% endstopwatch %}{% stopwatch "foo" %}something else{% endstopwatch %}', array('foo', 'foo')),
61+
);
62+
}
63+
64+
protected function getStopwatch($events = array())
65+
{
66+
$events = is_array($events) ? $events : array($events);
67+
$stopwatch = $this->getMock('Symfony\Component\Stopwatch\Stopwatch');
68+
69+
$i = -1;
70+
foreach ($events as $eventName) {
71+
$stopwatch->expects($this->at(++$i))
72+
->method('start')
73+
->with($this->equalTo($eventName), 'template')
74+
;
75+
$stopwatch->expects($this->at(++$i))
76+
->method('stop')
77+
->with($this->equalTo($eventName))
78+
;
79+
}
80+
81+
return $stopwatch;
82+
}
83+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Twig\TokenParser;
13+
14+
use Symfony\Bridge\Twig\Node\StopwatchNode;
15+
16+
/**
17+
* Token Parser for the stopwatch tag.
18+
*
19+
* @author Wouter J <wouter@wouterj.nl>
20+
*/
21+
class StopwatchTokenParser extends \Twig_TokenParser
22+
{
23+
protected $stopwatchIsAvailable;
24+
25+
public function __construct($stopwatchIsAvailable)
26+
{
27+
$this->stopwatchIsAvailable = $stopwatchIsAvailable;
28+
}
29+
30+
public function parse(\Twig_Token $token)
31+
{
32+
$lineno = $token->getLine();
33+
$stream = $this->parser->getStream();
34+
35+
// {% stopwatch 'bar' %}
36+
$name = $this->parser->getExpressionParser()->parseExpression();
37+
38+
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
39+
40+
// {% endstopwatch %}
41+
$body = $this->parser->subparse(array($this, 'decideStopwatchEnd'), true);
42+
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
43+
44+
if ($this->stopwatchIsAvailable) {
45+
return new StopwatchNode($name, $body, new \Twig_Node_Expression_AssignName($this->parser->getVarName(), $token->getLine()), $lineno, $this->getTag());
46+
}
47+
48+
return $body;
49+
}
50+
51+
public function decideStopwatchEnd(\Twig_Token $token)
52+
{
53+
return $token->test('endstopwatch');
54+
}
55+
56+
public function getTag()
57+
{
58+
return 'stopwatch';
59+
}
60+
}

src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<parameter key="twig.extension.yaml.class">Symfony\Bridge\Twig\Extension\YamlExtension</parameter>
1919
<parameter key="twig.extension.form.class">Symfony\Bridge\Twig\Extension\FormExtension</parameter>
2020
<parameter key="twig.extension.httpkernel.class">Symfony\Bridge\Twig\Extension\HttpKernelExtension</parameter>
21+
<parameter key="twig.extension.debug.stopwatch.class">Symfony\Bridge\Twig\Extension\StopwatchExtension</parameter>
2122
<parameter key="twig.form.engine.class">Symfony\Bridge\Twig\Form\TwigRendererEngine</parameter>
2223
<parameter key="twig.form.renderer.class">Symfony\Bridge\Twig\Form\TwigRenderer</parameter>
2324
<parameter key="twig.translation.extractor.class">Symfony\Bridge\Twig\Translation\TwigExtractor</parameter>
@@ -86,6 +87,11 @@
8687
<tag name="twig.extension" />
8788
</service>
8889

90+
<service id="twig.extension.debug.stopwatch" class="%twig.extension.debug.stopwatch.class%" public="false">
91+
<tag name="twig.extension" />
92+
<argument type="service" id="debug.stopwatch" on-invalid="ignore" />
93+
</service>
94+
8995
<service id="twig.extension.httpkernel" class="%twig.extension.httpkernel.class%" public="false">
9096
<argument type="service" id="fragment.handler" />
9197
</service>

0 commit comments

Comments
 (0)
0