Completed
Push — master ( 464a06...dc9e8b )
by
unknown
21:20 queued 06:29
created

TimeTracker::fixContent()   D

Complexity

Conditions 13
Paths 336

Size

Total Lines 52
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 32
c 1
b 0
f 0
nc 336
nop 4
dl 0
loc 52
rs 4.0833

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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