]>
Commit | Line | Data |
---|---|---|
1 | <?php | |
2 | ||
3 | /* | |
4 | * This file is part of Twig. | |
5 | * | |
6 | * (c) 2009 Fabien Potencier | |
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 | /** | |
13 | * Twig base exception. | |
14 | * | |
15 | * This exception class and its children must only be used when | |
16 | * an error occurs during the loading of a template, when a syntax error | |
17 | * is detected in a template, or when rendering a template. Other | |
18 | * errors must use regular PHP exception classes (like when the template | |
19 | * cache directory is not writable for instance). | |
20 | * | |
21 | * To help debugging template issues, this class tracks the original template | |
22 | * name and line where the error occurred. | |
23 | * | |
24 | * Whenever possible, you must set these information (original template name | |
25 | * and line number) yourself by passing them to the constructor. If some or all | |
26 | * these information are not available from where you throw the exception, then | |
27 | * this class will guess them automatically (when the line number is set to -1 | |
28 | * and/or the filename is set to null). As this is a costly operation, this | |
29 | * can be disabled by passing false for both the filename and the line number | |
30 | * when creating a new instance of this class. | |
31 | * | |
32 | * @author Fabien Potencier <fabien@symfony.com> | |
33 | */ | |
34 | class Twig_Error extends Exception | |
35 | { | |
36 | protected $lineno; | |
37 | protected $filename; | |
38 | protected $rawMessage; | |
39 | protected $previous; | |
40 | ||
41 | /** | |
42 | * Constructor. | |
43 | * | |
44 | * Set both the line number and the filename to false to | |
45 | * disable automatic guessing of the original template name | |
46 | * and line number. | |
47 | * | |
48 | * Set the line number to -1 to enable its automatic guessing. | |
49 | * Set the filename to null to enable its automatic guessing. | |
50 | * | |
51 | * By default, automatic guessing is enabled. | |
52 | * | |
53 | * @param string $message The error message | |
54 | * @param integer $lineno The template line where the error occurred | |
55 | * @param string $filename The template file name where the error occurred | |
56 | * @param Exception $previous The previous exception | |
57 | */ | |
58 | public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null) | |
59 | { | |
60 | if (version_compare(PHP_VERSION, '5.3.0', '<')) { | |
61 | $this->previous = $previous; | |
62 | parent::__construct(''); | |
63 | } else { | |
64 | parent::__construct('', 0, $previous); | |
65 | } | |
66 | ||
67 | $this->lineno = $lineno; | |
68 | $this->filename = $filename; | |
69 | ||
70 | if (-1 === $this->lineno || null === $this->filename) { | |
71 | $this->guessTemplateInfo(); | |
72 | } | |
73 | ||
74 | $this->rawMessage = $message; | |
75 | ||
76 | $this->updateRepr(); | |
77 | } | |
78 | ||
79 | /** | |
80 | * Gets the raw message. | |
81 | * | |
82 | * @return string The raw message | |
83 | */ | |
84 | public function getRawMessage() | |
85 | { | |
86 | return $this->rawMessage; | |
87 | } | |
88 | ||
89 | /** | |
90 | * Gets the filename where the error occurred. | |
91 | * | |
92 | * @return string The filename | |
93 | */ | |
94 | public function getTemplateFile() | |
95 | { | |
96 | return $this->filename; | |
97 | } | |
98 | ||
99 | /** | |
100 | * Sets the filename where the error occurred. | |
101 | * | |
102 | * @param string $filename The filename | |
103 | */ | |
104 | public function setTemplateFile($filename) | |
105 | { | |
106 | $this->filename = $filename; | |
107 | ||
108 | $this->updateRepr(); | |
109 | } | |
110 | ||
111 | /** | |
112 | * Gets the template line where the error occurred. | |
113 | * | |
114 | * @return integer The template line | |
115 | */ | |
116 | public function getTemplateLine() | |
117 | { | |
118 | return $this->lineno; | |
119 | } | |
120 | ||
121 | /** | |
122 | * Sets the template line where the error occurred. | |
123 | * | |
124 | * @param integer $lineno The template line | |
125 | */ | |
126 | public function setTemplateLine($lineno) | |
127 | { | |
128 | $this->lineno = $lineno; | |
129 | ||
130 | $this->updateRepr(); | |
131 | } | |
132 | ||
133 | public function guess() | |
134 | { | |
135 | $this->guessTemplateInfo(); | |
136 | $this->updateRepr(); | |
137 | } | |
138 | ||
139 | /** | |
140 | * For PHP < 5.3.0, provides access to the getPrevious() method. | |
141 | * | |
142 | * @param string $method The method name | |
143 | * @param array $arguments The parameters to be passed to the method | |
144 | * | |
145 | * @return Exception The previous exception or null | |
146 | * | |
147 | * @throws BadMethodCallException | |
148 | */ | |
149 | public function __call($method, $arguments) | |
150 | { | |
151 | if ('getprevious' == strtolower($method)) { | |
152 | return $this->previous; | |
153 | } | |
154 | ||
155 | throw new BadMethodCallException(sprintf('Method "Twig_Error::%s()" does not exist.', $method)); | |
156 | } | |
157 | ||
158 | protected function updateRepr() | |
159 | { | |
160 | $this->message = $this->rawMessage; | |
161 | ||
162 | $dot = false; | |
163 | if ('.' === substr($this->message, -1)) { | |
164 | $this->message = substr($this->message, 0, -1); | |
165 | $dot = true; | |
166 | } | |
167 | ||
168 | if ($this->filename) { | |
169 | if (is_string($this->filename) || (is_object($this->filename) && method_exists($this->filename, '__toString'))) { | |
170 | $filename = sprintf('"%s"', $this->filename); | |
171 | } else { | |
172 | $filename = json_encode($this->filename); | |
173 | } | |
174 | $this->message .= sprintf(' in %s', $filename); | |
175 | } | |
176 | ||
177 | if ($this->lineno && $this->lineno >= 0) { | |
178 | $this->message .= sprintf(' at line %d', $this->lineno); | |
179 | } | |
180 | ||
181 | if ($dot) { | |
182 | $this->message .= '.'; | |
183 | } | |
184 | } | |
185 | ||
186 | protected function guessTemplateInfo() | |
187 | { | |
188 | $template = null; | |
189 | ||
190 | if (version_compare(phpversion(), '5.3.6', '>=')) { | |
191 | $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT); | |
192 | } else { | |
193 | $backtrace = debug_backtrace(); | |
194 | } | |
195 | ||
196 | foreach ($backtrace as $trace) { | |
197 | if (isset($trace['object']) && $trace['object'] instanceof Twig_Template && 'Twig_Template' !== get_class($trace['object'])) { | |
198 | if (null === $this->filename || $this->filename == $trace['object']->getTemplateName()) { | |
199 | $template = $trace['object']; | |
200 | } | |
201 | } | |
202 | } | |
203 | ||
204 | // update template filename | |
205 | if (null !== $template && null === $this->filename) { | |
206 | $this->filename = $template->getTemplateName(); | |
207 | } | |
208 | ||
209 | if (null === $template || $this->lineno > -1) { | |
210 | return; | |
211 | } | |
212 | ||
213 | $r = new ReflectionObject($template); | |
214 | $file = $r->getFileName(); | |
215 | ||
216 | $exceptions = array($e = $this); | |
217 | while (($e instanceof self || method_exists($e, 'getPrevious')) && $e = $e->getPrevious()) { | |
218 | $exceptions[] = $e; | |
219 | } | |
220 | ||
221 | while ($e = array_pop($exceptions)) { | |
222 | $traces = $e->getTrace(); | |
223 | while ($trace = array_shift($traces)) { | |
224 | if (!isset($trace['file']) || !isset($trace['line']) || $file != $trace['file']) { | |
225 | continue; | |
226 | } | |
227 | ||
228 | foreach ($template->getDebugInfo() as $codeLine => $templateLine) { | |
229 | if ($codeLine <= $trace['line']) { | |
230 | // update template line | |
231 | $this->lineno = $templateLine; | |
232 | ||
233 | return; | |
234 | } | |
235 | } | |
236 | } | |
237 | } | |
238 | } | |
239 | } |