TimeTracker::printTSlog()   F
last analyzed

Complexity

Conditions 31
Paths > 20000

Size

Total Lines 120
Code Lines 89

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 31
eloc 89
nc 55321
nop 0
dl 0
loc 120
rs 0
c 0
b 0
f 0

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
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);
0 ignored issues
show
Documentation Bug introduced by
It seems like microtime(true) can also be of type string. However, the property $finishtime is declared as type double. Maybe add an additional type check?

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 the id property of an instance of the Account 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.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
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));
0 ignored issues
show
Bug introduced by
It seems like microtime(true) can also be of type string; however, parameter $starttime of TYPO3\CMS\Core\TimeTracker\TimeTracker::start() does only seem to accept double|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

345
            $this->start(/** @scrutinizer ignore-type */ microtime(true));
Loading history...
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)) {
0 ignored issues
show
introduced by
The condition is_array($arr) is always true.
Loading history...
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