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