-
-
Save wouterj/5521978 to your computer and use it in GitHub Desktop.
| <?php | |
| use Symfony\Component\Stopwatch\Stopwatch; | |
| class StopwatchExtension extends Twig_Extension | |
| { | |
| private $stopwatch; | |
| public function __construct(Stopwatch $stopwatch = null) | |
| { | |
| $this->stopwatch = $stopwatch; | |
| } | |
| public function getTokenParsers() | |
| { | |
| return array( | |
| /* | |
| * {% stopwatch foo %} | |
| * Some stuff which will be recorded on the timeline | |
| * {% endstopwatch %} | |
| */ | |
| new StopwatchTokenParser(), | |
| ); | |
| } | |
| public function getName() | |
| { | |
| return 'stopwatch'; | |
| } | |
| public function getStopwatch() | |
| { | |
| return $this->stopwatch; | |
| } | |
| public function startEvent($name) | |
| { | |
| null === $this->stopwatch ?: $this->stopwatch->start($name); | |
| } | |
| public function stopEvent($name) | |
| { | |
| null === $this->stopwatch ?: $this->stopwatch->stop($name); | |
| } | |
| } |
| <?php | |
| class StopwatchNode extends Twig_Node | |
| { | |
| public function __construct($name, $body, $lineno = 0, $tag = null) | |
| { | |
| parent::__construct(array('body' => $body), array('name' => $name), $lineno, $tag); | |
| } | |
| public function compile(Twig_Compiler $compiler) | |
| { | |
| $name = $this->getAttribute('name'); | |
| $compiler | |
| ->write('$this->env->getExtension(\'stopwatch\')->startEvent(\''.$name.'\');') | |
| ->subcompile($this->getNode('body')) | |
| ->write('$this->env->getExtension(\'stopwatch\')->stopEvent(\''.$name.'\');') | |
| ; | |
| } | |
| } |
| <?php | |
| class StopwatchTokenParser extends Twig_TokenParser | |
| { | |
| public function parse(Twig_Token $token) | |
| { | |
| $lineno = $token->getLine(); | |
| $stream = $this->parser->getStream(); | |
| // {% stopwatch bar %} | |
| $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue(); | |
| $stream->expect(Twig_Token::BLOCK_END_TYPE); | |
| // {% endstopwatch %} | |
| $body = $this->parser->subparse(array($this, 'decideStopwatchEnd'), true); | |
| $stream->expect(Twig_Token::BLOCK_END_TYPE); | |
| return new StopwatchNode($name, $body, $lineno, $this->getTag()); | |
| } | |
| public function decideStopwatchEnd(Twig_Token $token) | |
| { | |
| return $token->test('endstopwatch'); | |
| } | |
| public function getTag() | |
| { | |
| return 'stopwatch'; | |
| } | |
| } |
Hi Wouter and Lukas,
I saw this passing by on my Twitter timeline ;)
The code above would only run at compile time, but the interesting time here is runtime. A viable solution would require:
- A node visitor used to replace each RenderNode with a "TimedRenderNode" (or any other node that should be timed)
- This node should compile to something like
$this->env->getExtension('twig_stopwatch')->start($this->getName());
"parent::compile()"
$this->env->getExtension('twig_stopwatch')->stop($this->getName());
- Depending on the current container configuration, the start and stop methods of the twig_stopwatch extension do nothing, or record the duration.
If I can be of any help, just let me know!
Matthias
Hmm it doesnt seem to be executing the code enclosed by the stopwatch/endstopwatch. For your questions IMHO it should be in the WebProfilerBundle and in terms of the extension I guess:
<?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\Bundle\TwigBundle\Extension;
use Symfony\Bundle\TwigBundle\TokenParser\StopwatchTokenParser;
use Symfony\Component\Stopwatch\Stopwatch;
class StopWatchExtension extends \Twig_Extension
{
private $stopwatch;
public function __construct(Stopwatch $stopwatch = null)
{
$this->stopwatch = $stopwatch;
}
/**
* Returns the token parser instance to add to the existing list.
*
* @return array An array of Twig_TokenParser instances
*/
public function getTokenParsers()
{
return array(
/*
* {% stopwatch 'foo' %}
* Some stuff that will take same time that will be registered on the timeline
* {% endstopwatch %}
*/
new StopwatchTokenParser($this->stopwatch),
);
}
public function getName()
{
return 'stopwatch';
}
}
<service id="twig.extension.debug.stopwatch" class="%debug.templating.stopwatch.class%">
<argument type="service" id="debug.stopwatch" />
<tag name="twig.extension" />
</service>
Ah, sorry. That's a stupid mistake... Thank you guys!
And, @mathiasnoback thanks for your given solution. I'll do some research on it and tweet you when I need your help.
@lsmith77 thanks for your anwsers!
Very nice!
In my opinion the ternary operator should not be used to execute a command, but only to evaluate the value of an expression. So if ($this->stopwatch instanceof Stopwatch) { would be better.
In production you could use a node visitor to replace StopwatchNodes with their Body node, so you won't get any errors there.
@matthiasnoback, thank you! I've created a PR ( symfony/symfony#7953 ), I'll see what the stoffs say about the ternary operator (I like it to use it this way)
Hi Lukas,
I had 2 questions: