push() and ->pull() is called from the cObj->cObjGetSingle(). * This determines whether the TypoScript parsing activity is logged. But it also slows down the rendering. * * @internal */ public bool $LR = true; protected array $wrapError = [ LogLevel::INFO => ['', ''], LogLevel::NOTICE => ['', ''], LogLevel::WARNING => ['', ''], LogLevel::ERROR => ['', ''], ]; protected array $wrapIcon = [ LogLevel::INFO => '', LogLevel::NOTICE => 'actions-document-info', LogLevel::WARNING => 'status-dialog-warning', LogLevel::ERROR => 'status-dialog-error', ]; protected int $uniqueCounter = 0; protected array $tsStack = [[]]; protected int $tsStackLevel = 0; protected array $tsStackLevelMax = []; protected array $tsStackLog = []; protected int $tsStackPointer = 0; protected array $currentHashPointer = []; /** * @internal */ public function __construct(bool $isEnabled = true) { $this->isEnabled = $isEnabled; } /** * Pushes an element to the TypoScript tracking array * * @param string $tslabel Label string for the entry, eg. TypoScript property name * @param string $value Additional value (@internal, may vanish) * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::cObjGetSingle() * @see pull() */ public function push(string $tslabel, string $value = ''): void { if (!$this->isEnabled) { return; } $this->tsStack[$this->tsStackPointer][] = $tslabel; $this->currentHashPointer[] = 'timetracker_' . $this->uniqueCounter++; $this->tsStackLevel++; $this->tsStackLevelMax[] = $this->tsStackLevel; // setTSlog $k = end($this->currentHashPointer); $this->tsStackLog[$k] = [ 'level' => $this->tsStackLevel, 'tsStack' => $this->tsStack, 'value' => $value, 'starttime' => microtime(true), 'stackPointer' => $this->tsStackPointer, ]; } /** * Pulls an element from the TypoScript tracking array * * @param string $content The content string generated within the push/pull part. * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::cObjGetSingle() * @see push() */ public function pull(string $content = ''): void { if (!$this->isEnabled) { return; } $k = end($this->currentHashPointer); $this->tsStackLog[$k]['endtime'] = microtime(true); $this->tsStackLog[$k]['content'] = $content; $this->tsStackLevel--; array_pop($this->tsStack[$this->tsStackPointer]); array_pop($this->currentHashPointer); } /** * Logs the TypoScript entry * * @param string $content The message string * @param string $logLevel Message type: see LogLevel constants * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::CONTENT() */ public function setTSlogMessage(string $content, string $logLevel = LogLevel::INFO): void { if (!$this->isEnabled) { return; } end($this->currentHashPointer); $k = current($this->currentHashPointer); $placeholder = ''; // Enlarge the "details" column by adding a span if (strlen($content) > 30) { $placeholder = '
'; } $iconFactory = GeneralUtility::makeInstance(IconFactory::class); $this->tsStackLog[$k]['message'][] = $iconFactory->getIcon($this->wrapIcon[$logLevel], IconSize::SMALL)->render() . $this->wrapError[$logLevel][0] . htmlspecialchars($content) . $this->wrapError[$logLevel][1] . $placeholder; } /** * @internal */ public function setEnabled(bool $isEnabled = true): void { $this->isEnabled = $isEnabled; } /** * Sets the starting time * * @see finish() * @internal */ public function start(?float $starttime = null): void { if (!$this->isEnabled) { return; } $this->starttime = $this->getMilliseconds($starttime); } /** * Increases the stack pointer * * @see decStackPointer() * @see \TYPO3\CMS\Frontend\Page\PageGenerator::renderContent() * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::cObjGetSingle() * @internal */ public function incStackPointer(): void { if (!$this->isEnabled) { return; } $this->tsStackPointer++; $this->tsStack[$this->tsStackPointer] = []; } /** * Decreases the stack pointer * * @see incStackPointer() * @see \TYPO3\CMS\Frontend\Page\PageGenerator::renderContent() * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::cObjGetSingle() * @internal */ public function decStackPointer(): void { if (!$this->isEnabled) { return; } unset($this->tsStack[$this->tsStackPointer]); $this->tsStackPointer--; } /** * Gets a microtime value as milliseconds value. * * @param float|null $microtime The microtime value - if not set the current time is used * @return int The microtime value as milliseconds value */ protected function getMilliseconds(?float $microtime = null): int { if (!$this->isEnabled) { return 0; } if ($microtime === null) { $microtime = microtime(true); } return (int)round($microtime * 1000); } /** * Gets the difference between a given microtime value and the starting time as milliseconds. * * @param float|null $microtime The microtime value - if not set the current time is used * @return int The difference between a given microtime value and starting time as milliseconds * @internal */ public function getDifferenceToStarttime(?float $microtime = null): int { return $this->getMilliseconds($microtime) - $this->starttime; } /** * Usually called when the page generation and output is prepared. * * @see start() * @internal */ public function finish(): void { if ($this->isEnabled) { $this->finishtime = microtime(true); } } /** * Get total parse time in milliseconds * @internal */ public function getParseTime(): int { if (!$this->starttime) { $this->start(microtime(true)); } if (!$this->finishtime) { $this->finish(); } return $this->getDifferenceToStarttime($this->finishtime); } /** * @internal */ public function isEnabled(): bool { return $this->isEnabled; } /** * @internal */ public function getTypoScriptLogStack(): array { return $this->tsStackLog; } }