Development

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

You must first sign up to be able to contribute.

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

Revision 18492, 8.7 kB (checked in by nicolas, 6 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     $wrappedException = null;
27
28   /**
29    * Wraps an Exception.
30    *
31    * @param Exception $e An Exception instance
32    *
33    * @return sfException An sfException instance that wraps the given Exception object
34    */
35   static public function createFromException(Exception $e)
36   {
37     $exception = new sfException(sprintf('Wrapped %s: %s', get_class($e), $e->getMessage()));
38     $exception->setWrappedException($e);
39
40     return $exception;
41   }
42
43   /**
44    * Changes the wrapped exception.
45    *
46    * @param Exception $e An Exception instance
47    */
48   public function setWrappedException(Exception $e)
49   {
50     $this->wrappedException = $e;
51   }
52
53   /**
54    * Prints the stack trace for this exception.
55    */
56   public function printStackTrace()
57   {
58     $exception = is_null($this->wrappedException) ? $this : $this->wrappedException;
59
60     if (!sfConfig::get('sf_test'))
61     {
62       // log all exceptions in php log
63       error_log($exception->getMessage());
64
65       // clean current output buffer
66       while (ob_get_level())
67       {
68         if (!ob_end_clean())
69         {
70           break;
71         }
72       }
73
74       ob_start(sfConfig::get('sf_compressed') ? 'ob_gzhandler' : '');
75
76       header('HTTP/1.0 500 Internal Server Error');
77     }
78
79     try
80     {
81       $this->outputStackTrace($exception);
82     }
83     catch (Exception $e)
84     {
85     }
86
87     if (!sfConfig::get('sf_test'))
88     {
89       exit(1);
90     }
91   }
92
93   /**
94    * Gets the stack trace for this exception.
95    */
96   static protected function outputStackTrace($exception)
97   {
98     if (class_exists('sfContext', false) && sfContext::hasInstance())
99     {
100       $dispatcher = sfContext::getInstance()->getEventDispatcher();
101
102       if (sfConfig::get('sf_logging_enabled'))
103       {
104         $dispatcher->notify(new sfEvent($exception, 'application.log', array($exception->getMessage(), 'priority' => sfLogger::ERR)));
105       }
106
107       $event = $dispatcher->notifyUntil(new sfEvent($exception, 'application.throw_exception'));
108       if ($event->isProcessed())
109       {
110         return;
111       }
112     }
113
114     // send an error 500 if not in debug mode
115     if (!sfConfig::get('sf_debug'))
116     {
117       $files = array();
118
119       // first check for app/project specific error page, can only do this if we have a context
120       if (sfConfig::get('sf_app_config_dir'))
121       {
122         $files[] = sfConfig::get('sf_app_config_dir').'/error_500.php';
123       }
124       $files[] = sfConfig::get('sf_config_dir').'/error_500.php';
125       $files[] = sfConfig::get('sf_web_dir').'/errors/error500.php';
126       $files[] = dirname(__FILE__).'/data/error500.php';
127
128       foreach ($files as $file)
129       {
130         if (is_readable($file))
131         {
132           include $file;
133           return;
134         }
135       }
136     }
137
138     $message = null !== $exception->getMessage() ? $exception->getMessage() : 'n/a';
139     $name    = get_class($exception);
140     $format  = 0 == strncasecmp(PHP_SAPI, 'cli', 3) ? 'plain' : 'html';
141     $traces  = self::getTraces($exception, $format);
142
143     // dump main objects values
144     $sf_settings = '';
145     $settingsTable = $requestTable = $responseTable = $globalsTable = $userTable = '';
146     if (class_exists('sfContext', false) && sfContext::hasInstance())
147     {
148       $context = sfContext::getInstance();
149       $settingsTable = self::formatArrayAsHtml(sfDebug::settingsAsArray());
150       $requestTable  = self::formatArrayAsHtml(sfDebug::requestAsArray($context->getRequest()));
151       $responseTable = self::formatArrayAsHtml(sfDebug::responseAsArray($context->getResponse()));
152       $userTable     = self::formatArrayAsHtml(sfDebug::userAsArray($context->getUser()));
153       $globalsTable  = self::formatArrayAsHtml(sfDebug::globalsAsArray());
154     }
155
156     include dirname(__FILE__).'/data/exception.'.($format == 'html' ? 'php' : 'txt');
157   }
158
159   /**
160    * Returns an array of exception traces.
161    *
162    * @param Exception $exception  An Exception implementation instance
163    * @param string    $format     The trace format (plain or html)
164    *
165    * @return array An array of traces
166    */
167   static public function getTraces($exception, $format = 'plain')
168   {
169     $traceData = $exception->getTrace();
170     array_unshift($traceData, array(
171       'function' => '',
172       'file'     => $exception->getFile() != null ? $exception->getFile() : 'n/a',
173       'line'     => $exception->getLine() != null ? $exception->getLine() : 'n/a',
174       'args'     => array(),
175     ));
176
177     $traces = array();
178     if ($format == 'html')
179     {
180       $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>';
181     }
182     else
183     {
184       $lineFormat = 'at %s%s%s(%s) in %s line %s';
185     }
186     for ($i = 0, $count = count($traceData); $i < $count; $i++)
187     {
188       $line = isset($traceData[$i]['line']) ? $traceData[$i]['line'] : 'n/a';
189       $file = isset($traceData[$i]['file']) ? $traceData[$i]['file'] : 'n/a';
190       $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);
191       $args = isset($traceData[$i]['args']) ? $traceData[$i]['args'] : array();
192       $traces[] = sprintf($lineFormat,
193         (isset($traceData[$i]['class']) ? $traceData[$i]['class'] : ''),
194         (isset($traceData[$i]['type']) ? $traceData[$i]['type'] : ''),
195         $traceData[$i]['function'],
196         self::formatArgs($args, false, $format),
197         $shortFile,
198         $line,
199         'trace_'.$i,
200         'trace_'.$i,
201         $i == 0 ? 'block' : 'none',
202         self::fileExcerpt($file, $line)
203       );
204     }
205
206     return $traces;
207   }
208
209   /**
210    * Returns an HTML version of an array as YAML.
211    *
212    * @param array $values The values array
213    *
214    * @return string An HTML string
215    */
216   static protected function formatArrayAsHtml($values)
217   {
218     return '<pre>'.self::escape(@sfYaml::dump($values)).'</pre>';
219   }
220
221   /**
222    * Returns an excerpt of a code file around the given line number.
223    *
224    * @param string $file  A file path
225    * @param int    $line  The selected line number
226    *
227    * @return string An HTML string
228    */
229   static protected function fileExcerpt($file, $line)
230   {
231     if (is_readable($file))
232     {
233       $content = preg_split('#<br />#', highlight_file($file, true));
234
235       $lines = array();
236       for ($i = max($line - 3, 1), $max = min($line + 3, count($content)); $i <= $max; $i++)
237       {
238         $lines[] = '<li'.($i == $line ? ' class="selected"' : '').'>'.$content[$i - 1].'</li>';
239       }
240
241       return '<ol start="'.max($line - 3, 1).'">'.implode("\n", $lines).'</ol>';
242     }
243   }
244
245   /**
246    * Formats an array as a string.
247    *
248    * @param array   $args     The argument array
249    * @param boolean $single
250    * @param string  $format   The format string (html or plain)
251    *
252    * @return string
253    */
254   static protected function formatArgs($args, $single = false, $format = 'html')
255   {
256     $result = array();
257
258     $single and $args = array($args);
259
260     foreach ($args as $key => $value)
261     {
262       if (is_object($value))
263       {
264         $formattedValue = ($format == 'html' ? '<em>object</em>' : 'object').sprintf("('%s')", get_class($value));
265       }
266       else if (is_array($value))
267       {
268         $formattedValue = ($format == 'html' ? '<em>array</em>' : 'array').sprintf("(%s)", self::formatArgs($value));
269       }
270       else if (is_string($value))
271       {
272         $formattedValue = ($format == 'html' ? sprintf("'%s'", self::escape($value)) : "'$value'");
273       }
274       else if (is_null($value))
275       {
276         $formattedValue = ($format == 'html' ? '<em>null</em>' : 'null');
277       }
278       else
279       {
280         $formattedValue = $value;
281       }
282       
283       $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", self::escape($key), $formattedValue);
284     }
285
286     return implode(', ', $result);
287   }
288  
289   /**
290    * Escapes a string value with html entities
291    *
292    * @param  string  $value
293    *
294    * @return string
295    */
296   static protected function escape($value)
297   {
298     if (!is_string($value))
299     {
300       return $value;
301     }
302     
303     return htmlspecialchars($value, ENT_QUOTES, sfConfig::get('sf_charset', 'UTF-8'));
304   }
305 }
306
Note: See TracBrowser for help on using the browser.