‪TYPO3CMS  10.4
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 
23 
30 {
35  protected ‪$isEnabled = false;
36 
42  public ‪$starttime = 0;
43 
49  protected ‪$finishtime = 0;
50 
57  public ‪$LR = true;
58 
62  public ‪$printConf = [
63  'showParentKeys' => 1,
64  'contentLength' => 10000,
65  // Determines max length of displayed content before it gets cropped.
66  'contentLength_FILE' => 400,
67  // 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.
68  'flag_tree' => 1,
69  'flag_messages' => 1,
70  'flag_content' => 0,
71  'allTime' => 0,
72  'keyLgd' => 40
73  ];
74 
78  public ‪$wrapError = [
79  0 => ['', ''],
80  1 => ['<strong>', '</strong>'],
81  2 => ['<strong style="color:#ff6600;">', '</strong>'],
82  3 => ['<strong style="color:#ff0000;">', '</strong>']
83  ];
84 
88  public ‪$wrapIcon = [
89  0 => '',
90  1 => 'actions-document-info',
91  2 => 'status-dialog-warning',
92  3 => 'status-dialog-error'
93  ];
94 
98  public ‪$uniqueCounter = 0;
99 
103  public ‪$tsStack = [[]];
104 
108  public ‪$tsStackLevel = 0;
109 
113  public ‪$tsStackLevelMax = [];
114 
118  public ‪$tsStackLog = [];
119 
123  public ‪$tsStackPointer = 0;
124 
128  public ‪$currentHashPointer = [];
129 
135  public ‪$highlightLongerThan = 0;
136 
137  /*******************************************
138  *
139  * Logging parsing times in the scripts
140  *
141  *******************************************/
142 
148  public function ‪__construct(‪$isEnabled = true)
149  {
150  $this->isEnabled = ‪$isEnabled;
151  }
152 
156  public function ‪setEnabled(bool ‪$isEnabled = true)
157  {
158  $this->isEnabled = ‪$isEnabled;
159  }
160 
167  public function ‪start(?float ‪$starttime = null)
168  {
169  if (!$this->isEnabled) {
170  return;
171  }
172  $this->starttime = $this->‪getMilliseconds(‪$starttime);
173  }
174 
183  public function ‪push($tslabel, $value = '')
184  {
185  if (!$this->isEnabled) {
186  return;
187  }
188  $this->tsStack[‪$this->tsStackPointer][] = $tslabel;
189  $this->currentHashPointer[] = 'timetracker_' . $this->uniqueCounter++;
190  $this->tsStackLevel++;
191  $this->tsStackLevelMax[] = ‪$this->tsStackLevel;
192  // setTSlog
193  $k = end($this->currentHashPointer);
194  $this->tsStackLog[$k] = [
196  'tsStack' => ‪$this->tsStack,
197  'value' => $value,
198  'starttime' => microtime(true),
199  'stackPointer' => ‪$this->tsStackPointer
200  ];
201  }
202 
210  public function ‪pull($content = '')
211  {
212  if (!$this->isEnabled) {
213  return;
214  }
215  $k = end($this->currentHashPointer);
216  $this->tsStackLog[$k]['endtime'] = microtime(true);
217  $this->tsStackLog[$k]['content'] = $content;
218  $this->tsStackLevel--;
219  array_pop($this->tsStack[$this->tsStackPointer]);
220  array_pop($this->currentHashPointer);
221  }
222 
230  public function ‪setTSlogMessage($content, $num = 0)
231  {
232  if (!$this->isEnabled) {
233  return;
234  }
235  end($this->currentHashPointer);
236  $k = current($this->currentHashPointer);
237  $placeholder = '';
238  // Enlarge the "details" column by adding a span
239  if (strlen($content) > 30) {
240  $placeholder = '<br /><span style="width: 300px; height: 1px; display: inline-block;"></span>';
241  }
242  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
243  $this->tsStackLog[$k]['message'][] = $iconFactory->getIcon($this->wrapIcon[$num], ‪Icon::SIZE_SMALL)->render() . $this->wrapError[$num][0] . htmlspecialchars($content) . $this->wrapError[$num][1] . $placeholder;
244  }
245 
252  public function ‪setTSselectQuery(array $data, $msg = '')
253  {
254  if (!$this->isEnabled) {
255  return;
256  }
257  end($this->currentHashPointer);
258  $k = current($this->currentHashPointer);
259  if ($msg !== '') {
260  $data['msg'] = $msg;
261  }
262  $this->tsStackLog[$k]['selectQuery'][] = $data;
263  }
264 
272  public function ‪incStackPointer()
273  {
274  if (!$this->isEnabled) {
275  return;
276  }
277  $this->tsStackPointer++;
278  $this->tsStack[‪$this->tsStackPointer] = [];
279  }
280 
288  public function ‪decStackPointer()
289  {
290  if (!$this->isEnabled) {
291  return;
292  }
293  unset($this->tsStack[$this->tsStackPointer]);
294  $this->tsStackPointer--;
295  }
296 
303  public function ‪getMilliseconds($microtime = null)
304  {
305  if (!$this->isEnabled) {
306  return 0;
307  }
308  if (!isset($microtime)) {
309  $microtime = microtime(true);
310  }
311  return (int)round($microtime * 1000);
312  }
313 
320  public function ‪getDifferenceToStarttime($microtime = null)
321  {
322  return $this->‪getMilliseconds($microtime) - ‪$this->starttime;
323  }
324 
330  public function ‪finish(): void
331  {
332  if ($this->isEnabled) {
333  $this->finishtime = microtime(true);
334  }
335  }
336 
342  public function ‪getParseTime(): int
343  {
344  if (!$this->starttime) {
345  $this->‪start(microtime(true));
346  }
347  if (!$this->finishtime) {
348  $this->‪finish();
349  }
350  return $this->‪getDifferenceToStarttime($this->finishtime ?? null);
351  }
352 
353  /*******************************************
354  *
355  * Printing the parsing time information (for Admin Panel)
356  *
357  *******************************************/
363  public function ‪printTSlog()
364  {
365  if (!$this->isEnabled) {
366  return '';
367  }
368  // Calculate times and keys for the tsStackLog
369  foreach ($this->tsStackLog as $uniqueId => &$data) {
370  $data['endtime'] = $this->‪getDifferenceToStarttime($data['endtime']);
371  $data['starttime'] = $this->‪getDifferenceToStarttime($data['starttime']);
372  $data['deltatime'] = $data['endtime'] - $data['starttime'];
373  if (is_array($data['tsStack'])) {
374  $data['key'] = implode($data['stackPointer'] ? '.' : '/', end($data['tsStack']));
375  }
376  }
377  unset($data);
378  // Create hierarchical array of keys pointing to the stack
379  $arr = [];
380  foreach ($this->tsStackLog as $uniqueId => $data) {
381  $this->‪createHierarchyArray($arr, $data['level'], $uniqueId);
382  }
383  // Parsing the registered content and create icon-html for the tree
384  $this->tsStackLog[$arr['0.'][0]]['content'] = $this->‪fixContent($arr['0.'], $this->tsStackLog[$arr['0.'][0]]['content'] ?? '', '', $arr['0.'][0]);
385  // Displaying the tree:
386  $outputArr = [];
387  $outputArr[] = $this->‪fw('TypoScript Key');
388  $outputArr[] = $this->‪fw('Value');
389  if ($this->printConf['allTime']) {
390  $outputArr[] = $this->‪fw('Time');
391  $outputArr[] = $this->‪fw('Own');
392  $outputArr[] = $this->‪fw('Sub');
393  $outputArr[] = $this->‪fw('Total');
394  } else {
395  $outputArr[] = $this->‪fw('Own');
396  }
397  $outputArr[] = $this->‪fw('Details');
398  $out = '';
399  foreach ($outputArr as $row) {
400  $out .= '<th>' . $row . '</th>';
401  }
402  $out = '<thead><tr>' . $out . '</tr></thead>';
403  $flag_tree = $this->printConf['flag_tree'];
404  $flag_messages = $this->printConf['flag_messages'];
405  $flag_content = $this->printConf['flag_content'];
406  $keyLgd = (int)$this->printConf['keyLgd'];
407  $c = 0;
408  foreach ($this->tsStackLog as $uniqueId => $data) {
409  $logRowClass = '';
410  if ($this->highlightLongerThan && (int)$data['owntime'] > (int)$this->highlightLongerThan) {
411  $logRowClass = 'typo3-adminPanel-logRow-highlight';
412  }
413  $item = '';
414  // If first...
415  if (!$c) {
416  $data['icons'] = '';
417  $data['key'] = 'Script Start';
418  $data['value'] = '';
419  }
420  // Key label:
421  $keyLabel = '';
422  if (!$flag_tree && $data['stackPointer']) {
423  $temp = [];
424  foreach ($data['tsStack'] as $k => $v) {
425  $temp[] = GeneralUtility::fixed_lgd_cs(implode($k ? '.' : '/', $v), -$keyLgd);
426  }
427  array_pop($temp);
428  $temp = array_reverse($temp);
429  array_pop($temp);
430  if (!empty($temp)) {
431  $keyLabel = '<br /><span style="color:#999999;">' . implode('<br />', $temp) . '</span>';
432  }
433  }
434  if ($flag_tree) {
435  $tmp = ‪GeneralUtility::trimExplode('.', $data['key'], true);
436  $theLabel = end($tmp);
437  } else {
438  $theLabel = $data['key'];
439  }
440  $theLabel = GeneralUtility::fixed_lgd_cs($theLabel, -$keyLgd);
441  $theLabel = $data['stackPointer'] ? '<span class="stackPointer">' . $theLabel . '</span>' : $theLabel;
442  $keyLabel = $theLabel . $keyLabel;
443  $item .= '<th scope="row" class="typo3-adminPanel-table-cell-key ' . $logRowClass . '">' . ($flag_tree ? $data['icons'] : '') . $this->‪fw($keyLabel) . '</th>';
444  // Key value:
445  $keyValue = $data['value'];
446  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime">' . $this->‪fw(htmlspecialchars($keyValue)) . '</td>';
447  if ($this->printConf['allTime']) {
448  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw($data['starttime']) . '</td>';
449  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw($data['owntime']) . '</td>';
450  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw(($data['subtime'] ? '+' . $data['subtime'] : '')) . '</td>';
451  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw(($data['subtime'] ? '=' . $data['deltatime'] : '')) . '</td>';
452  } else {
453  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw($data['owntime']) . '</td>';
454  }
455  // Messages:
456  $msgArr = [];
457  $msg = '';
458  if ($flag_messages && is_array($data['message'])) {
459  foreach ($data['message'] as $v) {
460  $msgArr[] = nl2br($v);
461  }
462  }
463  if ($flag_content && (string)$data['content'] !== '') {
464  $maxlen = 120;
465  // Break lines which are too longer than $maxlen chars (can happen if content contains long paths...)
466  if (preg_match_all('/(\\S{' . $maxlen . ',})/', $data['content'], $reg)) {
467  foreach ($reg[1] as $key => $match) {
468  $match = preg_replace('/(.{' . $maxlen . '})/', '$1 ', $match);
469  $data['content'] = str_replace($reg[0][$key], $match, $data['content']);
470  }
471  }
472  $msgArr[] = nl2br($data['content']);
473  }
474  if (!empty($msgArr)) {
475  $msg = implode('<hr />', $msgArr);
476  }
477  $item .= '<td class="typo3-adminPanel-table-cell-content">' . $this->‪fw($msg) . '</td>';
478  $out .= '<tr>' . $item . '</tr>';
479  $c++;
480  }
481  $out = '<div class="typo3-adminPanel-table-overflow"><table class="typo3-adminPanel-table typo3-adminPanel-table-debug">' . $out . '</table></div>';
482  return $out;
483  }
484 
494  protected function ‪fixContent(&$arr, $content, $depthData = '', $vKey = '')
495  {
496  $entriesCount = 0;
497  $c = 0;
498  // First, find number of entries
499  foreach ($arr as $k => $v) {
500  //do not count subentries (the one ending with dot, eg. '9.'
502  $entriesCount++;
503  }
504  }
505  // Traverse through entries
506  $subtime = 0;
507  foreach ($arr as $k => $v) {
509  $c++;
510  $hasChildren = isset($arr[$k . '.']);
511  $lastEntry = $entriesCount === $c;
512 
513  $PM = '<span class="treeline-icon treeline-icon-join' . ($lastEntry ? 'bottom' : '') . '"></span>';
514 
515  $this->tsStackLog[$v]['icons'] = $depthData . $PM;
516  if ($this->tsStackLog[$v]['content'] !== '') {
517  $content = str_replace($this->tsStackLog[$v]['content'], $v, $content);
518  }
519  if ($hasChildren) {
520  $lineClass = $lastEntry ? 'treeline-icon-clear' : 'treeline-icon-line';
521  $this->tsStackLog[$v]['content'] = $this->‪fixContent($arr[$k . '.'], $this->tsStackLog[$v]['content'], $depthData . '<span class="treeline-icon ' . $lineClass . '"></span>', $v);
522  } else {
523  $this->tsStackLog[$v]['content'] = $this->‪fixCLen($this->tsStackLog[$v]['content'], $this->tsStackLog[$v]['value']);
524  $this->tsStackLog[$v]['subtime'] = '';
525  $this->tsStackLog[$v]['owntime'] = $this->tsStackLog[$v]['deltatime'];
526  }
527  $subtime += $this->tsStackLog[$v]['deltatime'];
528  }
529  }
530  // Set content with special chars
531  if (isset($this->tsStackLog[$vKey])) {
532  $this->tsStackLog[$vKey]['subtime'] = $subtime;
533  $this->tsStackLog[$vKey]['owntime'] = $this->tsStackLog[$vKey]['deltatime'] - $subtime;
534  }
535  $content = $this->‪fixCLen($content, $this->tsStackLog[$vKey]['value']);
536  // Traverse array again, this time substitute the unique hash with the red key
537  foreach ($arr as $k => $v) {
539  if ($this->tsStackLog[$v]['content'] !== '') {
540  $content = str_replace($v, '<strong style="color:red;">[' . $this->tsStackLog[$v]['key'] . ']</strong>', $content);
541  }
542  }
543  }
544  // Return the content
545  return $content;
546  }
547 
555  protected function ‪fixCLen($c, $v)
556  {
557  $len = $v === 'FILE' ? $this->printConf['contentLength_FILE'] : $this->printConf['contentLength'];
558  if (strlen($c) > $len) {
559  $c = '<span style="color:green;">' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($c, $len)) . '</span>';
560  } else {
561  $c = htmlspecialchars($c);
562  }
563  return $c;
564  }
565 
572  protected function ‪fw($str)
573  {
574  return '<span>' . $str . '</span>';
575  }
576 
586  protected function ‪createHierarchyArray(&$arr, $pointer, $uniqueId)
587  {
588  if (!is_array($arr)) {
589  $arr = [];
590  }
591  if ($pointer > 0) {
592  end($arr);
593  $k = key($arr);
594  $this->‪createHierarchyArray($arr[(int)$k . '.'], $pointer - 1, $uniqueId);
595  } else {
596  $arr[] = $uniqueId;
597  }
598  }
599 }
‪TYPO3\CMS\Core\Imaging\Icon\SIZE_SMALL
‪const SIZE_SMALL
Definition: Icon.php:30
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStackLog
‪array $tsStackLog
Definition: TimeTracker.php:106
‪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:57
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStack
‪array $tsStack
Definition: TimeTracker.php:94
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\incStackPointer
‪incStackPointer()
Definition: TimeTracker.php:257
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$wrapError
‪array $wrapError
Definition: TimeTracker.php:72
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\setTSselectQuery
‪setTSselectQuery(array $data, $msg='')
Definition: TimeTracker.php:237
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\decStackPointer
‪decStackPointer()
Definition: TimeTracker.php:273
‪TYPO3\CMS\Core\Imaging\Icon
Definition: Icon.php:26
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\setEnabled
‪setEnabled(bool $isEnabled=true)
Definition: TimeTracker.php:141
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$starttime
‪int $starttime
Definition: TimeTracker.php:40
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\getDifferenceToStarttime
‪int getDifferenceToStarttime($microtime=null)
Definition: TimeTracker.php:305
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\start
‪start(?float $starttime=null)
Definition: TimeTracker.php:152
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$isEnabled
‪bool $isEnabled
Definition: TimeTracker.php:34
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:33
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\getParseTime
‪int getParseTime()
Definition: TimeTracker.php:327
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStackPointer
‪int $tsStackPointer
Definition: TimeTracker.php:110
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\finish
‪finish()
Definition: TimeTracker.php:315
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\setTSlogMessage
‪setTSlogMessage($content, $num=0)
Definition: TimeTracker.php:215
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$wrapIcon
‪array $wrapIcon
Definition: TimeTracker.php:81
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$uniqueCounter
‪int $uniqueCounter
Definition: TimeTracker.php:90
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStackLevel
‪int $tsStackLevel
Definition: TimeTracker.php:98
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\fixContent
‪string fixContent(&$arr, $content, $depthData='', $vKey='')
Definition: TimeTracker.php:479
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$LR
‪bool $LR
Definition: TimeTracker.php:53
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\pull
‪pull($content='')
Definition: TimeTracker.php:195
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$finishtime
‪int $finishtime
Definition: TimeTracker.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static string[] trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:1059
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\fixCLen
‪string fixCLen($c, $v)
Definition: TimeTracker.php:540
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\getMilliseconds
‪int getMilliseconds($microtime=null)
Definition: TimeTracker.php:288
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$currentHashPointer
‪array $currentHashPointer
Definition: TimeTracker.php:114
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$highlightLongerThan
‪int $highlightLongerThan
Definition: TimeTracker.php:120
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:23
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:22
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\createHierarchyArray
‪createHierarchyArray(&$arr, $pointer, $uniqueId)
Definition: TimeTracker.php:571
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Core\TimeTracker\TimeTracker
Definition: TimeTracker.php:30
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStackLevelMax
‪array $tsStackLevelMax
Definition: TimeTracker.php:102
‪TYPO3\CMS\Core\TimeTracker
Definition: TimeTracker.php:16
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\__construct
‪__construct($isEnabled=true)
Definition: TimeTracker.php:133
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\printTSlog
‪string printTSlog()
Definition: TimeTracker.php:348
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\fw
‪string fw($str)
Definition: TimeTracker.php:557
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\push
‪push($tslabel, $value='')
Definition: TimeTracker.php:168