2006-06-28 Ernesto Baschny <ernst@cron-it.de>
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_timetrack.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2006 Kasper Skaarhoj (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 Skaarhoj
32 * XHTML compliant
33 *
34 * @author Kasper Skaarhoj <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
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 <kasperYYYY@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="typo3/gfx/icon_note.gif" width="18" height="16" align="absmiddle" alt="" />',
115 2 => '<img src="typo3/gfx/icon_warning.gif" width="18" height="16" align="absmiddle" alt="" />',
116 3 => '<img src="typo3/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->wrapIcon =array(
146 0 => '',
147 1 => '<img src="'.TYPO3_mainDir.'gfx/icon_note.gif" width="18" height="16" align="absmiddle" alt="" />',
148 2 => '<img src="'.TYPO3_mainDir.'gfx/icon_warning.gif" width="18" height="16" align="absmiddle" alt="" />',
149 3 => '<img src="'.TYPO3_mainDir.'gfx/icon_fatalerror.gif" width="18" height="16" align="absmiddle" alt="" />'
150 );
151
152 $this->starttime=0;
153 $this->starttime=$this->mtime();
154 }
155
156 /**
157 * Pushes an element to the TypoScript tracking array
158 *
159 * @param string Label string for the entry, eg. TypoScript property name
160 * @param string Additional value(?)
161 * @return void
162 * @see tslib_cObj::cObjGetSingle(), pull()
163 */
164 function push($tslabel, $value='') {
165 array_push($this->tsStack[$this->tsStackPointer], $tslabel);
166 array_push($this->currentHashPointer, 'timetracker_'.$this->uniqueCounter++);
167
168 $this->tsStackLevel++;
169 $this->tsStackLevelMax[] = $this->tsStackLevel;
170
171 // setTSlog
172 $k = end($this->currentHashPointer);
173 $this->tsStackLog[$k] = array(
174 'level' => $this->tsStackLevel,
175 'tsStack' => $this->tsStack,
176 'value' => $value,
177 'starttime' => microtime(),
178 'stackPointer' => $this->tsStackPointer
179 );
180 }
181
182 /**
183 * Pulls an element from the TypoScript tracking array
184 *
185 * @param string The content string generated within the push/pull part.
186 * @return void
187 * @see tslib_cObj::cObjGetSingle(), push()
188 */
189 function pull($content='') {
190 $k = end($this->currentHashPointer);
191 $this->tsStackLog[$k]['endtime'] = microtime();
192 $this->tsStackLog[$k]['content'] = $content;
193
194 $this->tsStackLevel--;
195 array_pop($this->tsStack[$this->tsStackPointer]);
196 array_pop($this->currentHashPointer);
197 }
198
199 /**
200 * Logs the TypoScript entry
201 *
202 * @param string The message string
203 * @param integer Message type: 0: information, 1: message, 2: warning, 3: error
204 * @return void
205 * @see tslib_cObj::CONTENT()
206 */
207 function setTSlogMessage($content,$num=0) {
208 end($this->currentHashPointer);
209 $k = current($this->currentHashPointer);
210
211 $this->tsStackLog[$k]['message'][] = $this->wrapIcon[$num].$this->wrapError[$num][0].htmlspecialchars($content).$this->wrapError[$num][1];
212 }
213
214 /**
215 * Set TSselectQuery - for messages in TypoScript debugger.
216 *
217 * @param string Query string
218 * @param string Message/Label to attach
219 * @return void
220 */
221 function setTSselectQuery($query,$msg) {
222 end($this->currentHashPointer);
223 $k = current($this->currentHashPointer);
224
225 $this->tsStackLog[$k]['selectQuery'][] = array('query'=>$query,'msg'=>$msg);
226 }
227
228 /**
229 * Increases the stack pointer
230 *
231 * @return void
232 * @see decStackPointer(), TSpagegen::renderContent(), tslib_cObj::cObjGetSingle()
233 */
234 function incStackPointer() {
235 $this->tsStackPointer++;
236 $this->tsStack[$this->tsStackPointer]=array();
237 }
238
239 /**
240 * Decreases the stack pointer
241 *
242 * @return void
243 * @see incStackPointer(), TSpagegen::renderContent(), tslib_cObj::cObjGetSingle()
244 */
245 function decStackPointer() {
246 unset($this->tsStack[$this->tsStackPointer]);
247 $this->tsStackPointer--;
248 }
249
250 /**
251 * Returns the current time in milliseconds
252 *
253 * @return integer
254 */
255 function mtime() {
256 return $this->convertMicrotime(microtime())-$this->starttime;
257 }
258
259 /**
260 * Returns microtime input to milliseconds
261 *
262 * @param string PHP microtime string
263 * @return integer
264 */
265 function convertMicrotime($microtime) {
266 $parts = explode(' ',$microtime);
267 return round(($parts[0]+$parts[1])*1000);
268 }
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286 /*******************************************
287 *
288 * Printing the parsing time information (for Admin Panel)
289 *
290 *******************************************/
291
292 /**
293 * Print TypoScript parsing log
294 *
295 * @return string HTML table with the information about parsing times.
296 * @see t3lib_tsfeBeUserAuth::extGetCategory_tsdebug()
297 */
298 function printTSlog() {
299 // Calculate times and keys for the tsStackLog
300 $preEndtime=0;
301 foreach($this->tsStackLog as $uniqueId=>$data) {
302 $this->tsStackLog[$uniqueId]['endtime'] = $this->convertMicrotime($this->tsStackLog[$uniqueId]['endtime'])-$this->starttime;
303 $this->tsStackLog[$uniqueId]['starttime'] = $this->convertMicrotime($this->tsStackLog[$uniqueId]['starttime'])-$this->starttime;
304 $this->tsStackLog[$uniqueId]['deltatime'] = $this->tsStackLog[$uniqueId]['endtime']-$this->tsStackLog[$uniqueId]['starttime'];
305 $this->tsStackLog[$uniqueId]['key'] = implode($this->tsStackLog[$uniqueId]['stackPointer']?'.':'/', end($data['tsStack']));
306 $preEndtime = $this->tsStackLog[$uniqueId]['endtime'];
307 }
308
309 // Create hierarchical array of keys pointing to the stack
310 $arr = array();
311 reset($this->tsStackLog);
312 while(list($uniqueId,$data)=each($this->tsStackLog)) {
313 $this->createHierarchyArray($arr,$data['level'], $uniqueId);
314 }
315 // Parsing the registeret content and create icon-html for the tree
316 $this->tsStackLog[$arr['0.'][0]]['content'] = $this->fixContent($arr['0.']['0.'], $this->tsStackLog[$arr['0.'][0]]['content'], '', 0, $arr['0.'][0]);
317
318 // Displaying the tree:
319 reset($this->tsStackLog);
320 $out='<tr>
321 <td bgcolor="#ABBBB4" align="center"><b>'.$this->fw('TypoScript Key').'</b></td>
322 <td bgcolor="#ABBBB4" align="center"><b>'.$this->fw('Value').'</b></td>';
323 if ($this->printConf['allTime']) {
324 $out.='
325 <td bgcolor="#ABBBB4" align="center"><b>'.$this->fw('Time').'</b></td>
326 <td bgcolor="#ABBBB4" align="center"><b>'.$this->fw('Own').'</b></td>
327 <td bgcolor="#ABBBB4" align="center"><b>'.$this->fw('Sub').'</b></td>
328 <td bgcolor="#ABBBB4" align="center"><b>'.$this->fw('Total').'</b></td>';
329 } else {
330 $out.='
331 <td bgcolor="#ABBBB4" align="center"><b>'.$this->fw('Own').'</b></td>';
332 }
333
334 $out.='
335 <td bgcolor="#ABBBB4" align="center"><b>'.$this->fw('Details').'</b></td>
336 </tr>';
337
338
339
340 $flag_tree=$this->printConf['flag_tree'];
341 $flag_messages=$this->printConf['flag_messages'];
342 $flag_content=$this->printConf['flag_content'];
343 $flag_queries=$this->printConf['flag_queries'];
344 $keyLgd=$this->printConf['keyLgd'];
345 $factor=$this->printConf['factor'];
346 $col=$this->printConf['col'];
347
348 $c=0;
349 while(list($uniqueId,$data)=each($this->tsStackLog)) {
350 $bgColor = ' bgcolor="'.($c%2 ? t3lib_div::modifyHTMLColor($col,$factor,$factor,$factor) : $col).'"';
351 $item='';
352 if (!$c) { // If first...
353 $data['icons']='';
354 $data['key']= 'Script Start';
355 $data['value'] = '';
356 }
357
358
359 // key label:
360 $keyLabel='';
361 if (!$flag_tree && $data['stackPointer']) {
362 $temp=array();
363 reset($data['tsStack']);
364 while(list($k,$v)=each($data['tsStack'])) {
365 $temp[]=t3lib_div::fixed_lgd_pre(implode($v,$k?'.':'/'),$keyLgd);
366 }
367 array_pop($temp);
368 $temp = array_reverse($temp);
369 array_pop($temp);
370 if (count($temp)) {
371 $keyLabel='<br /><font color="#999999">'.implode($temp,'<br />').'</font>';
372 }
373 }
374 if ($flag_tree) {
375 $tmp = t3lib_div::trimExplode('.',$data['key'],1);
376 $theLabel = end($tmp);
377 } else {
378 $theLabel = $data['key'];
379 }
380 $theLabel = t3lib_div::fixed_lgd_pre($theLabel, $keyLgd);
381 $theLabel = $data['stackPointer'] ? '<font color="maroon">'.$theLabel.'</font>' : $theLabel;
382 $keyLabel=$theLabel.$keyLabel;
383 $item.='<td valign="top" nowrap="nowrap"'.$bgColor.'>'.($flag_tree?$data['icons']:'').$this->fw($keyLabel).'</td>';
384
385 // key value:
386 $keyValue=$data['value'];
387 $item.='<td valign="top" nowrap="nowrap"'.$bgColor.'>'.$this->fw(htmlspecialchars($keyValue)).'</td>';
388
389 if ($this->printConf['allTime']) {
390 // deltatime:
391 $item.='<td valign="top" align="right" nowrap="nowrap"'.$bgColor.'>'.$this->fw($data['starttime']).'</td>';
392 $item.='<td valign="top" align="right" nowrap="nowrap"'.$bgColor.'>'.$this->fw($data['owntime']).'</td>';
393 $item.='<td valign="top" align="right" nowrap="nowrap"'.$bgColor.'>'.$this->fw($data['subtime'] ? '+'.$data['subtime'] : '').'</td>';
394 $item.='<td valign="top" align="right" nowrap="nowrap"'.$bgColor.'>'.$this->fw($data['subtime'] ? '='.$data['deltatime'] : '').'</td>';
395 } else {
396 // deltatime:
397 $item.='<td valign="top" align="right" nowrap="nowrap"'.$bgColor.'>'.$this->fw($data['owntime']).'</td>';
398 }
399
400
401 // messages:
402 $msgArr=array();
403 $msg='';
404 if ($flag_messages && is_array($data['message'])) {
405 reset($data['message']);
406 while(list(,$v)=each($data['message'])) {
407 $msgArr[]=nl2br($v);
408 }
409 }
410 if ($flag_queries && is_array($data['selectQuery'])) {
411 reset($data['selectQuery']);
412 while(list(,$v)=each($data['selectQuery'])) {
413 $res = $GLOBALS['TYPO3_DB']->sql_query('EXPLAIN '.$v['query']);
414 $v['mysql_error'] = $GLOBALS['TYPO3_DB']->sql_error();
415 if (!$GLOBALS['TYPO3_DB']->sql_error()) {
416 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
417 $v['explain'][]=$row;
418 }
419 }
420 $msgArr[]=t3lib_div::view_array($v);
421 }
422 }
423 if ($flag_content && strcmp($data['content'],'')) {
424 $msgArr[]='<font color="#000066">'.nl2br($data['content']).'</font>';
425 }
426 if (count($msgArr)) {
427 $msg=implode($msgArr,'<hr />');
428 }
429 $item.='<td valign="top"'.$bgColor.'>'.$this->fw($msg).'</td>';
430 $out.='<tr>'.$item.'</tr>';
431 $c++;
432 }
433 $out='<table border="0" cellpadding="0" cellspacing="0">'.$out.'</table>';
434 return $out;
435 }
436
437 /**
438 * Recursively generates the content to display
439 *
440 * @param array Array which is modified with content. Reference
441 * @param string Current content string for the level
442 * @param string Prefixed icons for new PM icons
443 * @param boolean Set this for the first call from outside.
444 * @param string Seems to be the previous tsStackLog key
445 * @return string Returns the $content string generated/modified. Also the $arr array is modified!
446 */
447 function fixContent(&$arr, $content, $depthData='', $first=0, $vKey='') {
448 $ac=0;
449 $c=0;
450 // First, find number of entries
451 reset($arr);
452 while(list($k,$v)=each($arr)) {
453 if (t3lib_div::testInt($k)) {
454 $ac++;
455 }
456 }
457 // Traverse through entries
458 $subtime=0;
459 reset($arr);
460 while(list($k,$v)=each($arr)) {
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 reset($arr);
493 while(list($k,$v)=each($arr)) {
494 if (t3lib_div::testInt($k)) {
495 if (strlen($this->tsStackLog[$v]['content'])) {
496 $content = str_replace($v, '<font color="red"><b>['.$this->tsStackLog[$v]['key'].']</b></font>', $content);
497 }
498 }
499 }
500 // return the content
501 return $content;
502 }
503
504 /**
505 * 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"
506 *
507 * @param string The content string
508 * @param string Command: If "FILE" then $this->printConf['contentLength_FILE'] is used for content length comparison, otherwise $this->printConf['contentLength']
509 * @return string
510 */
511 function fixCLen($c,$v) {
512 $len = $v=='FILE'?$this->printConf['contentLength_FILE']:$this->printConf['contentLength'];
513 if (strlen($c)>$len) {
514 $c='<font color="green">'.htmlspecialchars(t3lib_div::fixed_lgd($c,$len)).'</font>';
515 } else {
516 $c=htmlspecialchars($c);
517 }
518 return $c;
519 }
520
521 /**
522 * Wraps input string in a <font> tag with verdana, black and size 1
523 *
524 * @param string The string to be wrapped
525 * @return string
526 */
527 function fw($str) {
528 return '<font face="verdana" color="black" size="1" style="color:black;">'.$str.'&nbsp;</font>';
529 }
530
531 /**
532 * Helper function for internal data manipulation
533 *
534 * @param array Array (passed by reference) and modified
535 * @param integer Pointer value
536 * @param string Unique ID string
537 * @return void
538 * @access private
539 * @see printTSlog()
540 */
541 function createHierarchyArray(&$arr,$pointer,$uniqueId) {
542 if (!is_array($arr)) $arr=array();
543 if ($pointer>0) {
544 end($arr);
545 $k=key($arr);
546 $this->createHierarchyArray($arr[intval($k).'.'],$pointer-1,$uniqueId);
547 } else {
548 $arr[] = $uniqueId;
549 }
550 }
551
552 /**
553 * This prints out a TYPO3 error message.
554 *
555 * @param string Header string
556 * @param string Message string
557 * @param boolean If set, then this will produce a alert() line for inclusion in JavaScript.
558 * @param string URL for the <base> tag (if you want it)
559 * @return string
560 */
561 function debug_typo3PrintError($header,$text,$js,$baseUrl='') {
562 if ($js) {
563 echo"alert('".t3lib_div::slashJS($header."\n".$text)."');";
564 } else {
565 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
566 "http://www.w3.org/TR/xhtml1/DTD/xhtml11.dtd">
567 <?xml version="1.0" encoding="utf-8"?>
568 <html>
569 <head>
570 '.($baseUrl ? '<base href="'.htmlspecialchars($baseUrl).'" />' : '').'
571 <title>Error!</title>
572 <style type="text/css"><!--/*--><![CDATA[/*><!--*/
573 body { font-family: verdana,arial,helvetica; font-size: 90%; text-align: center; }
574 h1 { font-size: 1.2em; margin: 0 0 1em 0; }
575 p { margin: 0; text-align: left; }
576 img { border: 0; margin: 10px 0; }
577 div.center div { margin: 0 auto; }
578 .errorBox { width: 400px; padding: 0.5em; border: 1px solid black; background-color: #F4F0E8; }
579 /*]]>*/--></style>
580 </head>
581 <body>
582 <div class="center">
583 <img src="'.TYPO3_mainDir.'gfx/typo3logo.gif" width="123" height="34" alt="" />
584 <div class="errorBox">
585 <h1>'.$header.'</h1>
586 <p>'.$text.'</p>
587 </div>
588 </div>
589 </body>
590 </html>';
591 }
592 }
593 }
594 ?>