‪TYPO3CMS  9.5
TimeTracker.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
20 
27 {
32  protected ‪$isEnabled = false;
33 
39  public ‪$starttime = 0;
40 
47  public ‪$LR = true;
48 
52  public ‪$printConf = [
53  'showParentKeys' => 1,
54  'contentLength' => 10000,
55  // Determines max length of displayed content before it gets cropped.
56  'contentLength_FILE' => 400,
57  // 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.
58  'flag_tree' => 1,
59  'flag_messages' => 1,
60  'flag_content' => 0,
61  'allTime' => 0,
62  'keyLgd' => 40
63  ];
64 
68  public ‪$wrapError = [
69  0 => ['', ''],
70  1 => ['<strong>', '</strong>'],
71  2 => ['<strong style="color:#ff6600;">', '</strong>'],
72  3 => ['<strong style="color:#ff0000;">', '</strong>']
73  ];
74 
78  public ‪$wrapIcon = [
79  0 => '',
80  1 => 'actions-document-info',
81  2 => 'status-dialog-warning',
82  3 => 'status-dialog-error'
83  ];
84 
88  public ‪$uniqueCounter = 0;
89 
93  public ‪$tsStack = [[]];
94 
98  public ‪$tsStackLevel = 0;
99 
103  public ‪$tsStackLevelMax = [];
104 
108  public ‪$tsStackLog = [];
109 
113  public ‪$tsStackPointer = 0;
114 
118  public ‪$currentHashPointer = [];
119 
125  public ‪$highlightLongerThan = 0;
126 
127  /*******************************************
128  *
129  * Logging parsing times in the scripts
130  *
131  *******************************************/
132 
138  public function ‪__construct(‪$isEnabled = true)
139  {
140  $this->isEnabled = ‪$isEnabled;
141  }
142 
146  public function ‪start()
147  {
148  if (!$this->isEnabled) {
149  return;
150  }
151  $this->starttime = $this->‪getMilliseconds();
152  }
153 
161  public function ‪push($tslabel, $value = '')
162  {
163  if (!$this->isEnabled) {
164  return;
165  }
166  $this->tsStack[‪$this->tsStackPointer][] = $tslabel;
167  $this->currentHashPointer[] = 'timetracker_' . $this->uniqueCounter++;
168  $this->tsStackLevel++;
169  $this->tsStackLevelMax[] = ‪$this->tsStackLevel;
170  // setTSlog
171  $k = end($this->currentHashPointer);
172  $this->tsStackLog[$k] = [
174  'tsStack' => ‪$this->tsStack,
175  'value' => $value,
176  'starttime' => microtime(true),
177  'stackPointer' => ‪$this->tsStackPointer
178  ];
179  }
180 
187  public function ‪pull($content = '')
188  {
189  if (!$this->isEnabled) {
190  return;
191  }
192  $k = end($this->currentHashPointer);
193  $this->tsStackLog[$k]['endtime'] = microtime(true);
194  $this->tsStackLog[$k]['content'] = $content;
195  $this->tsStackLevel--;
196  array_pop($this->tsStack[$this->tsStackPointer]);
197  array_pop($this->currentHashPointer);
198  }
199 
207  public function ‪setTSlogMessage($content, $num = 0)
208  {
209  if (!$this->isEnabled) {
210  return;
211  }
212  end($this->currentHashPointer);
213  $k = current($this->currentHashPointer);
214  $placeholder = '';
215  // Enlarge the "details" column by adding a span
216  if (strlen($content) > 30) {
217  $placeholder = '<br /><span style="width: 300px; height: 1px; display: inline-block;"></span>';
218  }
219  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
220  $this->tsStackLog[$k]['message'][] = $iconFactory->getIcon($this->wrapIcon[$num], ‪Icon::SIZE_SMALL)->render() . $this->wrapError[$num][0] . htmlspecialchars($content) . $this->wrapError[$num][1] . $placeholder;
221  }
222 
229  public function ‪setTSselectQuery(array $data, $msg = '')
230  {
231  if (!$this->isEnabled) {
232  return;
233  }
234  end($this->currentHashPointer);
235  $k = current($this->currentHashPointer);
236  if ($msg !== '') {
237  $data['msg'] = $msg;
238  }
239  $this->tsStackLog[$k]['selectQuery'][] = $data;
240  }
241 
247  public function ‪incStackPointer()
248  {
249  if (!$this->isEnabled) {
250  return;
251  }
252  $this->tsStackPointer++;
253  $this->tsStack[‪$this->tsStackPointer] = [];
254  }
255 
261  public function ‪decStackPointer()
262  {
263  if (!$this->isEnabled) {
264  return;
265  }
266  unset($this->tsStack[$this->tsStackPointer]);
267  $this->tsStackPointer--;
268  }
269 
276  public function ‪getMilliseconds($microtime = null)
277  {
278  if (!$this->isEnabled) {
279  return 0;
280  }
281  if (!isset($microtime)) {
282  $microtime = microtime(true);
283  }
284  return round($microtime * 1000);
285  }
286 
293  public function ‪getDifferenceToStarttime($microtime = null)
294  {
295  return $this->‪getMilliseconds($microtime) - ‪$this->starttime;
296  }
297 
303  public function ‪getParseTime(): int
304  {
305  // Compensates for the time consumed with Back end user initialization.
306  $processStart = $this->‪getMilliseconds(‪$GLOBALS['TYPO3_MISC']['microtime_start'] ?? null);
307 
308  $beUserInitializationStart = $this->‪getMilliseconds(‪$GLOBALS['TYPO3_MISC']['microtime_BE_USER_start'] ?? null);
309  $beUserInitializationEnd = $this->‪getMilliseconds(‪$GLOBALS['TYPO3_MISC']['microtime_BE_USER_end'] ?? null);
310  $beUserInitialization = $beUserInitializationEnd - $beUserInitializationStart;
311 
312  $processEnd = $this->‪getMilliseconds(‪$GLOBALS['TYPO3_MISC']['microtime_end'] ?? null);
313  $totalParseTime = $processEnd - $processStart;
314 
315  if ($beUserInitialization > 0) {
316  $totalParseTime -= $beUserInitialization;
317  }
318 
319  return $totalParseTime;
320  }
321 
322  /*******************************************
323  *
324  * Printing the parsing time information (for Admin Panel)
325  *
326  *******************************************/
332  public function ‪printTSlog()
333  {
334  if (!$this->isEnabled) {
335  return '';
336  }
337  // Calculate times and keys for the tsStackLog
338  foreach ($this->tsStackLog as $uniqueId => &$data) {
339  $data['endtime'] = $this->‪getDifferenceToStarttime($data['endtime']);
340  $data['starttime'] = $this->‪getDifferenceToStarttime($data['starttime']);
341  $data['deltatime'] = $data['endtime'] - $data['starttime'];
342  if (is_array($data['tsStack'])) {
343  $data['key'] = implode($data['stackPointer'] ? '.' : '/', end($data['tsStack']));
344  }
345  }
346  unset($data);
347  // Create hierarchical array of keys pointing to the stack
348  $arr = [];
349  foreach ($this->tsStackLog as $uniqueId => $data) {
350  $this->‪createHierarchyArray($arr, $data['level'], $uniqueId);
351  }
352  // Parsing the registered content and create icon-html for the tree
353  $this->tsStackLog[$arr['0.'][0]]['content'] = $this->‪fixContent($arr['0.'], $this->tsStackLog[$arr['0.'][0]]['content'] ?? '', '', $arr['0.'][0]);
354  // Displaying the tree:
355  $outputArr = [];
356  $outputArr[] = $this->‪fw('TypoScript Key');
357  $outputArr[] = $this->‪fw('Value');
358  if ($this->printConf['allTime']) {
359  $outputArr[] = $this->‪fw('Time');
360  $outputArr[] = $this->‪fw('Own');
361  $outputArr[] = $this->‪fw('Sub');
362  $outputArr[] = $this->‪fw('Total');
363  } else {
364  $outputArr[] = $this->‪fw('Own');
365  }
366  $outputArr[] = $this->‪fw('Details');
367  $out = '';
368  foreach ($outputArr as $row) {
369  $out .= '<th>' . $row . '</th>';
370  }
371  $out = '<thead><tr>' . $out . '</tr></thead>';
372  $flag_tree = $this->printConf['flag_tree'];
373  $flag_messages = $this->printConf['flag_messages'];
374  $flag_content = $this->printConf['flag_content'];
375  $keyLgd = $this->printConf['keyLgd'];
376  $c = 0;
377  foreach ($this->tsStackLog as $uniqueId => $data) {
378  $logRowClass = '';
379  if ($this->highlightLongerThan && (int)$data['owntime'] > (int)$this->highlightLongerThan) {
380  $logRowClass = 'typo3-adminPanel-logRow-highlight';
381  }
382  $item = '';
383  // If first...
384  if (!$c) {
385  $data['icons'] = '';
386  $data['key'] = 'Script Start';
387  $data['value'] = '';
388  }
389  // Key label:
390  $keyLabel = '';
391  if (!$flag_tree && $data['stackPointer']) {
392  $temp = [];
393  foreach ($data['tsStack'] as $k => $v) {
394  $temp[] = GeneralUtility::fixed_lgd_cs(implode($k ? '.' : '/', $v), -$keyLgd);
395  }
396  array_pop($temp);
397  $temp = array_reverse($temp);
398  array_pop($temp);
399  if (!empty($temp)) {
400  $keyLabel = '<br /><span style="color:#999999;">' . implode('<br />', $temp) . '</span>';
401  }
402  }
403  if ($flag_tree) {
404  $tmp = GeneralUtility::trimExplode('.', $data['key'], true);
405  $theLabel = end($tmp);
406  } else {
407  $theLabel = $data['key'];
408  }
409  $theLabel = GeneralUtility::fixed_lgd_cs($theLabel, -$keyLgd);
410  $theLabel = $data['stackPointer'] ? '<span class="stackPointer">' . $theLabel . '</span>' : $theLabel;
411  $keyLabel = $theLabel . $keyLabel;
412  $item .= '<th scope="row" class="typo3-adminPanel-table-cell-key ' . $logRowClass . '">' . ($flag_tree ? $data['icons'] : '') . $this->‪fw($keyLabel) . '</th>';
413  // Key value:
414  $keyValue = $data['value'];
415  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime">' . $this->‪fw(htmlspecialchars($keyValue)) . '</td>';
416  if ($this->printConf['allTime']) {
417  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw($data['starttime']) . '</td>';
418  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw($data['owntime']) . '</td>';
419  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw(($data['subtime'] ? '+' . $data['subtime'] : '')) . '</td>';
420  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw(($data['subtime'] ? '=' . $data['deltatime'] : '')) . '</td>';
421  } else {
422  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->‪fw($data['owntime']) . '</td>';
423  }
424  // Messages:
425  $msgArr = [];
426  $msg = '';
427  if ($flag_messages && is_array($data['message'])) {
428  foreach ($data['message'] as $v) {
429  $msgArr[] = nl2br($v);
430  }
431  }
432  if ($flag_content && (string)$data['content'] !== '') {
433  $maxlen = 120;
434  // Break lines which are too longer than $maxlen chars (can happen if content contains long paths...)
435  if (preg_match_all('/(\\S{' . $maxlen . ',})/', $data['content'], $reg)) {
436  foreach ($reg[1] as $key => $match) {
437  $match = preg_replace('/(.{' . $maxlen . '})/', '$1 ', $match);
438  $data['content'] = str_replace($reg[0][$key], $match, $data['content']);
439  }
440  }
441  $msgArr[] = nl2br($data['content']);
442  }
443  if (!empty($msgArr)) {
444  $msg = implode('<hr />', $msgArr);
445  }
446  $item .= '<td class="typo3-adminPanel-table-cell-content">' . $this->‪fw($msg) . '</td>';
447  $out .= '<tr>' . $item . '</tr>';
448  $c++;
449  }
450  $out = '<div class="typo3-adminPanel-table-overflow"><table class="typo3-adminPanel-table typo3-adminPanel-table-debug">' . $out . '</table></div>';
451  return $out;
452  }
453 
463  protected function ‪fixContent(&$arr, $content, $depthData = '', $vKey = '')
464  {
465  $entriesCount = 0;
466  $c = 0;
467  // First, find number of entries
468  foreach ($arr as $k => $v) {
469  //do not count subentries (the one ending with dot, eg. '9.'
470  if (\‪TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($k)) {
471  $entriesCount++;
472  }
473  }
474  // Traverse through entries
475  $subtime = 0;
476  foreach ($arr as $k => $v) {
477  if (\‪TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($k)) {
478  $c++;
479  $hasChildren = isset($arr[$k . '.']);
480  $lastEntry = $entriesCount === $c;
481 
482  $PM = '<span class="treeline-icon treeline-icon-join' . ($lastEntry ? 'bottom' : '') . '"></span>';
483 
484  $this->tsStackLog[$v]['icons'] = $depthData . $PM;
485  if ($this->tsStackLog[$v]['content'] !== '') {
486  $content = str_replace($this->tsStackLog[$v]['content'], $v, $content);
487  }
488  if ($hasChildren) {
489  $lineClass = $lastEntry ? 'treeline-icon-clear' : 'treeline-icon-line';
490  $this->tsStackLog[$v]['content'] = $this->‪fixContent($arr[$k . '.'], $this->tsStackLog[$v]['content'], $depthData . '<span class="treeline-icon ' . $lineClass . '"></span>', $v);
491  } else {
492  $this->tsStackLog[$v]['content'] = $this->‪fixCLen($this->tsStackLog[$v]['content'], $this->tsStackLog[$v]['value']);
493  $this->tsStackLog[$v]['subtime'] = '';
494  $this->tsStackLog[$v]['owntime'] = $this->tsStackLog[$v]['deltatime'];
495  }
496  $subtime += $this->tsStackLog[$v]['deltatime'];
497  }
498  }
499  // Set content with special chars
500  if (isset($this->tsStackLog[$vKey])) {
501  $this->tsStackLog[$vKey]['subtime'] = $subtime;
502  $this->tsStackLog[$vKey]['owntime'] = $this->tsStackLog[$vKey]['deltatime'] - $subtime;
503  }
504  $content = $this->‪fixCLen($content, $this->tsStackLog[$vKey]['value']);
505  // Traverse array again, this time substitute the unique hash with the red key
506  foreach ($arr as $k => $v) {
507  if (\‪TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($k)) {
508  if ($this->tsStackLog[$v]['content'] !== '') {
509  $content = str_replace($v, '<strong style="color:red;">[' . $this->tsStackLog[$v]['key'] . ']</strong>', $content);
510  }
511  }
512  }
513  // Return the content
514  return $content;
515  }
516 
524  protected function ‪fixCLen($c, $v)
525  {
526  $len = $v === 'FILE' ? $this->printConf['contentLength_FILE'] : $this->printConf['contentLength'];
527  if (strlen($c) > $len) {
528  $c = '<span style="color:green;">' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($c, $len)) . '</span>';
529  } else {
530  $c = htmlspecialchars($c);
531  }
532  return $c;
533  }
534 
541  protected function ‪fw($str)
542  {
543  return '<span>' . $str . '</span>';
544  }
545 
555  protected function ‪createHierarchyArray(&$arr, $pointer, $uniqueId)
556  {
557  if (!is_array($arr)) {
558  $arr = [];
559  }
560  if ($pointer > 0) {
561  end($arr);
562  $k = key($arr);
563  $this->‪createHierarchyArray($arr[(int)$k . '.'], $pointer - 1, $uniqueId);
564  } else {
565  $arr[] = $uniqueId;
566  }
567  }
568 }
‪TYPO3\CMS\Core\Imaging\Icon\SIZE_SMALL
‪const SIZE_SMALL
Definition: Icon.php:29
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStackLog
‪array $tsStackLog
Definition: TimeTracker.php:97
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$printConf
‪array $printConf
Definition: TimeTracker.php:48
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStack
‪array $tsStack
Definition: TimeTracker.php:85
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\incStackPointer
‪incStackPointer()
Definition: TimeTracker.php:233
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$wrapError
‪array $wrapError
Definition: TimeTracker.php:63
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\setTSselectQuery
‪setTSselectQuery(array $data, $msg='')
Definition: TimeTracker.php:215
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\decStackPointer
‪decStackPointer()
Definition: TimeTracker.php:247
‪TYPO3\CMS\Core\Imaging\Icon
Definition: Icon.php:25
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$starttime
‪int $starttime
Definition: TimeTracker.php:37
‪TYPO3
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\getDifferenceToStarttime
‪int getDifferenceToStarttime($microtime=null)
Definition: TimeTracker.php:279
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$isEnabled
‪bool $isEnabled
Definition: TimeTracker.php:31
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:31
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\getParseTime
‪int getParseTime()
Definition: TimeTracker.php:289
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStackPointer
‪int $tsStackPointer
Definition: TimeTracker.php:101
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\setTSlogMessage
‪setTSlogMessage($content, $num=0)
Definition: TimeTracker.php:193
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$wrapIcon
‪array $wrapIcon
Definition: TimeTracker.php:72
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$uniqueCounter
‪int $uniqueCounter
Definition: TimeTracker.php:81
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStackLevel
‪int $tsStackLevel
Definition: TimeTracker.php:89
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\fixContent
‪string fixContent(&$arr, $content, $depthData='', $vKey='')
Definition: TimeTracker.php:449
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\start
‪start()
Definition: TimeTracker.php:132
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$LR
‪bool $LR
Definition: TimeTracker.php:44
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\pull
‪pull($content='')
Definition: TimeTracker.php:173
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\fixCLen
‪string fixCLen($c, $v)
Definition: TimeTracker.php:510
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\getMilliseconds
‪int getMilliseconds($microtime=null)
Definition: TimeTracker.php:262
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$currentHashPointer
‪array $currentHashPointer
Definition: TimeTracker.php:105
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$highlightLongerThan
‪int $highlightLongerThan
Definition: TimeTracker.php:111
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\createHierarchyArray
‪createHierarchyArray(&$arr, $pointer, $uniqueId)
Definition: TimeTracker.php:541
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Core\TimeTracker\TimeTracker
Definition: TimeTracker.php:27
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\$tsStackLevelMax
‪array $tsStackLevelMax
Definition: TimeTracker.php:93
‪TYPO3\CMS\Core\TimeTracker
Definition: TimeTracker.php:2
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\__construct
‪__construct($isEnabled=true)
Definition: TimeTracker.php:124
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\printTSlog
‪string printTSlog()
Definition: TimeTracker.php:318
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\fw
‪string fw($str)
Definition: TimeTracker.php:527
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\push
‪push($tslabel, $value='')
Definition: TimeTracker.php:147