|
|
@ -17,8 +17,6 @@ class HTMLHandler extends Handler { |
|
|
|
protected ?\DOMDocument $_document = null; |
|
|
|
/** The XPath path to the element where the errors should be inserted */ |
|
|
|
protected string $_errorPath = '/html/body'; |
|
|
|
/** If true the handler will output times to the output; defaults to true */ |
|
|
|
protected bool $_outputTime = true; |
|
|
|
/** The PHP-standard date format which to use for times printed to output */ |
|
|
|
protected string $_timeFormat = 'H:i:s'; |
|
|
|
|
|
|
@ -44,8 +42,8 @@ class HTMLHandler extends Handler { |
|
|
|
|
|
|
|
$this->xpath = new \DOMXPath($this->_document); |
|
|
|
$location = $this->xpath->query($this->_errorPath); |
|
|
|
if (count($location) === 0 || !$location->item(0) instanceof \DOMElement) { |
|
|
|
throw new \InvalidArgumentException('Option "errorPath" must correspond to a location that is an instance of \DOMElement'); |
|
|
|
if (count($location) === 0 || (!$location->item(0) instanceof \DOMElement && !$location->item(0) instanceof \DOMDocumentFragment)) { |
|
|
|
throw new \InvalidArgumentException('Option "errorPath" must correspond to a location that is an instance of \DOMElement or \DOMDocumentFragment'); |
|
|
|
} |
|
|
|
$this->errorLocation = $location->item(0); |
|
|
|
} |
|
|
@ -53,161 +51,145 @@ class HTMLHandler extends Handler { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected function buildThrowable(ThrowableController $controller): \DOMDocumentFragment { |
|
|
|
$throwable = $controller->getThrowable(); |
|
|
|
protected function buildOutputThrowable(array $outputThrowable, bool $previous = false): \DOMDocumentFragment { |
|
|
|
$frag = $this->_document->createDocumentFragment(); |
|
|
|
$tFrag = $this->_document->createDocumentFragment(); |
|
|
|
$ip = $frag; |
|
|
|
$hasSiblings = false; |
|
|
|
|
|
|
|
$b = $this->_document->createElement('b'); |
|
|
|
$type = $controller->getErrorType(); |
|
|
|
$class = $throwable::class; |
|
|
|
$b->appendChild($this->_document->createTextNode($type ?? $class)); |
|
|
|
if ($type !== null) { |
|
|
|
$b->firstChild->textContent .= ' '; |
|
|
|
$code = $this->_document->createElement('code'); |
|
|
|
$code->appendChild($this->_document->createTextNode("($class)")); |
|
|
|
$b->appendChild($code); |
|
|
|
if ($previous === false) { |
|
|
|
if (isset($outputThrowable['time'])) { |
|
|
|
$p = $this->_document->createElement('p'); |
|
|
|
$time = $this->_document->createElement('time'); |
|
|
|
$time->setAttribute('datetime', $outputThrowable['time']->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d\TH:i:s.vO')); |
|
|
|
$time->appendChild($this->_document->createTextNode($outputThrowable['time']->format($this->_timeFormat))); |
|
|
|
$p->appendChild($time); |
|
|
|
$frag->appendChild($p); |
|
|
|
|
|
|
|
$div = $this->_document->createElement('div'); |
|
|
|
$frag->appendChild($div); |
|
|
|
$ip = $div; |
|
|
|
} |
|
|
|
} else { |
|
|
|
$span = $this->_document->createElement('span'); |
|
|
|
$span->appendChild($this->_document->createTextNode('Caused by:')); |
|
|
|
$tFrag->appendChild($span); |
|
|
|
$tFrag->appendChild($this->_document->createTextNode(' ')); |
|
|
|
} |
|
|
|
$frag->appendChild($b); |
|
|
|
|
|
|
|
$frag->appendChild($this->_document->createTextNode(': ')); |
|
|
|
$b = $this->_document->createElement('b'); |
|
|
|
$code = $this->_document->createElement('code'); |
|
|
|
$code->appendChild($this->_document->createTextNode($outputThrowable['class'])); |
|
|
|
$b->appendChild($code); |
|
|
|
if (isset($outputThrowable['errorType'])) { |
|
|
|
$b->insertBefore($this->_document->createTextNode("{$outputThrowable['errorType']} ("), $code); |
|
|
|
$b->appendChild($this->_document->createTextNode(')')); |
|
|
|
} |
|
|
|
$tFrag->appendChild($b); |
|
|
|
$tFrag->appendChild($this->_document->createTextNode(': ')); |
|
|
|
$i = $this->_document->createElement('i'); |
|
|
|
$i->appendChild($this->_document->createTextNode($throwable->getMessage())); |
|
|
|
$frag->appendChild($i); |
|
|
|
$frag->appendChild($this->_document->createTextNode(' in file ')); |
|
|
|
$i->appendChild($this->_document->createTextNode($outputThrowable['message'])); |
|
|
|
$tFrag->appendChild($i); |
|
|
|
$tFrag->appendChild($this->_document->createTextNode(' in file ')); |
|
|
|
$code = $this->_document->createElement('code'); |
|
|
|
$code->appendChild($this->_document->createTextNode($throwable->getFile())); |
|
|
|
$frag->appendChild($code); |
|
|
|
$frag->appendChild($this->_document->createTextNode(' on line ' . $throwable->getLine())); |
|
|
|
return $frag; |
|
|
|
} |
|
|
|
|
|
|
|
protected function dispatchCallback(): void { |
|
|
|
$ul = $this->_document->createElement('ul'); |
|
|
|
$this->errorLocation->appendChild($ul); |
|
|
|
|
|
|
|
$allSilent = true; |
|
|
|
foreach ($this->outputBuffer as $o) { |
|
|
|
if ($o->outputCode & self::SILENT) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
$code->appendChild($this->_document->createTextNode($outputThrowable['file'])); |
|
|
|
$tFrag->appendChild($code); |
|
|
|
$tFrag->appendChild($this->_document->createTextNode(" on line {$outputThrowable['line']}")); |
|
|
|
|
|
|
|
$allSilent = false; |
|
|
|
if (isset($outputThrowable['previous'])) { |
|
|
|
$ul = $this->_document->createElement('ul'); |
|
|
|
$li = $this->_document->createElement('li'); |
|
|
|
$li->appendChild($o->output); |
|
|
|
$li->appendChild($this->buildOutputThrowable($outputThrowable['previous'], true)); |
|
|
|
$ul->appendChild($li); |
|
|
|
$ip->appendChild($ul); |
|
|
|
$hasSiblings = true; |
|
|
|
} |
|
|
|
|
|
|
|
if (!$allSilent) { |
|
|
|
$this->print($this->_document->saveHTML()); |
|
|
|
} |
|
|
|
} |
|
|
|
if ($previous === false && isset($outputThrowable['frames'])) { |
|
|
|
$p = $this->_document->createElement('p'); |
|
|
|
$p->appendChild($this->_document->createTextNode('Stack trace:')); |
|
|
|
$ip->appendChild($p); |
|
|
|
|
|
|
|
$ol = $this->_document->createElement('ol'); |
|
|
|
$ip->appendChild($ol); |
|
|
|
foreach ($outputThrowable['frames'] as $frame) { |
|
|
|
$li = $this->_document->createElement('li'); |
|
|
|
$ol->appendChild($li); |
|
|
|
if (isset($frame['args'])) { |
|
|
|
$t = $this->_document->createElement('p'); |
|
|
|
$li->appendChild($t); |
|
|
|
} else { |
|
|
|
$t = $li; |
|
|
|
} |
|
|
|
$b = $this->_document->createElement('b'); |
|
|
|
$code = $this->_document->createElement('code'); |
|
|
|
$b->appendChild($code); |
|
|
|
$t->appendChild($b); |
|
|
|
|
|
|
|
if (isset($frame['class'])) { |
|
|
|
$code->appendChild($this->_document->createTextNode($frame['class'])); |
|
|
|
|
|
|
|
if (isset($frame['errorType'])) { |
|
|
|
$b->insertBefore($this->_document->createTextNode("{$frame['errorType']} ("), $code); |
|
|
|
$b->appendChild($this->_document->createTextNode(')')); |
|
|
|
} elseif (isset($frame['function'])) { |
|
|
|
$code->firstChild->appendData("::{$frame['function']}"); |
|
|
|
} |
|
|
|
} elseif (!empty($frame['function'])) { |
|
|
|
$code->appendChild($this->_document->createTextNode($frame['function'])); |
|
|
|
} |
|
|
|
|
|
|
|
protected function handleCallback(ThrowableController $controller): HandlerOutput { |
|
|
|
$frag = $this->_document->createDocumentFragment(); |
|
|
|
$t->appendChild($this->_document->createTextNode("\u{00a0}\u{00a0}")); |
|
|
|
$code = $this->_document->createElement('code'); |
|
|
|
$code->appendChild($this->_document->createTextNode($frame['file'])); |
|
|
|
$t->appendChild($code); |
|
|
|
$t->appendChild($this->_document->createTextNode(":{$frame['line']}")); |
|
|
|
|
|
|
|
if ($this->_outputTime && $this->_timeFormat !== '') { |
|
|
|
$p = $this->_document->createElement('p'); |
|
|
|
$time = $this->_document->createElement('time'); |
|
|
|
$now = new \DateTimeImmutable(); |
|
|
|
$tz = $now->getTimezone()->getName(); |
|
|
|
if ($tz !== 'UTC' || !in_array($this->_timeFormat, [ 'c', 'Y-m-d\TH:i:sO', 'Y-m-d\TH:i:sP', 'Y-m-d\TH:i:s\Z' ])) { |
|
|
|
$n = ($tz !== 'UTC') ? $now->setTimezone(new \DateTimeZone('UTC')) : $now; |
|
|
|
$time->setAttribute('datetime', $n->format('Y-m-d\TH:i:s\Z')); |
|
|
|
if (isset($frame['args'])) { |
|
|
|
$pre = $this->_document->createElement('pre'); |
|
|
|
$pre->appendChild($this->_document->createTextNode(trim(print_r($frame['args'], true)))); |
|
|
|
$li->appendChild($pre); |
|
|
|
} |
|
|
|
} |
|
|
|
$time->appendChild($this->_document->createTextNode($now->format($this->_timeFormat))); |
|
|
|
$p->appendChild($time); |
|
|
|
$frag->appendChild($p); |
|
|
|
|
|
|
|
$ip = $this->_document->createElement('div'); |
|
|
|
$frag->appendChild($ip); |
|
|
|
$hasSiblings = true; |
|
|
|
} |
|
|
|
|
|
|
|
if ($hasSiblings) { |
|
|
|
$p = $this->_document->createElement('p'); |
|
|
|
$p->appendChild($tFrag); |
|
|
|
$ip->insertBefore($p, $ip->firstChild); |
|
|
|
} else { |
|
|
|
$ip = $frag; |
|
|
|
$ip->appendChild($tFrag); |
|
|
|
} |
|
|
|
|
|
|
|
$p = $this->_document->createElement('p'); |
|
|
|
$p->appendChild($this->buildThrowable($controller)); |
|
|
|
$ip->appendChild($p); |
|
|
|
|
|
|
|
if ($this->_outputPrevious) { |
|
|
|
$prev = $controller->getPrevious(); |
|
|
|
if ($prev !== null) { |
|
|
|
$ul = $this->_document->createElement('ul'); |
|
|
|
$ip->appendChild($ul); |
|
|
|
$f = null; |
|
|
|
while ($prev) { |
|
|
|
if ($f !== null) { |
|
|
|
$p = $this->_document->createElement('p'); |
|
|
|
$p->appendChild($f); |
|
|
|
$li->appendChild($p); |
|
|
|
$ul = $this->_document->createElement('ul'); |
|
|
|
$li->appendChild($ul); |
|
|
|
} |
|
|
|
return $frag; |
|
|
|
} |
|
|
|
|
|
|
|
$li = $this->_document->createElement('li'); |
|
|
|
$ul->appendChild($li); |
|
|
|
$f = $this->_document->createDocumentFragment(); |
|
|
|
$span = $this->_document->createElement('span'); |
|
|
|
$span->appendChild($this->_document->createTextNode('Caused by:')); |
|
|
|
$f->appendChild($span); |
|
|
|
$f->appendChild($this->_document->createTextNode(' ')); |
|
|
|
$f->appendChild($this->buildThrowable($prev)); |
|
|
|
protected function dispatchCallback(): void { |
|
|
|
$frag = $this->_document->createDocumentFragment(); |
|
|
|
$allSilent = true; |
|
|
|
foreach ($this->outputBuffer as $o) { |
|
|
|
if ($o['outputCode'] & self::SILENT) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
$prev = $prev->getPrevious(); |
|
|
|
} |
|
|
|
$li = $this->_document->createElement('li'); |
|
|
|
$li->appendChild($this->buildOutputThrowable($o)); |
|
|
|
$frag->appendChild($li); |
|
|
|
|
|
|
|
$li->appendChild($f); |
|
|
|
} |
|
|
|
$allSilent = false; |
|
|
|
} |
|
|
|
|
|
|
|
if ($this->_outputBacktrace) { |
|
|
|
$frames = $controller->getFrames(); |
|
|
|
if (count($frames) > 0) { |
|
|
|
$p = $this->_document->createElement('p'); |
|
|
|
$p->appendChild($this->_document->createTextNode('Stack trace:')); |
|
|
|
$ip->appendChild($p); |
|
|
|
|
|
|
|
$ol = $this->_document->createElement('ol'); |
|
|
|
$ip->appendChild($ol); |
|
|
|
|
|
|
|
$num = 0; |
|
|
|
foreach ($frames as $frame) { |
|
|
|
$li = $this->_document->createElement('li'); |
|
|
|
$ol->appendChild($li); |
|
|
|
|
|
|
|
$args = (isset($frame['args']) && $this->_backtraceArgFrameLimit >= ++$num); |
|
|
|
if ($args) { |
|
|
|
$t = $this->_document->createElement('p'); |
|
|
|
$li->appendChild($t); |
|
|
|
} else { |
|
|
|
$t = $li; |
|
|
|
} |
|
|
|
|
|
|
|
$b = $this->_document->createElement('b'); |
|
|
|
$code = $this->_document->createElement('code'); |
|
|
|
$b->appendChild($code); |
|
|
|
$t->appendChild($b); |
|
|
|
|
|
|
|
$text = $frame['error'] ?? $frame['class'] ?? ''; |
|
|
|
if (isset($frame['function'])) { |
|
|
|
$text = ((isset($frame['class'])) ? '::' : '') . "{$frame['function']}()"; |
|
|
|
} |
|
|
|
$code->appendChild($this->_document->createTextNode($text)); |
|
|
|
|
|
|
|
$t->appendChild($this->_document->createTextNode("\u{00a0}\u{00a0}")); |
|
|
|
$code = $this->_document->createElement('code'); |
|
|
|
$code->appendChild($this->_document->createTextNode($frame['file'])); |
|
|
|
$t->appendChild($code); |
|
|
|
$t->appendChild($this->_document->createTextNode(":{$frame['line']}")); |
|
|
|
|
|
|
|
if ($args) { |
|
|
|
$pre = $this->_document->createElement('pre'); |
|
|
|
$pre->appendChild($this->_document->createTextNode(print_r($frame['args'], true))); |
|
|
|
$li->appendChild($pre); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (!$allSilent) { |
|
|
|
$ul = $this->_document->createElement('ul'); |
|
|
|
$ul->appendChild($frag); |
|
|
|
$this->errorLocation->appendChild($ul); |
|
|
|
$this->print($this->serializeDocument()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return new HandlerOutput($this->getControlCode(), $this->getOutputCode(), $frag); |
|
|
|
protected function serializeDocument() { |
|
|
|
return $this->_document->saveHTML(); |
|
|
|
} |
|
|
|
} |