Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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 32641 2011-06-11 13:39:35Z fabien $
22
 */
23
class sfException extends Exception
24
{
25
  protected
26
    $wrappedException = null;
27
 
28
  static protected
29
    $lastException = null;
30
 
31
  /**
32
   * Wraps an Exception.
33
   *
34
   * @param Exception $e An Exception instance
35
   *
36
   * @return sfException An sfException instance that wraps the given Exception object
37
   */
38
  static public function createFromException(Exception $e)
39
  {
40
    $exception = new sfException(sprintf('Wrapped %s: %s', get_class($e), $e->getMessage()));
41
    $exception->setWrappedException($e);
42
    self::$lastException = $e;
43
 
44
    return $exception;
45
  }
46
 
47
  /**
48
   * Sets the wrapped exception.
49
   *
50
   * @param Exception $e An Exception instance
51
   */
52
  public function setWrappedException(Exception $e)
53
  {
54
    $this->wrappedException = $e;
55
 
56
    self::$lastException = $e;
57
  }
58
 
59
  /**
60
   * Gets the last wrapped exception.
61
   *
62
   * @return Exception An Exception instance
63
   */
64
  static public function getLastException()
65
  {
66
    return self::$lastException;
67
  }
68
 
69
  /**
70
   * Clears the $lastException property (added for #6342)
71
   */
72
  static public function clearLastException()
73
  {
74
  	self::$lastException = null;
75
  }
76
 
77
  /**
78
   * Prints the stack trace for this exception.
79
   */
80
  public function printStackTrace()
81
  {
82
    if (null === $this->wrappedException)
83
    {
84
      $this->setWrappedException($this);
85
    }
86
 
87
    $exception = $this->wrappedException;
88
 
89
    if (!sfConfig::get('sf_test'))
90
    {
91
      // log all exceptions in php log
92
      error_log($exception->getMessage());
93
 
94
      // clean current output buffer
95
      while (ob_get_level())
96
      {
97
        if (!ob_end_clean())
98
        {
99
          break;
100
        }
101
      }
102
 
103
      ob_start(sfConfig::get('sf_compressed') ? 'ob_gzhandler' : '');
104
 
105
      header('HTTP/1.0 500 Internal Server Error');
106
    }
107
 
108
    try
109
    {
110
      $this->outputStackTrace($exception);
111
    }
112
    catch (Exception $e)
113
    {
114
    }
115
 
116
    if (!sfConfig::get('sf_test'))
117
    {
118
      exit(1);
119
    }
120
  }
121
 
122
  /**
123
   * Gets the stack trace for this exception.
124
   */
125
  static protected function outputStackTrace(Exception $exception)
126
  {
127
    $format = 'html';
128
    $code   = '500';
129
    $text   = 'Internal Server Error';
130
 
131
    $response = null;
132
    if (class_exists('sfContext', false) && sfContext::hasInstance() && is_object($request = sfContext::getInstance()->getRequest()) && is_object($response = sfContext::getInstance()->getResponse()))
133
    {
134
      $dispatcher = sfContext::getInstance()->getEventDispatcher();
135
 
136
      if (sfConfig::get('sf_logging_enabled'))
137
      {
138
        $dispatcher->notify(new sfEvent($exception, 'application.log', array($exception->getMessage(), 'priority' => sfLogger::ERR)));
139
      }
140
 
141
      $event = $dispatcher->notifyUntil(new sfEvent($exception, 'application.throw_exception'));
142
      if ($event->isProcessed())
143
      {
144
        return;
145
      }
146
 
147
      if ($response->getStatusCode() < 300)
148
      {
149
        // status code has already been sent, but is included here for the purpose of testing
150
        $response->setStatusCode(500);
151
      }
152
 
153
      $response->setContentType('text/html');
154
 
155
      if (!sfConfig::get('sf_test'))
156
      {
157
        foreach ($response->getHttpHeaders() as $name => $value)
158
        {
159
          header($name.': '.$value);
160
        }
161
      }
162
 
163
      $code = $response->getStatusCode();
164
      $text = $response->getStatusText();
165
 
166
      $format = $request->getRequestFormat();
167
      if (!$format)
168
      {
169
        $format = 'html';
170
      }
171
 
172
      if ($mimeType = $request->getMimeType($format))
173
      {
174
        $response->setContentType($mimeType);
175
      }
176
    }
177
    else
178
    {
179
      // a backward compatible default
180
      if (!sfConfig::get('sf_test'))
181
      {
182
        header('Content-Type: text/html; charset='.sfConfig::get('sf_charset', 'utf-8'));
183
      }
184
    }
185
 
186
    // send an error 500 if not in debug mode
187
    if (!sfConfig::get('sf_debug'))
188
    {
189
      if ($template = self::getTemplatePathForError($format, false))
190
      {
191
        include $template;
192
        return;
193
      }
194
    }
195
 
196
    // when using CLI, we force the format to be TXT
197
    if (0 == strncasecmp(PHP_SAPI, 'cli', 3))
198
    {
199
      $format = 'txt';
200
    }
201
 
202
    $message = null === $exception->getMessage() ? 'n/a' : $exception->getMessage();
203
    $name    = get_class($exception);
204
    $traces  = self::getTraces($exception, $format);
205
 
206
    // dump main objects values
207
    $sf_settings = '';
208
    $settingsTable = $requestTable = $responseTable = $globalsTable = $userTable = '';
209
    if (class_exists('sfContext', false) && sfContext::hasInstance())
210
    {
211
      $context = sfContext::getInstance();
212
      $settingsTable = self::formatArrayAsHtml(sfDebug::settingsAsArray());
213
      $requestTable  = self::formatArrayAsHtml(sfDebug::requestAsArray($context->getRequest()));
214
      $responseTable = self::formatArrayAsHtml(sfDebug::responseAsArray($context->getResponse()));
215
      $userTable     = self::formatArrayAsHtml(sfDebug::userAsArray($context->getUser()));
216
      $globalsTable  = self::formatArrayAsHtml(sfDebug::globalsAsArray());
217
    }
218
 
219
    if (isset($response) && $response)
220
    {
221
      $response->sendHttpHeaders();
222
    }
223
 
224
    if ($template = self::getTemplatePathForError($format, true))
225
    {
226
      if (isset($dispatcher))
227
      {
228
        ob_start();
229
        include $template;
230
        $content = ob_get_clean();
231
 
232
        $event = $dispatcher->filter(new sfEvent($response, 'response.filter_content'), $content);
233
 
234
        echo $event->getReturnValue();
235
      }
236
      else
237
      {
238
        include $template;
239
      }
240
 
241
      return;
242
    }
243
  }
244
 
245
  /**
246
   * Returns the path for the template error message.
247
   *
248
   * @param  string  $format The request format
249
   * @param  Boolean $debug  Whether to return a template for the debug mode or not
250
   *
251
   * @return string|Boolean  false if the template cannot be found for the given format,
252
   *                         the absolute path to the template otherwise
253
   */
254
  static public function getTemplatePathForError($format, $debug)
255
  {
256
    $templatePaths = array(
257
      sfConfig::get('sf_app_config_dir').'/error',
258
      sfConfig::get('sf_config_dir').'/error',
259
      dirname(__FILE__).'/data',
260
    );
261
 
262
    $template = sprintf('%s.%s.php', $debug ? 'exception' : 'error', $format);
263
    foreach ($templatePaths as $path)
264
    {
265
      if (null !== $path && is_readable($file = $path.'/'.$template))
266
      {
267
        return $file;
268
      }
269
    }
270
 
271
    return false;
272
  }
273
 
274
  /**
275
   * Returns an array of exception traces.
276
   *
277
   * @param Exception $exception  An Exception implementation instance
278
   * @param string    $format     The trace format (txt or html)
279
   *
280
   * @return array An array of traces
281
   */
282
  static protected function getTraces($exception, $format = 'txt')
283
  {
284
    $traceData = $exception->getTrace();
285
    array_unshift($traceData, array(
286
      'function' => '',
287
      'file'     => $exception->getFile() != null ? $exception->getFile() : null,
288
      'line'     => $exception->getLine() != null ? $exception->getLine() : null,
289
      'args'     => array(),
290
    ));
291
 
292
    $traces = array();
293
    if ($format == 'html')
294
    {
295
      $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 class="code" id="%s" style="display: %s">%s</ul>';
296
    }
297
    else
298
    {
299
      $lineFormat = 'at %s%s%s(%s) in %s line %s';
300
    }
301
 
302
    for ($i = 0, $count = count($traceData); $i < $count; $i++)
303
    {
304
      $line = isset($traceData[$i]['line']) ? $traceData[$i]['line'] : null;
305
      $file = isset($traceData[$i]['file']) ? $traceData[$i]['file'] : null;
306
      $args = isset($traceData[$i]['args']) ? $traceData[$i]['args'] : array();
307
      $traces[] = sprintf($lineFormat,
308
        (isset($traceData[$i]['class']) ? $traceData[$i]['class'] : ''),
309
        (isset($traceData[$i]['type']) ? $traceData[$i]['type'] : ''),
310
        $traceData[$i]['function'],
311
        self::formatArgs($args, false, $format),
312
        self::formatFile($file, $line, $format, null === $file ? 'n/a' : sfDebug::shortenFilePath($file)),
313
        null === $line ? 'n/a' : $line,
314
        'trace_'.$i,
315
        'trace_'.$i,
316
        $i == 0 ? 'block' : 'none',
317
        self::fileExcerpt($file, $line)
318
      );
319
    }
320
 
321
    return $traces;
322
  }
323
 
324
  /**
325
   * Returns an HTML version of an array as YAML.
326
   *
327
   * @param array $values The values array
328
   *
329
   * @return string An HTML string
330
   */
331
  static protected function formatArrayAsHtml($values)
332
  {
333
    return '<pre>'.self::escape(@sfYaml::dump($values)).'</pre>';
334
  }
335
 
336
  /**
337
   * Returns an excerpt of a code file around the given line number.
338
   *
339
   * @param string $file  A file path
340
   * @param int    $line  The selected line number
341
   *
342
   * @return string An HTML string
343
   */
344
  static protected function fileExcerpt($file, $line)
345
  {
346
    if (is_readable($file))
347
    {
348
      $content = preg_split('#<br />#', preg_replace('/^<code>(.*)<\/code>$/s', '$1', highlight_file($file, true)));
349
 
350
      $lines = array();
351
      for ($i = max($line - 3, 1), $max = min($line + 3, count($content)); $i <= $max; $i++)
352
      {
353
        $lines[] = '<li'.($i == $line ? ' class="selected"' : '').'>'.$content[$i - 1].'</li>';
354
      }
355
 
356
      return '<ol start="'.max($line - 3, 1).'">'.implode("\n", $lines).'</ol>';
357
    }
358
  }
359
 
360
  /**
361
   * Formats an array as a string.
362
   *
363
   * @param array   $args     The argument array
364
   * @param boolean $single
365
   * @param string  $format   The format string (html or txt)
366
   *
367
   * @return string
368
   */
369
  static protected function formatArgs($args, $single = false, $format = 'html')
370
  {
371
    $result = array();
372
 
373
    $single and $args = array($args);
374
 
375
    foreach ($args as $key => $value)
376
    {
377
      if (is_object($value))
378
      {
379
        $formattedValue = ($format == 'html' ? '<em>object</em>' : 'object').sprintf("('%s')", get_class($value));
380
      }
381
      else if (is_array($value))
382
      {
383
        $formattedValue = ($format == 'html' ? '<em>array</em>' : 'array').sprintf("(%s)", self::formatArgs($value));
384
      }
385
      else if (is_string($value))
386
      {
387
        $formattedValue = ($format == 'html' ? sprintf("'%s'", self::escape($value)) : "'$value'");
388
      }
389
      else if (null === $value)
390
      {
391
        $formattedValue = ($format == 'html' ? '<em>null</em>' : 'null');
392
      }
393
      else
394
      {
395
        $formattedValue = $value;
396
      }
397
 
398
      $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", self::escape($key), $formattedValue);
399
    }
400
 
401
    return implode(', ', $result);
402
  }
403
 
404
  /**
405
   * Formats a file path.
406
   *
407
   * @param  string  $file   An absolute file path
408
   * @param  integer $line   The line number
409
   * @param  string  $format The output format (txt or html)
410
   * @param  string  $text   Use this text for the link rather than the file path
411
   *
412
   * @return string
413
   */
414
  static protected function formatFile($file, $line, $format = 'html', $text = null)
415
  {
416
    if (null === $text)
417
    {
418
      $text = $file;
419
    }
420
 
421
    if ('html' == $format && $file && $line && $linkFormat = sfConfig::get('sf_file_link_format', ini_get('xdebug.file_link_format')))
422
    {
423
      $link = strtr($linkFormat, array('%f' => $file, '%l' => $line));
424
      $text = sprintf('<a href="%s" title="Click to open this file" class="file_link">%s</a>', $link, $text);
425
    }
426
 
427
    return $text;
428
  }
429
 
430
  /**
431
   * Escapes a string value with html entities
432
   *
433
   * @param  string  $value
434
   *
435
   * @return string
436
   */
437
  static protected function escape($value)
438
  {
439
    if (!is_string($value))
440
    {
441
      return $value;
442
    }
443
 
444
    return htmlspecialchars($value, ENT_QUOTES, sfConfig::get('sf_charset', 'UTF-8'));
445
  }
446
}