TYPO3 CMS  TYPO3_6-2
TimeTracker.php
Go to the documentation of this file.
1 <?php
3 
24 class TimeTracker {
25 
26  // Is loaded with the millisecond time when this object is created
30  public $starttime = 0;
31 
32  // Log Rendering flag. If set, ->push() and ->pull() is called from the cObj->cObjGetSingle(). This determines whether or not the TypoScript parsing activity is logged. But it also slows down the rendering
36  public $LR = 1;
37 
41  public $printConf = array(
42  'showParentKeys' => 1,
43  'contentLength' => 10000,
44  // Determines max length of displayed content before it gets cropped.
45  'contentLength_FILE' => 400,
46  // 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.
47  'flag_tree' => 1,
48  'flag_messages' => 1,
49  'flag_queries' => 0,
50  'flag_content' => 0,
51  'allTime' => 0,
52  'keyLgd' => 40
53  );
54 
58  public $wrapError = array();
59 
63  public $wrapIcon = array();
64 
68  public $uniqueCounter = 0;
69 
73  public $tsStack = array(array());
74 
78  public $tsStackLevel = 0;
79 
83  public $tsStackLevelMax = array();
84 
88  public $tsStackLog = array();
89 
93  public $tsStackPointer = 0;
94 
98  public $currentHashPointer = array();
99 
100  // Log entries that take than this number of milliseconds (own time) will be highlighted during log display. Set 0 to disable highlighting.
105 
106  /*******************************************
107  *
108  * Logging parsing times in the scripts
109  *
110  *******************************************/
117  public function start() {
118  $this->wrapError = array(
119  0 => array('', ''),
120  1 => array('<strong>', '</strong>'),
121  2 => array('<strong style="color:#ff6600;">', '</strong>'),
122  3 => array('<strong style="color:#ff0000;">', '</strong>')
123  );
124  $this->wrapIcon = array(
125  0 => '',
126  1 => '<img src="' . TYPO3_mainDir . 'gfx/icon_note.gif" width="18" height="16" align="absmiddle" alt="" />',
127  2 => '<img src="' . TYPO3_mainDir . 'gfx/icon_warning2.gif" width="18" height="16" align="absmiddle" alt="" />',
128  3 => '<img src="' . TYPO3_mainDir . 'gfx/icon_fatalerror.gif" width="18" height="16" align="absmiddle" alt="" />'
129  );
130  $this->starttime = $this->getMilliseconds();
131  }
132 
141  public function push($tslabel, $value = '') {
142  array_push($this->tsStack[$this->tsStackPointer], $tslabel);
143  array_push($this->currentHashPointer, 'timetracker_' . $this->uniqueCounter++);
144  $this->tsStackLevel++;
145  $this->tsStackLevelMax[] = $this->tsStackLevel;
146  // setTSlog
147  $k = end($this->currentHashPointer);
148  $this->tsStackLog[$k] = array(
149  'level' => $this->tsStackLevel,
150  'tsStack' => $this->tsStack,
151  'value' => $value,
152  'starttime' => microtime(TRUE),
153  'stackPointer' => $this->tsStackPointer
154  );
155  }
156 
164  public function pull($content = '') {
165  $k = end($this->currentHashPointer);
166  $this->tsStackLog[$k]['endtime'] = microtime(TRUE);
167  $this->tsStackLog[$k]['content'] = $content;
168  $this->tsStackLevel--;
169  array_pop($this->tsStack[$this->tsStackPointer]);
170  array_pop($this->currentHashPointer);
171  }
172 
181  public function setTSlogMessage($content, $num = 0) {
182  end($this->currentHashPointer);
183  $k = current($this->currentHashPointer);
184  // Enlarge the "details" column by adding a wide clear.gif
185  if (strlen($content) > 30) {
186  $placeholder = '<br /><img src="' . TYPO3_mainDir . 'clear.gif" width="300" height="1" alt="" />';
187  }
188  $this->tsStackLog[$k]['message'][] = $this->wrapIcon[$num] . $this->wrapError[$num][0] . htmlspecialchars($content) . $this->wrapError[$num][1] . $placeholder;
189  }
190 
198  public function setTSselectQuery(array $data, $msg = '') {
199  end($this->currentHashPointer);
200  $k = current($this->currentHashPointer);
201  if (strlen($msg)) {
202  $data['msg'] = $msg;
203  }
204  $this->tsStackLog[$k]['selectQuery'][] = $data;
205  }
206 
213  public function incStackPointer() {
214  $this->tsStackPointer++;
215  $this->tsStack[$this->tsStackPointer] = array();
216  }
217 
224  public function decStackPointer() {
225  unset($this->tsStack[$this->tsStackPointer]);
226  $this->tsStackPointer--;
227  }
228 
235  public function getMilliseconds($microtime = NULL) {
236  if (!isset($microtime)) {
237  $microtime = microtime(TRUE);
238  }
239  return round($microtime * 1000);
240  }
241 
248  public function getDifferenceToStarttime($microtime = NULL) {
249  return $this->getMilliseconds($microtime) - $this->starttime;
250  }
251 
252  /*******************************************
253  *
254  * Printing the parsing time information (for Admin Panel)
255  *
256  *******************************************/
262  public function printTSlog() {
263  // Calculate times and keys for the tsStackLog
264  foreach ($this->tsStackLog as $uniqueId => &$data) {
265  $data['endtime'] = $this->getDifferenceToStarttime($data['endtime']);
266  $data['starttime'] = $this->getDifferenceToStarttime($data['starttime']);
267  $data['deltatime'] = $data['endtime'] - $data['starttime'];
268  if (is_array($data['tsStack'])) {
269  $data['key'] = implode($data['stackPointer'] ? '.' : '/', end($data['tsStack']));
270  }
271  }
272  unset($data);
273  // Create hierarchical array of keys pointing to the stack
274  $arr = array();
275  foreach ($this->tsStackLog as $uniqueId => $data) {
276  $this->createHierarchyArray($arr, $data['level'], $uniqueId);
277  }
278  // Parsing the registeret content and create icon-html for the tree
279  $this->tsStackLog[$arr['0.'][0]]['content'] = $this->fixContent($arr['0.'], $this->tsStackLog[$arr['0.'][0]]['content'], '', 0, $arr['0.'][0]);
280  // Displaying the tree:
281  $outputArr = array();
282  $outputArr[] = $this->fw('TypoScript Key');
283  $outputArr[] = $this->fw('Value');
284  if ($this->printConf['allTime']) {
285  $outputArr[] = $this->fw('Time');
286  $outputArr[] = $this->fw('Own');
287  $outputArr[] = $this->fw('Sub');
288  $outputArr[] = $this->fw('Total');
289  } else {
290  $outputArr[] = $this->fw('Own');
291  }
292  $outputArr[] = $this->fw('Details');
293  $out = '';
294  foreach ($outputArr as $row) {
295  $out .= '
296  <th><strong>' . $row . '</strong></th>';
297  }
298  $out = '<tr>' . $out . '</tr>';
299  $flag_tree = $this->printConf['flag_tree'];
300  $flag_messages = $this->printConf['flag_messages'];
301  $flag_content = $this->printConf['flag_content'];
302  $flag_queries = $this->printConf['flag_queries'];
303  $keyLgd = $this->printConf['keyLgd'];
304  $factor = $this->printConf['factor'];
305  $col = $this->printConf['col'];
306  $highlight_col = $this->printConf['highlight_col'];
307  $c = 0;
308  foreach ($this->tsStackLog as $uniqueId => $data) {
309  if ($this->highlightLongerThan && (int)$data['owntime'] > (int)$this->highlightLongerThan) {
310  $logRowClass = 'typo3-adminPanel-logRow-highlight';
311  } else {
312  $logRowClass = $c % 2 ? 'line-odd' : 'line-even';
313  }
314  $item = '';
315  // If first...
316  if (!$c) {
317  $data['icons'] = '';
318  $data['key'] = 'Script Start';
319  $data['value'] = '';
320  }
321  // Key label:
322  $keyLabel = '';
323  if (!$flag_tree && $data['stackPointer']) {
324  $temp = array();
325  foreach ($data['tsStack'] as $k => $v) {
326  $temp[] = \TYPO3\CMS\Core\Utility\GeneralUtility::fixed_lgd_cs(implode($v, $k ? '.' : '/'), -$keyLgd);
327  }
328  array_pop($temp);
329  $temp = array_reverse($temp);
330  array_pop($temp);
331  if (count($temp)) {
332  $keyLabel = '<br /><span style="color:#999999;">' . implode($temp, '<br />') . '</span>';
333  }
334  }
335  if ($flag_tree) {
336  $tmp = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode('.', $data['key'], TRUE);
337  $theLabel = end($tmp);
338  } else {
339  $theLabel = $data['key'];
340  }
341  $theLabel = \TYPO3\CMS\Core\Utility\GeneralUtility::fixed_lgd_cs($theLabel, -$keyLgd);
342  $theLabel = $data['stackPointer'] ? '<span class="stackPointer">' . $theLabel . '</span>' : $theLabel;
343  $keyLabel = $theLabel . $keyLabel;
344  $item .= '<td class="' . $logRowClass . '">' . ($flag_tree ? $data['icons'] : '') . $this->fw($keyLabel) . '</td>';
345  // Key value:
346  $keyValue = $data['value'];
347  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime">' . $this->fw(htmlspecialchars($keyValue)) . '</td>';
348  if ($this->printConf['allTime']) {
349  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw($data['starttime']) . '</td>';
350  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw($data['owntime']) . '</td>';
351  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw(($data['subtime'] ? '+' . $data['subtime'] : '')) . '</td>';
352  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw(($data['subtime'] ? '=' . $data['deltatime'] : '')) . '</td>';
353  } else {
354  $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw($data['owntime']) . '</td>';
355  }
356  // Messages:
357  $msgArr = array();
358  $msg = '';
359  if ($flag_messages && is_array($data['message'])) {
360  foreach ($data['message'] as $v) {
361  $msgArr[] = nl2br($v);
362  }
363  }
364  if ($flag_queries && is_array($data['selectQuery'])) {
365  $msgArr[] = \TYPO3\CMS\Core\Utility\DebugUtility::viewArray($data['selectQuery']);
366  }
367  if ($flag_content && (string)$data['content'] !== '') {
368  $maxlen = 120;
369  // Break lines which are too longer than $maxlen chars (can happen if content contains long paths...)
370  if (preg_match_all('/(\\S{' . $maxlen . ',})/', $data['content'], $reg)) {
371  foreach ($reg[1] as $key => $match) {
372  $match = preg_replace('/(.{' . $maxlen . '})/', '$1 ', $match);
373  $data['content'] = str_replace($reg[0][$key], $match, $data['content']);
374  }
375  }
376  $msgArr[] = '<span style="color:#000066;">' . nl2br($data['content']) . '</span>';
377  }
378  if (count($msgArr)) {
379  $msg = implode($msgArr, '<hr />');
380  }
381  $item .= '<td valign="top" class="' . $logRowClass . '" style="text-align:left;">' . $this->fw($msg) . '</td>';
382  $out .= '<tr>' . $item . '</tr>';
383  $c++;
384  }
385  $out = '<table class="admin-panel-table typo3-adminPanel-tsLog">' . $out . '</table>';
386  return $out;
387  }
388 
399  protected function fixContent(&$arr, $content, $depthData = '', $first = 0, $vKey = '') {
400  $ac = 0;
401  $c = 0;
402  // First, find number of entries
403  foreach ($arr as $k => $v) {
404  if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($k)) {
405  $ac++;
406  }
407  }
408  // Traverse through entries
409  $subtime = 0;
410  foreach ($arr as $k => $v) {
411  if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($k)) {
412  $c++;
413  $deeper = is_array($arr[$k . '.']) ? 1 : 0;
414  $PM = 'join';
415  $LN = $ac == $c ? 'blank' : 'line';
416  $BTM = $ac == $c ? 'bottom' : '';
417  $PM = is_array($arr[$k . '.']) ? ($deeper ? 'minus' : 'plus') : 'join';
418  $this->tsStackLog[$v]['icons'] = $depthData . ($first ? '' : '<img src="' . TYPO3_mainDir . 'gfx/ol/' . $PM . $BTM . '.gif" width="18" height="16" align="top" border="0" alt="" />');
419  if (strlen($this->tsStackLog[$v]['content'])) {
420  $content = str_replace($this->tsStackLog[$v]['content'], $v, $content);
421  }
422  if (is_array($arr[$k . '.'])) {
423  $this->tsStackLog[$v]['content'] = $this->fixContent($arr[$k . '.'], $this->tsStackLog[$v]['content'], $depthData . ($first ? '' : '<img src="' . TYPO3_mainDir . 'gfx/ol/' . $LN . '.gif" width="18" height="16" align="top" border="0" alt="" />'), 0, $v);
424  } else {
425  $this->tsStackLog[$v]['content'] = $this->fixCLen($this->tsStackLog[$v]['content'], $this->tsStackLog[$v]['value']);
426  $this->tsStackLog[$v]['subtime'] = '';
427  $this->tsStackLog[$v]['owntime'] = $this->tsStackLog[$v]['deltatime'];
428  }
429  $subtime += $this->tsStackLog[$v]['deltatime'];
430  }
431  }
432  // Set content with special chars
433  if (isset($this->tsStackLog[$vKey])) {
434  $this->tsStackLog[$vKey]['subtime'] = $subtime;
435  $this->tsStackLog[$vKey]['owntime'] = $this->tsStackLog[$vKey]['deltatime'] - $subtime;
436  }
437  $content = $this->fixCLen($content, $this->tsStackLog[$vKey]['value']);
438  // Traverse array again, this time substitute the unique hash with the red key
439  foreach ($arr as $k => $v) {
440  if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($k)) {
441  if (strlen($this->tsStackLog[$v]['content'])) {
442  $content = str_replace($v, '<strong style="color:red;">[' . $this->tsStackLog[$v]['key'] . ']</strong>', $content);
443  }
444  }
445  }
446  // Return the content
447  return $content;
448  }
449 
457  protected function fixCLen($c, $v) {
458  $len = $v == 'FILE' ? $this->printConf['contentLength_FILE'] : $this->printConf['contentLength'];
459  if (strlen($c) > $len) {
460  $c = '<span style="color:green;">' . htmlspecialchars(\TYPO3\CMS\Core\Utility\GeneralUtility::fixed_lgd_cs($c, $len)) . '</span>';
461  } else {
462  $c = htmlspecialchars($c);
463  }
464  return $c;
465  }
466 
473  protected function fw($str) {
474  return '<span>' . $str . '</span>';
475  }
476 
487  protected function createHierarchyArray(&$arr, $pointer, $uniqueId) {
488  if (!is_array($arr)) {
489  $arr = array();
490  }
491  if ($pointer > 0) {
492  end($arr);
493  $k = key($arr);
494  $this->createHierarchyArray($arr[(int)$k . '.'], $pointer - 1, $uniqueId);
495  } else {
496  $arr[] = $uniqueId;
497  }
498  }
499 
500 }
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='...')