Added $Id$ keywords, cleaned up comment tags
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_timetrack.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2003 Kasper Skaarhoj (kasper@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 Skaarhoj
32 * XHTML compliant
33 *
34 * @author Kasper Skaarhoj <kasper@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 * 157: function push($tslabel, $value='')
46 * 182: function pull($content='')
47 * 200: function setTSlogMessage($content,$num=0)
48 * 215: function setTSselectQuery($query,$msg)
49 * 228: function incStackPointer()
50 * 239: function decStackPointer()
51 * 249: function mtime()
52 * 259: function convertMicrotime($microtime)
53 *
54 * SECTION: Printing the parsing time information (for Admin Panel)
55 * 292: function printTSlog()
56 * 437: function fixContent(&$arr, $content, $depthData='', $first=0, $vKey='')
57 * 501: function fixCLen($c,$v)
58 * 517: function fw($str)
59 * 531: function createHierarchyArray(&$arr,$pointer,$uniqueId)
60 * 550: function debug_typo3PrintError($header,$text,$js)
61 *
62 * TOTAL FUNCTIONS: 15
63 * (This index is automatically created/updated by the extension "extdeveval")
64 *
65 */
66
67
68
69
70
71
72
73
74
75
76
77 /**
78 * Frontend Timetracking functions
79 *
80 * Is used to register how much time is used with operations in TypoScript
81 * Used by index_ts
82 *
83 * @author Kasper Skaarhoj <kasper@typo3.com>
84 * @package TYPO3
85 * @subpackage t3lib
86 * @see t3lib_tsfeBeUserAuth, tslib_fe, tslib_cObj, TSpagegen
87 */
88 class t3lib_timeTrack {
89 var $starttime = 0; // Is loaded with the millisecond time when this object is created
90
91 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
92 var $printConf=array(
93 'showParentKeys' => 1,
94 'contentLength' => 10000, // Determines max lenght of displayed content before it gets cropped.
95 '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.
96 'flag_tree' => 1,
97 'flag_messages' => 1,
98 'flag_queries' => 0,
99 'flag_content' => 0,
100 'allTime' => 0,
101 'keyLgd' => 40,
102 'factor' => 10,
103 'col' => '#D9D5C9'
104 );
105
106 var $wrapError =array(
107 0 => array('',''),
108 1 => array('<b>','</b>'),
109 2 => array('<b><font color="#ff6600">','</font></b>'),
110 3 => array('<b><font color="#ff0000">','</font></b>')
111 );
112 var $wrapIcon =array(
113 0 => '',
114 1 => '<img src="t3lib/gfx/icon_note.gif" width="18" height="16" align="absmiddle" alt="" />',
115 2 => '<img src="t3lib/gfx/icon_warning.gif" width="18" height="16" align="absmiddle" alt="" />',
116 3 => '<img src="t3lib/gfx/icon_fatalerror.gif" width="18" height="16" align="absmiddle" alt="" />'
117 );
118
119 var $uniqueCounter=0;
120 var $tsStack = array(array());
121 var $tsStackLevel = 0;
122 var $tsStackLevelMax=array();
123 var $tsStackLog = array();
124 var $tsStackPointer=0;
125 var $currentHashPointer=array();
126
127
128
129
130
131
132 /*******************************************
133 *
134 * Logging parsing times in the scripts
135 *
136 *******************************************/
137
138 /**
139 * Constructor
140 * Sets the starting time
141 *
142 * @return void
143 */
144 function start() {
145 $this->starttime=0;
146 $this->starttime=$this->mtime();
147 }
148
149 /**
150 * Pushes an element to the TypoScript tracking array
151 *
152 * @param string Label string for the entry, eg. TypoScript property name
153 * @param string Additional value(?)
154 * @return void
155 * @see tslib_cObj::cObjGetSingle(), pull()
156 */
157 function push($tslabel, $value='') {
158 array_push($this->tsStack[$this->tsStackPointer], $tslabel);
159 array_push($this->currentHashPointer, 'timetracker_'.$this->uniqueCounter++);
160
161 $this->tsStackLevel++;
162 $this->tsStackLevelMax[] = $this->tsStackLevel;
163
164 // setTSlog
165 $k = end($this->currentHashPointer);
166 $this->tsStackLog[$k] = array(
167 'level' => $this->tsStackLevel,
168 'tsStack' => $this->tsStack,
169 'value' => $value,
170 'starttime' => microtime(),
171 'stackPointer' => $this->tsStackPointer
172 );
173 }
174
175 /**
176 * Pulls an element from the TypoScript tracking array
177 *
178 * @param string The content string generated within the push/pull part.
179 * @return void
180 * @see tslib_cObj::cObjGetSingle(), push()
181 */
182 function pull($content='') {
183 $k = end($this->currentHashPointer);
184 $this->tsStackLog[$k]['endtime'] = microtime();
185 $this->tsStackLog[$k]['content'] = $content;
186
187 $this->tsStackLevel--;
188 array_pop($this->tsStack[$this->tsStackPointer]);
189 array_pop($this->currentHashPointer);
190 }
191
192 /**
193 * Logs the TypoScript entry
194 *
195 * @param string The message string
196 * @param integer Message type: 0: information, 1: message, 2: warning, 3: error
197 * @return void
198 * @see tslib_cObj::CONTENT()
199 */
200 function setTSlogMessage($content,$num=0) {
201 end($this->currentHashPointer);
202 $k = current($this->currentHashPointer);
203
204 $this->tsStackLog[$k]['message'][] = $this->wrapIcon[$num].$this->wrapError[$num][0].$content.$this->wrapError[$num][1];
205 }
206
207 /**
208 * Set TSselectQuery.
209 * Apparently not used anywhere?
210 *
211 * @param string Query
212 * @param string Message
213 * @return void
214 */
215 function setTSselectQuery($query,$msg) {
216 end($this->currentHashPointer);
217 $k = current($this->currentHashPointer);
218
219 $this->tsStackLog[$k]['selectQuery'][] = array('query'=>$query,'msg'=>$msg);
220 }
221
222 /**
223 * Increases the stack pointer
224 *
225 * @return void
226 * @see decStackPointer(), TSpagegen::renderContent(), tslib_cObj::cObjGetSingle()
227 */
228 function incStackPointer() {
229 $this->tsStackPointer++;
230 $this->tsStack[$this->tsStackPointer]=array();
231 }
232
233 /**
234 * Decreases the stack pointer
235 *
236 * @return void
237 * @see incStackPointer(), TSpagegen::renderContent(), tslib_cObj::cObjGetSingle()
238 */
239 function decStackPointer() {
240 unset($this->tsStack[$this->tsStackPointer]);
241 $this->tsStackPointer--;
242 }
243
244 /**
245 * Returns the current time in milliseconds
246 *
247 * @return integer
248 */
249 function mtime() {
250 return $this->convertMicrotime(microtime())-$this->starttime;
251 }
252
253 /**
254 * Returns microtime input to milliseconds
255 *
256 * @param string PHP microtime string
257 * @return integer
258 */
259 function convertMicrotime($microtime) {
260 $parts = explode(' ',$microtime);
261 return round(($parts[0]+$parts[1])*1000);
262 }
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280 /*******************************************
281 *
282 * Printing the parsing time information (for Admin Panel)
283 *
284 *******************************************/
285
286 /**
287 * Print TSlog
288 *
289 * @return string HTML table with the information about parsing times.
290 * @see t3lib_tsfeBeUserAuth::extGetCategory_tsdebug()
291 */
292 function printTSlog() {
293 // Calculate times and keys for the tsStackLog
294 reset($this->tsStackLog);
295 $preEndtime=0;
296 while(list($uniqueId,$data)=each($this->tsStackLog)) {
297 $this->tsStackLog[$uniqueId]['endtime'] = $this->convertMicrotime($this->tsStackLog[$uniqueId]['endtime'])-$this->starttime;
298 $this->tsStackLog[$uniqueId]['starttime'] = $this->convertMicrotime($this->tsStackLog[$uniqueId]['starttime'])-$this->starttime;
299 $this->tsStackLog[$uniqueId]['deltatime'] = $this->tsStackLog[$uniqueId]['endtime']-$this->tsStackLog[$uniqueId]['starttime'];
300 $this->tsStackLog[$uniqueId]['key'] = implode(end($data['tsStack']),$this->tsStackLog[$uniqueId]['stackPointer']?'.':'/');
301 $preEndtime = $this->tsStackLog[$uniqueId]['endtime'];
302 }
303
304 // Create hierarchical array of keys pointing to the stack
305 $arr = array();
306 reset($this->tsStackLog);
307 while(list($uniqueId,$data)=each($this->tsStackLog)) {
308 $this->createHierarchyArray($arr,$data['level'], $uniqueId);
309 }
310 // Parsing the registeret content and create icon-html for the tree
311 $this->tsStackLog[$arr['0.'][0]]['content'] = $this->fixContent($arr['0.']['0.'], $this->tsStackLog[$arr['0.'][0]]['content'], '', 0, $arr['0.'][0]);
312
313 // Displaying the tree:
314 reset($this->tsStackLog);
315 $out='<tr>
316 <td bgcolor="#ABBBB4" align="center"><b>'.$this->fw('TypoScript Key').'</b></td>
317 <td bgcolor="#ABBBB4" align="center"><b>'.$this->fw('Value').'</b></td>';
318 if ($this->printConf['allTime']) {
319 $out.='
320 <td bgcolor="#ABBBB4" align="center"><b>'.$this->fw('Time').'</b></td>
321 <td bgcolor="#ABBBB4" align="center"><b>'.$this->fw('Own').'</b></td>
322 <td bgcolor="#ABBBB4" align="center"><b>'.$this->fw('Sub').'</b></td>
323 <td bgcolor="#ABBBB4" align="center"><b>'.$this->fw('Total').'</b></td>';
324 } else {
325 $out.='
326 <td bgcolor="#ABBBB4" align="center"><b>'.$this->fw('Own').'</b></td>';
327 }
328
329 $out.='
330 <td bgcolor="#ABBBB4" align="center"><b>'.$this->fw('Details').'</b></td>
331 </tr>';
332
333
334
335 $flag_tree=$this->printConf['flag_tree'];
336 $flag_messages=$this->printConf['flag_messages'];
337 $flag_content=$this->printConf['flag_content'];
338 $flag_queries=$this->printConf['flag_queries'];
339 $keyLgd=$this->printConf['keyLgd'];
340 $factor=$this->printConf['factor'];
341 $col=$this->printConf['col'];
342
343 $c=0;
344 while(list($uniqueId,$data)=each($this->tsStackLog)) {
345 $bgColor = ' bgcolor="'.($c%2 ? t3lib_div::modifyHTMLColor($col,$factor,$factor,$factor) : $col).'"';
346 $item='';
347 if (!$c) { // If first...
348 $data['icons']='';
349 $data['key']= 'Script Start';
350 $data['value'] = '';
351 }
352
353
354 // key label:
355 $keyLabel='';
356 if (!$flag_tree && $data['stackPointer']) {
357 $temp=array();
358 reset($data['tsStack']);
359 while(list($k,$v)=each($data['tsStack'])) {
360 $temp[]=t3lib_div::fixed_lgd_pre(implode($v,$k?'.':'/'),$keyLgd);
361 }
362 array_pop($temp);
363 $temp = array_reverse($temp);
364 array_pop($temp);
365 if (count($temp)) {
366 $keyLabel='<br /><font color="#999999">'.implode($temp,'<br />').'</font>';
367 }
368 }
369 $theLabel = $flag_tree ? end(t3lib_div::trimExplode('.',$data['key'],1)) : $data['key'];
370 $theLabel = t3lib_div::fixed_lgd_pre($theLabel, $keyLgd);
371 $theLabel = $data['stackPointer'] ? '<font color="maroon">'.$theLabel.'</font>' : $theLabel;
372 $keyLabel=$theLabel.$keyLabel;
373 $item.='<td valign="top" nowrap="nowrap"'.$bgColor.'>'.($flag_tree?$data['icons']:'').$this->fw($keyLabel).'</td>';
374
375 // key value:
376 $keyValue=$data['value'];
377 $item.='<td valign="top" nowrap="nowrap"'.$bgColor.'>'.$this->fw(htmlspecialchars($keyValue)).'</td>';
378
379 if ($this->printConf['allTime']) {
380 // deltatime:
381 $item.='<td valign="top" align="right" nowrap="nowrap"'.$bgColor.'>'.$this->fw($data['starttime']).'</td>';
382 $item.='<td valign="top" align="right" nowrap="nowrap"'.$bgColor.'>'.$this->fw($data['owntime']).'</td>';
383 $item.='<td valign="top" align="right" nowrap="nowrap"'.$bgColor.'>'.$this->fw($data['subtime'] ? '+'.$data['subtime'] : '').'</td>';
384 $item.='<td valign="top" align="right" nowrap="nowrap"'.$bgColor.'>'.$this->fw($data['subtime'] ? '='.$data['deltatime'] : '').'</td>';
385 } else {
386 // deltatime:
387 $item.='<td valign="top" align="right" nowrap="nowrap"'.$bgColor.'>'.$this->fw($data['owntime']).'</td>';
388 }
389
390
391 // messages:
392 $msgArr=array();
393 $msg='';
394 if ($flag_messages && is_array($data['message'])) {
395 reset($data['message']);
396 while(list(,$v)=each($data['message'])) {
397 $msgArr[]=nl2br(htmlspecialchars($v));
398 }
399 }
400 if ($flag_queries && is_array($data['selectQuery'])) {
401 reset($data['selectQuery']);
402 while(list(,$v)=each($data['selectQuery'])) {
403 $res = mysql(TYPO3_db,'EXPLAIN '.$v['query']);
404 $v['mysql_error']=mysql_error();
405 if (!mysql_error()) {
406 while($row=mysql_fetch_assoc($res)) {
407 $v['explain'][]=$row;
408 }
409 }
410 $msgArr[]=t3lib_div::view_array($v);
411 }
412 }
413 if ($flag_content && strcmp($data['content'],'')) {
414 $msgArr[]='<font color="#000066">'.nl2br($data['content']).'</font>';
415 }
416 if (count($msgArr)) {
417 $msg=implode($msgArr,'<hr />');
418 }
419 $item.='<td valign="top"'.$bgColor.'>'.$this->fw($msg).'</td>';
420 $out.='<tr>'.$item.'</tr>';
421 $c++;
422 }
423 $out='<table border="0" cellpadding="0" cellspacing="0">'.$out.'</table>';
424 return $out;
425 }
426
427 /**
428 * Recursively generates the content to display
429 *
430 * @param array Array which is modified with content. Reference
431 * @param string Current content string for the level
432 * @param string Prefixed icons for new PM icons
433 * @param boolean Set this for the first call from outside.
434 * @param string Seems to be the previous tsStackLog key
435 * @return string Returns the $content string generated/modified. Also the $arr array is modified!
436 */
437 function fixContent(&$arr, $content, $depthData='', $first=0, $vKey='') {
438 $ac=0;
439 $c=0;
440 // First, find number of entries
441 reset($arr);
442 while(list($k,$v)=each($arr)) {
443 if (t3lib_div::testInt($k)) {
444 $ac++;
445 }
446 }
447 // Traverse through entries
448 $subtime=0;
449 reset($arr);
450 while(list($k,$v)=each($arr)) {
451 if (t3lib_div::testInt($k)) {
452 $c++;
453
454 $deeper = is_array($arr[$k.'.']) ? 1 : 0;
455 $PM = 'join';
456 $LN = ($ac==$c)?'blank':'line';
457 $BTM = ($ac==$c)?'bottom':'';
458 $PM = is_array($arr[$k.'.']) ? ($deeper ? 'minus':'plus') : 'join';
459 $this->tsStackLog[$v]['icons']=$depthData.($first?'':'<img src="t3lib/gfx/ol/'.$PM.$BTM.'.gif" width="18" height="16" align="top" border="0" alt="" />');
460
461 if (strlen($this->tsStackLog[$v]['content'])) {
462 $content = str_replace($this->tsStackLog[$v]['content'],$v, $content);
463 }
464 if (is_array($arr[$k.'.'])) {
465 $this->tsStackLog[$v]['content'] = $this->fixContent($arr[$k.'.'], $this->tsStackLog[$v]['content'], $depthData.($first?'':'<img src="t3lib/gfx/ol/'.$LN.'.gif" width="18" height="16" align="top" border="0" alt="" />'), 0, $v);
466 } else {
467 $this->tsStackLog[$v]['content'] = $this->fixCLen($this->tsStackLog[$v]['content'], $this->tsStackLog[$v]['value']);
468 $this->tsStackLog[$v]['subtime']='';
469 $this->tsStackLog[$v]['owntime']=$this->tsStackLog[$v]['deltatime'];
470 }
471 $subtime+=$this->tsStackLog[$v]['deltatime'];
472 }
473 }
474 // Set content with special chars
475 if (isset($this->tsStackLog[$vKey])) {
476 $this->tsStackLog[$vKey]['subtime']=$subtime;
477 $this->tsStackLog[$vKey]['owntime']=$this->tsStackLog[$vKey]['deltatime']-$subtime;
478 }
479 $content=$this->fixCLen($content, $this->tsStackLog[$vKey]['value']);
480
481 // Traverse array again, this time substitute the unique hash with the red key
482 reset($arr);
483 while(list($k,$v)=each($arr)) {
484 if (t3lib_div::testInt($k)) {
485 if (strlen($this->tsStackLog[$v]['content'])) {
486 $content = str_replace($v, '<font color="red"><b>['.$this->tsStackLog[$v]['key'].']</b></font>', $content);
487 }
488 }
489 }
490 // return the content
491 return $content;
492 }
493
494 /**
495 * Wraps the input content string in green colored font-tags IF the length o fthe input string exceeds $this->printConf['contentLength'] (or $this->printConf['contentLength_FILE'] if $v == "FILE"
496 *
497 * @param string The content string
498 * @param string Command: If "FILE" then $this->printConf['contentLength_FILE'] is used for content length comparison, otherwise $this->printConf['contentLength']
499 * @return string
500 */
501 function fixCLen($c,$v) {
502 $len = $v=='FILE'?$this->printConf['contentLength_FILE']:$this->printConf['contentLength'];
503 if (strlen($c)>$len) {
504 $c='<font color="green">'.htmlspecialchars(t3lib_div::fixed_lgd($c,$len)).'</font>';
505 } else {
506 $c=htmlspecialchars($c);
507 }
508 return $c;
509 }
510
511 /**
512 * Wraps input string in a <font> tag with verdana, black and size 1
513 *
514 * @param string The string to be wrapped
515 * @return string
516 */
517 function fw($str) {
518 return '<font face="verdana" color="black" size="1" style="color:black;">'.$str.'&nbsp;</font>';
519 }
520
521 /**
522 * Helper function for internal data manipulation
523 *
524 * @param array Array (passed by reference) and modified
525 * @param integer
526 * @param string
527 * @return void
528 * @access private
529 * @see printTSlog()
530 */
531 function createHierarchyArray(&$arr,$pointer,$uniqueId) {
532 if (!is_array($arr)) $arr=array();
533 if ($pointer>0) {
534 end($arr);
535 $k=key($arr);
536 $this->createHierarchyArray($arr[intval($k).'.'],$pointer-1,$uniqueId);
537 } else {
538 $arr[] = $uniqueId;
539 }
540 }
541
542 /**
543 * This prints out a TYPO3 error message.
544 *
545 * @param string Header string
546 * @param string Message string
547 * @param boolean If set, then this will produce a alert() line for inclusion in JavaScript.
548 * @return string
549 */
550 function debug_typo3PrintError($header,$text,$js) {
551 if ($js) {
552 echo"alert('".t3lib_div::slashJS($header."\n".$text)."');";
553 } else {
554 echo '
555 <html>
556 <head>
557 <title>Error!</title>
558 </head>
559 <body bgcolor="#cccccc">
560 <div align="center">
561 <table border="0" cellspacing="0" cellpadding="0" width="333" bgcolor="#cccccc">
562 <tr>
563 <td><img src="t3lib/gfx/typo3logo.gif" width="333" height="43" vspace="10" border="0" alt="" /></td>
564 </tr>
565 <tr>
566 <td bgcolor="black">
567 <table width="100%" border="0" cellspacing="1" cellpadding="10">
568 <tr>
569 <td bgcolor="#F4F0E8">
570 <font face="verdana,arial,helvetica" size="2">';
571 echo '<b><center><font size="+1">'.$header.'</font></center></b><br />'.$text;
572 echo ' </font>
573 </td>
574 </tr>
575 </table>
576 </td>
577 </tr>
578 </table>
579 </div>
580 </body>
581 </html>';
582 }
583 }
584 }
585 ?>