-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
VarDumper and DebugBundle #10640
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
VarDumper and DebugBundle #10640
Changes from all commits
eec5c92
4bf9300
07135a0
5b7ae28
3ddbf4b
c91bc83
da3e50a
0a92c08
c426d8b
0266072
1d5e3f4
fa81544
e6dde33
5eaa187
a69e962
297d373
9dea601
eb98c81
8d5d970
c8746a4
0d8a942
081363c
49f13c6
de05cd9
e4e00ef
5f59811
a8d81e4
d43ae82
0f8d30f
2e167ba
80fd736
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
<?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\Extension; | ||
|
||
use Symfony\Bridge\Twig\TokenParser\DumpTokenParser; | ||
use Symfony\Component\VarDumper\Cloner\ClonerInterface; | ||
use Symfony\Component\VarDumper\Dumper\HtmlDumper; | ||
|
||
/** | ||
* Provides integration of the dump() function with Twig. | ||
* | ||
* @author Nicolas Grekas <p@tchwork.com> | ||
*/ | ||
class DumpExtension extends \Twig_Extension | ||
{ | ||
public function __construct(ClonerInterface $cloner = null) | ||
{ | ||
$this->cloner = $cloner; | ||
} | ||
|
||
public function getFunctions() | ||
{ | ||
return array( | ||
new \Twig_SimpleFunction('dump', array($this, 'dump'), array('is_safe' => array('html'), 'needs_context' => true, 'needs_environment' => true)), | ||
); | ||
} | ||
|
||
public function getTokenParsers() | ||
{ | ||
return array(new DumpTokenParser()); | ||
} | ||
|
||
public function getName() | ||
{ | ||
return 'dump'; | ||
} | ||
|
||
public function dump(\Twig_Environment $env, $context) | ||
{ | ||
if (!$env->isDebug() || !$this->cloner) { | ||
return; | ||
} | ||
|
||
if (2 === func_num_args()) { | ||
$vars = array(); | ||
foreach ($context as $key => $value) { | ||
if (!$value instanceof \Twig_Template) { | ||
$vars[$key] = $value; | ||
} | ||
} | ||
|
||
$vars = array($vars); | ||
} else { | ||
$vars = func_get_args(); | ||
unset($vars[0], $vars[1]); | ||
} | ||
|
||
$html = ''; | ||
$dumper = new HtmlDumper(function ($line, $depth) use (&$html) { | ||
if (-1 !== $depth) { | ||
$html .= str_repeat(' ', $depth).$line."\n"; | ||
} | ||
}); | ||
|
||
foreach ($vars as $value) { | ||
$dumper->dump($this->cloner->cloneVar($value)); | ||
} | ||
|
||
return $html; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
<?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\Node; | ||
|
||
/** | ||
* @author Julien Galenski <julien.galenski@gmail.com> | ||
*/ | ||
class DumpNode extends \Twig_Node | ||
{ | ||
private $varPrefix; | ||
|
||
public function __construct($varPrefix, \Twig_NodeInterface $values = null, $lineno, $tag = null) | ||
{ | ||
parent::__construct(array('values' => $values), array(), $lineno, $tag); | ||
$this->varPrefix = $varPrefix; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function compile(\Twig_Compiler $compiler) | ||
{ | ||
$compiler | ||
->write("if (\$this->env->isDebug()) {\n") | ||
->indent(); | ||
|
||
$values = $this->getNode('values'); | ||
|
||
if (null === $values) { | ||
// remove embedded templates (macros) from the context | ||
$compiler | ||
->write(sprintf('$%svars = array();'."\n", $this->varPrefix)) | ||
->write(sprintf('foreach ($context as $%1$skey => $%1$sval) {'."\n", $this->varPrefix)) | ||
->indent() | ||
->write(sprintf('if (!$%sval instanceof \Twig_Template) {'."\n", $this->varPrefix)) | ||
->indent() | ||
->write(sprintf('$%1$svars[$%1$skey] = $%1$sval;'."\n", $this->varPrefix)) | ||
->outdent() | ||
->write("}\n") | ||
->outdent() | ||
->write("}\n") | ||
->addDebugInfo($this) | ||
->write(sprintf('\Symfony\Component\VarDumper\VarDumper::dump($%svars);'."\n", $this->varPrefix)); | ||
} elseif (1 === $values->count()) { | ||
$compiler | ||
->addDebugInfo($this) | ||
->write('\Symfony\Component\VarDumper\VarDumper::dump(') | ||
->subcompile($values- F987 >getNode(0)) | ||
->raw(");\n"); | ||
} else { | ||
$compiler | ||
->addDebugInfo($this) | ||
->write('\Symfony\Component\VarDumper\VarDumper::dump(array('."\n") | ||
->indent(); | ||
foreach ($values as $node) { | ||
$compiler->addIndentation(); | ||
if ($node->hasAttribute('name')) { | ||
$compiler | ||
->string($node->getAttribute('name')) | ||
->raw(' => '); | ||
} | ||
$compiler | ||
->subcompile($node) | ||
->raw(",\n"); | ||
} | ||
$compiler | ||
->outdent() | ||
->write("));\n"); | ||
} | ||
|
||
$compiler | ||
->outdent() | ||
->raw("}\n"); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
<?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\Tests\Extension; | ||
|
||
use Symfony\Bridge\Twig\Extension\DumpExtension; | ||
use Symfony\Component\VarDumper\VarDumper; | ||
use Symfony\Component\VarDumper\Cloner\PhpCloner; | ||
|
||
class DumpExtensionTest extends \PHPUnit_Framework_TestCase | ||
{ | ||
/** | ||
* @dataProvider getDumpTags | ||
*/ | ||
public function testDumpTag($template, $debug, $expectedOutput, $expectedDumped) | ||
{ | ||
$extension = new DumpExtension(new PhpCloner()); | ||
$twig = new \Twig_Environment(new \Twig_Loader_String(), array( | ||
'debug' => $debug, | ||
'cache' => false, | ||
'optimizations' => 0, | ||
)); | ||
$twig->addExtension($extension); | ||
|
||
$dumped = null; | ||
$exception = null; | ||
$prevDumper = VarDumper::setHandler(function ($var) use (&$dumped) {$dumped = $var;}); | ||
|
||
try { | ||
$this->assertEquals($expectedOutput, $twig->render($template)); | ||
} catch (\Exception $exception) { | ||
} | ||
|
||
VarDumper::setHandler($prevDumper); | ||
|
||
if (null !== $exception) { | ||
throw $exception; | ||
} | ||
|
||
$this->assertSame($expectedDumped, $dumped); | ||
} | ||
|
||
public function getDumpTags() | ||
{ | ||
return array( | ||
array('A{% dump %}B', true, 'AB', array()), | ||
array('A{% set foo="bar"%}B{% dump %}C', true, 'ABC', array('foo' => 'bar')), | ||
array('A{% dump %}B', false, 'AB', null), | ||
); | ||
} | ||
|
||
/** | ||
* @dataProvider getDumpArgs | ||
*/ | ||
public function testDump($context, $args, $expectedOutput, $debug = true) | ||
{ | ||
$extension = new DumpExtension(new PhpCloner()); | ||
$twig = new \Twig_Environment(new \Twig_Loader_String(), array( | ||
'debug' => $debug, | ||
'cache' =& 1241 gt; false, | ||
'optimizations' => 0, | ||
)); | ||
|
||
array_unshift($args, $context); | ||
array_unshift($args, $twig); | ||
|
||
$dump = call_user_func_array(array($extension, 'dump'), $args); | ||
|
||
if ($debug) { | ||
$this->assertStringStartsWith('<script>', $dump); | ||
$dump = preg_replace('/^.*?<pre/', '<pre', $dump); | ||
} | ||
$this->assertEquals($expectedOutput, $dump); | ||
} | ||
|
||
public function getDumpArgs() | ||
{ | ||
return array( | ||
array(array(), array(), '', false), | ||
array(array(), array(), "<pre id=sf-dump><span class=sf-dump-0>[]\n</span></pre><script>Sfjs.dump.instrument()</script>\n"), | ||
array( | ||
array(), | ||
array(123, 456), | ||
"<pre id=sf-dump><span class=sf-dump-0><span class=sf-dump-num>123</span>\n</span></pre><script>Sfjs.dump.instrument()</script>\n" | ||
."<pre id=sf-dump><span class=sf-dump-0><span class=sf-dump-num>456</span>\n</span></pre><script>Sfjs.dump.instrument()</script>\n", | ||
), | ||
array( | ||
array('foo' => 'bar'), | ||
array(), | ||
"<pre id=sf-dump><span class=sf-dump-0><span class=sf-dump-note>array:1</span> [<span name=sf-dump-child>\n" | ||
." <span class=sf-dump-1>\"<span class=sf-dump-meta>foo</span>\" => \"<span class=sf-dump-str>bar</span>\"\n" | ||
."</span></span>]\n" | ||
."</span></pre><script>Sfjs.dump.instrument()</script>\n", | ||
), | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
<?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\Tests\Node; | ||
|
||
use Symfony\Bridge\Twig\Node\DumpNode; | ||
|
||
class DumpNodeTest extends \PHPUnit_Framework_TestCase | ||
{ | ||
public function testNoVar() | ||
{ | ||
$node = new DumpNode('bar', null, 7); | ||
|
||
$env = new \Twig_Environment(); | ||
$compiler = new \Twig_Compiler($env); | ||
|
||
$expected = <<<'EOTXT' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why this is quoted, just curious, thanks in advance. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's the NOWDOC php syntax There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and the goal of using NOWDOC is to avoid having to escape NOWDOC vs HEREDOC is similar too single-quotes vs double-quotes for strings (not exactly though) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah what throws me off is that the closing EOTXT was not quoted also, i would expect it to be. Reference http://php.net/manual/en/language.types.string.php#language.types.string.syntax.single |
||
if ($this->env->isDebug()) { | ||
$barvars = array(); | ||
foreach ($context as $barkey => $barval) { | ||
if (!$barval instanceof \Twig_Template) { | ||
$barvars[$barkey] = $barval; | ||
} | ||
} | ||
// line 7 | ||
\Symfony\Component\VarDumper\VarDumper::dump($barvars); | ||
} | ||
|
||
EOTXT; | ||
|
||
$this->assertSame($expected, $compiler->compile($node)->getSource()); | ||
} | ||
|
||
public function testOneVar() | ||
{ | ||
$vars = new \Twig_Node(array( | ||
new \Twig_Node_Expression_Name('foo', 7), | ||
)); | ||
$node = new DumpNode('bar', $vars, 7); | ||
|
||
$env = new \Twig_Environment(); | ||
$compiler = new \Twig_Compiler($env); | ||
|
||
$expected = <<<'EOTXT' | ||
if ($this->env->isDebug()) { | ||
// line 7 | ||
\Symfony\Component\VarDumper\VarDumper::dump(%foo%); | ||
} | ||
|
||
EOTXT; | ||
$expected = preg_replace('/%(.*?)%/', version_compare(PHP_VERSION, '5.4.0') >= 0 ? '(isset($context["$1"]) ? $context["$1"] : null)' : '$this->getContext($context, "$1")', $expected); | ||
|
||
$this->assertSame($expected, $compiler->compile($node)->getSource()); | ||
} | ||
|
||
public function testMultiVars() | ||
{ | ||
$vars = new \Twig_Node(array( | ||
new \Twig_Node_Expression_Name('foo', 7), | ||
new \Twig_Node_Expression_Name('bar', 7), | ||
)); | ||
$node = new DumpNode('bar', $vars, 7); | ||
|
||
$env = new \Twig_Environment(); | ||
$compiler = new \Twig_Compiler($env); | ||
|
||
$expected = <<<'EOTXT' | ||
if ($this->env->isDebug()) { | ||
// line 7 | ||
\Symfony\Component\VarDumper\VarDumper::dump(array( | ||
"foo" => %foo%, | ||
"bar" => %bar%, | ||
)); | ||
} | ||
|
||
EOTXT; | ||
$expected = preg_replace('/%(.*?)%/', version_compare(PHP_VERSION, '5.4.0') >= 0 ? '(isset($context["$1"]) ? $context["$1"] : null)' : '$this->getContext($context, "$1")', $expected); | ||
|
||
$this->assertSame($expected, $compiler->compile($node)->getSource()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey
$lineno
without default value after an optional argument?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default value for
$values
is required. Otherwise, you wouldn't be able to passnull
as an argument because of the type hint.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's ok, but mandatory parameter should not follow an optional (not php error but...). Twig_Node has $lineno = 0 as default, so this should have too :) Nothing big, just noticed.