1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the TYPO3 CMS project. |
5
|
|
|
* |
6
|
|
|
* It is free software; you can redistribute it and/or modify it under |
7
|
|
|
* the terms of the GNU General Public License, either version 2 |
8
|
|
|
* of the License, or any later version. |
9
|
|
|
* |
10
|
|
|
* For the full copyright and license information, please read the |
11
|
|
|
* LICENSE.txt file that was distributed with this source code. |
12
|
|
|
* |
13
|
|
|
* The TYPO3 project - inspiring people to share! |
14
|
|
|
*/ |
15
|
|
|
|
16
|
|
|
namespace TYPO3\CMS\Core\TimeTracker; |
17
|
|
|
|
18
|
|
|
use TYPO3\CMS\Core\Imaging\Icon; |
19
|
|
|
use TYPO3\CMS\Core\Imaging\IconFactory; |
20
|
|
|
use TYPO3\CMS\Core\SingletonInterface; |
21
|
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility; |
22
|
|
|
use TYPO3\CMS\Core\Utility\MathUtility; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Frontend Timetracking functions |
26
|
|
|
* |
27
|
|
|
* Is used to register how much time is used with operations in TypoScript |
28
|
|
|
*/ |
29
|
|
|
class TimeTracker implements SingletonInterface |
30
|
|
|
{ |
31
|
|
|
/** |
32
|
|
|
* If set to true (see constructor) then then the timetracking is enabled |
33
|
|
|
* @var bool |
34
|
|
|
*/ |
35
|
|
|
protected $isEnabled = false; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Is loaded with the millisecond time when this object is created |
39
|
|
|
* |
40
|
|
|
* @var int |
41
|
|
|
*/ |
42
|
|
|
public $starttime = 0; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Is set via finish() with the millisecond time when the request handler is finished. |
46
|
|
|
* |
47
|
|
|
* @var float |
48
|
|
|
*/ |
49
|
|
|
protected $finishtime = 0; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Log Rendering flag. If set, ->push() and ->pull() is called from the cObj->cObjGetSingle(). |
53
|
|
|
* This determines whether or not the TypoScript parsing activity is logged. But it also slows down the rendering |
54
|
|
|
* |
55
|
|
|
* @var bool |
56
|
|
|
*/ |
57
|
|
|
public $LR = true; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @var array |
61
|
|
|
*/ |
62
|
|
|
public $printConf = [ |
63
|
|
|
'showParentKeys' => 1, |
64
|
|
|
'contentLength' => 10000, |
65
|
|
|
// Determines max length of displayed content before it gets cropped. |
66
|
|
|
'contentLength_FILE' => 400, |
67
|
|
|
// 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. |
68
|
|
|
'flag_tree' => 1, |
69
|
|
|
'flag_messages' => 1, |
70
|
|
|
'flag_content' => 0, |
71
|
|
|
'allTime' => 0, |
72
|
|
|
'keyLgd' => 40 |
73
|
|
|
]; |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* @var array |
77
|
|
|
*/ |
78
|
|
|
public $wrapError = [ |
79
|
|
|
0 => ['', ''], |
80
|
|
|
1 => ['<strong>', '</strong>'], |
81
|
|
|
2 => ['<strong style="color:#ff6600;">', '</strong>'], |
82
|
|
|
3 => ['<strong style="color:#ff0000;">', '</strong>'] |
83
|
|
|
]; |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* @var array |
87
|
|
|
*/ |
88
|
|
|
public $wrapIcon = [ |
89
|
|
|
0 => '', |
90
|
|
|
1 => 'actions-document-info', |
91
|
|
|
2 => 'status-dialog-warning', |
92
|
|
|
3 => 'status-dialog-error' |
93
|
|
|
]; |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* @var int |
97
|
|
|
*/ |
98
|
|
|
public $uniqueCounter = 0; |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* @var array |
102
|
|
|
*/ |
103
|
|
|
public $tsStack = [[]]; |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* @var int |
107
|
|
|
*/ |
108
|
|
|
public $tsStackLevel = 0; |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* @var array |
112
|
|
|
*/ |
113
|
|
|
public $tsStackLevelMax = []; |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* @var array |
117
|
|
|
*/ |
118
|
|
|
public $tsStackLog = []; |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* @var int |
122
|
|
|
*/ |
123
|
|
|
public $tsStackPointer = 0; |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* @var array |
127
|
|
|
*/ |
128
|
|
|
public $currentHashPointer = []; |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Log entries that take than this number of milliseconds (own time) will be highlighted during log display. Set 0 to disable highlighting. |
132
|
|
|
* |
133
|
|
|
* @var int |
134
|
|
|
*/ |
135
|
|
|
public $highlightLongerThan = 0; |
136
|
|
|
|
137
|
|
|
/******************************************* |
138
|
|
|
* |
139
|
|
|
* Logging parsing times in the scripts |
140
|
|
|
* |
141
|
|
|
*******************************************/ |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* TimeTracker constructor. |
145
|
|
|
* |
146
|
|
|
* @param bool $isEnabled |
147
|
|
|
*/ |
148
|
|
|
public function __construct($isEnabled = true) |
149
|
|
|
{ |
150
|
|
|
$this->isEnabled = $isEnabled; |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* @param bool $isEnabled |
155
|
|
|
*/ |
156
|
|
|
public function setEnabled(bool $isEnabled = true) |
157
|
|
|
{ |
158
|
|
|
$this->isEnabled = $isEnabled; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* Sets the starting time |
163
|
|
|
* |
164
|
|
|
* @see finish() |
165
|
|
|
* @param float|null $starttime |
166
|
|
|
*/ |
167
|
|
|
public function start(?float $starttime = null) |
168
|
|
|
{ |
169
|
|
|
if (!$this->isEnabled) { |
170
|
|
|
return; |
171
|
|
|
} |
172
|
|
|
$this->starttime = $this->getMilliseconds($starttime); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* Pushes an element to the TypoScript tracking array |
177
|
|
|
* |
178
|
|
|
* @param string $tslabel Label string for the entry, eg. TypoScript property name |
179
|
|
|
* @param string $value Additional value(?) |
180
|
|
|
* @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::cObjGetSingle() |
181
|
|
|
* @see pull() |
182
|
|
|
*/ |
183
|
|
|
public function push($tslabel, $value = '') |
184
|
|
|
{ |
185
|
|
|
if (!$this->isEnabled) { |
186
|
|
|
return; |
187
|
|
|
} |
188
|
|
|
$this->tsStack[$this->tsStackPointer][] = $tslabel; |
189
|
|
|
$this->currentHashPointer[] = 'timetracker_' . $this->uniqueCounter++; |
190
|
|
|
$this->tsStackLevel++; |
191
|
|
|
$this->tsStackLevelMax[] = $this->tsStackLevel; |
192
|
|
|
// setTSlog |
193
|
|
|
$k = end($this->currentHashPointer); |
194
|
|
|
$this->tsStackLog[$k] = [ |
195
|
|
|
'level' => $this->tsStackLevel, |
196
|
|
|
'tsStack' => $this->tsStack, |
197
|
|
|
'value' => $value, |
198
|
|
|
'starttime' => microtime(true), |
199
|
|
|
'stackPointer' => $this->tsStackPointer |
200
|
|
|
]; |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* Pulls an element from the TypoScript tracking array |
205
|
|
|
* |
206
|
|
|
* @param string $content The content string generated within the push/pull part. |
207
|
|
|
* @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::cObjGetSingle() |
208
|
|
|
* @see push() |
209
|
|
|
*/ |
210
|
|
|
public function pull($content = '') |
211
|
|
|
{ |
212
|
|
|
if (!$this->isEnabled) { |
213
|
|
|
return; |
214
|
|
|
} |
215
|
|
|
$k = end($this->currentHashPointer); |
216
|
|
|
$this->tsStackLog[$k]['endtime'] = microtime(true); |
217
|
|
|
$this->tsStackLog[$k]['content'] = $content; |
218
|
|
|
$this->tsStackLevel--; |
219
|
|
|
array_pop($this->tsStack[$this->tsStackPointer]); |
220
|
|
|
array_pop($this->currentHashPointer); |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* Logs the TypoScript entry |
225
|
|
|
* |
226
|
|
|
* @param string $content The message string |
227
|
|
|
* @param int $num Message type: 0: information, 1: message, 2: warning, 3: error |
228
|
|
|
* @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::CONTENT() |
229
|
|
|
*/ |
230
|
|
|
public function setTSlogMessage($content, $num = 0) |
231
|
|
|
{ |
232
|
|
|
if (!$this->isEnabled) { |
233
|
|
|
return; |
234
|
|
|
} |
235
|
|
|
end($this->currentHashPointer); |
236
|
|
|
$k = current($this->currentHashPointer); |
237
|
|
|
$placeholder = ''; |
238
|
|
|
// Enlarge the "details" column by adding a span |
239
|
|
|
if (strlen($content) > 30) { |
240
|
|
|
$placeholder = '<br /><span style="width: 300px; height: 1px; display: inline-block;"></span>'; |
241
|
|
|
} |
242
|
|
|
$iconFactory = GeneralUtility::makeInstance(IconFactory::class); |
243
|
|
|
$this->tsStackLog[$k]['message'][] = $iconFactory->getIcon($this->wrapIcon[$num], Icon::SIZE_SMALL)->render() . $this->wrapError[$num][0] . htmlspecialchars($content) . $this->wrapError[$num][1] . $placeholder; |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* Set TSselectQuery - for messages in TypoScript debugger. |
248
|
|
|
* |
249
|
|
|
* @param array $data Query array |
250
|
|
|
* @param string $msg Message/Label to attach |
251
|
|
|
*/ |
252
|
|
|
public function setTSselectQuery(array $data, $msg = '') |
253
|
|
|
{ |
254
|
|
|
if (!$this->isEnabled) { |
255
|
|
|
return; |
256
|
|
|
} |
257
|
|
|
end($this->currentHashPointer); |
258
|
|
|
$k = current($this->currentHashPointer); |
259
|
|
|
if ($msg !== '') { |
260
|
|
|
$data['msg'] = $msg; |
261
|
|
|
} |
262
|
|
|
$this->tsStackLog[$k]['selectQuery'][] = $data; |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
/** |
266
|
|
|
* Increases the stack pointer |
267
|
|
|
* |
268
|
|
|
* @see decStackPointer() |
269
|
|
|
* @see \TYPO3\CMS\Frontend\Page\PageGenerator::renderContent() |
270
|
|
|
* @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::cObjGetSingle() |
271
|
|
|
*/ |
272
|
|
|
public function incStackPointer() |
273
|
|
|
{ |
274
|
|
|
if (!$this->isEnabled) { |
275
|
|
|
return; |
276
|
|
|
} |
277
|
|
|
$this->tsStackPointer++; |
278
|
|
|
$this->tsStack[$this->tsStackPointer] = []; |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* Decreases the stack pointer |
283
|
|
|
* |
284
|
|
|
* @see incStackPointer() |
285
|
|
|
* @see \TYPO3\CMS\Frontend\Page\PageGenerator::renderContent() |
286
|
|
|
* @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::cObjGetSingle() |
287
|
|
|
*/ |
288
|
|
|
public function decStackPointer() |
289
|
|
|
{ |
290
|
|
|
if (!$this->isEnabled) { |
291
|
|
|
return; |
292
|
|
|
} |
293
|
|
|
unset($this->tsStack[$this->tsStackPointer]); |
294
|
|
|
$this->tsStackPointer--; |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
/** |
298
|
|
|
* Gets a microtime value as milliseconds value. |
299
|
|
|
* |
300
|
|
|
* @param float $microtime The microtime value - if not set the current time is used |
301
|
|
|
* @return int The microtime value as milliseconds value |
302
|
|
|
*/ |
303
|
|
|
public function getMilliseconds($microtime = null) |
304
|
|
|
{ |
305
|
|
|
if (!$this->isEnabled) { |
306
|
|
|
return 0; |
307
|
|
|
} |
308
|
|
|
if (!isset($microtime)) { |
309
|
|
|
$microtime = microtime(true); |
310
|
|
|
} |
311
|
|
|
return (int)round($microtime * 1000); |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
/** |
315
|
|
|
* Gets the difference between a given microtime value and the starting time as milliseconds. |
316
|
|
|
* |
317
|
|
|
* @param float $microtime The microtime value - if not set the current time is used |
318
|
|
|
* @return int The difference between a given microtime value and starting time as milliseconds |
319
|
|
|
*/ |
320
|
|
|
public function getDifferenceToStarttime($microtime = null) |
321
|
|
|
{ |
322
|
|
|
return $this->getMilliseconds($microtime) - $this->starttime; |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
/** |
326
|
|
|
* Usually called when the page generation and output is prepared. |
327
|
|
|
* |
328
|
|
|
* @see start() |
329
|
|
|
*/ |
330
|
|
|
public function finish(): void |
331
|
|
|
{ |
332
|
|
|
if ($this->isEnabled) { |
333
|
|
|
$this->finishtime = microtime(true); |
|
|
|
|
334
|
|
|
} |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
/** |
338
|
|
|
* Get total parse time in milliseconds |
339
|
|
|
* |
340
|
|
|
* @return int |
341
|
|
|
*/ |
342
|
|
|
public function getParseTime(): int |
343
|
|
|
{ |
344
|
|
|
if (!$this->starttime) { |
345
|
|
|
$this->start(microtime(true)); |
|
|
|
|
346
|
|
|
} |
347
|
|
|
if (!$this->finishtime) { |
348
|
|
|
$this->finish(); |
349
|
|
|
} |
350
|
|
|
return $this->getDifferenceToStarttime($this->finishtime ?? null); |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
/******************************************* |
354
|
|
|
* |
355
|
|
|
* Printing the parsing time information (for Admin Panel) |
356
|
|
|
* |
357
|
|
|
*******************************************/ |
358
|
|
|
/** |
359
|
|
|
* Print TypoScript parsing log |
360
|
|
|
* |
361
|
|
|
* @return string HTML table with the information about parsing times. |
362
|
|
|
*/ |
363
|
|
|
public function printTSlog() |
364
|
|
|
{ |
365
|
|
|
if (!$this->isEnabled) { |
366
|
|
|
return ''; |
367
|
|
|
} |
368
|
|
|
// Calculate times and keys for the tsStackLog |
369
|
|
|
foreach ($this->tsStackLog as $uniqueId => &$data) { |
370
|
|
|
$data['endtime'] = $this->getDifferenceToStarttime($data['endtime']); |
371
|
|
|
$data['starttime'] = $this->getDifferenceToStarttime($data['starttime']); |
372
|
|
|
$data['deltatime'] = $data['endtime'] - $data['starttime']; |
373
|
|
|
if (is_array($data['tsStack'])) { |
374
|
|
|
$data['key'] = implode($data['stackPointer'] ? '.' : '/', end($data['tsStack'])); |
375
|
|
|
} |
376
|
|
|
} |
377
|
|
|
unset($data); |
378
|
|
|
// Create hierarchical array of keys pointing to the stack |
379
|
|
|
$arr = []; |
380
|
|
|
foreach ($this->tsStackLog as $uniqueId => $data) { |
381
|
|
|
$this->createHierarchyArray($arr, $data['level'], $uniqueId); |
382
|
|
|
} |
383
|
|
|
// Parsing the registered content and create icon-html for the tree |
384
|
|
|
$this->tsStackLog[$arr['0.'][0]]['content'] = $this->fixContent($arr['0.'], $this->tsStackLog[$arr['0.'][0]]['content'] ?? '', '', $arr['0.'][0]); |
385
|
|
|
// Displaying the tree: |
386
|
|
|
$outputArr = []; |
387
|
|
|
$outputArr[] = $this->fw('TypoScript Key'); |
388
|
|
|
$outputArr[] = $this->fw('Value'); |
389
|
|
|
if ($this->printConf['allTime']) { |
390
|
|
|
$outputArr[] = $this->fw('Time'); |
391
|
|
|
$outputArr[] = $this->fw('Own'); |
392
|
|
|
$outputArr[] = $this->fw('Sub'); |
393
|
|
|
$outputArr[] = $this->fw('Total'); |
394
|
|
|
} else { |
395
|
|
|
$outputArr[] = $this->fw('Own'); |
396
|
|
|
} |
397
|
|
|
$outputArr[] = $this->fw('Details'); |
398
|
|
|
$out = ''; |
399
|
|
|
foreach ($outputArr as $row) { |
400
|
|
|
$out .= '<th>' . $row . '</th>'; |
401
|
|
|
} |
402
|
|
|
$out = '<thead><tr>' . $out . '</tr></thead>'; |
403
|
|
|
$flag_tree = $this->printConf['flag_tree']; |
404
|
|
|
$flag_messages = $this->printConf['flag_messages']; |
405
|
|
|
$flag_content = $this->printConf['flag_content']; |
406
|
|
|
$keyLgd = (int)$this->printConf['keyLgd']; |
407
|
|
|
$c = 0; |
408
|
|
|
foreach ($this->tsStackLog as $uniqueId => $data) { |
409
|
|
|
$logRowClass = ''; |
410
|
|
|
if ($this->highlightLongerThan && (int)$data['owntime'] > (int)$this->highlightLongerThan) { |
411
|
|
|
$logRowClass = 'typo3-adminPanel-logRow-highlight'; |
412
|
|
|
} |
413
|
|
|
$item = ''; |
414
|
|
|
// If first... |
415
|
|
|
if (!$c) { |
416
|
|
|
$data['icons'] = ''; |
417
|
|
|
$data['key'] = 'Script Start'; |
418
|
|
|
$data['value'] = ''; |
419
|
|
|
} |
420
|
|
|
// Key label: |
421
|
|
|
$keyLabel = ''; |
422
|
|
|
if (!$flag_tree && $data['stackPointer']) { |
423
|
|
|
$temp = []; |
424
|
|
|
foreach ($data['tsStack'] as $k => $v) { |
425
|
|
|
$temp[] = GeneralUtility::fixed_lgd_cs(implode($k ? '.' : '/', $v), -$keyLgd); |
426
|
|
|
} |
427
|
|
|
array_pop($temp); |
428
|
|
|
$temp = array_reverse($temp); |
429
|
|
|
array_pop($temp); |
430
|
|
|
if (!empty($temp)) { |
431
|
|
|
$keyLabel = '<br /><span style="color:#999999;">' . implode('<br />', $temp) . '</span>'; |
432
|
|
|
} |
433
|
|
|
} |
434
|
|
|
if ($flag_tree) { |
435
|
|
|
$tmp = GeneralUtility::trimExplode('.', $data['key'], true); |
436
|
|
|
$theLabel = end($tmp); |
437
|
|
|
} else { |
438
|
|
|
$theLabel = $data['key']; |
439
|
|
|
} |
440
|
|
|
$theLabel = GeneralUtility::fixed_lgd_cs($theLabel, -$keyLgd); |
441
|
|
|
$theLabel = $data['stackPointer'] ? '<span class="stackPointer">' . $theLabel . '</span>' : $theLabel; |
442
|
|
|
$keyLabel = $theLabel . $keyLabel; |
443
|
|
|
$item .= '<th scope="row" class="typo3-adminPanel-table-cell-key ' . $logRowClass . '">' . ($flag_tree ? $data['icons'] : '') . $this->fw($keyLabel) . '</th>'; |
444
|
|
|
// Key value: |
445
|
|
|
$keyValue = $data['value']; |
446
|
|
|
$item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime">' . $this->fw(htmlspecialchars($keyValue)) . '</td>'; |
447
|
|
|
if ($this->printConf['allTime']) { |
448
|
|
|
$item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw($data['starttime']) . '</td>'; |
449
|
|
|
$item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw($data['owntime']) . '</td>'; |
450
|
|
|
$item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw(($data['subtime'] ? '+' . $data['subtime'] : '')) . '</td>'; |
451
|
|
|
$item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw(($data['subtime'] ? '=' . $data['deltatime'] : '')) . '</td>'; |
452
|
|
|
} else { |
453
|
|
|
$item .= '<td class="' . $logRowClass . ' typo3-adminPanel-tsLogTime"> ' . $this->fw($data['owntime']) . '</td>'; |
454
|
|
|
} |
455
|
|
|
// Messages: |
456
|
|
|
$msgArr = []; |
457
|
|
|
$msg = ''; |
458
|
|
|
if ($flag_messages && is_array($data['message'])) { |
459
|
|
|
foreach ($data['message'] as $v) { |
460
|
|
|
$msgArr[] = nl2br($v); |
461
|
|
|
} |
462
|
|
|
} |
463
|
|
|
if ($flag_content && (string)$data['content'] !== '') { |
464
|
|
|
$maxlen = 120; |
465
|
|
|
// Break lines which are too longer than $maxlen chars (can happen if content contains long paths...) |
466
|
|
|
if (preg_match_all('/(\\S{' . $maxlen . ',})/', $data['content'], $reg)) { |
467
|
|
|
foreach ($reg[1] as $key => $match) { |
468
|
|
|
$match = preg_replace('/(.{' . $maxlen . '})/', '$1 ', $match); |
469
|
|
|
$data['content'] = str_replace($reg[0][$key], $match, $data['content']); |
470
|
|
|
} |
471
|
|
|
} |
472
|
|
|
$msgArr[] = nl2br($data['content']); |
473
|
|
|
} |
474
|
|
|
if (!empty($msgArr)) { |
475
|
|
|
$msg = implode('<hr />', $msgArr); |
476
|
|
|
} |
477
|
|
|
$item .= '<td class="typo3-adminPanel-table-cell-content">' . $this->fw($msg) . '</td>'; |
478
|
|
|
$out .= '<tr>' . $item . '</tr>'; |
479
|
|
|
$c++; |
480
|
|
|
} |
481
|
|
|
$out = '<div class="typo3-adminPanel-table-overflow"><table class="typo3-adminPanel-table typo3-adminPanel-table-debug">' . $out . '</table></div>'; |
482
|
|
|
return $out; |
483
|
|
|
} |
484
|
|
|
|
485
|
|
|
/** |
486
|
|
|
* Recursively generates the content to display |
487
|
|
|
* |
488
|
|
|
* @param array $arr Array which is modified with content. Reference |
489
|
|
|
* @param string $content Current content string for the level |
490
|
|
|
* @param string $depthData Prefixed icons for new PM icons |
491
|
|
|
* @param string $vKey Seems to be the previous tsStackLog key |
492
|
|
|
* @return string Returns the $content string generated/modified. Also the $arr array is modified! |
493
|
|
|
*/ |
494
|
|
|
protected function fixContent(&$arr, $content, $depthData = '', $vKey = '') |
495
|
|
|
{ |
496
|
|
|
$entriesCount = 0; |
497
|
|
|
$c = 0; |
498
|
|
|
// First, find number of entries |
499
|
|
|
foreach ($arr as $k => $v) { |
500
|
|
|
//do not count subentries (the one ending with dot, eg. '9.' |
501
|
|
|
if (MathUtility::canBeInterpretedAsInteger($k)) { |
502
|
|
|
$entriesCount++; |
503
|
|
|
} |
504
|
|
|
} |
505
|
|
|
// Traverse through entries |
506
|
|
|
$subtime = 0; |
507
|
|
|
foreach ($arr as $k => $v) { |
508
|
|
|
if (MathUtility::canBeInterpretedAsInteger($k)) { |
509
|
|
|
$c++; |
510
|
|
|
$hasChildren = isset($arr[$k . '.']); |
511
|
|
|
$lastEntry = $entriesCount === $c; |
512
|
|
|
|
513
|
|
|
$PM = '<span class="treeline-icon treeline-icon-join' . ($lastEntry ? 'bottom' : '') . '"></span>'; |
514
|
|
|
|
515
|
|
|
$this->tsStackLog[$v]['icons'] = $depthData . $PM; |
516
|
|
|
if ($this->tsStackLog[$v]['content'] !== '') { |
517
|
|
|
$content = str_replace($this->tsStackLog[$v]['content'], $v, $content); |
518
|
|
|
} |
519
|
|
|
if ($hasChildren) { |
520
|
|
|
$lineClass = $lastEntry ? 'treeline-icon-clear' : 'treeline-icon-line'; |
521
|
|
|
$this->tsStackLog[$v]['content'] = $this->fixContent($arr[$k . '.'], $this->tsStackLog[$v]['content'], $depthData . '<span class="treeline-icon ' . $lineClass . '"></span>', $v); |
522
|
|
|
} else { |
523
|
|
|
$this->tsStackLog[$v]['content'] = $this->fixCLen($this->tsStackLog[$v]['content'], $this->tsStackLog[$v]['value']); |
524
|
|
|
$this->tsStackLog[$v]['subtime'] = ''; |
525
|
|
|
$this->tsStackLog[$v]['owntime'] = $this->tsStackLog[$v]['deltatime']; |
526
|
|
|
} |
527
|
|
|
$subtime += $this->tsStackLog[$v]['deltatime']; |
528
|
|
|
} |
529
|
|
|
} |
530
|
|
|
// Set content with special chars |
531
|
|
|
if (isset($this->tsStackLog[$vKey])) { |
532
|
|
|
$this->tsStackLog[$vKey]['subtime'] = $subtime; |
533
|
|
|
$this->tsStackLog[$vKey]['owntime'] = $this->tsStackLog[$vKey]['deltatime'] - $subtime; |
534
|
|
|
} |
535
|
|
|
$content = $this->fixCLen($content, $this->tsStackLog[$vKey]['value']); |
536
|
|
|
// Traverse array again, this time substitute the unique hash with the red key |
537
|
|
|
foreach ($arr as $k => $v) { |
538
|
|
|
if (MathUtility::canBeInterpretedAsInteger($k)) { |
539
|
|
|
if ($this->tsStackLog[$v]['content'] !== '') { |
540
|
|
|
$content = str_replace($v, '<strong style="color:red;">[' . $this->tsStackLog[$v]['key'] . ']</strong>', $content); |
541
|
|
|
} |
542
|
|
|
} |
543
|
|
|
} |
544
|
|
|
// Return the content |
545
|
|
|
return $content; |
546
|
|
|
} |
547
|
|
|
|
548
|
|
|
/** |
549
|
|
|
* Wraps the input content string in green colored span-tags IF the length of the input string exceeds $this->printConf['contentLength'] (or $this->printConf['contentLength_FILE'] if $v == "FILE" |
550
|
|
|
* |
551
|
|
|
* @param string $c The content string |
552
|
|
|
* @param string $v Command: If "FILE" then $this->printConf['contentLength_FILE'] is used for content length comparison, otherwise $this->printConf['contentLength'] |
553
|
|
|
* @return string |
554
|
|
|
*/ |
555
|
|
|
protected function fixCLen($c, $v) |
556
|
|
|
{ |
557
|
|
|
$len = $v === 'FILE' ? $this->printConf['contentLength_FILE'] : $this->printConf['contentLength']; |
558
|
|
|
if (strlen($c) > $len) { |
559
|
|
|
$c = '<span style="color:green;">' . htmlspecialchars(GeneralUtility::fixed_lgd_cs($c, $len)) . '</span>'; |
560
|
|
|
} else { |
561
|
|
|
$c = htmlspecialchars($c); |
562
|
|
|
} |
563
|
|
|
return $c; |
564
|
|
|
} |
565
|
|
|
|
566
|
|
|
/** |
567
|
|
|
* Wraps input string in a <span> tag |
568
|
|
|
* |
569
|
|
|
* @param string $str The string to be wrapped |
570
|
|
|
* @return string |
571
|
|
|
*/ |
572
|
|
|
protected function fw($str) |
573
|
|
|
{ |
574
|
|
|
return '<span>' . $str . '</span>'; |
575
|
|
|
} |
576
|
|
|
|
577
|
|
|
/** |
578
|
|
|
* Helper function for internal data manipulation |
579
|
|
|
* |
580
|
|
|
* @param array $arr Array (passed by reference) and modified |
581
|
|
|
* @param int $pointer Pointer value |
582
|
|
|
* @param string $uniqueId Unique ID string |
583
|
|
|
* @internal |
584
|
|
|
* @see printTSlog() |
585
|
|
|
*/ |
586
|
|
|
protected function createHierarchyArray(&$arr, $pointer, $uniqueId) |
587
|
|
|
{ |
588
|
|
|
if (!is_array($arr)) { |
|
|
|
|
589
|
|
|
$arr = []; |
590
|
|
|
} |
591
|
|
|
if ($pointer > 0) { |
592
|
|
|
end($arr); |
593
|
|
|
$k = key($arr); |
594
|
|
|
$this->createHierarchyArray($arr[(int)$k . '.'], $pointer - 1, $uniqueId); |
595
|
|
|
} else { |
596
|
|
|
$arr[] = $uniqueId; |
597
|
|
|
} |
598
|
|
|
} |
599
|
|
|
} |
600
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.