[ Index ]

PHP Cross Reference of phpBB-3.2.11-deutsch

title

Body

[close]

/vendor/symfony/debug/ -> ExceptionHandler.php (source)

   1  <?php
   2  
   3  /*
   4   * This file is part of the Symfony package.
   5   *
   6   * (c) Fabien Potencier <fabien@symfony.com>
   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  namespace Symfony\Component\Debug;
  13  
  14  use Symfony\Component\Debug\Exception\FlattenException;
  15  use Symfony\Component\Debug\Exception\OutOfMemoryException;
  16  use Symfony\Component\HttpFoundation\Response;
  17  
  18  /**
  19   * ExceptionHandler converts an exception to a Response object.
  20   *
  21   * It is mostly useful in debug mode to replace the default PHP/XDebug
  22   * output with something prettier and more useful.
  23   *
  24   * As this class is mainly used during Kernel boot, where nothing is yet
  25   * available, the Response content is always HTML.
  26   *
  27   * @author Fabien Potencier <fabien@symfony.com>
  28   * @author Nicolas Grekas <p@tchwork.com>
  29   */
  30  class ExceptionHandler
  31  {
  32      private $debug;
  33      private $charset;
  34      private $handler;
  35      private $caughtBuffer;
  36      private $caughtLength;
  37      private $fileLinkFormat;
  38  
  39      public function __construct($debug = true, $charset = null, $fileLinkFormat = null)
  40      {
  41          if (false !== strpos($charset, '%')) {
  42              @trigger_error('Providing $fileLinkFormat as second argument to '.__METHOD__.' is deprecated since Symfony 2.8 and will be unsupported in 3.0. Please provide it as third argument, after $charset.', E_USER_DEPRECATED);
  43  
  44              // Swap $charset and $fileLinkFormat for BC reasons
  45              $pivot = $fileLinkFormat;
  46              $fileLinkFormat = $charset;
  47              $charset = $pivot;
  48          }
  49          $this->debug = $debug;
  50          $this->charset = $charset ?: ini_get('default_charset') ?: 'UTF-8';
  51          $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
  52      }
  53  
  54      /**
  55       * Registers the exception handler.
  56       *
  57       * @param bool        $debug          Enable/disable debug mode, where the stack trace is displayed
  58       * @param string|null $charset        The charset used by exception messages
  59       * @param string|null $fileLinkFormat The IDE link template
  60       *
  61       * @return static
  62       */
  63      public static function register($debug = true, $charset = null, $fileLinkFormat = null)
  64      {
  65          $handler = new static($debug, $charset, $fileLinkFormat);
  66  
  67          $prev = set_exception_handler(array($handler, 'handle'));
  68          if (\is_array($prev) && $prev[0] instanceof ErrorHandler) {
  69              restore_exception_handler();
  70              $prev[0]->setExceptionHandler(array($handler, 'handle'));
  71          }
  72  
  73          return $handler;
  74      }
  75  
  76      /**
  77       * Sets a user exception handler.
  78       *
  79       * @param callable $handler An handler that will be called on Exception
  80       *
  81       * @return callable|null The previous exception handler if any
  82       */
  83      public function setHandler($handler)
  84      {
  85          if (null !== $handler && !\is_callable($handler)) {
  86              throw new \LogicException('The exception handler must be a valid PHP callable.');
  87          }
  88          $old = $this->handler;
  89          $this->handler = $handler;
  90  
  91          return $old;
  92      }
  93  
  94      /**
  95       * Sets the format for links to source files.
  96       *
  97       * @param string $format The format for links to source files
  98       *
  99       * @return string The previous file link format
 100       */
 101      public function setFileLinkFormat($format)
 102      {
 103          $old = $this->fileLinkFormat;
 104          $this->fileLinkFormat = $format;
 105  
 106          return $old;
 107      }
 108  
 109      /**
 110       * Sends a response for the given Exception.
 111       *
 112       * To be as fail-safe as possible, the exception is first handled
 113       * by our simple exception handler, then by the user exception handler.
 114       * The latter takes precedence and any output from the former is cancelled,
 115       * if and only if nothing bad happens in this handling path.
 116       */
 117      public function handle(\Exception $exception)
 118      {
 119          if (null === $this->handler || $exception instanceof OutOfMemoryException) {
 120              $this->failSafeHandle($exception);
 121  
 122              return;
 123          }
 124  
 125          $caughtLength = $this->caughtLength = 0;
 126  
 127          ob_start(array($this, 'catchOutput'));
 128          $this->failSafeHandle($exception);
 129          while (null === $this->caughtBuffer && ob_end_flush()) {
 130              // Empty loop, everything is in the condition
 131          }
 132          if (isset($this->caughtBuffer[0])) {
 133              ob_start(array($this, 'cleanOutput'));
 134              echo $this->caughtBuffer;
 135              $caughtLength = ob_get_length();
 136          }
 137          $this->caughtBuffer = null;
 138  
 139          try {
 140              \call_user_func($this->handler, $exception);
 141              $this->caughtLength = $caughtLength;
 142          } catch (\Exception $e) {
 143              if (!$caughtLength) {
 144                  // All handlers failed. Let PHP handle that now.
 145                  throw $exception;
 146              }
 147          }
 148      }
 149  
 150      /**
 151       * Sends a response for the given Exception.
 152       *
 153       * If you have the Symfony HttpFoundation component installed,
 154       * this method will use it to create and send the response. If not,
 155       * it will fallback to plain PHP functions.
 156       */
 157      private function failSafeHandle(\Exception $exception)
 158      {
 159          if (class_exists('Symfony\Component\HttpFoundation\Response', false)
 160              && __CLASS__ !== \get_class($this)
 161              && ($reflector = new \ReflectionMethod($this, 'createResponse'))
 162              && __CLASS__ !== $reflector->class
 163          ) {
 164              $response = $this->createResponse($exception);
 165              $response->sendHeaders();
 166              $response->sendContent();
 167              @trigger_error(sprintf("The %s::createResponse method is deprecated since Symfony 2.8 and won't be called anymore when handling an exception in 3.0.", $reflector->class), E_USER_DEPRECATED);
 168  
 169              return;
 170          }
 171  
 172          $this->sendPhpResponse($exception);
 173      }
 174  
 175      /**
 176       * Sends the error associated with the given Exception as a plain PHP response.
 177       *
 178       * This method uses plain PHP functions like header() and echo to output
 179       * the response.
 180       *
 181       * @param \Exception|FlattenException $exception An \Exception or FlattenException instance
 182       */
 183      public function sendPhpResponse($exception)
 184      {
 185          if (!$exception instanceof FlattenException) {
 186              $exception = FlattenException::create($exception);
 187          }
 188  
 189          if (!headers_sent()) {
 190              header(sprintf('HTTP/1.0 %s', $exception->getStatusCode()));
 191              foreach ($exception->getHeaders() as $name => $value) {
 192                  header($name.': '.$value, false);
 193              }
 194              header('Content-Type: text/html; charset='.$this->charset);
 195          }
 196  
 197          echo $this->decorate($this->getContent($exception), $this->getStylesheet($exception));
 198      }
 199  
 200      /**
 201       * Creates the error Response associated with the given Exception.
 202       *
 203       * @param \Exception|FlattenException $exception An \Exception or FlattenException instance
 204       *
 205       * @return Response A Response instance
 206       *
 207       * @deprecated since 2.8, to be removed in 3.0.
 208       */
 209      public function createResponse($exception)
 210      {
 211          @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0.', E_USER_DEPRECATED);
 212  
 213          if (!$exception instanceof FlattenException) {
 214              $exception = FlattenException::create($exception);
 215          }
 216  
 217          return Response::create($this->getHtml($exception), $exception->getStatusCode(), $exception->getHeaders())->setCharset($this->charset);
 218      }
 219  
 220      /**
 221       * Gets the full HTML content associated with the given exception.
 222       *
 223       * @param \Exception|FlattenException $exception An \Exception or FlattenException instance
 224       *
 225       * @return string The HTML content as a string
 226       */
 227      public function getHtml($exception)
 228      {
 229          if (!$exception instanceof FlattenException) {
 230              $exception = FlattenException::create($exception);
 231          }
 232  
 233          return $this->decorate($this->getContent($exception), $this->getStylesheet($exception));
 234      }
 235  
 236      /**
 237       * Gets the HTML content associated with the given exception.
 238       *
 239       * @return string The content as a string
 240       */
 241      public function getContent(FlattenException $exception)
 242      {
 243          switch ($exception->getStatusCode()) {
 244              case 404:
 245                  $title = 'Sorry, the page you are looking for could not be found.';
 246                  break;
 247              default:
 248                  $title = 'Whoops, looks like something went wrong.';
 249          }
 250  
 251          $content = '';
 252          if ($this->debug) {
 253              try {
 254                  $count = \count($exception->getAllPrevious());
 255                  $total = $count + 1;
 256                  foreach ($exception->toArray() as $position => $e) {
 257                      $ind = $count - $position + 1;
 258                      $class = $this->formatClass($e['class']);
 259                      $message = nl2br($this->escapeHtml($e['message']));
 260                      $content .= sprintf(<<<'EOF'
 261                          <h2 class="block_exception clear_fix">
 262                              <span class="exception_counter">%d/%d</span>
 263                              <span class="exception_title">%s%s:</span>
 264                              <span class="exception_message">%s</span>
 265                          </h2>
 266                          <div class="block">
 267                              <ol class="traces list_exception">
 268  
 269  EOF
 270                          , $ind, $total, $class, $this->formatPath($e['trace'][0]['file'], $e['trace'][0]['line']), $message);
 271                      foreach ($e['trace'] as $trace) {
 272                          $content .= '       <li>';
 273                          if ($trace['function']) {
 274                              $content .= sprintf('at %s%s%s(%s)', $this->formatClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args']));
 275                          }
 276                          if (isset($trace['file']) && isset($trace['line'])) {
 277                              $content .= $this->formatPath($trace['file'], $trace['line']);
 278                          }
 279                          $content .= "</li>\n";
 280                      }
 281  
 282                      $content .= "    </ol>\n</div>\n";
 283                  }
 284              } catch (\Exception $e) {
 285                  // something nasty happened and we cannot throw an exception anymore
 286                  if ($this->debug) {
 287                      $title = sprintf('Exception thrown when handling an exception (%s: %s)', \get_class($e), $this->escapeHtml($e->getMessage()));
 288                  } else {
 289                      $title = 'Whoops, looks like something went wrong.';
 290                  }
 291              }
 292          }
 293  
 294          return <<<EOF
 295              <div id="sf-resetcontent" class="sf-reset">
 296                  <h1>$title</h1>
 297                  $content
 298              </div>
 299  EOF;
 300      }
 301  
 302      /**
 303       * Gets the stylesheet associated with the given exception.
 304       *
 305       * @return string The stylesheet as a string
 306       */
 307      public function getStylesheet(FlattenException $exception)
 308      {
 309          return <<<'EOF'
 310              .sf-reset { font: 11px Verdana, Arial, sans-serif; color: #333 }
 311              .sf-reset .clear { clear:both; height:0; font-size:0; line-height:0; }
 312              .sf-reset .clear_fix:after { display:block; height:0; clear:both; visibility:hidden; }
 313              .sf-reset .clear_fix { display:inline-block; }
 314              .sf-reset * html .clear_fix { height:1%; }
 315              .sf-reset .clear_fix { display:block; }
 316              .sf-reset, .sf-reset .block { margin: auto }
 317              .sf-reset abbr { border-bottom: 1px dotted #000; cursor: help; }
 318              .sf-reset p { font-size:14px; line-height:20px; color:#868686; padding-bottom:20px }
 319              .sf-reset strong { font-weight:bold; }
 320              .sf-reset a { color:#6c6159; cursor: default; }
 321              .sf-reset a img { border:none; }
 322              .sf-reset a:hover { text-decoration:underline; }
 323              .sf-reset em { font-style:italic; }
 324              .sf-reset h1, .sf-reset h2 { font: 20px Georgia, "Times New Roman", Times, serif }
 325              .sf-reset .exception_counter { background-color: #fff; color: #333; padding: 6px; float: left; margin-right: 10px; float: left; display: block; }
 326              .sf-reset .exception_title { margin-left: 3em; margin-bottom: 0.7em; display: block; }
 327              .sf-reset .exception_message { margin-left: 3em; display: block; }
 328              .sf-reset .traces li { font-size:12px; padding: 2px 4px; list-style-type:decimal; margin-left:20px; }
 329              .sf-reset .block { background-color:#FFFFFF; padding:10px 28px; margin-bottom:20px;
 330                  -webkit-border-bottom-right-radius: 16px;
 331                  -webkit-border-bottom-left-radius: 16px;
 332                  -moz-border-radius-bottomright: 16px;
 333                  -moz-border-radius-bottomleft: 16px;
 334                  border-bottom-right-radius: 16px;
 335                  border-bottom-left-radius: 16px;
 336                  border-bottom:1px solid #ccc;
 337                  border-right:1px solid #ccc;
 338                  border-left:1px solid #ccc;
 339                  word-wrap: break-word;
 340              }
 341              .sf-reset .block_exception { background-color:#ddd; color: #333; padding:20px;
 342                  -webkit-border-top-left-radius: 16px;
 343                  -webkit-border-top-right-radius: 16px;
 344                  -moz-border-radius-topleft: 16px;
 345                  -moz-border-radius-topright: 16px;
 346                  border-top-left-radius: 16px;
 347                  border-top-right-radius: 16px;
 348                  border-top:1px solid #ccc;
 349                  border-right:1px solid #ccc;
 350                  border-left:1px solid #ccc;
 351                  overflow: hidden;
 352                  word-wrap: break-word;
 353              }
 354              .sf-reset a { background:none; color:#868686; text-decoration:none; }
 355              .sf-reset a:hover { background:none; color:#313131; text-decoration:underline; }
 356              .sf-reset ol { padding: 10px 0; }
 357              .sf-reset h1 { background-color:#FFFFFF; padding: 15px 28px; margin-bottom: 20px;
 358                  -webkit-border-radius: 10px;
 359                  -moz-border-radius: 10px;
 360                  border-radius: 10px;
 361                  border: 1px solid #ccc;
 362              }
 363  EOF;
 364      }
 365  
 366      private function decorate($content, $css)
 367      {
 368          return <<<EOF
 369  <!DOCTYPE html>
 370  <html>
 371      <head>
 372          <meta charset="{$this->charset}" />
 373          <meta name="robots" content="noindex,nofollow" />
 374          <style>
 375              /* Copyright (c) 2010, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.com/yui/license.html */
 376              html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:text-top;}sub{vertical-align:text-bottom;}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}input,textarea,select{*font-size:100%;}legend{color:#000;}
 377  
 378              html { background: #eee; padding: 10px }
 379              img { border: 0; }
 380              #sf-resetcontent { width:970px; margin:0 auto; }
 381              $css
 382          </style>
 383      </head>
 384      <body>
 385          $content
 386      </body>
 387  </html>
 388  EOF;
 389      }
 390  
 391      private function formatClass($class)
 392      {
 393          $parts = explode('\\', $class);
 394  
 395          return sprintf('<abbr title="%s">%s</abbr>', $class, array_pop($parts));
 396      }
 397  
 398      private function formatPath($path, $line)
 399      {
 400          $path = $this->escapeHtml($path);
 401          $file = preg_match('#[^/\\\\]*$#', $path, $file) ? $file[0] : $path;
 402  
 403          if ($linkFormat = $this->fileLinkFormat) {
 404              $link = strtr($this->escapeHtml($linkFormat), array('%f' => $path, '%l' => (int) $line));
 405  
 406              return sprintf(' in <a href="%s" title="Go to source">%s line %d</a>', $link, $file, $line);
 407          }
 408  
 409          return sprintf(' in <a title="%s line %3$d" ondblclick="var f=this.innerHTML;this.innerHTML=this.title;this.title=f;">%s line %d</a>', $path, $file, $line);
 410      }
 411  
 412      /**
 413       * Formats an array as a string.
 414       *
 415       * @param array $args The argument array
 416       *
 417       * @return string
 418       */
 419      private function formatArgs(array $args)
 420      {
 421          $result = array();
 422          foreach ($args as $key => $item) {
 423              if ('object' === $item[0]) {
 424                  $formattedValue = sprintf('<em>object</em>(%s)', $this->formatClass($item[1]));
 425              } elseif ('array' === $item[0]) {
 426                  $formattedValue = sprintf('<em>array</em>(%s)', \is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]);
 427              } elseif ('string' === $item[0]) {
 428                  $formattedValue = sprintf("'%s'", $this->escapeHtml($item[1]));
 429              } elseif ('null' === $item[0]) {
 430                  $formattedValue = '<em>null</em>';
 431              } elseif ('boolean' === $item[0]) {
 432                  $formattedValue = '<em>'.strtolower(var_export($item[1], true)).'</em>';
 433              } elseif ('resource' === $item[0]) {
 434                  $formattedValue = '<em>resource</em>';
 435              } else {
 436                  $formattedValue = str_replace("\n", '', var_export($this->escapeHtml((string) $item[1]), true));
 437              }
 438  
 439              $result[] = \is_int($key) ? $formattedValue : sprintf("'%s' => %s", $this->escapeHtml($key), $formattedValue);
 440          }
 441  
 442          return implode(', ', $result);
 443      }
 444  
 445      /**
 446       * Returns an UTF-8 and HTML encoded string.
 447       *
 448       * @deprecated since version 2.7, to be removed in 3.0.
 449       */
 450      protected static function utf8Htmlize($str)
 451      {
 452          @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.7 and will be removed in 3.0.', E_USER_DEPRECATED);
 453  
 454          return htmlspecialchars($str, ENT_QUOTES | (\PHP_VERSION_ID >= 50400 ? ENT_SUBSTITUTE : 0), 'UTF-8');
 455      }
 456  
 457      /**
 458       * HTML-encodes a string.
 459       */
 460      private function escapeHtml($str)
 461      {
 462          return htmlspecialchars($str, ENT_QUOTES | (\PHP_VERSION_ID >= 50400 ? ENT_SUBSTITUTE : 0), $this->charset);
 463      }
 464  
 465      /**
 466       * @internal
 467       */
 468      public function catchOutput($buffer)
 469      {
 470          $this->caughtBuffer = $buffer;
 471  
 472          return '';
 473      }
 474  
 475      /**
 476       * @internal
 477       */
 478      public function cleanOutput($buffer)
 479      {
 480          if ($this->caughtLength) {
 481              // use substr_replace() instead of substr() for mbstring overloading resistance
 482              $cleanBuffer = substr_replace($buffer, '', 0, $this->caughtLength);
 483              if (isset($cleanBuffer[0])) {
 484                  $buffer = $cleanBuffer;
 485              }
 486          }
 487  
 488          return $buffer;
 489      }
 490  }


Generated: Wed Nov 11 20:33:01 2020 Cross-referenced by PHPXref 0.7.1