Project

General

Profile

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 int       $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 (PHP_VERSION_ID < 50300) {
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 int 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 int $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
    public function appendMessage($rawMessage)
159
    {
160
        $this->rawMessage .= $rawMessage;
161
        $this->updateRepr();
162
    }
163

    
164
    /**
165
     * @internal
166
     */
167
    protected function updateRepr()
168
    {
169
        $this->message = $this->rawMessage;
170

    
171
        $dot = false;
172
        if ('.' === substr($this->message, -1)) {
173
            $this->message = substr($this->message, 0, -1);
174
            $dot = true;
175
        }
176

    
177
        $questionMark = false;
178
        if ('?' === substr($this->message, -1)) {
179
            $this->message = substr($this->message, 0, -1);
180
            $questionMark = true;
181
        }
182

    
183
        if ($this->filename) {
184
            if (is_string($this->filename) || (is_object($this->filename) && method_exists($this->filename, '__toString'))) {
185
                $filename = sprintf('"%s"', $this->filename);
186
            } else {
187
                $filename = json_encode($this->filename);
188
            }
189
            $this->message .= sprintf(' in %s', $filename);
190
        }
191

    
192
        if ($this->lineno && $this->lineno >= 0) {
193
            $this->message .= sprintf(' at line %d', $this->lineno);
194
        }
195

    
196
        if ($dot) {
197
            $this->message .= '.';
198
        }
199

    
200
        if ($questionMark) {
201
            $this->message .= '?';
202
        }
203
    }
204

    
205
    /**
206
     * @internal
207
     */
208
    protected function guessTemplateInfo()
209
    {
210
        $template = null;
211
        $templateClass = null;
212

    
213
        if (PHP_VERSION_ID >= 50306) {
214
            $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT);
215
        } else {
216
            $backtrace = debug_backtrace();
217
        }
218

    
219
        foreach ($backtrace as $trace) {
220
            if (isset($trace['object']) && $trace['object'] instanceof Twig_Template && 'Twig_Template' !== get_class($trace['object'])) {
221
                $currentClass = get_class($trace['object']);
222
                $isEmbedContainer = 0 === strpos($templateClass, $currentClass);
223
                if (null === $this->filename || ($this->filename == $trace['object']->getTemplateName() && !$isEmbedContainer)) {
224
                    $template = $trace['object'];
225
                    $templateClass = get_class($trace['object']);
226
                }
227
            }
228
        }
229

    
230
        // update template filename
231
        if (null !== $template && null === $this->filename) {
232
            $this->filename = $template->getTemplateName();
233
        }
234

    
235
        if (null === $template || $this->lineno > -1) {
236
            return;
237
        }
238

    
239
        $r = new ReflectionObject($template);
240
        $file = $r->getFileName();
241

    
242
        // hhvm has a bug where eval'ed files comes out as the current directory
243
        if (is_dir($file)) {
244
            $file = '';
245
        }
246

    
247
        $exceptions = array($e = $this);
248
        while (($e instanceof self || method_exists($e, 'getPrevious')) && $e = $e->getPrevious()) {
249
            $exceptions[] = $e;
250
        }
251

    
252
        while ($e = array_pop($exceptions)) {
253
            $traces = $e->getTrace();
254
            array_unshift($traces, array('file' => $e->getFile(), 'line' => $e->getLine()));
255

    
256
            while ($trace = array_shift($traces)) {
257
                if (!isset($trace['file']) || !isset($trace['line']) || $file != $trace['file']) {
258
                    continue;
259
                }
260

    
261
                foreach ($template->getDebugInfo() as $codeLine => $templateLine) {
262
                    if ($codeLine <= $trace['line']) {
263
                        // update template line
264
                        $this->lineno = $templateLine;
265

    
266
                        return;
267
                    }
268
                }
269
            }
270
        }
271
    }
272
}
(7-7/43)