[TASK] Re-work/simplify copyright header in PHP files - Part 2
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / TimeTracker / TimeTracker.php
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 */
16
17 /**
18 * Frontend Timetracking functions
19 *
20 * Is used to register how much time is used with operations in TypoScript
21 *
22 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
23 */
24 class TimeTracker {
25
26 // Is loaded with the millisecond time when this object is created
27 /**
28 * @todo Define visibility
29 */
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
33 /**
34 * @todo Define visibility
35 */
36 public $LR = 1;
37
38 /**
39 * @todo Define visibility
40 */
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
55 /**
56 * @todo Define visibility
57 */
58 public $wrapError = array();
59
60 /**
61 * @todo Define visibility
62 */
63 public $wrapIcon = array();
64
65 /**
66 * @todo Define visibility
67 */
68 public $uniqueCounter = 0;
69
70 /**
71 * @todo Define visibility
72 */
73 public $tsStack = array(array());
74
75 /**
76 * @todo Define visibility
77 */
78 public $tsStackLevel = 0;
79
80 /**
81 * @todo Define visibility
82 */
83 public $tsStackLevelMax = array();
84
85 /**
86 * @todo Define visibility
87 */
88 public $tsStackLog = array();
89
90 /**
91 * @todo Define visibility
92 */
93 public $tsStackPointer = 0;
94
95 /**
96 * @todo Define visibility
97 */
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.
101 /**
102 * @todo Define visibility
103 */
104 public $highlightLongerThan = 0;
105
106 /*******************************************
107 *
108 * Logging parsing times in the scripts
109 *
110 *******************************************/
111 /**
112 * Constructor
113 * Sets the starting time
114 *
115 * @return void
116 */
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
133 /**
134 * Pushes an element to the TypoScript tracking array
135 *
136 * @param string $tslabel Label string for the entry, eg. TypoScript property name
137 * @param string $value Additional value(?)
138 * @return void
139 * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::cObjGetSingle(), pull()
140 */
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
157 /**
158 * Pulls an element from the TypoScript tracking array
159 *
160 * @param string $content The content string generated within the push/pull part.
161 * @return void
162 * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::cObjGetSingle(), push()
163 */
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
173 /**
174 * Logs the TypoScript entry
175 *
176 * @param string $content The message string
177 * @param integer $num Message type: 0: information, 1: message, 2: warning, 3: error
178 * @return void
179 * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::CONTENT()
180 */
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
191 /**
192 * Set TSselectQuery - for messages in TypoScript debugger.
193 *
194 * @param array $data Query array
195 * @param string $msg Message/Label to attach
196 * @return void
197 */
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
207 /**
208 * Increases the stack pointer
209 *
210 * @return void
211 * @see decStackPointer(), TSpagegen::renderContent(), \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::cObjGetSingle()
212 */
213 public function incStackPointer() {
214 $this->tsStackPointer++;
215 $this->tsStack[$this->tsStackPointer] = array();
216 }
217
218 /**
219 * Decreases the stack pointer
220 *
221 * @return void
222 * @see incStackPointer(), TSpagegen::renderContent(), \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::cObjGetSingle()
223 */
224 public function decStackPointer() {
225 unset($this->tsStack[$this->tsStackPointer]);
226 $this->tsStackPointer--;
227 }
228
229 /**
230 * Gets a microtime value as milliseconds value.
231 *
232 * @param float $microtime The microtime value - if not set the current time is used
233 * @return integer The microtime value as milliseconds value
234 */
235 public function getMilliseconds($microtime = NULL) {
236 if (!isset($microtime)) {
237 $microtime = microtime(TRUE);
238 }
239 return round($microtime * 1000);
240 }
241
242 /**
243 * Gets the difference between a given microtime value and the starting time as milliseconds.
244 *
245 * @param float $microtime The microtime value - if not set the current time is used
246 * @return integer The difference between a given microtime value and starting time as milliseconds
247 */
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 *******************************************/
257 /**
258 * Print TypoScript parsing log
259 *
260 * @return string HTML table with the information about parsing times.
261 */
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
389 /**
390 * Recursively generates the content to display
391 *
392 * @param array $arr Array which is modified with content. Reference
393 * @param string $content Current content string for the level
394 * @param string $depthData Prefixed icons for new PM icons
395 * @param boolean $first Set this for the first call from outside.
396 * @param string $vKey Seems to be the previous tsStackLog key
397 * @return string Returns the $content string generated/modified. Also the $arr array is modified!
398 */
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
450 /**
451 * Wraps the input content string in green colored span-tags IF the length o fthe input string exceeds $this->printConf['contentLength'] (or $this->printConf['contentLength_FILE'] if $v == "FILE"
452 *
453 * @param string $c The content string
454 * @param string $v Command: If "FILE" then $this->printConf['contentLength_FILE'] is used for content length comparison, otherwise $this->printConf['contentLength']
455 * @return string
456 */
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
467 /**
468 * Wraps input string in a <span> tag
469 *
470 * @param string $str The string to be wrapped
471 * @return string
472 */
473 protected function fw($str) {
474 return '<span>' . $str . '</span>';
475 }
476
477 /**
478 * Helper function for internal data manipulation
479 *
480 * @param array $arr Array (passed by reference) and modified
481 * @param integer $pointer Pointer value
482 * @param string $uniqueId Unique ID string
483 * @return void
484 * @access private
485 * @see printTSlog()
486 */
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 }