Development

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

You must first sign up to be able to contribute.

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

Revision 23913, 10.3 kB (checked in by bschussek, 5 years ago)

[1.2] Reverted [23901] in 1.2 branch because the bug did not apply there

  • 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 $exception)
97   {
98     $format = 'html';
99     $code   = '500';
100     $text   = 'Internal Server Error';
101
102     if (class_exists('sfContext', false) && sfContext::hasInstance() && is_object($request = sfContext::getInstance()->getRequest()) && is_object($response = sfContext::getInstance()->getResponse()))
103     {
104       $dispatcher = sfContext::getInstance()->getEventDispatcher();
105
106       if (sfConfig::get('sf_logging_enabled'))
107       {
108         $dispatcher->notify(new sfEvent($exception, 'application.log', array($exception->getMessage(), 'priority' => sfLogger::ERR)));
109       }
110
111       $event = $dispatcher->notifyUntil(new sfEvent($exception, 'application.throw_exception'));
112       if ($event->isProcessed())
113       {
114         return;
115       }
116
117       if ($response->getStatusCode() < 300)
118       {
119         // status code has already been sent, but is included here for the purpose of testing
120         $response->setStatusCode(500);
121       }
122
123       $response->setContentType('text/html');
124
125       if (!sfConfig::get('sf_test'))
126       {
127         foreach ($response->getHttpHeaders() as $name => $value)
128         {
129           header($name.': '.$value);
130         }
131       }
132
133       $code = $response->getStatusCode();
134       $text = $response->getStatusText();
135
136       $format = $request->getRequestFormat();
137       if (!$format)
138       {
139         $format = 'html';
140       }
141
142       if ($mimeType = $request->getMimeType($format))
143       {
144         $response->setContentType($mimeType);
145       }
146     }
147     else
148     {
149       // a backward compatible default
150       if (!sfConfig::get('sf_test'))
151       {
152         header('Content-Type: text/html; charset='.sfConfig::get('sf_charset', 'utf-8'));
153       }
154     }
155
156     // send an error 500 if not in debug mode
157     if (!sfConfig::get('sf_debug'))
158     {
159       if ($template = self::getTemplatePathForError($format, false))
160       {
161         include $template;
162         return;
163       }
164     }
165
166     $message = is_null($exception->getMessage()) ? 'n/a' : $exception->getMessage();
167     $name    = get_class($exception);
168     $traces  = self::getTraces($exception, 'html' != $format || 0 == strncasecmp(PHP_SAPI, 'cli', 3) ? 'plain' : 'html');
169
170     // dump main objects values
171     $sf_settings = '';
172     $settingsTable = $requestTable = $responseTable = $globalsTable = $userTable = '';
173     if (class_exists('sfContext', false) && sfContext::hasInstance())
174     {
175       $context = sfContext::getInstance();
176       $settingsTable = self::formatArrayAsHtml(sfDebug::settingsAsArray());
177       $requestTable  = self::formatArrayAsHtml(sfDebug::requestAsArray($context->getRequest()));
178       $responseTable = self::formatArrayAsHtml(sfDebug::responseAsArray($context->getResponse()));
179       $userTable     = self::formatArrayAsHtml(sfDebug::userAsArray($context->getUser()));
180       $globalsTable  = self::formatArrayAsHtml(sfDebug::globalsAsArray());
181     }
182
183     if ($response)
184     {
185       $response->sendHttpHeaders();
186     }
187
188     if ($template = self::getTemplatePathForError($format, true))
189     {
190       include $template;
191       return;
192     }
193   }
194
195   /**
196    * Returns the path for the template error message.
197    *
198    * @param string  $format The request format
199    * @param Boolean $debug  Whether to return a template for the debug mode or not
200    *
201    * @return string|Boolean false if the template cannot be found for the given format,
202    *                        the absolute path to the template otherwise
203    */
204   static public function getTemplatePathForError($format, $debug)
205   {
206     $templatePaths = array(
207       sfConfig::get('sf_app_config_dir').'/error',
208       sfConfig::get('sf_config_dir').'/error',
209       dirname(__FILE__).'/data',
210     );
211
212     $template = sprintf('%s.%s.php', $debug ? 'exception' : 'error', $format);
213     foreach ($templatePaths as $path)
214     {
215       if (!is_null($path) && is_readable($file = $path.'/'.$template))
216       {
217         return $file;
218       }
219     }
220
221     return false;
222   }
223
224   /**
225    * Returns an array of exception traces.
226    *
227    * @param Exception $exception An Exception implementation instance
228    * @param string    $format    The trace format (plain or html)
229    *
230    * @return array An array of traces
231    */
232   static protected function getTraces($exception, $format = 'plain')
233   {
234     $traceData = $exception->getTrace();
235     array_unshift($traceData, array(
236       'function' => '',
237       'file'     => $exception->getFile() != null ? $exception->getFile() : 'n/a',
238       'line'     => $exception->getLine() != null ? $exception->getLine() : 'n/a',
239       'args'     => array(),
240     ));
241
242     $traces = array();
243     if ($format == 'html')
244     {
245       $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>';
246     }
247     else
248     {
249       $lineFormat = 'at %s%s%s(%s) in %s line %s';
250     }
251     for ($i = 0, $count = count($traceData); $i < $count; $i++)
252     {
253       $line = isset($traceData[$i]['line']) ? $traceData[$i]['line'] : 'n/a';
254       $file = isset($traceData[$i]['file']) ? $traceData[$i]['file'] : 'n/a';
255       $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);
256       $args = isset($traceData[$i]['args']) ? $traceData[$i]['args'] : array();
257       $traces[] = sprintf($lineFormat,
258         (isset($traceData[$i]['class']) ? $traceData[$i]['class'] : ''),
259         (isset($traceData[$i]['type']) ? $traceData[$i]['type'] : ''),
260         $traceData[$i]['function'],
261         self::formatArgs($args, false, $format),
262         $shortFile,
263         $line,
264         'trace_'.$i,
265         'trace_'.$i,
266         $i == 0 ? 'block' : 'none',
267         self::fileExcerpt($file, $line)
268       );
269     }
270
271     return $traces;
272   }
273
274   /**
275    * Returns an HTML version of an array as YAML.
276    *
277    * @param array $values The values array
278    *
279    * @return string An HTML string
280    */
281   static protected function formatArrayAsHtml($values)
282   {
283     return '<pre>'.self::escape(@sfYaml::dump($values)).'</pre>';
284   }
285
286   /**
287    * Returns an excerpt of a code file around the given line number.
288    *
289    * @param string $file A file path
290    * @param int    $line The selected line number
291    *
292    * @return string An HTML string
293    */
294   static protected function fileExcerpt($file, $line)
295   {
296     if (is_readable($file))
297     {
298       $content = preg_split('#<br />#', highlight_file($file, true));
299
300       $lines = array();
301       for ($i = max($line - 3, 1), $max = min($line + 3, count($content)); $i <= $max; $i++)
302       {
303         $lines[] = '<li'.($i == $line ? ' class="selected"' : '').'>'.$content[$i - 1].'</li>';
304       }
305
306       return '<ol start="'.max($line - 3, 1).'">'.implode("\n", $lines).'</ol>';
307     }
308   }
309
310   /**
311    * Formats an array as a string.
312    *
313    * @param array   $args   The argument array
314    * @param boolean $single
315    * @param string  $format The format string (html or plain)
316    *
317    * @return string
318    */
319   static protected function formatArgs($args, $single = false, $format = 'html')
320   {
321     $result = array();
322
323     $single and $args = array($args);
324
325     foreach ($args as $key => $value)
326     {
327       if (is_object($value))
328       {
329         $formattedValue = ($format == 'html' ? '<em>object</em>' : 'object').sprintf("('%s')", get_class($value));
330       }
331       else if (is_array($value))
332       {
333         $formattedValue = ($format == 'html' ? '<em>array</em>' : 'array').sprintf("(%s)", self::formatArgs($value));
334       }
335       else if (is_string($value))
336       {
337         $formattedValue = ($format == 'html' ? sprintf("'%s'", self::escape($value)) : "'$value'");
338       }
339       else if (is_null($value))
340       {
341         $formattedValue = ($format == 'html' ? '<em>null</em>' : 'null');
342       }
343       else
344       {
345         $formattedValue = $value;
346       }
347       
348       $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", self::escape($key), $formattedValue);
349     }
350
351     return implode(', ', $result);
352   }
353  
354   /**
355    * Escapes a string value with html entities
356    *
357    * @param  string  $value
358    *
359    * @return string
360    */
361   static protected function escape($value)
362   {
363     if (!is_string($value))
364     {
365       return $value;
366     }
367     
368     return htmlspecialchars($value, ENT_QUOTES, sfConfig::get('sf_charset', 'UTF-8'));
369   }
370 }
371
Note: See TracBrowser for help on using the browser.