TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
TimeTracker.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Core\TimeTracker;
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 = 1;
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 
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 
148  public function start()
149  {
150  if (!$this->isEnabled) {
151  return;
152  }
153  $this->starttime = $this->getMilliseconds();
154  }
155 
164  public function push($tslabel, $value = '')
165  {
166  if (!$this->isEnabled) {
167  return;
168  }
169  array_push($this->tsStack[$this->tsStackPointer], $tslabel);
170  array_push($this->currentHashPointer, 'timetracker_' . $this->uniqueCounter++);
171  $this->tsStackLevel++;
172  $this->tsStackLevelMax[] = $this->tsStackLevel;
173  // setTSlog
174  $k = end($this->currentHashPointer);
175  $this->tsStackLog[$k] = [
176  'level' => $this->tsStackLevel,
177  'tsStack' => $this->tsStack,
178  'value' => $value,
179  'starttime' => microtime(true),
180  'stackPointer' => $this->tsStackPointer
181  ];
182  }
183 
191  public function pull($content = '')
192  {
193  if (!$this->isEnabled) {
194  return;
195  }
196  $k = end($this->currentHashPointer);
197  $this->tsStackLog[$k]['endtime'] = microtime(true);
198  $this->tsStackLog[$k]['content'] = $content;
199  $this->tsStackLevel--;
200  array_pop($this->tsStack[$this->tsStackPointer]);
201  array_pop($this->currentHashPointer);
202  }
203 
212  public function setTSlogMessage($content, $num = 0)
213  {
214  if (!$this->isEnabled) {
215  return;
216  }
217  end($this->currentHashPointer);
218  $k = current($this->currentHashPointer);
219  // Enlarge the "details" column by adding a span
220  if (strlen($content) > 30) {
221  $placeholder = '<br /><span style="width: 300px; height: 1px; display: inline-block;"></span>';
222  }
223  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
224  $this->tsStackLog[$k]['message'][] = $iconFactory->getIcon($this->wrapIcon[$num], Icon::SIZE_SMALL)->render() . $this->wrapError[$num][0] . htmlspecialchars($content) . $this->wrapError[$num][1] . $placeholder;
225  }
226 
234  public function setTSselectQuery(array $data, $msg = '')
235  {
236  if (!$this->isEnabled) {
237  return;
238  }
239  end($this->currentHashPointer);
240  $k = current($this->currentHashPointer);
241  if ($msg !== '') {
242  $data['msg'] = $msg;
243  }
244  $this->tsStackLog[$k]['selectQuery'][] = $data;
245  }
246 
253  public function incStackPointer()
254  {
255  if (!$this->isEnabled) {
256  return;
257  }
258  $this->tsStackPointer++;
259  $this->tsStack[$this->tsStackPointer] = [];
260  }
261 
268  public function decStackPointer()
269  {
270  if (!$this->isEnabled) {
271  return;
272  }
273  unset($this->tsStack[$this->tsStackPointer]);
274  $this->tsStackPointer--;
275  }
276 
283  public function getMilliseconds($microtime = null)
284  {
285  if (!$this->isEnabled) {
286  return 0;
287  }
288  if (!isset($microtime)) {
289  $microtime = microtime(true);
290  }
291  return round($microtime * 1000);
292  }
293 
300  public function getDifferenceToStarttime($microtime = null)
301  {
302  return $this->getMilliseconds($microtime) - $this->starttime;
303  }
304 
305  /*******************************************
306  *
307  * Printing the parsing time information (for Admin Panel)
308  *
309  *******************************************/
315  public function printTSlog()
316  {
317  if (!$this->isEnabled) {
318  return '';
319  }
320  // Calculate times and keys for the tsStackLog
321  foreach ($this->tsStackLog as $uniqueId => &$data) {
322  $data['endtime'] = $this->getDifferenceToStarttime($data['endtime']);
323  $data['starttime'] = $this->getDifferenceToStarttime($data['starttime']);
324  $data['deltatime'] = $data['endtime'] - $data['starttime'];
325  if (is_array($data['tsStack'])) {
326  $data['key'] = implode($data['stackPointer'] ? '.' : '/', end($data['tsStack']));
327  }
328  }
329  unset($data);
330  // Create hierarchical array of keys pointing to the stack
331  $arr = [];
332  foreach ($this->tsStackLog as $uniqueId => $data) {
333  $this->createHierarchyArray($arr, $data['level'], $uniqueId);
334  }
335  // Parsing the registeret content and create icon-html for the tree
336  $this->tsStackLog[$arr['0.'][0]]['content'] = $this->fixContent($arr['0.'], $this->tsStackLog[$arr['0.'][0]]['content'], '', 0, $arr['0.'][0]);
337  // Displaying the tree:
338  $outputArr = [];
339  $outputArr[] = $this->fw('TypoScript Key');
340  $outputArr[] = $this->fw('Value');
341  if ($this->printConf['allTime']) {
342  $outputArr[] = $this->fw('Time');
343  $outputArr[] = $this->fw('Own');
344  $outputArr[] = $this->fw('Sub');
345  $outputArr[] = $this->fw('Total');
346  } else {
347  $outputArr[] = $this->fw('Own');
348  }
349  $outputArr[] = $this->fw('Details');
350  $out = '';
351  foreach ($outputArr as $row) {
352  $out .= '
353  <th><strong>' . $row . '</strong></th>';
354  }
355  $out = '<tr class="typo3-adminPanel-itemRow">' . $out . '</tr>';
356  $flag_tree = $this->printConf['flag_tree'];
357  $flag_messages = $this->printConf['flag_messages'];
358  $flag_content = $this->printConf['flag_content'];
359  $keyLgd = $this->printConf['keyLgd'];
360  $c = 0;
361  foreach ($this->tsStackLog as $uniqueId => $data) {
362  if ($this->highlightLongerThan && (int)$data['owntime'] > (int)$this->highlightLongerThan) {
363  $logRowClass = 'typo3-adminPanel-logRow-highlight';
364  } else {
365  $logRowClass = $c % 2 ? 'line-odd' : 'line-even';
366  }
367  $logRowClass .= ' typo3-adminPanel-section-content-title';
368  $item = '';
369  // If first...
370  if (!$c) {
371  $data['icons'] = '';
372  $data['key'] = 'Script Start';
373  $data['value'] = '';
374  }
375  // Key label:
376  $keyLabel = '';
377  if (!$flag_tree && $data['stackPointer']) {
378  $temp = [];
379  foreach ($data['tsStack'] as $k => $v) {
380  $temp[] = GeneralUtility::fixed_lgd_cs(implode($v, $k ? '.' : '/'), -$keyLgd);
381  }
382  array_pop($temp);
383  $temp = array_reverse($temp);
384  array_pop($temp);
385  if (!empty($temp)) {
386  $keyLabel = '<br /><span style="color:#999999;">' . implode($temp, '<br />') . '</span>';
387  }
388  }
389  if ($flag_tree) {
390  $tmp = GeneralUtility::trimExplode('.', $data['key'], true);
391  $theLabel = end($tmp);
392  } else {
393  $theLabel = $data['key'];
394  }
395  $theLabel = GeneralUtility::fixed_lgd_cs($theLabel, -$keyLgd);
396  $theLabel = $data['stackPointer'] ? '<span class="stackPointer">' . $theLabel . '</span>' : $theLabel;
397  $keyLabel = $theLabel . $keyLabel;
398  $item .= '<td class="' . $logRowClass . '">' . ($flag_tree ? $data['icons'] : '') . $this->fw($keyLabel) . '</td>';
399  // Key value:
400  $keyValue = $data['value'];
401  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime">' . $this->fw(htmlspecialchars($keyValue)) . '</td>';
402  if ($this->printConf['allTime']) {
403  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw($data['starttime']) . '</td>';
404  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw($data['owntime']) . '</td>';
405  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw(($data['subtime'] ? '+' . $data['subtime'] : '')) . '</td>';
406  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw(($data['subtime'] ? '=' . $data['deltatime'] : '')) . '</td>';
407  } else {
408  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw($data['owntime']) . '</td>';
409  }
410  // Messages:
411  $msgArr = [];
412  $msg = '';
413  if ($flag_messages && is_array($data['message'])) {
414  foreach ($data['message'] as $v) {
415  $msgArr[] = nl2br($v);
416  }
417  }
418  if ($flag_content && (string)$data['content'] !== '') {
419  $maxlen = 120;
420  // Break lines which are too longer than $maxlen chars (can happen if content contains long paths...)
421  if (preg_match_all('/(\\S{' . $maxlen . ',})/', $data['content'], $reg)) {
422  foreach ($reg[1] as $key => $match) {
423  $match = preg_replace('/(.{' . $maxlen . '})/', '$1 ', $match);
424  $data['content'] = str_replace($reg[0][$key], $match, $data['content']);
425  }
426  }
427  $msgArr[] = '<span style="color:#000066;">' . nl2br($data['content']) . '</span>';
428  }
429  if (!empty($msgArr)) {
430  $msg = implode($msgArr, '<hr />');
431  }
432  $item .= '<td valign="top" class="' . $logRowClass . '" style="text-align:left;">' . $this->fw($msg) . '</td>';
433  $out .= '<tr class="typo3-adminPanel-itemRow">' . $item . '</tr>';
434  $c++;
435  }
436  $out = '<table class="typo3-adminPanel-table typo3-adminPanel-tsLog">' . $out . '</table>';
437  return $out;
438  }
439 
450  protected function fixContent(&$arr, $content, $depthData = '', $first = 0, $vKey = '')
451  {
452  $ac = 0;
453  $c = 0;
454  // First, find number of entries
455  foreach ($arr as $k => $v) {
456  if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($k)) {
457  $ac++;
458  }
459  }
460  // Traverse through entries
461  $subtime = 0;
462  foreach ($arr as $k => $v) {
463  if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($k)) {
464  $c++;
465  $deeper = is_array($arr[$k . '.']) ? 1 : 0;
466  $LN = $ac == $c ? 'blank' : 'line';
467 
468  $BTM = $ac == $c ? 'bottom' : '';
469  $PM = is_array($arr[$k . '.']) ? '<i class="fa fa-' . ($deeper ? 'minus' : 'plus') . '-square-o"></i>' : '<span class="treeline-icon treeline-icon-join' . ($BTM ? 'bottom' : '') . '"></span>';
470 
471  $this->tsStackLog[$v]['icons'] = $depthData . ($first ? '' : $PM);
472  if ($this->tsStackLog[$v]['content'] !== '') {
473  $content = str_replace($this->tsStackLog[$v]['content'], $v, $content);
474  }
475  if (is_array($arr[$k . '.'])) {
476  $this->tsStackLog[$v]['content'] = $this->fixContent($arr[$k . '.'], $this->tsStackLog[$v]['content'], $depthData . ($first ? '' : '<span class="treeline-icon treeline-icon-' . $LN . '"></span>'), 0, $v);
477  } else {
478  $this->tsStackLog[$v]['content'] = $this->fixCLen($this->tsStackLog[$v]['content'], $this->tsStackLog[$v]['value']);
479  $this->tsStackLog[$v]['subtime'] = '';
480  $this->tsStackLog[$v]['owntime'] = $this->tsStackLog[$v]['deltatime'];
481  }
482  $subtime += $this->tsStackLog[$v]['deltatime'];
483  }
484  }
485  // Set content with special chars
486  if (isset($this->tsStackLog[$vKey])) {
487  $this->tsStackLog[$vKey]['subtime'] = $subtime;
488  $this->tsStackLog[$vKey]['owntime'] = $this->tsStackLog[$vKey]['deltatime'] - $subtime;
489  }
490  $content = $this->fixCLen($content, $this->tsStackLog[$vKey]['value']);
491  // Traverse array again, this time substitute the unique hash with the red key
492  foreach ($arr as $k => $v) {
493  if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($k)) {
494  if ($this->tsStackLog[$v]['content'] !== '') {
495  $content = str_replace($v, '<strong style="color:red;">[' . $this->tsStackLog[$v]['key'] . ']</strong>', $content);
496  }
497  }
498  }
499  // Return the content
500  return $content;
501  }
502 
510  protected function fixCLen($c, $v)
511  {
512  $len = $v == 'FILE' ? $this->printConf['contentLength_FILE'] : $this->printConf['contentLength'];
513  if (strlen($c) > $len) {
514  $c = '<span style="color:green;">' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($c, $len)) . '</span>';
515  } else {
516  $c = htmlspecialchars($c);
517  }
518  return $c;
519  }
520 
527  protected function fw($str)
528  {
529  return '<span>' . $str . '</span>';
530  }
531 
542  protected function createHierarchyArray(&$arr, $pointer, $uniqueId)
543  {
544  if (!is_array($arr)) {
545  $arr = [];
546  }
547  if ($pointer > 0) {
548  end($arr);
549  $k = key($arr);
550  $this->createHierarchyArray($arr[(int)$k . '.'], $pointer - 1, $uniqueId);
551  } else {
552  $arr[] = $uniqueId;
553  }
554  }
555 }
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
setTSselectQuery(array $data, $msg= '')
fixContent(&$arr, $content, $depthData= '', $first=0, $vKey= '')
static makeInstance($className,...$constructorArguments)
createHierarchyArray(&$arr, $pointer, $uniqueId)