|
30 | 30 | class ExceptionHandler
|
31 | 31 | {
|
32 | 32 | private $debug;
|
| 33 | + private $charset; |
33 | 34 | private $handler;
|
34 | 35 | private $caughtBuffer;
|
35 | 36 | private $caughtLength;
|
36 | 37 | private $fileLinkFormat;
|
37 | 38 |
|
38 |
| - public function __construct($debug = true, $fileLinkFormat = null) |
| 39 | + public function __construct($debug = true, $charset = null, $fileLinkFormat = null) |
39 | 40 | {
|
| 41 | + if (false !== strpos($charset, '%') xor false === strpos($fileLinkFormat, '%')) { |
| 42 | + // Swap $charset and $fileLinkFormat for BC reasons |
| 43 | + $pivot = $fileLinkFormat; |
| 44 | + $fileLinkFormat = $charset; |
| 45 | + $charset = $pivot; |
| 46 | + } |
40 | 47 | $this->debug = $debug;
|
| 48 | + $this->charset = $charset ?: ini_get('default_charset') ?: 'UTF-8'; |
41 | 49 | $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
|
42 | 50 | }
|
43 | 51 |
|
44 | 52 | /**
|
45 | 53 | * Registers the exception handler.
|
46 | 54 | *
|
47 |
| - * @param bool $debug |
| 55 | + * @param bool $debug Enable/disable debug mode, where the stack trace is displayed |
| 56 | + * @param string|null $charset The charset used by exception messages |
| 57 | + * @param string|null $fileLinkFormat The IDE link template |
48 | 58 | *
|
49 | 59 | * @return ExceptionHandler The registered exception handler
|
50 | 60 | */
|
51 |
| - public static function register($debug = true, $fileLinkFormat = null) |
| 61 | + public static function register($debug = true, $charset = null, $fileLinkFormat = null) |
52 | 62 | {
|
53 |
| - $handler = new static($debug, $fileLinkFormat); |
| 63 | + $handler = new static($debug, $charset, $fileLinkFormat); |
54 | 64 |
|
55 | 65 | $prev = set_exception_handler(array($handler, 'handle'));
|
56 | 66 | if (is_array($prev) && $prev[0] instanceof ErrorHandler) {
|
@@ -224,7 +234,7 @@ public function getContent(FlattenException $exception)
|
224 | 234 | foreach ($exception->toArray() as $position => $e) {
|
225 | 235 | $ind = $count - $position + 1;
|
226 | 236 | $class = $this->formatClass($e['class']);
|
227 |
| - $message = nl2br(self::utf8Htmlize($e['message'])); |
| 237 | + $message = nl2br($this->escapeHtml($e['message'])); |
228 | 238 | $content .= sprintf(<<<EOF
|
229 | 239 | <h2 class="block_exception clear_fix">
|
230 | 240 | <span class="exception_counter">%d/%d</span>
|
@@ -252,7 +262,7 @@ public function getContent(FlattenException $exception)
|
252 | 262 | } catch (\Exception $e) {
|
253 | 263 | // something nasty happened and we cannot throw an exception anymore
|
254 | 264 | if ($this->debug) {
|
255 |
| - $title = sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), self::utf8Htmlize($e->getMessage())); |
| 265 | + $title = sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $this->escapeHtml($e->getMessage())); |
256 | 266 | } else {
|
257 | 267 | $title = 'Whoops, looks like something went wrong.';
|
258 | 268 | }
|
@@ -338,7 +348,7 @@ private function decorate($content, $css)
|
338 | 348 | <!DOCTYPE html>
|
339 | 349 | <html>
|
340 | 350 | <head>
|
341 |
| - <meta charset="UTF-8" /> |
| 351 | + <meta charset="{$this->charset}" /> |
342 | 352 | <meta name="robots" content="noindex,nofollow" />
|
343 | 353 | <style>
|
344 | 354 | /* Copyright (c) 2010, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.com/yui/license.html */
|
@@ -366,7 +376,7 @@ private function formatClass($class)
|
366 | 376 |
|
367 | 377 | private function formatPath($path, $line)
|
368 | 378 | {
|
369 |
| - $path = self::utf8Htmlize($path); |
| 379 | + $path = $this->escapeHtml($path); |
370 | 380 | $file = preg_match('#[^/\\\\]*$#', $path, $file) ? $file[0] : $path;
|
371 | 381 |
|
372 | 382 | if ($linkFormat = $this->fileLinkFormat) {
|
@@ -394,15 +404,15 @@ private function formatArgs(array $args)
|
394 | 404 | } elseif ('array' === $item[0]) {
|
395 | 405 | $formattedValue = sprintf("<em>array</em>(%s)", is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]);
|
396 | 406 | } elseif ('string' === $item[0]) {
|
397 |
| - $formattedValue = sprintf("'%s'", self::utf8Htmlize($item[1])); |
| 407 | + $formattedValue = sprintf("'%s'", $this->escapeHtml($item[1])); |
398 | 408 | } elseif ('null' === $item[0]) {
|
399 | 409 | $formattedValue = '<em>null</em>';
|
400 | 410 | } elseif ('boolean' === $item[0]) {
|
401 | 411 | $formattedValue = '<em>'.strtolower(var_export($item[1], true)).'</em>';
|
402 | 412 | } elseif ('resource' === $item[0]) {
|
403 | 413 | $formattedValue = '<em>resource</em>';
|
404 | 414 | } else {
|
405 |
| - $formattedValue = str_replace("\n", '', var_export(self::utf8Htmlize((string) $item[1]), true)); |
| 415 | + $formattedValue = str_replace("\n", '', var_export($this->escapeHtml((string) $item[1]), true)); |
406 | 416 | }
|
407 | 417 |
|
408 | 418 | $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue);
|
@@ -430,6 +440,14 @@ protected static function utf8Htmlize($str)
|
430 | 440 | return htmlspecialchars($str, ENT_QUOTES | (PHP_VERSION_ID >= 50400 ? ENT_SUBSTITUTE : 0), 'UTF-8');
|
431 | 441 | }
|
432 | 442 |
|
| 443 | + /** |
| 444 | + * HTML-encodes a string |
| 445 | + */ |
| 446 | + private function escapeHtml($str) |
| 447 | + { |
| 448 | + return htmlspecialchars($str, ENT_QUOTES | (PHP_VERSION_ID >= 50400 ? ENT_SUBSTITUTE : 0), $this->charset); |
| 449 | + } |
| 450 | + |
433 | 451 | /**
|
434 | 452 | * @internal
|
435 | 453 | */
|
|
0 commit comments