‪TYPO3CMS  11.5
TimeTracker.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
18 use Psr\Log\LogLevel;
24 
31 {
36  protected ‪$isEnabled = false;
37 
43  public ‪$starttime = 0;
44 
50  protected ‪$finishtime = 0;
51 
58  public ‪$LR = true;
59 
63  public ‪$printConf = [
64  'showParentKeys' => 1,
65  'contentLength' => 10000,
66  // Determines max length of displayed content before it gets cropped.
67  'contentLength_FILE' => 400,
68  // 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.
69  'flag_tree' => 1,
70  'flag_messages' => 1,
71  'flag_content' => 0,
72  'allTime' => 0,
73  'keyLgd' => 40,
74  ];
75 
79  public ‪$wrapError = [
80  // the numeric items can be removed in TYPO3 v12.0.
81  0 => ['', ''],
82  1 => ['<strong>', '</strong>'],
83  2 => ['<strong style="color:#ff6600;">', '</strong>'],
84  3 => ['<strong style="color:#ff0000;">', '</strong>'],
85  LogLevel::INFO => ['', ''],
86  LogLevel::NOTICE => ['<strong>', '</strong>'],
87  LogLevel::WARNING => ['<strong style="color:#ff6600;">', '</strong>'],
88  LogLevel::ERROR => ['<strong style="color:#ff0000;">', '</strong>'],
89  ];
90 
94  public ‪$wrapIcon = [
95  // the numeric items can be removed in TYPO3 v12.0.
96  0 => '',
97  1 => 'actions-document-info',
98  2 => 'status-dialog-warning',
99  3 => 'status-dialog-error',
100  LogLevel::INFO => '',
101  LogLevel::NOTICE => 'actions-document-info',
102  LogLevel::WARNING => 'status-dialog-warning',
103  LogLevel::ERROR => 'status-dialog-error',
104  ];
105 
109  public ‪$uniqueCounter = 0;
110 
114  public ‪$tsStack = [[]];
115 
119  public ‪$tsStackLevel = 0;
120 
124  public ‪$tsStackLevelMax = [];
125 
129  public ‪$tsStackLog = [];
130 
134  public ‪$tsStackPointer = 0;
135 
139  public ‪$currentHashPointer = [];
140 
146  public ‪$highlightLongerThan = 0;
147 
148  /*******************************************
149  *
150  * Logging parsing times in the scripts
151  *
152  *******************************************/
153 
159  public function ‪__construct(‪$isEnabled = true)
160  {
161  $this->isEnabled = ‪$isEnabled;
162  }
163 
167  public function ‪setEnabled(bool ‪$isEnabled = true)
168  {
169  $this->isEnabled = ‪$isEnabled;
170  }
171 
178  public function ‪start(?float ‪$starttime = null)
179  {
180  if (!$this->isEnabled) {
181  return;
182  }
183  $this->starttime = $this->‪getMilliseconds(‪$starttime);
184  }
185 
194  public function ‪push($tslabel, $value = '')
195  {
196  if (!$this->isEnabled) {
197  return;
198  }
199  $this->tsStack[‪$this->tsStackPointer][] = $tslabel;
200  $this->currentHashPointer[] = 'timetracker_' . $this->uniqueCounter++;
201  $this->tsStackLevel++;
202  $this->tsStackLevelMax[] = ‪$this->tsStackLevel;
203  // setTSlog
204  $k = end($this->currentHashPointer);
205  $this->tsStackLog[$k] = [
207  'tsStack' => ‪$this->tsStack,
208  'value' => $value,
209  'starttime' => microtime(true),
210  'stackPointer' => ‪$this->tsStackPointer,
211  ];
212  }
213 
221  public function ‪pull($content = '')
222  {
223  if (!$this->isEnabled) {
224  return;
225  }
226  $k = end($this->currentHashPointer);
227  $this->tsStackLog[$k]['endtime'] = microtime(true);
228  $this->tsStackLog[$k]['content'] = $content;
229  $this->tsStackLevel--;
230  array_pop($this->tsStack[$this->tsStackPointer]);
231  array_pop($this->currentHashPointer);
232  }
233 
241  public function ‪setTSlogMessage($content, $logLevel = LogLevel::INFO)
242  {
243  if (!$this->isEnabled) {
244  return;
245  }
246  end($this->currentHashPointer);
247  $k = current($this->currentHashPointer);
248  $placeholder = '';
249  // Enlarge the "details" column by adding a span
250  if (strlen($content) > 30) {
251  $placeholder = '<br /><span style="width: 300px; height: 1px; display: inline-block;"></span>';
252  }
253  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
254  $this->tsStackLog[$k]['message'][] = $iconFactory->getIcon($this->wrapIcon[$logLevel], ‪Icon::SIZE_SMALL)->render() . $this->wrapError[$logLevel][0] . htmlspecialchars($content) . $this->wrapError[$logLevel][1] . $placeholder;
255  }
256 
263  public function ‪setTSselectQuery(array $data, $msg = '')
264  {
265  if (!$this->isEnabled) {
266  return;
267  }
268  end($this->currentHashPointer);
269  $k = current($this->currentHashPointer);
270  if ($msg !== '') {
271  $data['msg'] = $msg;
272  }
273  $this->tsStackLog[$k]['selectQuery'][] = $data;
274  }
275 
283  public function ‪incStackPointer()
284  {
285  if (!$this->isEnabled) {
286  return;
287  }
288  $this->tsStackPointer++;
289  $this->tsStack[‪$this->tsStackPointer] = [];
290  }
291 
299  public function ‪decStackPointer()
300  {
301  if (!$this->isEnabled) {
302  return;
303  }
304  unset($this->tsStack[$this->tsStackPointer]);
305  $this->tsStackPointer--;
306  }
307 
314  public function ‪getMilliseconds($microtime = null)
315  {
316  if (!$this->isEnabled) {
317  return 0;
318  }
319  if (!isset($microtime)) {
320  $microtime = microtime(true);
321  }
322  return (int)round($microtime * 1000);
323  }
324 
331  public function ‪getDifferenceToStarttime($microtime = null)
332  {
333  return $this->‪getMilliseconds($microtime) - ‪$this->starttime;
334  }
335 
341  public function ‪finish(): void
342  {
343  if ($this->isEnabled) {
344  $this->finishtime = microtime(true);
345  }
346  }
347 
353  public function ‪getParseTime(): int
354  {
355  if (!$this->starttime) {
356  $this->‪start(microtime(true));
357  }
358  if (!$this->finishtime) {
359  $this->‪finish();
360  }
361  return $this->‪getDifferenceToStarttime($this->finishtime ?? null);
362  }
363 
364  /*******************************************
365  *
366  * Printing the parsing time information (for Admin Panel)
367  *
368  *******************************************/
374  public function ‪printTSlog()
375  {
376  if (!$this->isEnabled) {
377  return '';
378  }
379  // Calculate times and keys for the tsStackLog
380  foreach ($this->tsStackLog as $uniqueId => &$data) {
381  $data['endtime'] = $this->‪getDifferenceToStarttime($data['endtime'] ?? 0);
382  $data['starttime'] = $this->‪getDifferenceToStarttime($data['starttime'] ?? 0);
383  $data['deltatime'] = $data['endtime'] - $data['starttime'];
384  if (isset($data['tsStack']) && is_array($data['tsStack'])) {
385  $data['key'] = implode($data['stackPointer'] ? '.' : '/', end($data['tsStack']));
386  }
387  }
388  unset($data);
389  // Create hierarchical array of keys pointing to the stack
390  $arr = [];
391  foreach ($this->tsStackLog as $uniqueId => $data) {
392  $this->‪createHierarchyArray($arr, $data['level'] ?? 0, $uniqueId);
393  }
394  // Parsing the registered content and create icon-html for the tree
395  $this->tsStackLog[$arr['0.'][0]]['content'] = $this->‪fixContent($arr['0.'], $this->tsStackLog[$arr['0.'][0]]['content'] ?? '', '', $arr['0.'][0]);
396  // Displaying the tree:
397  $outputArr = [];
398  $outputArr[] = $this->‪fw('TypoScript Key');
399  $outputArr[] = $this->‪fw('Value');
400  if ($this->printConf['allTime']) {
401  $outputArr[] = $this->‪fw('Time');
402  $outputArr[] = $this->‪fw('Own');
403  $outputArr[] = $this->‪fw('Sub');
404  $outputArr[] = $this->‪fw('Total');
405  } else {
406  $outputArr[] = $this->‪fw('Own');
407  }
408  $outputArr[] = $this->‪fw('Details');
409  $out = '';
410  foreach ($outputArr as $row) {
411  $out .= '<th>' . $row . '</th>';
412  }
413  $out = '<thead><tr>' . $out . '</tr></thead>';
414  $flag_tree = $this->printConf['flag_tree'];
415  $flag_messages = $this->printConf['flag_messages'];
416  $flag_content = $this->printConf['flag_content'];
417  $keyLgd = (int)$this->printConf['keyLgd'];
418  $c = 0;
419  foreach ($this->tsStackLog as $uniqueId => $data) {
420  $logRowClass = '';
421  if ($this->highlightLongerThan && (int)$data['owntime'] > (int)$this->highlightLongerThan) {
422  $logRowClass = 'typo3-adminPanel-logRow-highlight';
423  }
424  $item = '';
425  // If first...
426  if (!$c) {
427  $data['icons'] = '';
428  $data['key'] = 'Script Start';
429  $data['value'] = '';
430  }
431  // Key label:
432  $keyLabel = '';
433  $stackPointer = $data['stackPointer'] ?? false;
434  if (!$flag_tree && $stackPointer) {
435  $temp = [];
436  foreach ($data['tsStack'] as $k => $v) {
437  $temp[] = GeneralUtility::fixed_lgd_cs(implode($k ? '.' : '/', $v), -$keyLgd);
438  }
439  array_pop($temp);
440  $temp = array_reverse($temp);
441  array_pop($temp);
442  if (!empty($temp)) {
443  $keyLabel = '<br /><span style="color:#999999;">' . implode('<br />', $temp) . '</span>';
444  }
445  }
446  if ($flag_tree) {
447  $tmp = ‪GeneralUtility::trimExplode('.', $data['key'], true);
448  $theLabel = end($tmp);
449  } else {
450  $theLabel = $data['key'];
451  }
452  $theLabel = GeneralUtility::fixed_lgd_cs($theLabel, -$keyLgd);
453  $theLabel = $stackPointer ? '<span class="stackPointer">' . $theLabel . '</span>' : $theLabel;
454  $keyLabel = $theLabel . $keyLabel;
455  $item .= '<th scope="row" class="typo3-adminPanel-table-cell-key ' . $logRowClass . '">' . ($flag_tree ? $data['icons'] : '') . $this->‪fw($keyLabel) . '</th>';
456  // Key value:
457  $keyValue = $data['value'];
458  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime">' . $this->‪fw(htmlspecialchars($keyValue)) . '</td>';
459  $ownTime = (string)($data['owntime'] ?? '');
460  if ($this->printConf['allTime']) {
461  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw((string)$data['starttime']) . '</td>';
462  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw($ownTime) . '</td>';
463  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw(($data['subtime'] ? '+' . $data['subtime'] : '')) . '</td>';
464  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw(($data['subtime'] ? '=' . $data['deltatime'] : '')) . '</td>';
465  } else {
466  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw($ownTime) . '</td>';
467  }
468  // Messages:
469  $msgArr = [];
470  $msg = '';
471  if ($flag_messages && is_array($data['message'] ?? null)) {
472  foreach ($data['message'] as $v) {
473  $msgArr[] = nl2br($v);
474  }
475  }
476  if ($flag_content && (string)$data['content'] !== '') {
477  $maxlen = 120;
478  // Break lines which are too longer than $maxlen chars (can happen if content contains long paths...)
479  if (preg_match_all('/(\\S{' . $maxlen . ',})/', $data['content'], $reg)) {
480  foreach ($reg[1] as $key => $match) {
481  $match = preg_replace('/(.{' . $maxlen . '})/', '$1 ', $match);
482  $data['content'] = str_replace($reg[0][$key], $match, $data['content']);
483  }
484  }
485  $msgArr[] = nl2br($data['content']);
486  }
487  if (!empty($msgArr)) {
488  $msg = implode('<hr />', $msgArr);
489  }
490  $item .= '<td class="typo3-adminPanel-table-cell-content">' . $this->‪fw($msg) . '</td>';
491  $out .= '<tr>' . $item . '</tr>';
492  $c++;
493  }
494  $out = '<div class="typo3-adminPanel-table-overflow"><table class="typo3-adminPanel-table typo3-adminPanel-table-debug">' . $out . '</table></div>';
495  return $out;
496  }
497 
507  protected function ‪fixContent(&$arr, $content, $depthData = '', $vKey = '')
508  {
509  $entriesCount = 0;
510  $c = 0;
511  // First, find number of entries
512  foreach ($arr as $k => $v) {
513  //do not count subentries (the one ending with dot, eg. '9.'
515  $entriesCount++;
516  }
517  }
518  // Traverse through entries
519  $subtime = 0;
520  foreach ($arr as $k => $v) {
522  $c++;
523  $hasChildren = isset($arr[$k . '.']);
524  $lastEntry = $entriesCount === $c;
525 
526  $PM = '<span class="treeline-icon treeline-icon-join' . ($lastEntry ? 'bottom' : '') . '"></span>';
527 
528  $this->tsStackLog[$v]['icons'] = $depthData . $PM;
529  if (($this->tsStackLog[$v]['content'] ?? '') !== '') {
530  $content = str_replace($this->tsStackLog[$v]['content'], $v, $content);
531  }
532  if ($hasChildren) {
533  $lineClass = $lastEntry ? 'treeline-icon-clear' : 'treeline-icon-line';
534  $this->tsStackLog[$v]['content'] = $this->‪fixContent(
535  $arr[$k . '.'],
536  ($this->tsStackLog[$v]['content'] ?? ''),
537  $depthData . '<span class="treeline-icon ' . $lineClass . '"></span>',
538  $v
539  );
540  } else {
541  $this->tsStackLog[$v]['content'] = $this->‪fixCLen(($this->tsStackLog[$v]['content'] ?? ''), $this->tsStackLog[$v]['value']);
542  $this->tsStackLog[$v]['subtime'] = '';
543  $this->tsStackLog[$v]['owntime'] = $this->tsStackLog[$v]['deltatime'];
544  }
545  $subtime += $this->tsStackLog[$v]['deltatime'];
546  }
547  }
548  // Set content with special chars
549  if (isset($this->tsStackLog[$vKey])) {
550  $this->tsStackLog[$vKey]['subtime'] = $subtime;
551  $this->tsStackLog[$vKey]['owntime'] = $this->tsStackLog[$vKey]['deltatime'] - $subtime;
552  }
553  $content = $this->‪fixCLen($content, $this->tsStackLog[$vKey]['value']);
554  // Traverse array again, this time substitute the unique hash with the red key
555  foreach ($arr as $k => $v) {
557  if ($this->tsStackLog[$v]['content'] !== '') {
558  $content = str_replace($v, '<strong style="color:red;">[' . $this->tsStackLog[$v]['key'] . ']</strong>', $content);
559  }
560  }
561  }
562  // Return the content
563  return $content;
564  }
565 
573  protected function ‪fixCLen($c, $v)
574  {
575  $len = $v === 'FILE' ? $this->printConf['contentLength_FILE'] : $this->printConf['contentLength'];
576  if (strlen($c) > $len) {
577  $c = '<span style="color:green;">' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($c, $len)) . '</span>';
578  } else {
579  $c = htmlspecialchars($c);
580  }
581  return $c;
582  }
583 
590  protected function ‪fw($str)
591  {
592  return '<span>' . $str . '</span>';
593  }
594 
604  protected function ‪createHierarchyArray(&$arr, $pointer, $uniqueId)
605  {
606  if (!is_array($arr)) {
607  $arr = [];
608  }
609  if ($pointer > 0) {
610  end($arr);
611  $k = key($arr);
612  if (!is_array($arr[(int)$k . '.'] ?? null)) {
613  $arr[(int)$k . '.'] = [];
614  }
615  $this->‪createHierarchyArray($arr[(int)$k . '.'], $pointer - 1, $uniqueId);
616  } else {
617  $arr[] = $uniqueId;
618  }
619  }
620 }
‪TYPO3\CMS\Core\Imaging\Icon\SIZE_SMALL
‪const SIZE_SMALL
Definition: Icon.php:30
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:999
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStackLog
‪array $tsStackLog
Definition: TimeTracker.php:117
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger($var)
Definition: MathUtility.php:74
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$printConf
‪array $printConf
Definition: TimeTracker.php:58
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStack
‪array $tsStack
Definition: TimeTracker.php:105
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\incStackPointer
‪incStackPointer()
Definition: TimeTracker.php:268
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$wrapError
‪array $wrapError
Definition: TimeTracker.php:73
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\setTSselectQuery
‪setTSselectQuery(array $data, $msg='')
Definition: TimeTracker.php:248
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\decStackPointer
‪decStackPointer()
Definition: TimeTracker.php:284
‪TYPO3\CMS\Core\Imaging\Icon
Definition: Icon.php:26
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\setEnabled
‪setEnabled(bool $isEnabled=true)
Definition: TimeTracker.php:152
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$starttime
‪int $starttime
Definition: TimeTracker.php:41
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\setTSlogMessage
‪setTSlogMessage($content, $logLevel=LogLevel::INFO)
Definition: TimeTracker.php:226
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\getDifferenceToStarttime
‪int getDifferenceToStarttime($microtime=null)
Definition: TimeTracker.php:316
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\start
‪start(?float $starttime=null)
Definition: TimeTracker.php:163
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$isEnabled
‪bool $isEnabled
Definition: TimeTracker.php:35
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:34
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\getParseTime
‪int getParseTime()
Definition: TimeTracker.php:338
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStackPointer
‪int $tsStackPointer
Definition: TimeTracker.php:121
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\finish
‪finish()
Definition: TimeTracker.php:326
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$wrapIcon
‪array $wrapIcon
Definition: TimeTracker.php:87
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$finishtime
‪float $finishtime
Definition: TimeTracker.php:47
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$uniqueCounter
‪int $uniqueCounter
Definition: TimeTracker.php:101
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStackLevel
‪int $tsStackLevel
Definition: TimeTracker.php:109
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\fixContent
‪string fixContent(&$arr, $content, $depthData='', $vKey='')
Definition: TimeTracker.php:492
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$LR
‪bool $LR
Definition: TimeTracker.php:54
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\pull
‪pull($content='')
Definition: TimeTracker.php:206
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\fixCLen
‪string fixCLen($c, $v)
Definition: TimeTracker.php:558
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\getMilliseconds
‪int getMilliseconds($microtime=null)
Definition: TimeTracker.php:299
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$currentHashPointer
‪array $currentHashPointer
Definition: TimeTracker.php:125
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$highlightLongerThan
‪int $highlightLongerThan
Definition: TimeTracker.php:131
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:22
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\createHierarchyArray
‪createHierarchyArray(&$arr, $pointer, $uniqueId)
Definition: TimeTracker.php:589
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\TimeTracker\TimeTracker
Definition: TimeTracker.php:31
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStackLevelMax
‪array $tsStackLevelMax
Definition: TimeTracker.php:113
‪TYPO3\CMS\Core\TimeTracker
Definition: TimeTracker.php:16
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\__construct
‪__construct($isEnabled=true)
Definition: TimeTracker.php:144
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\printTSlog
‪string printTSlog()
Definition: TimeTracker.php:359
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\fw
‪string fw($str)
Definition: TimeTracker.php:575
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\push
‪push($tslabel, $value='')
Definition: TimeTracker.php:179