TYPO3 CMS  TYPO3_7-6
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  */
19 
26 {
32  public $starttime = 0;
33 
40  public $LR = 1;
41 
45  public $printConf = [
46  'showParentKeys' => 1,
47  'contentLength' => 10000,
48  // Determines max length of displayed content before it gets cropped.
49  'contentLength_FILE' => 400,
50  // 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.
51  'flag_tree' => 1,
52  'flag_messages' => 1,
53  'flag_queries' => 0,
54  'flag_content' => 0,
55  'allTime' => 0,
56  'keyLgd' => 40
57  ];
58 
62  public $wrapError = [];
63 
67  public $wrapIcon = [];
68 
72  public $uniqueCounter = 0;
73 
77  public $tsStack = [[]];
78 
82  public $tsStackLevel = 0;
83 
87  public $tsStackLevelMax = [];
88 
92  public $tsStackLog = [];
93 
97  public $tsStackPointer = 0;
98 
102  public $currentHashPointer = [];
103 
110 
111  /*******************************************
112  *
113  * Logging parsing times in the scripts
114  *
115  *******************************************/
122  public function start()
123  {
124  $this->wrapError = [
125  0 => ['', ''],
126  1 => ['<strong>', '</strong>'],
127  2 => ['<strong style="color:#ff6600;">', '</strong>'],
128  3 => ['<strong style="color:#ff0000;">', '</strong>']
129  ];
130  $this->wrapIcon = [
131  0 => '',
132  1 => 'actions-document-info',
133  2 => 'status-dialog-warning',
134  3 => 'status-dialog-error'
135  ];
136  $this->starttime = $this->getMilliseconds();
137  }
138 
147  public function push($tslabel, $value = '')
148  {
149  array_push($this->tsStack[$this->tsStackPointer], $tslabel);
150  array_push($this->currentHashPointer, 'timetracker_' . $this->uniqueCounter++);
151  $this->tsStackLevel++;
152  $this->tsStackLevelMax[] = $this->tsStackLevel;
153  // setTSlog
154  $k = end($this->currentHashPointer);
155  $this->tsStackLog[$k] = [
156  'level' => $this->tsStackLevel,
157  'tsStack' => $this->tsStack,
158  'value' => $value,
159  'starttime' => microtime(true),
160  'stackPointer' => $this->tsStackPointer
161  ];
162  }
163 
171  public function pull($content = '')
172  {
173  $k = end($this->currentHashPointer);
174  $this->tsStackLog[$k]['endtime'] = microtime(true);
175  $this->tsStackLog[$k]['content'] = $content;
176  $this->tsStackLevel--;
177  array_pop($this->tsStack[$this->tsStackPointer]);
178  array_pop($this->currentHashPointer);
179  }
180 
189  public function setTSlogMessage($content, $num = 0)
190  {
191  end($this->currentHashPointer);
192  $k = current($this->currentHashPointer);
193  // Enlarge the "details" column by adding a span
194  if (strlen($content) > 30) {
195  $placeholder = '<br /><span style="width: 300px; height: 1px; display: inline-block;"></span>';
196  }
197  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
198  $this->tsStackLog[$k]['message'][] = $iconFactory->getIcon($this->wrapIcon[$num], Icon::SIZE_SMALL)->render() . $this->wrapError[$num][0] . htmlspecialchars($content) . $this->wrapError[$num][1] . $placeholder;
199  }
200 
208  public function setTSselectQuery(array $data, $msg = '')
209  {
210  end($this->currentHashPointer);
211  $k = current($this->currentHashPointer);
212  if ($msg !== '') {
213  $data['msg'] = $msg;
214  }
215  $this->tsStackLog[$k]['selectQuery'][] = $data;
216  }
217 
224  public function incStackPointer()
225  {
226  $this->tsStackPointer++;
227  $this->tsStack[$this->tsStackPointer] = [];
228  }
229 
236  public function decStackPointer()
237  {
238  unset($this->tsStack[$this->tsStackPointer]);
239  $this->tsStackPointer--;
240  }
241 
248  public function getMilliseconds($microtime = null)
249  {
250  if (!isset($microtime)) {
251  $microtime = microtime(true);
252  }
253  return round($microtime * 1000);
254  }
255 
262  public function getDifferenceToStarttime($microtime = null)
263  {
264  return $this->getMilliseconds($microtime) - $this->starttime;
265  }
266 
267  /*******************************************
268  *
269  * Printing the parsing time information (for Admin Panel)
270  *
271  *******************************************/
277  public function printTSlog()
278  {
279  // Calculate times and keys for the tsStackLog
280  foreach ($this->tsStackLog as $uniqueId => &$data) {
281  $data['endtime'] = $this->getDifferenceToStarttime($data['endtime']);
282  $data['starttime'] = $this->getDifferenceToStarttime($data['starttime']);
283  $data['deltatime'] = $data['endtime'] - $data['starttime'];
284  if (is_array($data['tsStack'])) {
285  $data['key'] = implode($data['stackPointer'] ? '.' : '/', end($data['tsStack']));
286  }
287  }
288  unset($data);
289  // Create hierarchical array of keys pointing to the stack
290  $arr = [];
291  foreach ($this->tsStackLog as $uniqueId => $data) {
292  $this->createHierarchyArray($arr, $data['level'], $uniqueId);
293  }
294  // Parsing the registeret content and create icon-html for the tree
295  $this->tsStackLog[$arr['0.'][0]]['content'] = $this->fixContent($arr['0.'], $this->tsStackLog[$arr['0.'][0]]['content'], '', 0, $arr['0.'][0]);
296  // Displaying the tree:
297  $outputArr = [];
298  $outputArr[] = $this->fw('TypoScript Key');
299  $outputArr[] = $this->fw('Value');
300  if ($this->printConf['allTime']) {
301  $outputArr[] = $this->fw('Time');
302  $outputArr[] = $this->fw('Own');
303  $outputArr[] = $this->fw('Sub');
304  $outputArr[] = $this->fw('Total');
305  } else {
306  $outputArr[] = $this->fw('Own');
307  }
308  $outputArr[] = $this->fw('Details');
309  $out = '';
310  foreach ($outputArr as $row) {
311  $out .= '
312  <th><strong>' . $row . '</strong></th>';
313  }
314  $out = '<tr class="typo3-adminPanel-itemRow">' . $out . '</tr>';
315  $flag_tree = $this->printConf['flag_tree'];
316  $flag_messages = $this->printConf['flag_messages'];
317  $flag_content = $this->printConf['flag_content'];
318  $flag_queries = $this->printConf['flag_queries'];
319  $keyLgd = $this->printConf['keyLgd'];
320  $factor = $this->printConf['factor'];
321  $col = $this->printConf['col'];
322  $highlight_col = $this->printConf['highlight_col'];
323  $c = 0;
324  foreach ($this->tsStackLog as $uniqueId => $data) {
325  if ($this->highlightLongerThan && (int)$data['owntime'] > (int)$this->highlightLongerThan) {
326  $logRowClass = 'typo3-adminPanel-logRow-highlight';
327  } else {
328  $logRowClass = $c % 2 ? 'line-odd' : 'line-even';
329  }
330  $logRowClass .= ' typo3-adminPanel-section-content-title';
331  $item = '';
332  // If first...
333  if (!$c) {
334  $data['icons'] = '';
335  $data['key'] = 'Script Start';
336  $data['value'] = '';
337  }
338  // Key label:
339  $keyLabel = '';
340  if (!$flag_tree && $data['stackPointer']) {
341  $temp = [];
342  foreach ($data['tsStack'] as $k => $v) {
343  $temp[] = GeneralUtility::fixed_lgd_cs(implode($v, $k ? '.' : '/'), -$keyLgd);
344  }
345  array_pop($temp);
346  $temp = array_reverse($temp);
347  array_pop($temp);
348  if (!empty($temp)) {
349  $keyLabel = '<br /><span style="color:#999999;">' . implode($temp, '<br />') . '</span>';
350  }
351  }
352  if ($flag_tree) {
353  $tmp = GeneralUtility::trimExplode('.', $data['key'], true);
354  $theLabel = end($tmp);
355  } else {
356  $theLabel = $data['key'];
357  }
358  $theLabel = GeneralUtility::fixed_lgd_cs($theLabel, -$keyLgd);
359  $theLabel = $data['stackPointer'] ? '<span class="stackPointer">' . $theLabel . '</span>' : $theLabel;
360  $keyLabel = $theLabel . $keyLabel;
361  $item .= '<td class="' . $logRowClass . '">' . ($flag_tree ? $data['icons'] : '') . $this->fw($keyLabel) . '</td>';
362  // Key value:
363  $keyValue = $data['value'];
364  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime">' . $this->fw(htmlspecialchars($keyValue)) . '</td>';
365  if ($this->printConf['allTime']) {
366  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw($data['starttime']) . '</td>';
367  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw($data['owntime']) . '</td>';
368  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw(($data['subtime'] ? '+' . $data['subtime'] : '')) . '</td>';
369  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw(($data['subtime'] ? '=' . $data['deltatime'] : '')) . '</td>';
370  } else {
371  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw($data['owntime']) . '</td>';
372  }
373  // Messages:
374  $msgArr = [];
375  $msg = '';
376  if ($flag_messages && is_array($data['message'])) {
377  foreach ($data['message'] as $v) {
378  $msgArr[] = nl2br($v);
379  }
380  }
381  if ($flag_queries && is_array($data['selectQuery'])) {
382  $msgArr[] = \TYPO3\CMS\Core\Utility\DebugUtility::viewArray($data['selectQuery']);
383  }
384  if ($flag_content && (string)$data['content'] !== '') {
385  $maxlen = 120;
386  // Break lines which are too longer than $maxlen chars (can happen if content contains long paths...)
387  if (preg_match_all('/(\\S{' . $maxlen . ',})/', $data['content'], $reg)) {
388  foreach ($reg[1] as $key => $match) {
389  $match = preg_replace('/(.{' . $maxlen . '})/', '$1 ', $match);
390  $data['content'] = str_replace($reg[0][$key], $match, $data['content']);
391  }
392  }
393  $msgArr[] = '<span style="color:#000066;">' . nl2br($data['content']) . '</span>';
394  }
395  if (!empty($msgArr)) {
396  $msg = implode($msgArr, '<hr />');
397  }
398  $item .= '<td valign="top" class="' . $logRowClass . '" style="text-align:left;">' . $this->fw($msg) . '</td>';
399  $out .= '<tr class="typo3-adminPanel-itemRow">' . $item . '</tr>';
400  $c++;
401  }
402  $out = '<table class="typo3-adminPanel-table typo3-adminPanel-tsLog">' . $out . '</table>';
403  return $out;
404  }
405 
416  protected function fixContent(&$arr, $content, $depthData = '', $first = 0, $vKey = '')
417  {
418  $ac = 0;
419  $c = 0;
420  // First, find number of entries
421  foreach ($arr as $k => $v) {
422  if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($k)) {
423  $ac++;
424  }
425  }
426  // Traverse through entries
427  $subtime = 0;
428  foreach ($arr as $k => $v) {
429  if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($k)) {
430  $c++;
431  $deeper = is_array($arr[$k . '.']) ? 1 : 0;
432  $LN = $ac == $c ? 'blank' : 'line';
433 
434  $BTM = $ac == $c ? 'bottom' : '';
435  $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>';
436 
437  $this->tsStackLog[$v]['icons'] = $depthData . ($first ? '' : $PM);
438  if ($this->tsStackLog[$v]['content'] !== '') {
439  $content = str_replace($this->tsStackLog[$v]['content'], $v, $content);
440  }
441  if (is_array($arr[$k . '.'])) {
442  $this->tsStackLog[$v]['content'] = $this->fixContent($arr[$k . '.'], $this->tsStackLog[$v]['content'], $depthData . ($first ? '' : '<span class="treeline-icon treeline-icon-' . $LN . '"></span>'), 0, $v);
443  } else {
444  $this->tsStackLog[$v]['content'] = $this->fixCLen($this->tsStackLog[$v]['content'], $this->tsStackLog[$v]['value']);
445  $this->tsStackLog[$v]['subtime'] = '';
446  $this->tsStackLog[$v]['owntime'] = $this->tsStackLog[$v]['deltatime'];
447  }
448  $subtime += $this->tsStackLog[$v]['deltatime'];
449  }
450  }
451  // Set content with special chars
452  if (isset($this->tsStackLog[$vKey])) {
453  $this->tsStackLog[$vKey]['subtime'] = $subtime;
454  $this->tsStackLog[$vKey]['owntime'] = $this->tsStackLog[$vKey]['deltatime'] - $subtime;
455  }
456  $content = $this->fixCLen($content, $this->tsStackLog[$vKey]['value']);
457  // Traverse array again, this time substitute the unique hash with the red key
458  foreach ($arr as $k => $v) {
459  if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($k)) {
460  if ($this->tsStackLog[$v]['content'] !== '') {
461  $content = str_replace($v, '<strong style="color:red;">[' . $this->tsStackLog[$v]['key'] . ']</strong>', $content);
462  }
463  }
464  }
465  // Return the content
466  return $content;
467  }
468 
476  protected function fixCLen($c, $v)
477  {
478  $len = $v == 'FILE' ? $this->printConf['contentLength_FILE'] : $this->printConf['contentLength'];
479  if (strlen($c) > $len) {
480  $c = '<span style="color:green;">' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($c, $len)) . '</span>';
481  } else {
482  $c = htmlspecialchars($c);
483  }
484  return $c;
485  }
486 
493  protected function fw($str)
494  {
495  return '<span>' . $str . '</span>';
496  }
497 
508  protected function createHierarchyArray(&$arr, $pointer, $uniqueId)
509  {
510  if (!is_array($arr)) {
511  $arr = [];
512  }
513  if ($pointer > 0) {
514  end($arr);
515  $k = key($arr);
516  $this->createHierarchyArray($arr[(int)$k . '.'], $pointer - 1, $uniqueId);
517  } else {
518  $arr[] = $uniqueId;
519  }
520  }
521 }
fixContent(&$arr, $content, $depthData='', $first=0, $vKey='')
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
setTSselectQuery(array $data, $msg='')
createHierarchyArray(&$arr, $pointer, $uniqueId)
static fixed_lgd_cs($string, $chars, $appendString='...')