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