Development

/branches/1.0/lib/exception/sfException.class.php

You must first sign up to be able to contribute.

root/branches/1.0/lib/exception/sfException.class.php

Revision 18492, 8.4 kB (checked in by nicolas, 5 years ago)

fixed exception page should execute html/javascript code when using html format - closes #5743

  • Property svn:mime-type set to text/x-php
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Rev Date
Line 
1 <?php
2
3 /*
4  * This file is part of the symfony package.
5  * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
6  * (c) 2004-2006 Sean Kerr <sean@code-box.org>
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  * sfException is the base class for all symfony related exceptions and
14  * provides an additional method for printing up a detailed view of an
15  * exception.
16  *
17  * @package    symfony
18  * @subpackage exception
19  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
20  * @author     Sean Kerr <sean@code-box.org>
21  * @version    SVN: $Id$
22  */
23 class sfException extends Exception
24 {
25   protected
26     $name = null;
27
28   /**
29    * Class constructor.
30    *
31    * @param string The error message
32    * @param int    The error code
33    */
34   public function __construct($message = null, $code = 0)
35   {
36     if ($this->getName() === null)
37     {
38       $this->setName('sfException');
39     }
40
41     parent::__construct($message, $code);
42
43     if (sfConfig::get('sf_logging_enabled') && $this->getName() != 'sfStopException')
44     {
45       sfLogger::getInstance()->err('{'.$this->getName().'} '.$message);
46     }
47   }
48
49   /**
50    * Retrieves the name of this exception.
51    *
52    * @return string This exception's name
53    */
54   public function getName()
55   {
56     return $this->name;
57   }
58
59   /**
60    * Prints the stack trace for this exception.
61    *
62    * @param Exception An Exception implementation instance
63    */
64   public function printStackTrace($exception = null)
65   {
66     if (!$exception)
67     {
68       $exception = $this;
69     }
70
71     // don't print message if it is an sfStopException exception
72     if (method_exists($exception, 'getName') && $exception->getName() == 'sfStopException')
73     {
74       if (!sfConfig::get('sf_test'))
75       {
76         exit(1);
77       }
78
79       return;
80     }
81
82     if (class_exists('sfMixer', false))
83     {
84       foreach (sfMixer::getCallables('sfException:printStackTrace:printStackTrace') as $callable)
85       {
86         $ret = call_user_func($callable, $this, $exception);
87         if ($ret)
88         {
89           if (!sfConfig::get('sf_test'))
90           {
91             exit(1);
92           }
93
94           return;
95         }
96       }
97     }
98
99     if (!sfConfig::get('sf_test'))
100     {
101       header('HTTP/1.0 500 Internal Server Error');
102
103       // clean current output buffer
104       while (@ob_end_clean());
105
106       ob_start(sfConfig::get('sf_compressed') ? 'ob_gzhandler' : '');
107     }
108
109     // send an error 500 if not in debug mode
110     if (!sfConfig::get('sf_debug'))
111     {
112       error_log($exception->getMessage());
113
114       $file = sfConfig::get('sf_web_dir').'/errors/error500.php';
115       include(is_readable($file) ? $file : sfConfig::get('sf_symfony_data_dir').'/web/errors/error500.php');
116
117       if (!sfConfig::get('sf_test'))
118       {
119         exit(1);
120       }
121
122       return;
123     }
124
125     $message = null !== $exception->getMessage() ? $exception->getMessage() : 'n/a';
126     $name    = get_class($exception);
127     $format  = 0 == strncasecmp(PHP_SAPI, 'cli', 3) ? 'plain' : 'html';
128     $traces  = $this->getTraces($exception, $format);
129
130     // extract error reference from message
131     $error_reference = '';
132     if (preg_match('/\[(err\d+)\]/', $message, $matches))
133     {
134       $error_reference = $matches[1];
135     }
136
137     // dump main objects values
138     $sf_settings = '';
139     $settingsTable = $requestTable = $responseTable = $globalsTable = '';
140     if (class_exists('sfContext', false) && sfContext::hasInstance())
141     {
142       $context = sfContext::getInstance();
143       $settingsTable = $this->formatArrayAsHtml(sfDebug::settingsAsArray());
144       $requestTable  = $this->formatArrayAsHtml(sfDebug::requestAsArray($context->getRequest()));
145       $responseTable = $this->formatArrayAsHtml(sfDebug::responseAsArray($context->getResponse()));
146       $globalsTable  = $this->formatArrayAsHtml(sfDebug::globalsAsArray());
147     }
148
149     include(sfConfig::get('sf_symfony_data_dir').'/data/exception.'.($format == 'html' ? 'php' : 'txt'));
150
151     // if test, do not exit
152     if (!sfConfig::get('sf_test'))
153     {
154       exit(1);
155     }
156   }
157
158   /**
159    * Returns an array of exception traces.
160    *
161    * @param Exception An Exception implementation instance
162    * @param string The trace format (plain or html)
163    *
164    * @return array An array of traces
165    */
166   public function getTraces($exception, $format = 'plain')
167   {
168     $traceData = $exception->getTrace();
169     array_unshift($traceData, array(
170       'function' => '',
171       'file'     => $exception->getFile() != null ? $exception->getFile() : 'n/a',
172       'line'     => $exception->getLine() != null ? $exception->getLine() : 'n/a',
173       'args'     => array(),
174     ));
175
176     $traces = array();
177     if ($format == 'html')
178     {
179       $lineFormat = 'at <strong>%s%s%s</strong>(%s)<br />in <em>%s</em> line %s <a href="#" onclick="toggle(\'%s\'); return false;">...</a><br /><ul id="%s" style="display: %s">%s</ul>';
180     }
181     else
182     {
183       $lineFormat = 'at %s%s%s(%s) in %s line %s';
184     }
185     for ($i = 0, $count = count($traceData); $i < $count; $i++)
186     {
187       $line = isset($traceData[$i]['line']) ? $traceData[$i]['line'] : 'n/a';
188       $file = isset($traceData[$i]['file']) ? $traceData[$i]['file'] : 'n/a';
189       $shortFile = preg_replace(array('#^'.preg_quote(sfConfig::get('sf_root_dir')).'#', '#^'.preg_quote(realpath(sfConfig::get('sf_symfony_lib_dir'))).'#'), array('SF_ROOT_DIR', 'SF_SYMFONY_LIB_DIR'), $file);
190       $args = isset($traceData[$i]['args']) ? $traceData[$i]['args'] : array();
191       $traces[] = sprintf($lineFormat,
192         (isset($traceData[$i]['class']) ? $traceData[$i]['class'] : ''),
193         (isset($traceData[$i]['type']) ? $traceData[$i]['type'] : ''),
194         $traceData[$i]['function'],
195         $this->formatArgs($args, false, $format),
196         $shortFile,
197         $line,
198         'trace_'.$i,
199         'trace_'.$i,
200         $i == 0 ? 'block' : 'none',
201         $this->fileExcerpt($file, $line)
202       );
203     }
204
205     return $traces;
206   }
207
208   /**
209    * Returns an HTML version of an array as YAML.
210    *
211    * @param array The values array
212    *
213    * @return string An HTML string
214    */
215   protected function formatArrayAsHtml($values)
216   {
217     return '<pre>'.self::escape(@sfYaml::dump($values)).'</pre>';
218   }
219
220   /**
221    * Returns an excerpt of a code file around the given line number.
222    *
223    * @param string A file path
224    * @param int The selected line number
225    *
226    * @return string An HTML string
227    */
228   protected function fileExcerpt($file, $line)
229   {
230     if (is_readable($file))
231     {
232       $content = preg_split('#<br />#', highlight_file($file, true));
233
234       $lines = array();
235       for ($i = max($line - 3, 1), $max = min($line + 3, count($content)); $i <= $max; $i++)
236       {
237         $lines[] = '<li'.($i == $line ? ' class="selected"' : '').'>'.$content[$i - 1].'</li>';
238       }
239
240       return '<ol start="'.max($line - 3, 1).'">'.implode("\n", $lines).'</ol>';
241     }
242   }
243
244   /**
245    * Formats an array as a string.
246    *
247    * @param array The argument array
248    * @param boolean
249    * @param string The format string (html or plain)
250    *
251    * @return string
252    */
253   protected function formatArgs($args, $single = false, $format = 'html')
254   {
255     $result = array();
256
257     $single and $args = array($args);
258
259     foreach ($args as $key => $value)
260     {
261       if (is_object($value))
262       {
263         $formattedValue = ($format == 'html' ? '<em>object</em>' : 'object').sprintf("('%s')", get_class($value));
264       }
265       else if (is_array($value))
266       {
267         $formattedValue = ($format == 'html' ? '<em>array</em>' : 'array').sprintf("(%s)", self::formatArgs($value));
268       }
269       else if (is_string($value))
270       {
271         $formattedValue = ($format == 'html' ? sprintf("'%s'", self::escape($value)) : "'$value'");
272       }
273       else if (is_null($value))
274       {
275         $formattedValue = ($format == 'html' ? '<em>null</em>' : 'null');
276       }
277       else
278       {
279         $formattedValue = $value;
280       }
281       
282       $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", self::escape($key), $formattedValue);
283     }
284
285     return implode(', ', $result);
286   }
287  
288   /**
289    * Escapes a string value with html entities
290    *
291    * @param  string  $value
292    *
293    * @return string
294    */
295   static protected function escape($value)
296   {
297     if (!is_string($value))
298     {
299       return $value;
300     }
301     
302     return htmlspecialchars($value, ENT_QUOTES, sfConfig::get('sf_charset', 'UTF-8'));
303   }
304
305   /**
306    * Sets the name of this exception.
307    *
308    * @param string An exception name
309    */
310   protected function setName($name)
311   {
312     $this->name = $name;
313   }
314 }
315
Note: See TracBrowser for help on using the browser.