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