start(); } public function start(): void { foreach (get_included_files() as $file) { if (fread(fopen($file, 'r'), 3) === self::BOM) { $this->list[] = [$file, 1, self::BOM]; } } ob_start([$this, 'handler'], 1); } /** @internal */ public function handler(string $s, int $phase): ?string { $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); if (isset($trace[0]['file'], $trace[0]['line'])) { $stack = $trace; unset($stack[0]['line'], $stack[0]['args']); $i = count($this->list); if ($i && $this->list[$i - 1][3] === $stack) { $this->list[$i - 1][2] .= $s; } else { $this->list[] = [$trace[0]['file'], $trace[0]['line'], $s, $stack]; } } return $phase === PHP_OUTPUT_HANDLER_FINAL ? $this->renderHtml() : null; } private function renderHtml(): string { $res = ''; foreach ($this->list as $item) { $stack = []; foreach (array_slice($item[3], 1) as $t) { $t += ['class' => '', 'type' => '', 'function' => '']; $stack[] = "$t[class]$t[type]$t[function]()" . (isset($t['file'], $t['line']) ? ' in ' . basename($t['file']) . ":$t[line]" : ''); } $res .= '' . Helpers::editorLink($item[0], $item[1]) . ' ' . str_replace(self::BOM, 'BOM', Dumper::toHtml($item[2])) . "
\n"; } return $res . '
'; } }