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