‪TYPO3CMS  ‪main
TimeTracker.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\Log\LogLevel;
22 use TYPO3\CMS\Core\Imaging\IconSize;
26 
32 {
36  protected bool ‪$isEnabled = false;
37 
41  public int ‪$starttime = 0;
42 
46  protected float ‪$finishtime = 0;
47 
52  public bool ‪$LR = true;
53 
54  public array ‪$printConf = [
55  'showParentKeys' => true,
56  'contentLength' => 10000,
57  // Determines max length of displayed content before it gets cropped.
58  'contentLength_FILE' => 400,
59  // Determines max length of displayed content FROM FILE cObjects before it gets cropped. Reason is that most FILE cObjects are huge and often used as template-code.
60  'flag_tree' => true,
61  'flag_messages' => true,
62  'flag_content' => false,
63  'allTime' => false,
64  'keyLgd' => 40,
65  ];
66 
67  public array ‪$wrapError = [
68  LogLevel::INFO => ['', ''],
69  LogLevel::NOTICE => ['<strong>', '</strong>'],
70  LogLevel::WARNING => ['<strong style="color:#ff6600;">', '</strong>'],
71  LogLevel::ERROR => ['<strong style="color:#ff0000;">', '</strong>'],
72  ];
73 
74  public array ‪$wrapIcon = [
75  LogLevel::INFO => '',
76  LogLevel::NOTICE => 'actions-document-info',
77  LogLevel::WARNING => 'status-dialog-warning',
78  LogLevel::ERROR => 'status-dialog-error',
79  ];
80 
81  public int ‪$uniqueCounter = 0;
82  public array ‪$tsStack = [[]];
83  public int ‪$tsStackLevel = 0;
84  public array ‪$tsStackLevelMax = [];
85  public array ‪$tsStackLog = [];
86  public int ‪$tsStackPointer = 0;
87  public array ‪$currentHashPointer = [];
88 
93  public int ‪$highlightLongerThan = 0;
94 
95  public function ‪__construct(bool ‪$isEnabled = true)
96  {
97  $this->isEnabled = ‪$isEnabled;
98  }
99 
100  public function ‪setEnabled(bool ‪$isEnabled = true)
101  {
102  $this->isEnabled = ‪$isEnabled;
103  }
104 
110  public function ‪start(?float ‪$starttime = null)
111  {
112  if (!$this->isEnabled) {
113  return;
114  }
115  $this->starttime = $this->‪getMilliseconds($starttime);
116  }
117 
126  public function ‪push(string $tslabel, string $value = ''): void
127  {
128  if (!$this->isEnabled) {
129  return;
130  }
131  $this->tsStack[‪$this->tsStackPointer][] = $tslabel;
132  $this->currentHashPointer[] = 'timetracker_' . $this->uniqueCounter++;
133  $this->tsStackLevel++;
134  $this->tsStackLevelMax[] = ‪$this->tsStackLevel;
135  // setTSlog
136  $k = end($this->currentHashPointer);
137  $this->tsStackLog[$k] = [
138  'level' => ‪$this->tsStackLevel,
139  'tsStack' => ‪$this->tsStack,
140  'value' => $value,
141  'starttime' => microtime(true),
142  'stackPointer' => ‪$this->tsStackPointer,
143  ];
144  }
145 
153  public function ‪pull(string $content = ''): void
154  {
155  if (!$this->isEnabled) {
156  return;
157  }
158  $k = end($this->currentHashPointer);
159  $this->tsStackLog[$k]['endtime'] = microtime(true);
160  $this->tsStackLog[$k]['content'] = $content;
161  $this->tsStackLevel--;
162  array_pop($this->tsStack[$this->tsStackPointer]);
163  array_pop($this->currentHashPointer);
164  }
165 
173  public function ‪setTSlogMessage(string $content, string $logLevel = LogLevel::INFO)
174  {
175  if (!$this->isEnabled) {
176  return;
177  }
178  end($this->currentHashPointer);
179  $k = current($this->currentHashPointer);
180  $placeholder = '';
181  // Enlarge the "details" column by adding a span
182  if (strlen($content) > 30) {
183  $placeholder = '<br /><span style="width: 300px; height: 1px; display: inline-block;"></span>';
184  }
185  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
186  $this->tsStackLog[$k]['message'][] = $iconFactory->getIcon($this->wrapIcon[$logLevel], IconSize::SMALL)->render() . $this->wrapError[$logLevel][0] . htmlspecialchars($content) . $this->wrapError[$logLevel][1] . $placeholder;
187  }
188 
195  public function ‪setTSselectQuery(array $data, string $msg = ''): void
196  {
197  if (!$this->isEnabled) {
198  return;
199  }
200  end($this->currentHashPointer);
201  $k = current($this->currentHashPointer);
202  if ($msg !== '') {
203  $data['msg'] = $msg;
204  }
205  $this->tsStackLog[$k]['selectQuery'][] = $data;
206  }
207 
215  public function ‪incStackPointer(): void
216  {
217  if (!$this->isEnabled) {
218  return;
219  }
220  $this->tsStackPointer++;
221  $this->tsStack[‪$this->tsStackPointer] = [];
222  }
223 
231  public function ‪decStackPointer(): void
232  {
233  if (!$this->isEnabled) {
234  return;
235  }
236  unset($this->tsStack[$this->tsStackPointer]);
237  $this->tsStackPointer--;
238  }
239 
246  public function ‪getMilliseconds(?float $microtime = null): int
247  {
248  if (!$this->isEnabled) {
249  return 0;
250  }
251  if ($microtime === null) {
252  $microtime = microtime(true);
253  }
254  return (int)round($microtime * 1000);
255  }
256 
263  public function ‪getDifferenceToStarttime(float $microtime = null): int
264  {
265  return $this->‪getMilliseconds($microtime) - ‪$this->starttime;
266  }
267 
273  public function ‪finish(): void
274  {
275  if ($this->isEnabled) {
276  $this->finishtime = microtime(true);
277  }
278  }
279 
283  public function ‪getParseTime(): int
284  {
285  if (!$this->starttime) {
286  $this->‪start(microtime(true));
287  }
288  if (!$this->finishtime) {
289  $this->‪finish();
290  }
291  return $this->‪getDifferenceToStarttime($this->finishtime ?? null);
292  }
293 
294  /*******************************************
295  *
296  * Printing the parsing time information (for Admin Panel)
297  *
298  *******************************************/
304  public function ‪printTSlog(): string
305  {
306  if (!$this->isEnabled) {
307  return '';
308  }
309  // Calculate times and keys for the tsStackLog
310  foreach ($this->tsStackLog as &$data) {
311  $data['endtime'] = $this->‪getDifferenceToStarttime($data['endtime'] ?? 0);
312  $data['starttime'] = $this->‪getDifferenceToStarttime($data['starttime'] ?? 0);
313  $data['deltatime'] = $data['endtime'] - $data['starttime'];
314  if (isset($data['tsStack']) && is_array($data['tsStack'])) {
315  $data['key'] = implode($data['stackPointer'] ? '.' : '/', end($data['tsStack']));
316  }
317  }
318  unset($data);
319  // Create hierarchical array of keys pointing to the stack
320  $arr = [];
321  foreach ($this->tsStackLog as $uniqueId => $data) {
322  $this->‪createHierarchyArray($arr, $data['level'] ?? 0, $uniqueId);
323  }
324  // Parsing the registered content and create icon-html for the tree
325  $this->tsStackLog[$arr['0.'][0]]['content'] = $this->‪fixContent($arr['0.'], $this->tsStackLog[$arr['0.'][0]]['content'] ?? '', '', $arr['0.'][0]);
326  // Displaying the tree:
327  $outputArr = [];
328  $outputArr[] = $this->‪fw('TypoScript Key');
329  $outputArr[] = $this->‪fw('Value');
330  if ($this->printConf['allTime']) {
331  $outputArr[] = $this->‪fw('Time');
332  $outputArr[] = $this->‪fw('Own');
333  $outputArr[] = $this->‪fw('Sub');
334  $outputArr[] = $this->‪fw('Total');
335  } else {
336  $outputArr[] = $this->‪fw('Own');
337  }
338  $outputArr[] = $this->‪fw('Details');
339  $out = '';
340  foreach ($outputArr as $row) {
341  $out .= '<th>' . $row . '</th>';
342  }
343  $out = '<thead><tr>' . $out . '</tr></thead>';
344  $flag_tree = $this->printConf['flag_tree'];
345  $flag_messages = $this->printConf['flag_messages'];
346  $flag_content = $this->printConf['flag_content'];
347  $keyLgd = (int)$this->printConf['keyLgd'];
348  $c = 0;
349  foreach ($this->tsStackLog as $data) {
350  $logRowClass = '';
351  if ($this->highlightLongerThan && (int)$data['owntime'] > ‪$this->highlightLongerThan) {
352  $logRowClass = 'typo3-adminPanel-logRow-highlight';
353  }
354  $item = '';
355  // If first...
356  if (!$c) {
357  $data['icons'] = '';
358  $data['key'] = 'Script Start';
359  $data['value'] = '';
360  }
361  // Key label:
362  $keyLabel = '';
363  $stackPointer = $data['stackPointer'] ?? false;
364  if (!$flag_tree && $stackPointer) {
365  $temp = [];
366  foreach ($data['tsStack'] as $k => $v) {
367  $temp[] = ‪GeneralUtility::fixed_lgd_cs(implode($k ? '.' : '/', $v), -$keyLgd);
368  }
369  array_pop($temp);
370  $temp = array_reverse($temp);
371  array_pop($temp);
372  if (!empty($temp)) {
373  $keyLabel = '<br /><span style="color:#999999;">' . implode('<br />', $temp) . '</span>';
374  }
375  }
376  if ($flag_tree) {
377  $tmp = GeneralUtility::trimExplode('.', $data['key'], true);
378  $theLabel = end($tmp);
379  } else {
380  $theLabel = $data['key'];
381  }
382  $theLabel = ‪GeneralUtility::fixed_lgd_cs($theLabel, -$keyLgd);
383  $theLabel = $stackPointer ? '<span class="stackPointer">' . $theLabel . '</span>' : $theLabel;
384  $keyLabel = $theLabel . $keyLabel;
385  $item .= '<th scope="row" class="typo3-adminPanel-table-cell-key ' . $logRowClass . '">' . ($flag_tree ? $data['icons'] : '') . $this->‪fw($keyLabel) . '</th>';
386  // Key value:
387  $keyValue = $data['value'];
388  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime">' . $this->‪fw(htmlspecialchars($keyValue)) . '</td>';
389  $ownTime = (string)($data['owntime'] ?? '');
390  if ($this->printConf['allTime']) {
391  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw((string)$data['starttime']) . '</td>';
392  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw($ownTime) . '</td>';
393  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw(($data['subtime'] ? '+' . $data['subtime'] : '')) . '</td>';
394  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw(($data['subtime'] ? '=' . $data['deltatime'] : '')) . '</td>';
395  } else {
396  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw($ownTime) . '</td>';
397  }
398  // Messages:
399  $msgArr = [];
400  $msg = '';
401  if ($flag_messages && is_array($data['message'] ?? null)) {
402  foreach ($data['message'] as $v) {
403  $msgArr[] = nl2br($v);
404  }
405  }
406  if ($flag_content && (string)$data['content'] !== '') {
407  $maxlen = 120;
408  // Break lines which are too longer than $maxlen chars (can happen if content contains long paths...)
409  if (preg_match_all('/(\\S{' . $maxlen . ',})/', $data['content'], $reg)) {
410  foreach ($reg[1] as $key => $match) {
411  $match = preg_replace('/(.{' . $maxlen . '})/', '$1 ', $match);
412  $data['content'] = str_replace($reg[0][$key], $match, $data['content']);
413  }
414  }
415  $msgArr[] = nl2br($data['content']);
416  }
417  if (!empty($msgArr)) {
418  $msg = implode('<br>', $msgArr);
419  }
420  $item .= '<td class="typo3-adminPanel-table-cell-content">' . $this->‪fw($msg) . '</td>';
421  $out .= '<tr>' . $item . '</tr>';
422  $c++;
423  }
424  return '<div class="typo3-adminPanel-table-overflow"><table class="typo3-adminPanel-table typo3-adminPanel-table-debug">' . $out . '</table></div>';
425  }
426 
436  protected function ‪fixContent(array &$arr, string $content, string $depthData = '', string $vKey = ''): string
437  {
438  $entriesCount = 0;
439  $c = 0;
440  // First, find number of entries
441  foreach ($arr as $k => $v) {
442  //do not count subentries (the one ending with dot, eg. '9.'
444  $entriesCount++;
445  }
446  }
447  // Traverse through entries
448  $subtime = 0;
449  foreach ($arr as $k => $v) {
451  $c++;
452  $hasChildren = isset($arr[$k . '.']);
453  $lastEntry = $entriesCount === $c;
454 
455  $PM = '<span class="treeline-icon treeline-icon-join' . ($lastEntry ? 'bottom' : '') . '"></span>';
456 
457  $this->tsStackLog[$v]['icons'] = $depthData . $PM;
458  if (($this->tsStackLog[$v]['content'] ?? '') !== '') {
459  $content = str_replace($this->tsStackLog[$v]['content'], $v, $content);
460  }
461  if ($hasChildren) {
462  $lineClass = $lastEntry ? 'treeline-icon-clear' : 'treeline-icon-line';
463  $this->tsStackLog[$v]['content'] = $this->‪fixContent(
464  $arr[$k . '.'],
465  ($this->tsStackLog[$v]['content'] ?? ''),
466  $depthData . '<span class="treeline-icon ' . $lineClass . '"></span>',
467  $v
468  );
469  } else {
470  $this->tsStackLog[$v]['content'] = $this->‪fixCLen(($this->tsStackLog[$v]['content'] ?? ''), $this->tsStackLog[$v]['value']);
471  $this->tsStackLog[$v]['subtime'] = '';
472  $this->tsStackLog[$v]['owntime'] = $this->tsStackLog[$v]['deltatime'];
473  }
474  $subtime += $this->tsStackLog[$v]['deltatime'];
475  }
476  }
477  // Set content with special chars
478  if (isset($this->tsStackLog[$vKey])) {
479  $this->tsStackLog[$vKey]['subtime'] = $subtime;
480  $this->tsStackLog[$vKey]['owntime'] = $this->tsStackLog[$vKey]['deltatime'] - $subtime;
481  }
482  $content = $this->‪fixCLen($content, $this->tsStackLog[$vKey]['value']);
483  // Traverse array again, this time substitute the unique hash with the red key
484  foreach ($arr as $k => $v) {
486  if ($this->tsStackLog[$v]['content'] !== '') {
487  $content = str_replace($v, '<strong style="color:red;">[' . $this->tsStackLog[$v]['key'] . ']</strong>', $content);
488  }
489  }
490  }
491  // Return the content
492  return $content;
493  }
494 
501  protected function ‪fixCLen(string $c, string $v): string
502  {
503  $len = (int)($v === 'FILE' ? $this->printConf['contentLength_FILE'] : $this->printConf['contentLength']);
504  if (strlen($c) > $len) {
505  $c = '<span style="color:green;">' . htmlspecialchars(‪GeneralUtility::fixed_lgd_cs($c, $len)) . '</span>';
506  } else {
507  $c = htmlspecialchars($c);
508  }
509  return $c;
510  }
511 
517  protected function ‪fw(string $str): string
518  {
519  return '<span>' . $str . '</span>';
520  }
521 
531  protected function ‪createHierarchyArray(array &$arr, int $pointer, string $uniqueId): void
532  {
533  if ($pointer > 0) {
534  end($arr);
535  $k = key($arr);
536  if (!is_array($arr[(int)$k . '.'] ?? null)) {
537  $arr[(int)$k . '.'] = [];
538  }
539  $this->‪createHierarchyArray($arr[(int)$k . '.'], $pointer - 1, $uniqueId);
540  } else {
541  $arr[] = $uniqueId;
542  }
543  }
544 }
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStackLog
‪array $tsStackLog
Definition: TimeTracker.php:85
‪TYPO3\CMS\Core\Utility\GeneralUtility\fixed_lgd_cs
‪static string fixed_lgd_cs(string $string, int $chars, string $appendString='...')
Definition: GeneralUtility.php:92
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$printConf
‪array $printConf
Definition: TimeTracker.php:54
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStack
‪array $tsStack
Definition: TimeTracker.php:82
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\incStackPointer
‪incStackPointer()
Definition: TimeTracker.php:215
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$wrapError
‪array $wrapError
Definition: TimeTracker.php:67
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\decStackPointer
‪decStackPointer()
Definition: TimeTracker.php:231
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\setEnabled
‪setEnabled(bool $isEnabled=true)
Definition: TimeTracker.php:100
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$starttime
‪int $starttime
Definition: TimeTracker.php:41
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\start
‪start(?float $starttime=null)
Definition: TimeTracker.php:110
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$isEnabled
‪bool $isEnabled
Definition: TimeTracker.php:36
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\__construct
‪__construct(bool $isEnabled=true)
Definition: TimeTracker.php:95
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:34
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\fw
‪fw(string $str)
Definition: TimeTracker.php:517
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStackPointer
‪int $tsStackPointer
Definition: TimeTracker.php:86
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\finish
‪finish()
Definition: TimeTracker.php:273
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$wrapIcon
‪array $wrapIcon
Definition: TimeTracker.php:74
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\pull
‪pull(string $content='')
Definition: TimeTracker.php:153
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$finishtime
‪float $finishtime
Definition: TimeTracker.php:46
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$uniqueCounter
‪int $uniqueCounter
Definition: TimeTracker.php:81
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStackLevel
‪int $tsStackLevel
Definition: TimeTracker.php:83
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\createHierarchyArray
‪createHierarchyArray(array &$arr, int $pointer, string $uniqueId)
Definition: TimeTracker.php:531
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\fixContent
‪string fixContent(array &$arr, string $content, string $depthData='', string $vKey='')
Definition: TimeTracker.php:436
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\getParseTime
‪getParseTime()
Definition: TimeTracker.php:283
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$LR
‪bool $LR
Definition: TimeTracker.php:52
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\getDifferenceToStarttime
‪int getDifferenceToStarttime(float $microtime=null)
Definition: TimeTracker.php:263
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\getMilliseconds
‪int getMilliseconds(?float $microtime=null)
Definition: TimeTracker.php:246
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$currentHashPointer
‪array $currentHashPointer
Definition: TimeTracker.php:87
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$highlightLongerThan
‪int $highlightLongerThan
Definition: TimeTracker.php:93
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\setTSlogMessage
‪setTSlogMessage(string $content, string $logLevel=LogLevel::INFO)
Definition: TimeTracker.php:173
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\setTSselectQuery
‪setTSselectQuery(array $data, string $msg='')
Definition: TimeTracker.php:195
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\fixCLen
‪fixCLen(string $c, string $v)
Definition: TimeTracker.php:501
‪TYPO3\CMS\Core\TimeTracker\TimeTracker
Definition: TimeTracker.php:32
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStackLevelMax
‪array $tsStackLevelMax
Definition: TimeTracker.php:84
‪TYPO3\CMS\Core\TimeTracker
Definition: TimeTracker.php:18
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\push
‪push(string $tslabel, string $value='')
Definition: TimeTracker.php:126
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\printTSlog
‪string printTSlog()
Definition: TimeTracker.php:304