889466637007def12d04d4c1680c83ca23c8c17f
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / TimeTracker / TimeTracker.php
1 <?php
2 namespace TYPO3\CMS\Core\TimeTracker;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com)
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the textfile GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29 /**
30 * Contains class with time tracking functions
31 *
32 * Revised for TYPO3 3.6 July/2003 by Kasper Skårhøj
33 * XHTML compliant
34 *
35 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
36 */
37 /**
38 * Frontend Timetracking functions
39 *
40 * Is used to register how much time is used with operations in TypoScript
41 * Used by index_ts
42 *
43 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
44 * @package TYPO3
45 * @subpackage t3lib
46 * @see t3lib_tsfeBeUserAuth, tslib_fe, tslib_cObj, TSpagegen
47 */
48 class TimeTracker {
49
50 // Is loaded with the millisecond time when this object is created
51 /**
52 * @todo Define visibility
53 */
54 public $starttime = 0;
55
56 // 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
57 /**
58 * @todo Define visibility
59 */
60 public $LR = 1;
61
62 /**
63 * @todo Define visibility
64 */
65 public $printConf = array(
66 'showParentKeys' => 1,
67 'contentLength' => 10000,
68 // Determines max length of displayed content before it gets cropped.
69 'contentLength_FILE' => 400,
70 // 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.
71 'flag_tree' => 1,
72 'flag_messages' => 1,
73 'flag_queries' => 0,
74 'flag_content' => 0,
75 'allTime' => 0,
76 'keyLgd' => 40
77 );
78
79 /**
80 * @todo Define visibility
81 */
82 public $wrapError = array();
83
84 /**
85 * @todo Define visibility
86 */
87 public $wrapIcon = array();
88
89 /**
90 * @todo Define visibility
91 */
92 public $uniqueCounter = 0;
93
94 /**
95 * @todo Define visibility
96 */
97 public $tsStack = array(array());
98
99 /**
100 * @todo Define visibility
101 */
102 public $tsStackLevel = 0;
103
104 /**
105 * @todo Define visibility
106 */
107 public $tsStackLevelMax = array();
108
109 /**
110 * @todo Define visibility
111 */
112 public $tsStackLog = array();
113
114 /**
115 * @todo Define visibility
116 */
117 public $tsStackPointer = 0;
118
119 /**
120 * @todo Define visibility
121 */
122 public $currentHashPointer = array();
123
124 // Log entries that take than this number of milliseconds (own time) will be highlighted during log display. Set 0 to disable highlighting.
125 /**
126 * @todo Define visibility
127 */
128 public $highlightLongerThan = 0;
129
130 /*******************************************
131 *
132 * Logging parsing times in the scripts
133 *
134 *******************************************/
135 /**
136 * Constructor
137 * Sets the starting time
138 *
139 * @return void
140 */
141 public function start() {
142 $this->wrapError = array(
143 0 => array('', ''),
144 1 => array('<strong>', '</strong>'),
145 2 => array('<strong style="color:#ff6600;">', '</strong>'),
146 3 => array('<strong style="color:#ff0000;">', '</strong>')
147 );
148 $this->wrapIcon = array(
149 0 => '',
150 1 => '<img src="' . TYPO3_mainDir . 'gfx/icon_note.gif" width="18" height="16" align="absmiddle" alt="" />',
151 2 => '<img src="' . TYPO3_mainDir . 'gfx/icon_warning.gif" width="18" height="16" align="absmiddle" alt="" />',
152 3 => '<img src="' . TYPO3_mainDir . 'gfx/icon_fatalerror.gif" width="18" height="16" align="absmiddle" alt="" />'
153 );
154 $this->starttime = $this->getMilliseconds();
155 }
156
157 /**
158 * Pushes an element to the TypoScript tracking array
159 *
160 * @param string $tslabel Label string for the entry, eg. TypoScript property name
161 * @param string $value Additional value(?)
162 * @return void
163 * @see tslib_cObj::cObjGetSingle(), pull()
164 */
165 public function push($tslabel, $value = '') {
166 array_push($this->tsStack[$this->tsStackPointer], $tslabel);
167 array_push($this->currentHashPointer, 'timetracker_' . $this->uniqueCounter++);
168 $this->tsStackLevel++;
169 $this->tsStackLevelMax[] = $this->tsStackLevel;
170 // setTSlog
171 $k = end($this->currentHashPointer);
172 $this->tsStackLog[$k] = array(
173 'level' => $this->tsStackLevel,
174 'tsStack' => $this->tsStack,
175 'value' => $value,
176 'starttime' => microtime(TRUE),
177 'stackPointer' => $this->tsStackPointer
178 );
179 }
180
181 /**
182 * Pulls an element from the TypoScript tracking array
183 *
184 * @param string $content The content string generated within the push/pull part.
185 * @return void
186 * @see tslib_cObj::cObjGetSingle(), push()
187 */
188 public function pull($content = '') {
189 $k = end($this->currentHashPointer);
190 $this->tsStackLog[$k]['endtime'] = microtime(TRUE);
191 $this->tsStackLog[$k]['content'] = $content;
192 $this->tsStackLevel--;
193 array_pop($this->tsStack[$this->tsStackPointer]);
194 array_pop($this->currentHashPointer);
195 }
196
197 /**
198 * Logs the TypoScript entry
199 *
200 * @param string $content The message string
201 * @param integer $num Message type: 0: information, 1: message, 2: warning, 3: error
202 * @return void
203 * @see tslib_cObj::CONTENT()
204 */
205 public function setTSlogMessage($content, $num = 0) {
206 end($this->currentHashPointer);
207 $k = current($this->currentHashPointer);
208 // Enlarge the "details" column by adding a wide clear.gif
209 if (strlen($content) > 30) {
210 $placeholder = '<br /><img src="' . TYPO3_mainDir . 'clear.gif" width="300" height="1" alt="" />';
211 }
212 $this->tsStackLog[$k]['message'][] = $this->wrapIcon[$num] . $this->wrapError[$num][0] . htmlspecialchars($content) . $this->wrapError[$num][1] . $placeholder;
213 }
214
215 /**
216 * Set TSselectQuery - for messages in TypoScript debugger.
217 *
218 * @param array $data Query array
219 * @param string $msg Message/Label to attach
220 * @return void
221 */
222 public function setTSselectQuery(array $data, $msg = '') {
223 end($this->currentHashPointer);
224 $k = current($this->currentHashPointer);
225 if (strlen($msg)) {
226 $data['msg'] = $msg;
227 }
228 $this->tsStackLog[$k]['selectQuery'][] = $data;
229 }
230
231 /**
232 * Increases the stack pointer
233 *
234 * @return void
235 * @see decStackPointer(), TSpagegen::renderContent(), tslib_cObj::cObjGetSingle()
236 */
237 public function incStackPointer() {
238 $this->tsStackPointer++;
239 $this->tsStack[$this->tsStackPointer] = array();
240 }
241
242 /**
243 * Decreases the stack pointer
244 *
245 * @return void
246 * @see incStackPointer(), TSpagegen::renderContent(), tslib_cObj::cObjGetSingle()
247 */
248 public function decStackPointer() {
249 unset($this->tsStack[$this->tsStackPointer]);
250 $this->tsStackPointer--;
251 }
252
253 /**
254 * Gets a microtime value as milliseconds value.
255 *
256 * @param float $microtime The microtime value - if not set the current time is used
257 * @return integer The microtime value as milliseconds value
258 */
259 public function getMilliseconds($microtime = NULL) {
260 if (!isset($microtime)) {
261 $microtime = microtime(TRUE);
262 }
263 return round($microtime * 1000);
264 }
265
266 /**
267 * Gets the difference between a given microtime value and the starting time as milliseconds.
268 *
269 * @param float $microtime The microtime value - if not set the current time is used
270 * @return integer The difference between a given microtime value and starting time as milliseconds
271 */
272 public function getDifferenceToStarttime($microtime = NULL) {
273 return $this->getMilliseconds($microtime) - $this->starttime;
274 }
275
276 /*******************************************
277 *
278 * Printing the parsing time information (for Admin Panel)
279 *
280 *******************************************/
281 /**
282 * Print TypoScript parsing log
283 *
284 * @return string HTML table with the information about parsing times.
285 * @see t3lib_tsfeBeUserAuth::extGetCategory_tsdebug()
286 */
287 public function printTSlog() {
288 // Calculate times and keys for the tsStackLog
289 foreach ($this->tsStackLog as $uniqueId => &$data) {
290 $data['endtime'] = $this->getDifferenceToStarttime($data['endtime']);
291 $data['starttime'] = $this->getDifferenceToStarttime($data['starttime']);
292 $data['deltatime'] = $data['endtime'] - $data['starttime'];
293 if (is_array($data['tsStack'])) {
294 $data['key'] = implode($data['stackPointer'] ? '.' : '/', end($data['tsStack']));
295 }
296 }
297 unset($data);
298 // Create hierarchical array of keys pointing to the stack
299 $arr = array();
300 foreach ($this->tsStackLog as $uniqueId => $data) {
301 $this->createHierarchyArray($arr, $data['level'], $uniqueId);
302 }
303 // Parsing the registeret content and create icon-html for the tree
304 $this->tsStackLog[$arr['0.'][0]]['content'] = $this->fixContent($arr['0.']['0.'], $this->tsStackLog[$arr['0.'][0]]['content'], '', 0, $arr['0.'][0]);
305 // Displaying the tree:
306 $outputArr = array();
307 $outputArr[] = $this->fw('TypoScript Key');
308 $outputArr[] = $this->fw('Value');
309 if ($this->printConf['allTime']) {
310 $outputArr[] = $this->fw('Time');
311 $outputArr[] = $this->fw('Own');
312 $outputArr[] = $this->fw('Sub');
313 $outputArr[] = $this->fw('Total');
314 } else {
315 $outputArr[] = $this->fw('Own');
316 }
317 $outputArr[] = $this->fw('Details');
318 $out = '';
319 foreach ($outputArr as $row) {
320 $out .= '
321 <th><strong>' . $row . '</strong></th>';
322 }
323 $out = '<tr>' . $out . '</tr>';
324 $flag_tree = $this->printConf['flag_tree'];
325 $flag_messages = $this->printConf['flag_messages'];
326 $flag_content = $this->printConf['flag_content'];
327 $flag_queries = $this->printConf['flag_queries'];
328 $keyLgd = $this->printConf['keyLgd'];
329 $factor = $this->printConf['factor'];
330 $col = $this->printConf['col'];
331 $highlight_col = $this->printConf['highlight_col'];
332 $c = 0;
333 foreach ($this->tsStackLog as $uniqueId => $data) {
334 if ($this->highlightLongerThan && intval($data['owntime']) > intval($this->highlightLongerThan)) {
335 $logRowClass = 'typo3-adminPanel-logRow-highlight';
336 } else {
337 $logRowClass = $c % 2 ? 'typo3-adminPanel-logRow-odd' : 'typo3-adminPanel-logRow-even';
338 }
339 $item = '';
340 // If first...
341 if (!$c) {
342 $data['icons'] = '';
343 $data['key'] = 'Script Start';
344 $data['value'] = '';
345 }
346 // Key label:
347 $keyLabel = '';
348 if (!$flag_tree && $data['stackPointer']) {
349 $temp = array();
350 foreach ($data['tsStack'] as $k => $v) {
351 $temp[] = \TYPO3\CMS\Core\Utility\GeneralUtility::fixed_lgd_cs(implode($v, $k ? '.' : '/'), -$keyLgd);
352 }
353 array_pop($temp);
354 $temp = array_reverse($temp);
355 array_pop($temp);
356 if (count($temp)) {
357 $keyLabel = '<br /><span style="color:#999999;">' . implode($temp, '<br />') . '</span>';
358 }
359 }
360 if ($flag_tree) {
361 $tmp = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode('.', $data['key'], 1);
362 $theLabel = end($tmp);
363 } else {
364 $theLabel = $data['key'];
365 }
366 $theLabel = \TYPO3\CMS\Core\Utility\GeneralUtility::fixed_lgd_cs($theLabel, -$keyLgd);
367 $theLabel = $data['stackPointer'] ? '<span class="stackPointer">' . $theLabel . '</span>' : $theLabel;
368 $keyLabel = $theLabel . $keyLabel;
369 $item .= '<td class="' . $logRowClass . '" style="padding-left:2px;">' . ($flag_tree ? $data['icons'] : '') . $this->fw($keyLabel) . '</td>';
370 // Key value:
371 $keyValue = $data['value'];
372 $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime">' . $this->fw(htmlspecialchars($keyValue)) . '</td>';
373 if ($this->printConf['allTime']) {
374 $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw($data['starttime']) . '</td>';
375 $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw($data['owntime']) . '</td>';
376 $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw(($data['subtime'] ? '+' . $data['subtime'] : '')) . '</td>';
377 $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw(($data['subtime'] ? '=' . $data['deltatime'] : '')) . '</td>';
378 } else {
379 $item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw($data['owntime']) . '</td>';
380 }
381 // Messages:
382 $msgArr = array();
383 $msg = '';
384 if ($flag_messages && is_array($data['message'])) {
385 foreach ($data['message'] as $v) {
386 $msgArr[] = nl2br($v);
387 }
388 }
389 if ($flag_queries && is_array($data['selectQuery'])) {
390 $msgArr[] = \TYPO3\CMS\Core\Utility\DebugUtility::viewArray($data['selectQuery']);
391 }
392 if ($flag_content && strcmp($data['content'], '')) {
393 $maxlen = 120;
394 // Break lines which are too longer than $maxlen chars (can happen if content contains long paths...)
395 if (preg_match_all('/(\\S{' . $maxlen . ',})/', $data['content'], $reg)) {
396 foreach ($reg[1] as $key => $match) {
397 $match = preg_replace('/(.{' . $maxlen . '})/', '$1 ', $match);
398 $data['content'] = str_replace($reg[0][$key], $match, $data['content']);
399 }
400 }
401 $msgArr[] = '<span style="color:#000066;">' . nl2br($data['content']) . '</span>';
402 }
403 if (count($msgArr)) {
404 $msg = implode($msgArr, '<hr />');
405 }
406 $item .= '<td valign="top" class="' . $logRowClass . '" style="text-align:left;">' . $this->fw($msg) . '</td>';
407 $out .= '<tr>' . $item . '</tr>';
408 $c++;
409 }
410 $out = '<table id="typo3-adminPanel-tsLog">' . $out . '</table>';
411 return $out;
412 }
413
414 /**
415 * Recursively generates the content to display
416 *
417 * @param array $arr Array which is modified with content. Reference
418 * @param string $content Current content string for the level
419 * @param string $depthData Prefixed icons for new PM icons
420 * @param boolean $first Set this for the first call from outside.
421 * @param string $vKey Seems to be the previous tsStackLog key
422 * @return string Returns the $content string generated/modified. Also the $arr array is modified!
423 */
424 protected function fixContent(&$arr, $content, $depthData = '', $first = 0, $vKey = '') {
425 $ac = 0;
426 $c = 0;
427 // First, find number of entries
428 foreach ($arr as $k => $v) {
429 if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($k)) {
430 $ac++;
431 }
432 }
433 // Traverse through entries
434 $subtime = 0;
435 foreach ($arr as $k => $v) {
436 if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($k)) {
437 $c++;
438 $deeper = is_array($arr[$k . '.']) ? 1 : 0;
439 $PM = 'join';
440 $LN = $ac == $c ? 'blank' : 'line';
441 $BTM = $ac == $c ? 'bottom' : '';
442 $PM = is_array($arr[$k . '.']) ? ($deeper ? 'minus' : 'plus') : 'join';
443 $this->tsStackLog[$v]['icons'] = $depthData . ($first ? '' : '<img src="' . TYPO3_mainDir . 'gfx/ol/' . $PM . $BTM . '.gif" width="18" height="16" align="top" border="0" alt="" />');
444 if (strlen($this->tsStackLog[$v]['content'])) {
445 $content = str_replace($this->tsStackLog[$v]['content'], $v, $content);
446 }
447 if (is_array($arr[$k . '.'])) {
448 $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);
449 } else {
450 $this->tsStackLog[$v]['content'] = $this->fixCLen($this->tsStackLog[$v]['content'], $this->tsStackLog[$v]['value']);
451 $this->tsStackLog[$v]['subtime'] = '';
452 $this->tsStackLog[$v]['owntime'] = $this->tsStackLog[$v]['deltatime'];
453 }
454 $subtime += $this->tsStackLog[$v]['deltatime'];
455 }
456 }
457 // Set content with special chars
458 if (isset($this->tsStackLog[$vKey])) {
459 $this->tsStackLog[$vKey]['subtime'] = $subtime;
460 $this->tsStackLog[$vKey]['owntime'] = $this->tsStackLog[$vKey]['deltatime'] - $subtime;
461 }
462 $content = $this->fixCLen($content, $this->tsStackLog[$vKey]['value']);
463 // Traverse array again, this time substitute the unique hash with the red key
464 foreach ($arr as $k => $v) {
465 if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($k)) {
466 if (strlen($this->tsStackLog[$v]['content'])) {
467 $content = str_replace($v, '<strong style="color:red;">[' . $this->tsStackLog[$v]['key'] . ']</strong>', $content);
468 }
469 }
470 }
471 // Return the content
472 return $content;
473 }
474
475 /**
476 * 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"
477 *
478 * @param string $c The content string
479 * @param string $v Command: If "FILE" then $this->printConf['contentLength_FILE'] is used for content length comparison, otherwise $this->printConf['contentLength']
480 * @return string
481 */
482 protected function fixCLen($c, $v) {
483 $len = $v == 'FILE' ? $this->printConf['contentLength_FILE'] : $this->printConf['contentLength'];
484 if (strlen($c) > $len) {
485 $c = '<span style="color:green;">' . htmlspecialchars(\TYPO3\CMS\Core\Utility\GeneralUtility::fixed_lgd_cs($c, $len)) . '</span>';
486 } else {
487 $c = htmlspecialchars($c);
488 }
489 return $c;
490 }
491
492 /**
493 * Wraps input string in a <span> tag with black verdana font
494 *
495 * @param string $str The string to be wrapped
496 * @return string
497 */
498 protected function fw($str) {
499 return '<span style="font-family:Verdana,Arial,Helvetica,sans-serif; font-size:10px; color:black; vertical-align:top;">' . $str . '&nbsp;</span>';
500 }
501
502 /**
503 * Helper function for internal data manipulation
504 *
505 * @param array $arr Array (passed by reference) and modified
506 * @param integer $pointer Pointer value
507 * @param string $uniqueId Unique ID string
508 * @return void
509 * @access private
510 * @see printTSlog()
511 */
512 protected function createHierarchyArray(&$arr, $pointer, $uniqueId) {
513 if (!is_array($arr)) {
514 $arr = array();
515 }
516 if ($pointer > 0) {
517 end($arr);
518 $k = key($arr);
519 $this->createHierarchyArray($arr[intval($k) . '.'], $pointer - 1, $uniqueId);
520 } else {
521 $arr[] = $uniqueId;
522 }
523 }
524
525 }
526
527
528 ?>