PageRenderTime 39ms CodeModel.GetById 1ms app.highlight 30ms RepoModel.GetById 2ms app.codeStats 0ms

/symfony/lib/exception/sfException.class.php

https://github.com/mustdo/www
PHP | 346 lines | 222 code | 40 blank | 84 comment | 45 complexity | c782f61dc0cb36334ab348cdbc2a6269 MD5 | raw file
  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: sfException.class.php 12751 2008-11-08 09:38:42Z fabien $
 22 */
 23class 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();
 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 ($template = self::getTemplatePathForError($format, true))
184    {
185      include $template;
186      return;
187    }
188  }
189
190  /**
191   * Returns the path for the template error message.
192   *
193   * @param  string  $format The request format
194   * @param  Boolean $debug  Whether to return a template for the debug mode or not
195   *
196   * @return string|Boolean  false if the template cannot be found for the given format,
197   *                         the absolute path to the template otherwise
198   */
199  static public function getTemplatePathForError($format, $debug)
200  {
201    $templatePaths = array(
202      sfConfig::get('sf_app_config_dir').'/error',
203      sfConfig::get('sf_config_dir').'/error',
204      dirname(__FILE__).'/data',
205    );
206
207    $template = sprintf('%s.%s.php', $debug ? 'exception' : 'error', $format);
208    foreach ($templatePaths as $path)
209    {
210      if (!is_null($path) && is_readable($file = $path.'/'.$template))
211      {
212        return $file;
213      }
214    }
215
216    return false;
217  }
218
219  /**
220   * Returns an array of exception traces.
221   *
222   * @param Exception $exception  An Exception implementation instance
223   * @param string    $format     The trace format (plain or html)
224   *
225   * @return array An array of traces
226   */
227  static protected function getTraces($exception, $format = 'plain')
228  {
229    $traceData = $exception->getTrace();
230    array_unshift($traceData, array(
231      'function' => '',
232      'file'     => $exception->getFile() != null ? $exception->getFile() : 'n/a',
233      'line'     => $exception->getLine() != null ? $exception->getLine() : 'n/a',
234      'args'     => array(),
235    ));
236
237    $traces = array();
238    if ($format == 'html')
239    {
240      $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>';
241    }
242    else
243    {
244      $lineFormat = 'at %s%s%s(%s) in %s line %s';
245    }
246    for ($i = 0, $count = count($traceData); $i < $count; $i++)
247    {
248      $line = isset($traceData[$i]['line']) ? $traceData[$i]['line'] : 'n/a';
249      $file = isset($traceData[$i]['file']) ? $traceData[$i]['file'] : 'n/a';
250      $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);
251      $args = isset($traceData[$i]['args']) ? $traceData[$i]['args'] : array();
252      $traces[] = sprintf($lineFormat,
253        (isset($traceData[$i]['class']) ? $traceData[$i]['class'] : ''),
254        (isset($traceData[$i]['type']) ? $traceData[$i]['type'] : ''),
255        $traceData[$i]['function'],
256        self::formatArgs($args, false, $format),
257        $shortFile,
258        $line,
259        'trace_'.$i,
260        'trace_'.$i,
261        $i == 0 ? 'block' : 'none',
262        self::fileExcerpt($file, $line)
263      );
264    }
265
266    return $traces;
267  }
268
269  /**
270   * Returns an HTML version of an array as YAML.
271   *
272   * @param array $values The values array
273   *
274   * @return string An HTML string
275   */
276  static protected function formatArrayAsHtml($values)
277  {
278    return '<pre>'.@sfYaml::Dump($values).'</pre>';
279  }
280
281  /**
282   * Returns an excerpt of a code file around the given line number.
283   *
284   * @param string $file  A file path
285   * @param int    $line  The selected line number
286   *
287   * @return string An HTML string
288   */
289  static protected function fileExcerpt($file, $line)
290  {
291    if (is_readable($file))
292    {
293      $content = preg_split('#<br />#', highlight_file($file, true));
294
295      $lines = array();
296      for ($i = max($line - 3, 1), $max = min($line + 3, count($content)); $i <= $max; $i++)
297      {
298        $lines[] = '<li'.($i == $line ? ' class="selected"' : '').'>'.$content[$i - 1].'</li>';
299      }
300
301      return '<ol start="'.max($line - 3, 1).'">'.implode("\n", $lines).'</ol>';
302    }
303  }
304
305  /**
306   * Formats an array as a string.
307   *
308   * @param array   $args     The argument array
309   * @param boolean $single
310   * @param string  $format   The format string (html or plain)
311   *
312   * @return string
313   */
314  static protected function formatArgs($args, $single = false, $format = 'html')
315  {
316    $result = array();
317
318    $single and $args = array($args);
319
320    foreach ($args as $key => $value)
321    {
322      if (is_object($value))
323      {
324        $result[] = ($format == 'html' ? '<em>object</em>' : 'object').'(\''.get_class($value).'\')';
325      }
326      else if (is_array($value))
327      {
328        $result[] = ($format == 'html' ? '<em>array</em>' : 'array').'('.self::formatArgs($value).')';
329      }
330      else if ($value === null)
331      {
332        $result[] = $format == 'html' ? '<em>null</em>' : 'null';
333      }
334      else if (!is_int($key))
335      {
336        $result[] = $format == 'html' ? "'$key' =&gt; '$value'" : "'$key' => '$value'";
337      }
338      else
339      {
340        $result[] = "'".$value."'";
341      }
342    }
343
344    return implode(', ', $result);
345  }
346}