... | ... |
@@ -22,6 +22,7 @@ class Twig_Compiler implements Twig_CompilerInterface |
22 | 22 |
protected $source; |
23 | 23 |
protected $indentation; |
24 | 24 |
protected $env; |
25 |
+ protected $debugInfo; |
|
25 | 26 |
|
26 | 27 |
/** |
27 | 28 |
* Constructor. |
... | ... |
@@ -31,6 +32,7 @@ class Twig_Compiler implements Twig_CompilerInterface |
31 | 32 |
public function __construct(Twig_Environment $env) |
32 | 33 |
{ |
33 | 34 |
$this->env = $env; |
35 |
+ $this->debugInfo = array(); |
|
34 | 36 |
} |
35 | 37 |
|
36 | 38 |
/** |
... | ... |
@@ -178,6 +180,8 @@ class Twig_Compiler implements Twig_CompilerInterface |
178 | 180 |
public function addDebugInfo(Twig_NodeInterface $node) |
179 | 181 |
{ |
180 | 182 |
if ($node->getLine() != $this->lastLine) { |
183 |
+ $this->debugInfo[substr_count($this->source, "\n")] = $node->getLine(); |
|
184 |
+ |
|
181 | 185 |
$this->lastLine = $node->getLine(); |
182 | 186 |
$this->write("// line {$node->getLine()}\n"); |
183 | 187 |
} |
... | ... |
@@ -185,6 +189,11 @@ class Twig_Compiler implements Twig_CompilerInterface |
185 | 189 |
return $this; |
186 | 190 |
} |
187 | 191 |
|
192 |
+ public function getDebugInfo() |
|
193 |
+ { |
|
194 |
+ return $this->debugInfo; |
|
195 |
+ } |
|
196 |
+ |
|
188 | 197 |
/** |
189 | 198 |
* Indents the generated code. |
190 | 199 |
* |
... | ... |
@@ -33,7 +33,15 @@ class Twig_Error extends Exception |
33 | 33 |
public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null) |
34 | 34 |
{ |
35 | 35 |
if (-1 === $lineno || null === $filename) { |
36 |
- list($lineno, $filename) = $this->findTemplateInfo(null !== $previous ? $previous : $this, $lineno, $filename); |
|
36 |
+ if ($trace = $this->getTemplateTrace()) { |
|
37 |
+ if (-1 === $lineno) { |
|
38 |
+ $lineno = $this->guessTemplateLine($trace); |
|
39 |
+ } |
|
40 |
+ |
|
41 |
+ if (null === $filename) { |
|
42 |
+ $filename = $trace['object']->getTemplateName(); |
|
43 |
+ } |
|
44 |
+ } |
|
37 | 45 |
} |
38 | 46 |
|
39 | 47 |
$this->lineno = $lineno; |
... | ... |
@@ -144,52 +152,23 @@ class Twig_Error extends Exception |
144 | 152 |
} |
145 | 153 |
} |
146 | 154 |
|
147 |
- protected function findTemplateInfo(Exception $e, $currentLine, $currentFile) |
|
155 |
+ protected function getTemplateTrace() |
|
148 | 156 |
{ |
149 |
- if (!function_exists('token_get_all')) { |
|
150 |
- return array($currentLine, $currentFile); |
|
151 |
- } |
|
152 |
- |
|
153 |
- $traces = $e->getTrace(); |
|
154 |
- foreach ($traces as $i => $trace) { |
|
155 |
- if (!isset($trace['class']) || 'Twig_Template' === $trace['class']) { |
|
156 |
- continue; |
|
157 |
- } |
|
158 |
- |
|
159 |
- $r = new ReflectionClass($trace['class']); |
|
160 |
- if (!$r->implementsInterface('Twig_TemplateInterface')) { |
|
161 |
- continue; |
|
162 |
- } |
|
163 |
- |
|
164 |
- if (!is_file($r->getFilename())) { |
|
165 |
- // probably an eval()'d code |
|
166 |
- return array($currentLine, $currentFile); |
|
157 |
+ foreach (debug_backtrace() as $trace) { |
|
158 |
+ if (isset($trace['object']) && $trace['object'] instanceof Twig_Template) { |
|
159 |
+ return $trace; |
|
167 | 160 |
} |
161 |
+ } |
|
162 |
+ } |
|
168 | 163 |
|
169 |
- if (0 === $i) { |
|
170 |
- $line = $e->getLine(); |
|
171 |
- } else { |
|
172 |
- $line = isset($traces[$i - 1]['line']) ? $traces[$i - 1]['line'] : -log(0); |
|
173 |
- } |
|
174 |
- |
|
175 |
- $tokens = token_get_all(file_get_contents($r->getFilename())); |
|
176 |
- $templateline = -1; |
|
177 |
- $template = null; |
|
178 |
- foreach ($tokens as $token) { |
|
179 |
- if (isset($token[2]) && $token[2] >= $line) { |
|
180 |
- return array($templateline, $template); |
|
181 |
- } |
|
182 |
- |
|
183 |
- if (T_COMMENT === $token[0] && null === $template && preg_match('#/\* +(.+) +\*/#', $token[1], $match)) { |
|
184 |
- $template = $match[1]; |
|
185 |
- } elseif (T_COMMENT === $token[0] && preg_match('#^//\s*line (\d+)\s*$#', $token[1], $match)) { |
|
186 |
- $templateline = $match[1]; |
|
187 |
- } |
|
164 |
+ protected function guessTemplateLine($trace) |
|
165 |
+ { |
|
166 |
+ foreach ($trace['object']->getDebugInfo() as $codeLine => $templateLine) { |
|
167 |
+ if ($codeLine <= $trace['line']) { |
|
168 |
+ return $templateLine; |
|
188 | 169 |
} |
189 |
- |
|
190 |
- return array($currentLine, $template); |
|
191 | 170 |
} |
192 | 171 |
|
193 |
- return array($currentLine, $currentFile); |
|
172 |
+ return -1; |
|
194 | 173 |
} |
195 | 174 |
} |
... | ... |
@@ -57,6 +57,8 @@ class Twig_Node_Module extends Twig_Node |
57 | 57 |
|
58 | 58 |
$this->compileIsTraitable($compiler); |
59 | 59 |
|
60 |
+ $this->compileDebugInfo($compiler); |
|
61 |
+ |
|
60 | 62 |
$this->compileClassFooter($compiler); |
61 | 63 |
} |
62 | 64 |
|
... | ... |
@@ -294,6 +296,17 @@ class Twig_Node_Module extends Twig_Node |
294 | 296 |
->indent() |
295 | 297 |
->write(sprintf("return %s;\n", $traitable ? 'true' : 'false')) |
296 | 298 |
->outdent() |
299 |
+ ->write("}\n\n") |
|
300 |
+ ; |
|
301 |
+ } |
|
302 |
+ |
|
303 |
+ public function compileDebugInfo(Twig_Compiler $compiler) |
|
304 |
+ { |
|
305 |
+ $compiler |
|
306 |
+ ->write("public function getDebugInfo()\n", "{\n") |
|
307 |
+ ->indent() |
|
308 |
+ ->write(sprintf("return %s;\n", str_replace("\n", '', var_export(array_reverse($compiler->getDebugInfo(), true), true)))) |
|
309 |
+ ->outdent() |
|
297 | 310 |
->write("}\n") |
298 | 311 |
; |
299 | 312 |
} |
... | ... |
@@ -1,7 +1,5 @@ |
1 | 1 |
<?php |
2 | 2 |
|
3 |
-require_once dirname(__FILE__).'/TestCase.php'; |
|
4 |
- |
|
5 | 3 |
/* |
6 | 4 |
* This file is part of Twig. |
7 | 5 |
* |
... | ... |
@@ -11,12 +9,12 @@ require_once dirname(__FILE__).'/TestCase.php'; |
11 | 9 |
* file that was distributed with this source code. |
12 | 10 |
*/ |
13 | 11 |
|
14 |
-class Twig_Tests_ErrorTest extends Twig_Tests_TestCase |
|
12 |
+class Twig_Tests_ErrorTest extends PHPUnit_Framework_TestCase |
|
15 | 13 |
{ |
16 | 14 |
public function testTwigExceptionAddsFileAndLineWhenMissing() |
17 | 15 |
{ |
18 | 16 |
$loader = new Twig_Loader_Array(array('index' => "\n\n{{ foo.bar }}")); |
19 |
- $twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => $this->getTempDir())); |
|
17 |
+ $twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => false)); |
|
20 | 18 |
|
21 | 19 |
$template = $twig->loadTemplate('index'); |
22 | 20 |
|
... | ... |
@@ -34,7 +32,7 @@ class Twig_Tests_ErrorTest extends Twig_Tests_TestCase |
34 | 32 |
public function testRenderWrapsExceptions() |
35 | 33 |
{ |
36 | 34 |
$loader = new Twig_Loader_Array(array('index' => "\n\n\n{{ foo.bar }}")); |
37 |
- $twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => $this->getTempDir())); |
|
35 |
+ $twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => false)); |
|
38 | 36 |
|
39 | 37 |
$template = $twig->loadTemplate('index'); |
40 | 38 |
|
... | ... |
@@ -48,6 +46,30 @@ class Twig_Tests_ErrorTest extends Twig_Tests_TestCase |
48 | 46 |
$this->assertEquals('index', $e->getTemplateFile()); |
49 | 47 |
} |
50 | 48 |
} |
49 |
+ |
|
50 |
+ public function testTwigExceptionAddsFileAndLineWhenMissingWithInheritance() |
|
51 |
+ { |
|
52 |
+ $loader = new Twig_Loader_Array(array( |
|
53 |
+ 'index' => "{% extends 'base' %} |
|
54 |
+ {% block content %} |
|
55 |
+ {{ foo.bar }} |
|
56 |
+ {% endblock %}", |
|
57 |
+ 'base' => '{% block content %}{% endblock %}' |
|
58 |
+ )); |
|
59 |
+ $twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => false)); |
|
60 |
+ |
|
61 |
+ $template = $twig->loadTemplate('index'); |
|
62 |
+ |
|
63 |
+ try { |
|
64 |
+ $template->render(array()); |
|
65 |
+ |
|
66 |
+ $this->fail(); |
|
67 |
+ } catch (Twig_Error_Runtime $e) { |
|
68 |
+ $this->assertEquals('Variable "foo" does not exist in "index" at line 3', $e->getMessage()); |
|
69 |
+ $this->assertEquals(3, $e->getTemplateLine()); |
|
70 |
+ $this->assertEquals('index', $e->getTemplateFile()); |
|
71 |
+ } |
|
72 |
+ } |
|
51 | 73 |
} |
52 | 74 |
|
53 | 75 |
class Twig_Tests_ErrorTest_Foo |
... | ... |
@@ -38,7 +38,7 @@ class Twig_Tests_Extension_SandboxTest extends PHPUnit_Framework_TestCase |
38 | 38 |
|
39 | 39 |
/** |
40 | 40 |
* @expectedException Twig_Sandbox_SecurityError |
41 |
- * @expectedExceptionMessage Filter "json_encode" is not allowed. |
|
41 |
+ * @expectedExceptionMessage Filter "json_encode" is not allowed in "1_child". |
|
42 | 42 |
*/ |
43 | 43 |
public function testSandboxWithInheritance() |
44 | 44 |
{ |
... | ... |
@@ -79,6 +79,10 @@ class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template |
79 | 79 |
return "foo.twig"; |
80 | 80 |
} |
81 | 81 |
|
82 |
+ public function getDebugInfo() |
|
83 |
+ { |
|
84 |
+ return array (); |
|
85 |
+ } |
|
82 | 86 |
} |
83 | 87 |
EOF |
84 | 88 |
, $twig); |
... | ... |
@@ -115,6 +119,11 @@ class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template |
115 | 119 |
{ |
116 | 120 |
return false; |
117 | 121 |
} |
122 |
+ |
|
123 |
+ public function getDebugInfo() |
|
124 |
+ { |
|
125 |
+ return array (); |
|
126 |
+ } |
|
118 | 127 |
} |
119 | 128 |
EOF |
120 | 129 |
, $twig); |
... | ... |
@@ -153,6 +162,11 @@ class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template |
153 | 162 |
{ |
154 | 163 |
return false; |
155 | 164 |
} |
165 |
+ |
|
166 |
+ public function getDebugInfo() |
|
167 |
+ { |
|
168 |
+ return array (); |
|
169 |
+ } |
|
156 | 170 |
} |
157 | 171 |
EOF |
158 | 172 |
, $twig); |
... | ... |
@@ -86,6 +86,10 @@ class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template |
86 | 86 |
return "foo.twig"; |
87 | 87 |
} |
88 | 88 |
|
89 |
+ public function getDebugInfo() |
|
90 |
+ { |
|
91 |
+ return array (); |
|
92 |
+ } |
|
89 | 93 |
} |
90 | 94 |
EOF |
91 | 95 |
, $twig); |
... | ... |
@@ -134,6 +138,11 @@ class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template |
134 | 138 |
{ |
135 | 139 |
return false; |
136 | 140 |
} |
141 |
+ |
|
142 |
+ public function getDebugInfo() |
|
143 |
+ { |
|
144 |
+ return array (); |
|
145 |
+ } |
|
137 | 146 |
} |
138 | 147 |
EOF |
139 | 148 |
, $twig); |