function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next) { /** @var ResponseInterface $response */ $response = $next(); $lang = $this->locale->locale(); // The check for app->translation is made here instead of conditionally adding this middleware // because loaded modules can change this setting. if (!$this->settings->translation || !$lang) { return $response; } if (!isset(self::$translation[$lang])) { // Load and merge all translation files now. self::$translation[$lang] = []; $trans =& self::$translation[$lang]; $folders = $this->settings->languageFolders; foreach ($folders as $folder) { $path = "{$folder}/{$lang}.ini"; $newTrans = fileExists($path) ? parse_ini_file($path) : null; if ($newTrans) { $trans = array_merge($trans, $newTrans); } } if (!$trans) { $paths = array_map(function ($path) { return "<li>" . ErrorConsole::shortFileName($path); }, $folders); throw new ConfigException("A translation file for language <b>{$lang}</b> was not found.\n<p>Search paths:\n<ul>" . implode('', $paths) . "</ul>", FlashType::ERROR); } } $out = preg_replace_callback(self::FIND_TRANS_KEY, function ($args) use($lang) { $a = $args[1]; return empty(self::$translation[$lang][$a]) ? '$' . $a : preg_replace('#\\r?\\n#', '<br>', self::$translation[$lang][$a]); }, $response->getBody()); return $response->withBody($this->responseFactory->makeBody($out)); }
function render(ServerRequestInterface $request, ResponseInterface $response, $error = null) { if ($error) { // On debug mode, a debugging error popup is displayed for Exceptions/Errors. if ($this->devEnv && Http::clientAccepts($request, 'text/html')) { return ErrorConsole::display($error, $this->responseFactory->makeHtmlResponse()); } $status = $error instanceof HttpException ? $error->getCode() : 500; // Errors may contain an additional `getTitle()` method. if (method_exists($error, 'getTitle')) { // The title is assumed to be a plain, one-line string (no formatting). If not, make it so. $title = $error->getTitle(); $message = $error->getMessage(); } else { list($title, $message) = array_pad(explode(PHP_EOL, $error->getMessage(), 2), 2, ''); } $response = $response->withStatus($status); } else { $status = $response->getStatusCode(); $title = $response->getReasonPhrase(); $message = strval($response->getBody()); } /** @var ResponseInterface $response */ $response = $response->withBody($body = $this->responseFactory->makeBody()); // Otherwise, errors are rendered into a format accepted by the HTML client. if (Http::clientAccepts($request, 'text/html')) { $response = $response->withHeader('Content-Type', 'text/html'); $customRenderer = $this->settings->getCustomRenderer($status); if ($customRenderer) { if ($customRenderer instanceof RenderableInterface) { $class = $customRenderer->getContextClass(); $customRenderer->setContext($this->injector->make($class)); } $response = $customRenderer($request, $response, nop()); } else { ob_start(); $this->htmlTemplate($status, $title, $message); $body->write(ob_get_clean()); } } else { $title = strip_tags($title); $message = strip_tags($message); if (Http::clientAccepts($request, 'text/plain') || Http::clientAccepts($request, '*/*')) { $response = $response->withHeader('Content-Type', 'text/plain'); $body->write("{$title}\n{$message}"); } elseif (Http::clientAccepts($request, 'application/json')) { $response = $response->withHeader('Content-Type', 'application/json'); $body->write(json_encode(['error' => ['code' => $status, 'message' => $title, 'info' => $message]], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); } elseif (Http::clientAccepts($request, 'application/xml')) { $response = $response->withHeader('Content-Type', 'application/xml'); $body->write("<?xml version=\"1.0\"?><error><code>{$status}</code><message>{$title}</message><info>{$message}</info></error>"); } } // else render nothing return $response; }
/** * Initializes the web server and sets up the request object. */ function setup() { /** @var ServerRequestInterface $request */ $request = ServerRequestFactory::fromGlobals(); $baseUrl = dirnameEx(get($request->getServerParams(), 'SCRIPT_NAME'), $this->kernelSettings->urlDepth + 1); $this->kernelSettings->baseUrl = $baseUrl; ErrorConsole::setEditorUrl(($baseUrl ? "{$baseUrl}/" : '') . $this->kernelSettings->editorUrl); $request = $request->withAttribute('originalUri', $request->getUri()); $request = $request->withAttribute('baseUri', $this->kernelSettings->baseUrl); $this->request = $request->withAttribute('virtualUri', $this->getVirtualUri($request)); }
public static function globalExceptionHandler($exception) { $handled = false; if (ErrorConsole::$devEnv) { ErrorConsole::display($exception); $handled = true; } if (self::$nextExceptionHandler) { call_user_func(self::$nextExceptionHandler, $exception); } if (!$handled) { @ob_end_clean(); echo "<style>body{background:silver}table {font-family:Menlo,sans-serif;font-size:12px}</style>"; throw $exception; } exit; }
/** * @param string $rootDir */ private function setupDebugging($rootDir) { set_exception_handler([$this, 'exceptionHandler']); $devEnv = env('DEV'); $this->injector->defineParam('devEnv', $devEnv); $webConsole = env('CONSOLE'); $this->injector->defineParam('webConsole', $webConsole); ErrorConsole::init($devEnv, $rootDir); ErrorConsole::setAppName($this->kernelSettings->appName); // Note: the editorUrl can't be set yet. See: WebServer. $settings = new DebugConsoleSettings(); $settings->defaultPanelTitle = 'Inspector'; $settings->defaultPanelIcon = 'fa fa-search'; DebugConsole::init($devEnv, $settings); // Temporarily set framework path mapping here for errors thrown during modules loading. ErrorConsole::setPathsMap($this->kernelSettings->getMainPathMap()); }
/** * Outputs a stack trace up to the first call to a trace function (which may be this one or a wrapper that calls * this one). * <p>It displays detailed timing and memory consumption information about each function/method call. * * <p>It requires a logger panel named 'trace' to be defined. * <p>It also requires XDebug to be installed. * * ##### Usage * * Put the following code at the place where you want the trace log to be captured: * * \PhpKit\WebConsole\DebugConsole\DebugConsole::trace (); * * @throws Exception */ public static function trace() { if (!extension_loaded('xdebug')) { throw new Exception("<kbd>trace()</kbd> requires Xdebug to be installed."); } $v = ini_get('xdebug.collect_params'); ob_start(); ini_set('xdebug.collect_params', 2); xdebug_print_function_stack(); $trace = ob_get_clean(); $trace = preg_replace('@^(?:.*?)<table class=\'xdebug-error xe-xdebug\'(.*?)<tr>(?:.*?)>Location</th></tr>@s', '<table class="__console-table trace"$1<colgroup> <col width=40><col width=72><col width=72><col width=72><col width=75%><col width=25%> <thead><tr><th>#<th>Time (ms)<th>Delta (ms)<th>Mem.(MB)<th>Function<th>Location</tr></thead>', $trace); $trace = preg_replace(['@</table>.*@s', "/align='center'/", '@(trace\\( \\)</td>.*?</tr>)(.*)</table>@s'], ['</table>', 'align=right', '$1</table>'], $trace); $prev = 0; $trace = preg_replace_callback('#<tr><td (.*?)>(.*?)</td><td (.*?)>(.*?)</td><td (.*?)>(.*?)</td><td (.*?)>(.*?)</td><td title=\'(.*?)\'(.*?)>(.*?)</td></tr>#', function ($m) use(&$prev) { $t = $m[4] * 1000; $s = $t - $prev; $d = number_format($s, 1); $dd = $s >= self::PROFILER_WARNING_TRESHOLD ? ' class=__alert' : ''; $prev = $t; $t = number_format($t, 1); $r = number_format($m[6] / 1048576, 3); $p = ErrorConsole::shortFileName($m[9]); $f = substr($m[11], 3); list($fn, $args) = explode('(', $m[8], 2); $info = preg_replace('/[\\w{}]+$/', '<b>$0</b>', $fn) . '(' . $args; return "<tr><th {$m['1']}>{$m['2']}<td {$m['3']}>{$t}<td align=right{$dd}>{$d}<td {$m['5']}>{$r}<td {$m['7']}>{$info}<td class='__type' title='{$p}'{$m['10']}>{$f}</tr>"; }, $trace); ini_set('xdebug.collect_params', $v); self::logger('trace')->write($trace); }
public function showCallLocation() { $namespace = Debug::libraryNamespace(); $base = __DIR__; $stack = debug_backtrace(0); $FNS = self::GLOBAL_LOG_FNS; // Discard frames of all functions that belong to this library. while (!empty($stack) && (isset($stack[0]['file']) && stripos($stack[0]['file'], $base) === 0 || isset($stack[0]['class']) && stripos($stack[0]['class'], $namespace) === 0 || isset($stack[0]['function']) && !isset($FNS[$stack[0]['function']]))) { array_shift($stack); } $trace = $stack ? $stack[0] : []; $path = isset($trace['file']) ? $trace['file'] : ''; $line = isset($trace['line']) ? $trace['line'] : ''; $shortPath = ErrorConsole::shortFileName($path); $shortPath = str_segmentsLast($shortPath, '/'); $location = empty($line) ? $shortPath : ErrorConsole::errorLink($path, $line, 1, "{$shortPath}:{$line}", 'hint--rounded hint--left', 'data-hint'); if ($path != '') { $path = <<<HTML <div class="__debug-location">At {$location}</div> HTML; } $this->write($path); return $this; }
/** * Note: if the exception has a `getTitle()` method, that value is displayed as the popup's header, otherwise the * exception's class name will be shown instead. * * @param Exception|Error $exception * @param string $popupTitle * @param string $stackTrace */ static function renderPopup($exception, $popupTitle, $stackTrace) { self::renderStyles(); ?> <!DOCTYPE HTML><html> <head> <meta charset="UTF-8"> <title><?php echo $popupTitle; ?> </title> </head> <body id="__error"> <div id="__panel"> <div class="__title-bar"><?php echo $popupTitle; ?> </div> <div class="__panel-body"> <div id="__feedback">Please switch to PHPStorm/IDEA to view the code at the error location.</div> <img src="<?php echo self::getIcon(); ?> "> <div class="__message"> <?php $title = method_exists($exception, 'getTitle') ? $exception->getTitle() : self::friendlyClass(get_class($exception)); if ($title) { echo "<h3>{$title}</h3>"; } echo "<div>" . ucfirst(ErrorConsole::processMessage($exception->getMessage())) . "</div>"; if (!empty($exception->info)) { echo "<div class='__info'>{$exception->info}</div>"; } ?> </div> <div id="__error-location"> <?php $link = ErrorConsole::errorLink($exception->getFile(), $exception->getLine(), 1); if ($link) { echo "Thrown from {$link}, line <b>{$exception->getLine()}</b>"; } ?> <div class="__more"> <a id="__more" class=" __btn" href="javascript:void(document.getElementById('__panel').className='__show')" onclick="this.style.display='none';window.setTimeout(function(){document.body.scrollTop=document.getElementById('__error-location').offsetTop})"> Stack trace <span style="font-size:16px">▾</span></a> </div> </div> </div> <div id="__trace"> <?php echo $stackTrace; ?> </div> <iframe name="hidden" style="display:none"></iframe> </body><html> <?php }
/** * Shortcut to log a formatted exception on the provided logger. * * @param LoggerInterface $logger * @param Exception $exception */ public static function logException(LoggerInterface $logger, Exception $exception) { $logger->error(sprintf("%s, at %s(%s)", $exception->getMessage(), ErrorConsole::shortFileName($exception->getFile()), $exception->getLine())); }
/** * @param \Error|\Exception $e * @param Expression $exp * @throws ComponentException */ private function evalError($e, Expression $exp) { throw new ComponentException($this, Debug::grid(['Expression' => Debug::RAW_TEXT . "<kbd>{$exp}</kbd>", 'Compiled' => sprintf('%s<code>%s</code>', Debug::RAW_TEXT, \PhpCode::highlight("{$exp->translated}")), 'Error' => sprintf('%s%s %s', Debug::RAW_TEXT, Debug::typeInfoOf($e), $e->getMessage()), 'At' => sprintf('%s%s, line <b>%s</b>', Debug::RAW_TEXT, ErrorConsole::errorLink($e->getFile(), $e->getLine()), $e->getLine())], 'Error while evaluating data-binding expression')); }
/** * For use by renderers. * * @param string $msg * @return string */ public static function processMessage($msg) { $msg = preg_replace_callback('#<path>([^<]*)</path>#', function ($m) { return ErrorConsole::errorLink($m[1], 1, 1, basename($m[1])); }, $msg); return $msg; }