This repository has been archived on 2022-02-10. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
rangitaki/vendor/slim/slim/Slim/Handlers/Error.php
2016-05-07 12:59:40 +02:00

300 lines
8.9 KiB
PHP

<?php
/**
* Slim Framework (http://slimframework.com)
*
* @link https://github.com/slimphp/Slim
* @copyright Copyright (c) 2011-2016 Josh Lockhart
* @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License)
*/
namespace Slim\Handlers;
use Exception;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Http\Body;
/**
* Default Slim application error handler
*
* It outputs the error message and diagnostic information in either JSON, XML,
* or HTML based on the Accept header.
*/
class Error
{
protected $displayErrorDetails;
/**
* Known handled content types
*
* @var array
*/
protected $knownContentTypes = [
'application/json',
'application/xml',
'text/xml',
'text/html',
];
/**
* Constructor
*
* @param boolean $displayErrorDetails Set to true to display full details
*/
public function __construct($displayErrorDetails = false)
{
$this->displayErrorDetails = (bool)$displayErrorDetails;
}
/**
* Invoke error handler
*
* @param ServerRequestInterface $request The most recent Request object
* @param ResponseInterface $response The most recent Response object
* @param Exception $exception The caught Exception object
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, Exception $exception)
{
$contentType = $this->determineContentType($request);
switch ($contentType) {
case 'application/json':
$output = $this->renderJsonErrorMessage($exception);
break;
case 'text/xml':
case 'application/xml':
$output = $this->renderXmlErrorMessage($exception);
break;
case 'text/html':
$output = $this->renderHtmlErrorMessage($exception);
break;
}
$this->writeToErrorLog($exception);
$body = new Body(fopen('php://temp', 'r+'));
$body->write($output);
return $response
->withStatus(500)
->withHeader('Content-type', $contentType)
->withBody($body);
}
/**
* Write to the error log if displayErrorDetails is false
*
* @param Exception $exception
* @return void
*/
protected function writeToErrorLog($exception)
{
if ($this->displayErrorDetails) {
return;
}
$message = 'Slim Application Error:' . PHP_EOL;
$message .= $this->renderTextException($exception);
while ($exception = $exception->getPrevious()) {
$message .= PHP_EOL . 'Previous exception:' . PHP_EOL;
$message .= $this->renderTextException($exception);
}
$message .= PHP_EOL . 'View in rendered output by enabling the "displayErrorDetails" setting.' . PHP_EOL;
error_log($message);
}
/**
* Render exception as Text.
*
* @param Exception $exception
*
* @return string
*/
protected function renderTextException(Exception $exception)
{
$text = sprintf('Type: %s' . PHP_EOL, get_class($exception));
if (($code = $exception->getCode())) {
$text .= sprintf('Code: %s' . PHP_EOL, $code);
}
if (($message = $exception->getMessage())) {
$text .= sprintf('Message: %s' . PHP_EOL, htmlentities($message));
}
if (($file = $exception->getFile())) {
$text .= sprintf('File: %s' . PHP_EOL, $file);
}
if (($line = $exception->getLine())) {
$text .= sprintf('Line: %s' . PHP_EOL, $line);
}
if (($trace = $exception->getTraceAsString())) {
$text .= sprintf('Trace: %s', $trace);
}
return $text;
}
/**
* Render HTML error page
*
* @param Exception $exception
* @return string
*/
protected function renderHtmlErrorMessage(Exception $exception)
{
$title = 'Slim Application Error';
if ($this->displayErrorDetails) {
$html = '<p>The application could not run because of the following error:</p>';
$html .= '<h2>Details</h2>';
$html .= $this->renderHtmlException($exception);
while ($exception = $exception->getPrevious()) {
$html .= '<h2>Previous exception</h2>';
$html .= $this->renderHtmlException($exception);
}
} else {
$html = '<p>A website error has occurred. Sorry for the temporary inconvenience.</p>';
}
$output = sprintf(
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'>" .
"<title>%s</title><style>body{margin:0;padding:30px;font:12px/1.5 Helvetica,Arial,Verdana," .
"sans-serif;}h1{margin:0;font-size:48px;font-weight:normal;line-height:48px;}strong{" .
"display:inline-block;width:65px;}</style></head><body><h1>%s</h1>%s</body></html>",
$title,
$title,
$html
);
return $output;
}
/**
* Render exception as HTML.
*
* @param Exception $exception
*
* @return string
*/
protected function renderHtmlException(Exception $exception)
{
$html = sprintf('<div><strong>Type:</strong> %s</div>', get_class($exception));
if (($code = $exception->getCode())) {
$html .= sprintf('<div><strong>Code:</strong> %s</div>', $code);
}
if (($message = $exception->getMessage())) {
$html .= sprintf('<div><strong>Message:</strong> %s</div>', htmlentities($message));
}
if (($file = $exception->getFile())) {
$html .= sprintf('<div><strong>File:</strong> %s</div>', $file);
}
if (($line = $exception->getLine())) {
$html .= sprintf('<div><strong>Line:</strong> %s</div>', $line);
}
if (($trace = $exception->getTraceAsString())) {
$html .= '<h2>Trace</h2>';
$html .= sprintf('<pre>%s</pre>', htmlentities($trace));
}
return $html;
}
/**
* Render JSON error
*
* @param Exception $exception
* @return string
*/
protected function renderJsonErrorMessage(Exception $exception)
{
$error = [
'message' => 'Slim Application Error',
];
if ($this->displayErrorDetails) {
$error['exception'] = [];
do {
$error['exception'][] = [
'type' => get_class($exception),
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'trace' => explode("\n", $exception->getTraceAsString()),
];
} while ($exception = $exception->getPrevious());
}
return json_encode($error, JSON_PRETTY_PRINT);
}
/**
* Render XML error
*
* @param Exception $exception
* @return string
*/
protected function renderXmlErrorMessage(Exception $exception)
{
$xml = "<error>\n <message>Slim Application Error</message>\n";
if ($this->displayErrorDetails) {
do {
$xml .= " <exception>\n";
$xml .= " <type>" . get_class($exception) . "</type>\n";
$xml .= " <code>" . $exception->getCode() . "</code>\n";
$xml .= " <message>" . $this->createCdataSection($exception->getMessage()) . "</message>\n";
$xml .= " <file>" . $exception->getFile() . "</file>\n";
$xml .= " <line>" . $exception->getLine() . "</line>\n";
$xml .= " <trace>" . $this->createCdataSection($exception->getTraceAsString()) . "</trace>\n";
$xml .= " </exception>\n";
} while ($exception = $exception->getPrevious());
}
$xml .= "</error>";
return $xml;
}
/**
* Returns a CDATA section with the given content.
*
* @param string $content
* @return string
*/
private function createCdataSection($content)
{
return sprintf('<![CDATA[%s]]>', str_replace(']]>', ']]]]><![CDATA[>', $content));
}
/**
* Determine which content type we know about is wanted using Accept header
*
* @param ServerRequestInterface $request
* @return string
*/
private function determineContentType(ServerRequestInterface $request)
{
$acceptHeader = $request->getHeaderLine('Accept');
$selectedContentTypes = array_intersect(explode(',', $acceptHeader), $this->knownContentTypes);
if (count($selectedContentTypes)) {
return $selectedContentTypes[0];
}
return 'text/html';
}
}