1<?php 2/** 3 * Slim Framework (https://slimframework.com) 4 * 5 * @license https://github.com/slimphp/Slim/blob/3.x/LICENSE.md (MIT License) 6 */ 7 8namespace Slim\Handlers; 9 10use Exception; 11use Psr\Http\Message\ResponseInterface; 12use Psr\Http\Message\ServerRequestInterface; 13use RuntimeException; 14use Slim\Http\Body; 15use UnexpectedValueException; 16 17class Error extends AbstractError 18{ 19 /** 20 * @param ServerRequestInterface $request The most recent Request object 21 * @param ResponseInterface $response The most recent Response object 22 * @param Exception $exception The caught Exception object 23 * 24 * @return ResponseInterface 25 * 26 * @throws UnexpectedValueException 27 */ 28 public function __invoke(ServerRequestInterface $request, ResponseInterface $response, Exception $exception) 29 { 30 $contentType = $this->determineContentType($request); 31 switch ($contentType) { 32 case 'application/json': 33 $output = $this->renderJsonErrorMessage($exception); 34 break; 35 36 case 'text/xml': 37 case 'application/xml': 38 $output = $this->renderXmlErrorMessage($exception); 39 break; 40 41 case 'text/html': 42 $output = $this->renderHtmlErrorMessage($exception); 43 break; 44 45 default: 46 throw new UnexpectedValueException('Cannot render unknown content type ' . $contentType); 47 } 48 49 $this->writeToErrorLog($exception); 50 51 $body = new Body(fopen('php://temp', 'r+')); 52 $body->write($output); 53 54 return $response 55 ->withStatus(500) 56 ->withHeader('Content-type', $contentType) 57 ->withBody($body); 58 } 59 60 /** 61 * Render HTML error page 62 * 63 * @param Exception $exception 64 * 65 * @return string 66 */ 67 protected function renderHtmlErrorMessage(Exception $exception) 68 { 69 $title = 'Slim Application Error'; 70 71 if ($this->displayErrorDetails) { 72 $html = '<p>The application could not run because of the following error:</p>'; 73 $html .= '<h2>Details</h2>'; 74 $html .= $this->renderHtmlException($exception); 75 76 while ($exception = $exception->getPrevious()) { 77 $html .= '<h2>Previous exception</h2>'; 78 $html .= $this->renderHtmlExceptionOrError($exception); 79 } 80 } else { 81 $html = '<p>A website error has occurred. Sorry for the temporary inconvenience.</p>'; 82 } 83 84 $output = sprintf( 85 "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'>" . 86 "<title>%s</title><style>body{margin:0;padding:30px;font:12px/1.5 Helvetica,Arial,Verdana," . 87 "sans-serif;}h1{margin:0;font-size:48px;font-weight:normal;line-height:48px;}strong{" . 88 "display:inline-block;width:65px;}</style></head><body><h1>%s</h1>%s</body></html>", 89 $title, 90 $title, 91 $html 92 ); 93 94 return $output; 95 } 96 97 /** 98 * Render exception as HTML. 99 * 100 * Provided for backwards compatibility; use renderHtmlExceptionOrError(). 101 * 102 * @param Exception $exception 103 * 104 * @return string 105 */ 106 protected function renderHtmlException(Exception $exception) 107 { 108 return $this->renderHtmlExceptionOrError($exception); 109 } 110 111 /** 112 * Render exception or error as HTML. 113 * 114 * @param Exception|\Error $exception 115 * 116 * @return string 117 * 118 * @throws RuntimeException 119 */ 120 protected function renderHtmlExceptionOrError($exception) 121 { 122 if (!$exception instanceof Exception && !$exception instanceof \Error) { 123 throw new RuntimeException("Unexpected type. Expected Exception or Error."); 124 } 125 126 $html = sprintf('<div><strong>Type:</strong> %s</div>', get_class($exception)); 127 128 if (($code = $exception->getCode())) { 129 $html .= sprintf('<div><strong>Code:</strong> %s</div>', $code); 130 } 131 132 if (($message = $exception->getMessage())) { 133 $html .= sprintf('<div><strong>Message:</strong> %s</div>', htmlentities($message)); 134 } 135 136 if (($file = $exception->getFile())) { 137 $html .= sprintf('<div><strong>File:</strong> %s</div>', $file); 138 } 139 140 if (($line = $exception->getLine())) { 141 $html .= sprintf('<div><strong>Line:</strong> %s</div>', $line); 142 } 143 144 if (($trace = $exception->getTraceAsString())) { 145 $html .= '<h2>Trace</h2>'; 146 $html .= sprintf('<pre>%s</pre>', htmlentities($trace)); 147 } 148 149 return $html; 150 } 151 152 /** 153 * Render JSON error 154 * 155 * @param Exception $exception 156 * 157 * @return string 158 */ 159 protected function renderJsonErrorMessage(Exception $exception) 160 { 161 $error = [ 162 'message' => 'Slim Application Error', 163 ]; 164 165 if ($this->displayErrorDetails) { 166 $error['exception'] = []; 167 168 do { 169 $error['exception'][] = [ 170 'type' => get_class($exception), 171 'code' => $exception->getCode(), 172 'message' => $exception->getMessage(), 173 'file' => $exception->getFile(), 174 'line' => $exception->getLine(), 175 'trace' => explode("\n", $exception->getTraceAsString()), 176 ]; 177 } while ($exception = $exception->getPrevious()); 178 } 179 180 return json_encode($error, JSON_PRETTY_PRINT); 181 } 182 183 /** 184 * Render XML error 185 * 186 * @param Exception $exception 187 * 188 * @return string 189 */ 190 protected function renderXmlErrorMessage(Exception $exception) 191 { 192 $xml = "<error>\n <message>Slim Application Error</message>\n"; 193 if ($this->displayErrorDetails) { 194 do { 195 $xml .= " <exception>\n"; 196 $xml .= " <type>" . get_class($exception) . "</type>\n"; 197 $xml .= " <code>" . $exception->getCode() . "</code>\n"; 198 $xml .= " <message>" . $this->createCdataSection($exception->getMessage()) . "</message>\n"; 199 $xml .= " <file>" . $exception->getFile() . "</file>\n"; 200 $xml .= " <line>" . $exception->getLine() . "</line>\n"; 201 $xml .= " <trace>" . $this->createCdataSection($exception->getTraceAsString()) . "</trace>\n"; 202 $xml .= " </exception>\n"; 203 } while ($exception = $exception->getPrevious()); 204 } 205 $xml .= "</error>"; 206 207 return $xml; 208 } 209 210 /** 211 * Returns a CDATA section with the given content. 212 * 213 * @param string $content 214 * 215 * @return string 216 */ 217 private function createCdataSection($content) 218 { 219 return sprintf('<![CDATA[%s]]>', str_replace(']]>', ']]]]><![CDATA[>', $content)); 220 } 221} 222