Test Failed
Branch master (7b1793)
by Tymoteusz
15:35
created

TemplateService::setCacheEntry()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 3
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
namespace TYPO3\CMS\Core\TypoScript;
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
17
use TYPO3\CMS\Backend\Utility\BackendUtility;
18
use TYPO3\CMS\Core\Cache\CacheManager;
19
use TYPO3\CMS\Core\Database\Connection;
20
use TYPO3\CMS\Core\Database\ConnectionPool;
21
use TYPO3\CMS\Core\Database\Query\Restriction\AbstractRestrictionContainer;
22
use TYPO3\CMS\Core\Database\Query\Restriction\DefaultRestrictionContainer;
23
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
24
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
25
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
26
use TYPO3\CMS\Core\Utility\ArrayUtility;
27
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
28
use TYPO3\CMS\Core\Utility\GeneralUtility;
29
use TYPO3\CMS\Core\Utility\MathUtility;
30
use TYPO3\CMS\Core\Utility\PathUtility;
31
use TYPO3\CMS\Frontend\Configuration\TypoScript\ConditionMatching\ConditionMatcher;
32
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
33
use TYPO3\CMS\Frontend\Page\PageRepository;
34
35
/**
36
 * Template object that is responsible for generating the TypoScript template based on template records.
37
 * @see \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser
38
 * @see \TYPO3\CMS\Core\Configuration\TypoScript\ConditionMatching\AbstractConditionMatcher
39
 */
40
class TemplateService
41
{
42
    /**
43
     * option to enable logging, time-tracking (FE-only)
44
     * usually, this is only done when
45
     *  - in FE a BE_USER is logged-in
46
     *  - in BE when the BE_USER needs information about the template (TypoScript module)
47
     * @var bool
48
     */
49
    protected $verbose = false;
50
51
    /**
52
     * If set, the global tt-timeobject is used to log the performance.
53
     *
54
     * @var bool
55
     */
56
    public $tt_track = true;
57
58
    /**
59
     * If set, the template is always rendered. Used from Admin Panel.
60
     *
61
     * @var bool
62
     */
63
    public $forceTemplateParsing = false;
64
65
    /**
66
     * This array is passed on to matchObj by generateConfig().
67
     * If it holds elements, they are used for matching instead. See comment at the match-class.
68
     * Used for backend modules only. Never frontend!
69
     *
70
     * @var array
71
     */
72
    public $matchAlternative = [];
73
74
    /**
75
     * If set, the match-class matches everything! Used for backend modules only. Never frontend!
76
     *
77
     * @var bool
78
     */
79
    public $matchAll = false;
80
81
    /**
82
     * Externally set breakpoints (used by Backend Modules)
83
     *
84
     * @var int
85
     */
86
    public $ext_constants_BRP = 0;
87
88
    /**
89
     * @var int
90
     */
91
    public $ext_config_BRP = 0;
92
93
    /**
94
     * @var bool
95
     */
96
    public $ext_regLinenumbers = false;
97
98
    /**
99
     * @var bool
100
     */
101
    public $ext_regComments = false;
102
103
    /**
104
     * This MUST be initialized by the init() function
105
     *
106
     * @var string
107
     */
108
    public $whereClause = '';
109
110
    /**
111
     * @var bool
112
     */
113
    public $debug = false;
114
115
    /**
116
     * This is the only paths (relative!!) that are allowed for resources in TypoScript.
117
     * Should all be appended with '/'. You can extend these by the global array TYPO3_CONF_VARS. See init() function.
118
     *
119
     * @var array
120
     */
121
    public $allowedPaths = [];
122
123
    /**
124
     * See init(); Set if preview of some kind is enabled.
125
     *
126
     * @var int
127
     */
128
    public $simulationHiddenOrTime = 0;
129
130
    /**
131
     * Set, if the TypoScript template structure is loaded and OK, see ->start()
132
     *
133
     * @var bool
134
     */
135
    public $loaded = false;
136
137
    /**
138
     * @var array Contains TypoScript setup part after parsing
139
     */
140
    public $setup = [];
141
142
    /**
143
     * @var array
144
     */
145
    public $flatSetup = [];
146
147
    /**
148
     * For fetching TypoScript code from template hierarchy before parsing it.
149
     * Each array contains code field values from template records/files:
150
     * Setup field
151
     *
152
     * @var array
153
     */
154
    public $config = [];
155
156
    /**
157
     * Constant field
158
     *
159
     * @var array
160
     */
161
    public $constants = [];
162
163
    /**
164
     * Holds the include paths of the templates (empty if from database)
165
     *
166
     * @var array
167
     */
168
    protected $templateIncludePaths = [];
169
170
    /**
171
     * For Template Analyser in backend
172
     *
173
     * @var array
174
     */
175
    public $hierarchyInfo = [];
176
177
    /**
178
     * For Template Analyser in backend (setup content only)
179
     *
180
     * @var array
181
     */
182
    public $hierarchyInfoToRoot = [];
183
184
    /**
185
     * Next-level flag (see runThroughTemplates())
186
     *
187
     * @var int
188
     */
189
    public $nextLevel = 0;
190
191
    /**
192
     * The Page UID of the root page
193
     *
194
     * @var int
195
     */
196
    public $rootId;
197
198
    /**
199
     * The rootline from current page to the root page
200
     *
201
     * @var array
202
     */
203
    public $rootLine;
204
205
    /**
206
     * Rootline all the way to the root. Set but runThroughTemplates
207
     *
208
     * @var array
209
     */
210
    public $absoluteRootLine;
211
212
    /**
213
     * A pointer to the last entry in the rootline where a template was found.
214
     *
215
     * @var int
216
     */
217
    public $outermostRootlineIndexWithTemplate = 0;
218
219
    /**
220
     * Array of arrays with title/uid of templates in hierarchy
221
     *
222
     * @var array
223
     */
224
    public $rowSum;
225
226
    /**
227
     * The current site title field.
228
     *
229
     * @var string
230
     */
231
    public $sitetitle = '';
232
233
    /**
234
     * Tracking all conditions found during parsing of TypoScript. Used for the "all" key in currentPageData
235
     *
236
     * @var string
237
     */
238
    public $sections;
239
240
    /**
241
     * Tracking all matching conditions found
242
     *
243
     * @var array
244
     */
245
    public $sectionsMatch;
246
247
    /**
248
     * Used by Backend only (Typoscript Template Analyzer)
249
     */
250
    public $clearList_const = [];
251
252
    /**
253
     * Used by Backend only (Typoscript Template Analyzer)
254
     *
255
     * @var array
256
     */
257
    public $clearList_setup = [];
258
259
    /**
260
     * @var array
261
     */
262
    public $parserErrors = [];
263
264
    /**
265
     * @var array
266
     */
267
    public $setup_constants = [];
268
269
    /**
270
     * Used by getFileName for caching of references to file resources
271
     *
272
     * @var array
273
     */
274
    public $fileCache = [];
275
276
    /**
277
     * Keys are frame names and values are type-values, which must be used to refer correctly to the content of the frames.
278
     *
279
     * @var array
280
     */
281
    public $frames = [];
282
283
    /**
284
     * Contains mapping of Page id numbers to MP variables.
285
     *
286
     * @var string
287
     */
288
    public $MPmap = '';
289
290
    /**
291
     * Indicator that extension statics are processed.
292
     *
293
     * These files are considered if either a root template
294
     * has been processed or the $processExtensionStatics
295
     * property has been set to TRUE.
296
     *
297
     * @var bool
298
     */
299
    protected $extensionStaticsProcessed = false;
300
301
    /**
302
     * Trigger value, to ensure that extension statics are processed.
303
     *
304
     * @var bool
305
     */
306
    protected $processExtensionStatics = false;
307
308
    /**
309
     * Set to TRUE after the default TypoScript was added during parsing.
310
     * This prevents double inclusion of the same TypoScript code.
311
     *
312
     * @see addDefaultTypoScript()
313
     * @var bool
314
     */
315
    protected $isDefaultTypoScriptAdded = false;
316
317
    /**
318
     * Set to TRUE after $this->config and $this->constants have processed all <INCLUDE_TYPOSCRIPT:> instructions.
319
     *
320
     * This prevents double processing of INCLUDES.
321
     *
322
     * @see processIncludes()
323
     * @var bool
324
     */
325
    protected $processIncludesHasBeenRun = false;
326
327
    /**
328
     * Contains the restrictions about deleted, and some frontend related topics
329
     * @var AbstractRestrictionContainer
330
     */
331
    protected $queryBuilderRestrictions;
332
333
    /**
334
     * @return bool
335
     */
336
    public function getProcessExtensionStatics()
337
    {
338
        return $this->processExtensionStatics;
339
    }
340
341
    /**
342
     * @param bool $processExtensionStatics
343
     */
344
    public function setProcessExtensionStatics($processExtensionStatics)
345
    {
346
        $this->processExtensionStatics = (bool)$processExtensionStatics;
347
    }
348
349
    /**
350
     * sets the verbose parameter
351
     * @param bool $verbose
352
     */
353
    public function setVerbose($verbose)
354
    {
355
        $this->verbose = (bool)$verbose;
356
    }
357
358
    /**
359
     * Initialize
360
     * MUST be called directly after creating a new template-object
361
     *
362
     * @see \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController::initTemplate()
363
     */
364
    public function init()
365
    {
366
        $this->initializeDatabaseQueryRestrictions();
367
368
        if ($this->getTypoScriptFrontendController()->showHiddenRecords || $GLOBALS['SIM_ACCESS_TIME'] !== $GLOBALS['ACCESS_TIME']) {
369
            // Set the simulation flag, if simulation is detected!
370
            $this->simulationHiddenOrTime = 1;
371
        }
372
373
        // Sets the paths from where TypoScript resources are allowed to be used:
374
        $this->allowedPaths = [
375
            $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'],
376
            // fileadmin/ path
377
            'uploads/',
378
            'typo3temp/',
379
            TYPO3_mainDir . 'ext/',
380
            TYPO3_mainDir . 'sysext/',
381
            'typo3conf/ext/'
382
        ];
383
        if ($GLOBALS['TYPO3_CONF_VARS']['FE']['addAllowedPaths']) {
384
            $pathArr = GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['FE']['addAllowedPaths'], true);
385
            foreach ($pathArr as $p) {
386
                // Once checked for path, but as this may run from typo3/mod/web/ts/ dir, that'll not work!! So the paths ar uncritically included here.
387
                $this->allowedPaths[] = $p;
388
            }
389
        }
390
    }
391
392
    /**
393
     * $this->whereclause is kept for backwards compatibility
394
     */
395
    protected function initializeDatabaseQueryRestrictions()
396
    {
397
        // $this->whereClause is used only to select templates from sys_template.
398
        // $GLOBALS['SIM_ACCESS_TIME'] is used so that we're able to simulate a later time as a test...
399
        $this->whereClause = 'AND deleted=0 ';
400
        if (!$this->getTypoScriptFrontendController()->showHiddenRecords) {
401
            $this->whereClause .= 'AND hidden=0 ';
402
        }
403
        $this->whereClause .= 'AND (starttime<=' . $GLOBALS['SIM_ACCESS_TIME'] . ') AND (endtime=0 OR endtime>' . $GLOBALS['SIM_ACCESS_TIME'] . ')';
404
405
        // set up the query builder restrictions
406
        $this->queryBuilderRestrictions = GeneralUtility::makeInstance(DefaultRestrictionContainer::class);
407
408
        if ($this->getTypoScriptFrontendController()->showHiddenRecords) {
409
            $this->queryBuilderRestrictions
410
                ->removeByType(HiddenRestriction::class);
411
        }
412
    }
413
414
    /**
415
     * Fetches the "currentPageData" array from cache
416
     *
417
     * NOTE about currentPageData:
418
     * It holds information about the TypoScript conditions along with the list
419
     * of template uid's which is used on the page. In the getFromCache() function
420
     * in TSFE, currentPageData is used to evaluate if there is a template and
421
     * if the matching conditions are alright. Unfortunately this does not take
422
     * into account if the templates in the rowSum of currentPageData has
423
     * changed composition, eg. due to hidden fields or start/end time. So if a
424
     * template is hidden or times out, it'll not be discovered unless the page
425
     * is regenerated - at least the this->start function must be called,
426
     * because this will make a new portion of data in currentPageData string.
427
     *
428
     * @return array Returns the unmatched array $currentPageData if found cached in "cache_pagesection". Otherwise FALSE is returned which means that the array must be generated and stored in the cache
429
     */
430
    public function getCurrentPageData()
431
    {
432
        return GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_pagesection')->get((int)$this->getTypoScriptFrontendController()->id . '_' . GeneralUtility::md5int($this->getTypoScriptFrontendController()->MP));
433
    }
434
435
    /**
436
     * Fetches data about which TypoScript-matches there are at this page. Then it performs a matchingtest.
437
     *
438
     * @param array $cc An array with three keys, "all", "rowSum" and "rootLine" - all coming from the "currentPageData" array
439
     * @return array The input array but with a new key added, "match" which contains the items from the "all" key which when passed to tslib_matchCondition returned TRUE.
440
     */
441
    public function matching($cc)
442
    {
443
        if (is_array($cc['all'])) {
444
            /** @var $matchObj ConditionMatcher */
445
            $matchObj = GeneralUtility::makeInstance(ConditionMatcher::class);
446
            $matchObj->setRootline((array)$cc['rootLine']);
447
            $sectionsMatch = [];
448
            foreach ($cc['all'] as $key => $pre) {
449
                if ($matchObj->match($pre)) {
450
                    $sectionsMatch[$key] = $pre;
451
                }
452
            }
453
            $cc['match'] = $sectionsMatch;
454
        }
455
        return $cc;
456
    }
457
458
    /**
459
     * This is all about fetching the right TypoScript template structure. If it's not cached then it must be generated and cached!
460
     * The method traverses the rootline structure from out to in, fetches the hierarchy of template records and based on this either finds the cached TypoScript template structure or parses the template and caches it for next time.
461
     * Sets $this->setup to the parsed TypoScript template array
462
     *
463
     * @param array $theRootLine The rootline of the current page (going ALL the way to tree root)
464
     * @see \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController::getConfigArray()
465
     */
466
    public function start($theRootLine)
467
    {
468
        if (is_array($theRootLine)) {
469
            $constantsData = [];
470
            $setupData = [];
471
            $cacheIdentifier = '';
472
            // Flag that indicates that the existing data in cache_pagesection
473
            // could be used (this is the case if $TSFE->all is set, and the
474
            // rowSum still matches). Based on this we decide if cache_pagesection
475
            // needs to be updated...
476
            $isCached = false;
477
            $this->runThroughTemplates($theRootLine);
478
            if ($this->getTypoScriptFrontendController()->all) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getTypoScriptFrontendController()->all of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
479
                $cc = $this->getTypoScriptFrontendController()->all;
480
                // The two rowSums must NOT be different from each other - which they will be if start/endtime or hidden has changed!
481
                if (serialize($this->rowSum) !== serialize($cc['rowSum'])) {
482
                    unset($cc);
483
                } else {
484
                    // If $TSFE->all contains valid data, we don't need to update cache_pagesection (because this data was fetched from there already)
485
                    if (serialize($this->rootLine) === serialize($cc['rootLine'])) {
486
                        $isCached = true;
487
                    }
488
                    // When the data is serialized below (ROWSUM hash), it must not contain the rootline by concept. So this must be removed (and added again later)...
489
                    unset($cc['rootLine']);
490
                }
491
            }
492
            // This is about getting the hash string which is used to fetch the cached TypoScript template.
493
            // If there was some cached currentPageData ($cc) then that's good (it gives us the hash).
494
            if (isset($cc) && is_array($cc)) {
495
                // If currentPageData was actually there, we match the result (if this wasn't done already in $TSFE->getFromCache()...)
496
                if (!$cc['match']) {
497
                    // @todo check if this can ever be the case - otherwise remove
498
                    $cc = $this->matching($cc);
499
                    ksort($cc);
500
                }
501
                $cacheIdentifier = md5(serialize($cc));
502
            } else {
503
                // If currentPageData was not there, we first find $rowSum (freshly generated). After that we try to see, if it is stored with a list of all conditions. If so we match the result.
504
                $rowSumHash = md5('ROWSUM:' . serialize($this->rowSum));
505
                $result = $this->getCacheEntry($rowSumHash);
506
                if (is_array($result)) {
507
                    $cc = [];
508
                    $cc['all'] = $result;
509
                    $cc['rowSum'] = $this->rowSum;
510
                    $cc = $this->matching($cc);
511
                    ksort($cc);
512
                    $cacheIdentifier = md5(serialize($cc));
513
                }
514
            }
515
            if ($cacheIdentifier) {
516
                // Get TypoScript setup array
517
                $cachedData = $this->getCacheEntry($cacheIdentifier);
518
                if (is_array($cachedData)) {
519
                    $constantsData = $cachedData['constants'];
520
                    $setupData = $cachedData['setup'];
521
                }
522
            }
523
            if (!empty($setupData) && !$this->forceTemplateParsing) {
524
                // TypoScript constants + setup are found in the cache
525
                $this->setup_constants = $constantsData;
526
                $this->setup = $setupData;
527
                if ($this->tt_track) {
528
                    $this->getTimeTracker()->setTSlogMessage('Using cached TS template data');
529
                }
530
            } else {
531
                if ($this->tt_track) {
532
                    $this->getTimeTracker()->setTSlogMessage('Not using any cached TS data');
533
                }
534
535
                // Make configuration
536
                $this->generateConfig();
537
                // This stores the template hash thing
538
                $cc = [];
539
                // All sections in the template at this point is found
540
                $cc['all'] = $this->sections;
541
                // The line of templates is collected
542
                $cc['rowSum'] = $this->rowSum;
543
                $cc = $this->matching($cc);
544
                ksort($cc);
545
                $cacheIdentifier = md5(serialize($cc));
546
                // This stores the data.
547
                $this->setCacheEntry($cacheIdentifier, ['constants' => $this->setup_constants, 'setup' => $this->setup], 'TS_TEMPLATE');
548
                if ($this->tt_track) {
549
                    $this->getTimeTracker()->setTSlogMessage('TS template size, serialized: ' . strlen(serialize($this->setup)) . ' bytes');
550
                }
551
                $rowSumHash = md5('ROWSUM:' . serialize($this->rowSum));
552
                $this->setCacheEntry($rowSumHash, $cc['all'], 'TMPL_CONDITIONS_ALL');
553
            }
554
            // Add rootLine
555
            $cc['rootLine'] = $this->rootLine;
556
            ksort($cc);
557
            // Make global and save
558
            $this->getTypoScriptFrontendController()->all = $cc;
559
            // Matching must be executed for every request, so this must never be part of the pagesection cache!
560
            unset($cc['match']);
561
            if (!$isCached && !$this->simulationHiddenOrTime && !$this->getTypoScriptFrontendController()->no_cache) {
562
                // Only save the data if we're not simulating by hidden/starttime/endtime
563
                $mpvarHash = GeneralUtility::md5int($this->getTypoScriptFrontendController()->MP);
564
                /** @var $pageSectionCache \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface */
565
                $pageSectionCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_pagesection');
566
                $pageSectionCache->set((int)$this->getTypoScriptFrontendController()->id . '_' . $mpvarHash, $cc, [
567
                    'pageId_' . (int)$this->getTypoScriptFrontendController()->id,
568
                    'mpvarHash_' . $mpvarHash
569
                ]);
570
            }
571
            // If everything OK.
572
            if ($this->rootId && $this->rootLine && $this->setup) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->rootLine of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
573
                $this->loaded = true;
574
            }
575
        }
576
    }
577
578
    /*******************************************************************
579
     *
580
     * Fetching TypoScript code text for the Template Hierarchy
581
     *
582
     *******************************************************************/
583
    /**
584
     * Traverses the rootLine from the root and out. For each page it checks if there is a template record. If there is a template record, $this->processTemplate() is called.
585
     * Resets and affects internal variables like $this->constants, $this->config and $this->rowSum
586
     * Also creates $this->rootLine which is a root line stopping at the root template (contrary to $this->getTypoScriptFrontendController()->rootLine which goes all the way to the root of the tree
587
     *
588
     * @param array $theRootLine The rootline of the current page (going ALL the way to tree root)
589
     * @param int $start_template_uid Set specific template record UID to select; this is only for debugging/development/analysis use in backend modules like "Web > Template". For parsing TypoScript templates in the frontend it should be 0 (zero)
590
     * @see start()
591
     */
592
    public function runThroughTemplates($theRootLine, $start_template_uid = 0)
593
    {
594
        $this->constants = [];
595
        $this->config = [];
596
        $this->rowSum = [];
597
        $this->hierarchyInfoToRoot = [];
598
        $this->absoluteRootLine = $theRootLine;
599
        $this->isDefaultTypoScriptAdded = false;
600
601
        reset($this->absoluteRootLine);
602
        $c = count($this->absoluteRootLine);
603
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_template');
604
        for ($a = 0; $a < $c; $a++) {
605
            // If some template loaded before has set a template-id for the next level, then load this template first!
606
            if ($this->nextLevel) {
607
                $queryBuilder->setRestrictions($this->queryBuilderRestrictions);
608
                $queryResult = $queryBuilder
609
                    ->select('*')
610
                    ->from('sys_template')
611
                    ->where(
612
                        $queryBuilder->expr()->eq(
613
                            'uid',
614
                            $queryBuilder->createNamedParameter($this->nextLevel, \PDO::PARAM_INT)
615
                        )
616
                    )
617
                    ->execute();
618
                $this->nextLevel = 0;
619 View Code Duplication
                if ($row = $queryResult->fetch()) {
620
                    $this->versionOL($row);
621
                    if (is_array($row)) {
622
                        $this->processTemplate($row, 'sys_' . $row['uid'], $this->absoluteRootLine[$a]['uid'], 'sys_' . $row['uid']);
623
                        $this->outermostRootlineIndexWithTemplate = $a;
624
                    }
625
                }
626
            }
627
628
            $where = [
629
                $queryBuilder->expr()->eq(
630
                    'pid',
631
                    $queryBuilder->createNamedParameter($this->absoluteRootLine[$a]['uid'], \PDO::PARAM_INT)
632
                )
633
            ];
634
            // If first loop AND there is set an alternative template uid, use that
635
            if ($a === $c - 1 && $start_template_uid) {
636
                $where[] = $queryBuilder->expr()->eq(
637
                    'uid',
638
                    $queryBuilder->createNamedParameter($start_template_uid, \PDO::PARAM_INT)
639
                );
640
            }
641
            $queryBuilder->setRestrictions($this->queryBuilderRestrictions);
642
            $queryResult = $queryBuilder
643
                ->select('*')
644
                ->from('sys_template')
645
                ->where(...$where)
646
                ->orderBy('root', 'DESC')
647
                ->addOrderBy('sorting')
648
                ->setMaxResults(1)
649
                ->execute();
650 View Code Duplication
            if ($row = $queryResult->fetch()) {
651
                $this->versionOL($row);
652
                if (is_array($row)) {
653
                    $this->processTemplate($row, 'sys_' . $row['uid'], $this->absoluteRootLine[$a]['uid'], 'sys_' . $row['uid']);
654
                    $this->outermostRootlineIndexWithTemplate = $a;
655
                }
656
            }
657
            $this->rootLine[] = $this->absoluteRootLine[$a];
658
        }
659
660
        // Hook into the default TypoScript to add custom typoscript logic
661
        $hookParameters = [
662
            'extensionStaticsProcessed' => &$this->extensionStaticsProcessed,
663
            'isDefaultTypoScriptAdded'  => &$this->isDefaultTypoScriptAdded,
664
            'absoluteRootLine' => &$this->absoluteRootLine,
665
            'rootLine'         => &$this->rootLine,
666
            'startTemplateUid' => $start_template_uid,
667
        ];
668
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['Core/TypoScript/TemplateService']['runThroughTemplatesPostProcessing'] ?? [] as $listener) {
669
            GeneralUtility::callUserFunction($listener, $hookParameters, $this);
670
        }
671
672
        // Process extension static files if not done yet, but explicitly requested
673
        if (!$this->extensionStaticsProcessed && $this->processExtensionStatics) {
674
            $this->addExtensionStatics('sys_0', 'sys_0', 0, []);
675
        }
676
677
        // Add the global default TypoScript from the TYPO3_CONF_VARS
678
        $this->addDefaultTypoScript();
679
680
        $this->processIncludes();
681
    }
682
683
    /**
684
     * Checks if the template ($row) has some included templates and after including them it fills the arrays with the setup
685
     * Builds up $this->rowSum
686
     *
687
     * @param array $row A full TypoScript template record (sys_template/forged "dummy" record made from static template file)
688
     * @param string $idList A list of already processed template ids including the current; The list is on the form "[prefix]_[uid]" where [prefix] is "sys" for "sys_template" records, records and "ext_" for static include files (from extensions). The list is used to check that the recursive inclusion of templates does not go into circles: Simply it is used to NOT include a template record/file which has already BEEN included somewhere in the recursion.
689
     * @param int $pid The PID of the input template record
690
     * @param string $templateID The id of the current template. Same syntax as $idList ids, eg. "sys_123
691
     * @param string $templateParent Parent template id (during recursive call); Same syntax as $idList ids, eg. "sys_123
692
     * @param string $includePath Specifies the path from which the template was included (used with static_includes)
693
     * @see runThroughTemplates()
694
     */
695
    public function processTemplate($row, $idList, $pid, $templateID = '', $templateParent = '', $includePath = '')
696
    {
697
        // Adding basic template record information to rowSum array
698
        $this->rowSum[] = [$row['uid'], $row['title'], $row['tstamp']];
699
        // Processing "Clear"-flags
700
        $clConst = 0;
701
        $clConf = 0;
702
        if ($row['clear']) {
703
            $clConst = $row['clear'] & 1;
704
            $clConf = $row['clear'] & 2;
705
            if ($clConst) {
706
                // Keep amount of items to stay in sync with $this->templateIncludePaths so processIncludes() does not break
707
                foreach ($this->constants as &$constantConfiguration) {
708
                    $constantConfiguration = '';
709
                }
710
                unset($constantConfiguration);
711
                $this->clearList_const = [];
712
            }
713
            if ($clConf) {
714
                // Keep amount of items to stay in sync with $this->templateIncludePaths so processIncludes() does not break
715
                foreach ($this->config as &$configConfiguration) {
716
                    $configConfiguration = '';
717
                }
718
                unset($configConfiguration);
719
                $this->hierarchyInfoToRoot = [];
720
                $this->clearList_setup = [];
721
            }
722
        }
723
        // Include files (from extensions) (#1/2)
724
        // NORMAL inclusion, The EXACT same code is found below the basedOn inclusion!!!
725
        if (!$row['includeStaticAfterBasedOn']) {
726
            $this->includeStaticTypoScriptSources($idList, $templateID, $pid, $row);
727
        }
728
        // Include "Based On" sys_templates:
729
        // 'basedOn' is a list of templates to include
730
        if (trim($row['basedOn'])) {
731
            // Normal Operation, which is to include the "based-on" sys_templates,
732
            // if they are not already included, and maintaining the sorting of the templates
733
            $basedOnIds = GeneralUtility::intExplode(',', $row['basedOn'], true);
734
            // skip template if it's already included
735
            foreach ($basedOnIds as $key => $basedOnId) {
736
                if (GeneralUtility::inList($idList, 'sys_' . $basedOnId)) {
737
                    unset($basedOnIds[$key]);
738
                }
739
            }
740
            if (!empty($basedOnIds)) {
741
                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_template');
742
                $queryBuilder->setRestrictions($this->queryBuilderRestrictions);
743
                $queryResult = $queryBuilder
744
                    ->select('*')
745
                    ->from('sys_template')
746
                    ->where(
747
                        $queryBuilder->expr()->in(
748
                            'uid',
749
                            $queryBuilder->createNamedParameter($basedOnIds, Connection::PARAM_INT_ARRAY)
750
                        )
751
                    )
752
                    ->execute();
753
                // make it an associative array with the UID as key
754
                $subTemplates = [];
755
                while ($rowItem = $queryResult->fetch()) {
756
                    $subTemplates[(int)$rowItem['uid']] = $rowItem;
757
                }
758
                // Traversing list again to ensure the sorting of the templates
759
                foreach ($basedOnIds as $id) {
760
                    if (is_array($subTemplates[$id])) {
761
                        $this->versionOL($subTemplates[$id]);
762
                        $this->processTemplate($subTemplates[$id], $idList . ',sys_' . $id, $pid, 'sys_' . $id, $templateID);
763
                    }
764
                }
765
            }
766
        }
767
        // Include files (from extensions) (#2/2)
768
        if ($row['includeStaticAfterBasedOn']) {
769
            $this->includeStaticTypoScriptSources($idList, $templateID, $pid, $row);
770
        }
771
        // Creating hierarchy information; Used by backend analysis tools
772
        $this->hierarchyInfo[] = ($this->hierarchyInfoToRoot[] = [
773
            'root' => trim($row['root']),
774
            'next' => $row['nextLevel'],
775
            'clConst' => $clConst,
776
            'clConf' => $clConf,
777
            'templateID' => $templateID,
778
            'templateParent' => $templateParent,
779
            'title' => $row['title'],
780
            'uid' => $row['uid'],
781
            'pid' => $row['pid'],
782
            'configLines' => substr_count($row['config'], LF) + 1
783
        ]);
784
        // Adding the content of the fields constants (Constants) and config (Setup)
785
        $this->constants[] = $row['constants'];
786
        $this->config[] = $row['config'];
787
        $this->templateIncludePaths[] = $includePath;
788
        // For backend analysis (Template Analyser) provide the order of added constants/config template IDs
789
        $this->clearList_const[] = $templateID;
790
        $this->clearList_setup[] = $templateID;
791
        if (trim($row['sitetitle'])) {
792
            $this->sitetitle = $row['sitetitle'];
793
        }
794
        // If the template record is a Rootlevel record, set the flag and clear the template rootLine (so it starts over from this point)
795
        if (trim($row['root'])) {
796
            $this->rootId = $pid;
797
            $this->rootLine = [];
798
        }
799
        // If a template is set to be active on the next level set this internal value to point to this UID. (See runThroughTemplates())
800
        if ($row['nextLevel']) {
801
            $this->nextLevel = $row['nextLevel'];
802
        } else {
803
            $this->nextLevel = 0;
804
        }
805
    }
806
807
    /**
808
     * This function can be used to update the data of the current rootLine
809
     * e.g. when a different language is used.
810
     *
811
     * This function must not be used if there are different pages in the
812
     * rootline as before!
813
     *
814
     * @param array $fullRootLine Array containing the FULL rootline (up to the TYPO3 root)
815
     * @throws \RuntimeException If the given $fullRootLine does not contain all pages that are in the current template rootline
816
     */
817
    public function updateRootlineData($fullRootLine)
818
    {
819
        if (!is_array($this->rootLine) || empty($this->rootLine)) {
820
            return;
821
        }
822
823
        $fullRootLineByUid = [];
824
        foreach ($fullRootLine as $rootLineData) {
825
            $fullRootLineByUid[$rootLineData['uid']] = $rootLineData;
826
        }
827
828
        foreach ($this->rootLine as $level => $dataArray) {
829
            $currentUid = $dataArray['uid'];
830
831
            if (!array_key_exists($currentUid, $fullRootLineByUid)) {
832
                throw new \RuntimeException(sprintf('The full rootLine does not contain data for the page with the uid %d that is contained in the template rootline.', $currentUid), 1370419654);
833
            }
834
835
            $this->rootLine[$level] = $fullRootLineByUid[$currentUid];
836
        }
837
    }
838
839
    /**
840
     * Includes static template files (from extensions) for the input template record row.
841
     *
842
     * @param string $idList A list of already processed template ids including the current; The list is on the form "[prefix]_[uid]" where [prefix] is "sys" for "sys_template" records and "ext_" for static include files (from extensions). The list is used to check that the recursive inclusion of templates does not go into circles: Simply it is used to NOT include a template record/file which has already BEEN included somewhere in the recursion.
843
     * @param string $templateID The id of the current template. Same syntax as $idList ids, eg. "sys_123
844
     * @param int $pid The PID of the input template record
845
     * @param array $row A full TypoScript template record
846
     * @see processTemplate()
847
     */
848
    public function includeStaticTypoScriptSources($idList, $templateID, $pid, $row)
849
    {
850
        // Call function for link rendering:
851
        $_params = [
852
            'idList' => &$idList,
853
            'templateId' => &$templateID,
854
            'pid' => &$pid,
855
            'row' => &$row
856
        ];
857
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tstemplate.php']['includeStaticTypoScriptSources'] ?? [] as $_funcRef) {
858
            GeneralUtility::callUserFunction($_funcRef, $_params, $this);
859
        }
860
        // If "Include before all static templates if root-flag is set" is set:
861
        if ($row['static_file_mode'] == 3 && strpos($templateID, 'sys_') === 0 && $row['root']) {
862
            $this->addExtensionStatics($idList, $templateID, $pid, $row);
863
        }
864
        // Static Template Files (Text files from extensions): include_static_file is a list of static files to include (from extensions)
865
        if (trim($row['include_static_file'])) {
866
            $include_static_fileArr = GeneralUtility::trimExplode(',', $row['include_static_file'], true);
867
            // Traversing list
868
            foreach ($include_static_fileArr as $ISF_file) {
869
                if (strpos($ISF_file, 'EXT:') === 0) {
870
                    list($ISF_extKey, $ISF_localPath) = explode('/', substr($ISF_file, 4), 2);
0 ignored issues
show
Bug introduced by
It seems like substr($ISF_file, 4) can also be of type false; however, parameter $string of explode() does only seem to accept string, 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

870
                    list($ISF_extKey, $ISF_localPath) = explode('/', /** @scrutinizer ignore-type */ substr($ISF_file, 4), 2);
Loading history...
871
                    if ((string)$ISF_extKey !== '' && ExtensionManagementUtility::isLoaded($ISF_extKey) && (string)$ISF_localPath !== '') {
872
                        $ISF_localPath = rtrim($ISF_localPath, '/') . '/';
873
                        $ISF_filePath = ExtensionManagementUtility::extPath($ISF_extKey) . $ISF_localPath;
874
                        if (@is_dir($ISF_filePath)) {
875
                            $mExtKey = str_replace('_', '', $ISF_extKey . '/' . $ISF_localPath);
876
                            $subrow = [
877
                                'constants' => $this->getTypoScriptSourceFileContent($ISF_filePath, 'constants'),
878
                                'config' => $this->getTypoScriptSourceFileContent($ISF_filePath, 'setup'),
879
                                'include_static' => @file_exists(($ISF_filePath . 'include_static.txt')) ? implode(',', array_unique(GeneralUtility::intExplode(',', file_get_contents($ISF_filePath . 'include_static.txt')))) : '',
0 ignored issues
show
Bug introduced by
It seems like file_get_contents($ISF_f.... 'include_static.txt') can also be of type false; however, parameter $string of TYPO3\CMS\Core\Utility\G...alUtility::intExplode() does only seem to accept string, 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

879
                                'include_static' => @file_exists(($ISF_filePath . 'include_static.txt')) ? implode(',', array_unique(GeneralUtility::intExplode(',', /** @scrutinizer ignore-type */ file_get_contents($ISF_filePath . 'include_static.txt')))) : '',
Loading history...
880
                                'include_static_file' => @file_exists(($ISF_filePath . 'include_static_file.txt')) ? implode(',', array_unique(explode(',', file_get_contents($ISF_filePath . 'include_static_file.txt')))) : '',
881
                                'title' => $ISF_file,
882
                                'uid' => $mExtKey
883
                            ];
884
                            $subrow = $this->prependStaticExtra($subrow);
885
                            $this->processTemplate($subrow, $idList . ',ext_' . $mExtKey, $pid, 'ext_' . $mExtKey, $templateID, $ISF_filePath);
886
                        }
887
                    }
888
                }
889
            }
890
        }
891
        // If "Default (include before if root flag is set)" is set OR
892
        // "Always include before this template record" AND root-flag are set
893
        if ($row['static_file_mode'] == 1 || $row['static_file_mode'] == 0 && substr($templateID, 0, 4) === 'sys_' && $row['root']) {
894
            $this->addExtensionStatics($idList, $templateID, $pid, $row);
895
        }
896
        // Include Static Template Records after all other TypoScript has been included.
897
        $_params = [
898
            'idList' => &$idList,
899
            'templateId' => &$templateID,
900
            'pid' => &$pid,
901
            'row' => &$row
902
        ];
903
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tstemplate.php']['includeStaticTypoScriptSourcesAtEnd'] ?? [] as $_funcRef) {
904
            GeneralUtility::callUserFunction($_funcRef, $_params, $this);
905
        }
906
    }
907
908
    /**
909
     * Retrieves the content of the first existing file by extension order.
910
     * Returns the empty string if no file is found.
911
     *
912
     * @param string $filePath The location of the file.
913
     * @param string $baseName The base file name. "constants" or "setup".
914
     * @return string
915
     */
916
    protected function getTypoScriptSourceFileContent($filePath, $baseName)
917
    {
918
        $extensions = ['.typoscript', '.ts', '.txt'];
919
        foreach ($extensions as $extension) {
920
            $fileName = $filePath . $baseName . $extension;
921
            if (@file_exists($fileName)) {
922
                return file_get_contents($fileName);
923
            }
924
        }
925
        return '';
926
    }
927
928
    /**
929
     * Adds the default TypoScript files for extensions if any.
930
     *
931
     * @param string $idList A list of already processed template ids including the current; The list is on the form "[prefix]_[uid]" where [prefix] is "sys" for "sys_template" records and "ext_" for static include files (from extensions). The list is used to check that the recursive inclusion of templates does not go into circles: Simply it is used to NOT include a template record/file which has already BEEN included somewhere in the recursion.
932
     * @param string $templateID The id of the current template. Same syntax as $idList ids, eg. "sys_123
933
     * @param int $pid The PID of the input template record
934
     * @param array $row A full TypoScript template record
935
     * @access private
936
     * @see includeStaticTypoScriptSources()
937
     */
938
    public function addExtensionStatics($idList, $templateID, $pid, $row)
0 ignored issues
show
Unused Code introduced by
The parameter $row is not used and could be removed. ( Ignorable by Annotation )

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

938
    public function addExtensionStatics($idList, $templateID, $pid, /** @scrutinizer ignore-unused */ $row)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
939
    {
940
        $this->extensionStaticsProcessed = true;
941
942
        // @todo Change to use new API
943
        foreach ($GLOBALS['TYPO3_LOADED_EXT'] as $extKey => $files) {
944
            if ((is_array($files) || $files instanceof \ArrayAccess)
945
                && (
946
                    !empty($files['ext_typoscript_constants.txt'])
947
                    || !empty($files['ext_typoscript_constants.typoscript'])
948
                    || !empty($files['ext_typoscript_setup.txt'])
949
                    || !empty($files['ext_typoscript_setup.typoscript'])
950
                )
951
            ) {
952
                $mExtKey = str_replace('_', '', $extKey);
953
                $constants = '';
954
                $config = '';
955
956
                if (!empty($files['ext_typoscript_constants.typoscript'])) {
957
                    $constants = @file_get_contents($files['ext_typoscript_constants.typoscript']);
958
                } elseif (!empty($files['ext_typoscript_constants.txt'])) {
959
                    $constants = @file_get_contents($files['ext_typoscript_constants.txt']);
960
                }
961
962
                if (!empty($files['ext_typoscript_setup.typoscript'])) {
963
                    $config = @file_get_contents($files['ext_typoscript_setup.typoscript']);
964
                } elseif (!empty($files['ext_typoscript_setup.txt'])) {
965
                    $config = @file_get_contents($files['ext_typoscript_setup.txt']);
966
                }
967
968
                $this->processTemplate(
969
                    $this->prependStaticExtra([
970
                        'constants' => $constants,
971
                        'config' => $config,
972
                        'title' => $extKey,
973
                        'uid' => $mExtKey
974
                    ]),
975
                    $idList . ',ext_' . $mExtKey,
976
                    $pid,
977
                    'ext_' . $mExtKey,
978
                    $templateID,
979
                    ExtensionManagementUtility::extPath($extKey)
980
                );
981
            }
982
        }
983
    }
984
985
    /**
986
     * Appends (not prepends) additional TypoScript code to static template records/files as set in TYPO3_CONF_VARS
987
     * For files the "uid" value is the extension key but with any underscores removed. Possibly with a path if its a static file selected in the template record
988
     *
989
     * @param array $subrow Static template record/file
990
     * @return array Returns the input array where the values for keys "config" and "constants" may have been modified with prepended code.
991
     * @access private
992
     * @see addExtensionStatics(), includeStaticTypoScriptSources()
993
     */
994
    public function prependStaticExtra($subrow)
995
    {
996
        // the identifier can be "43" if coming from "static template" extension or a path like "cssstyledcontent/static/"
997
        $identifier = $subrow['uid'];
998
        $subrow['config'] .= $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'][$identifier];
999
        $subrow['constants'] .= $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_constants.'][$identifier];
1000
        // if this is a template of type "default content rendering", also see if other extensions have added their TypoScript that should be included after the content definitions
1001
        if (in_array($identifier, $GLOBALS['TYPO3_CONF_VARS']['FE']['contentRenderingTemplates'], true)) {
1002
            $subrow['config'] .= $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.']['defaultContentRendering'];
1003
            $subrow['constants'] .= $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_constants.']['defaultContentRendering'];
1004
        }
1005
        return $subrow;
1006
    }
1007
1008
    /**
1009
     * Creating versioning overlay of a sys_template record.
1010
     * This will use either frontend or backend overlay functionality depending on environment.
1011
     *
1012
     * @param array $row Row to overlay (passed by reference)
1013
     */
1014
    public function versionOL(&$row)
1015
    {
1016
        // Distinguish frontend and backend call:
1017
        // To do the fronted call a full frontend is required, just checking for
1018
        // TYPO3_MODE === 'FE' is not enough. This could otherwise lead to fatals in
1019
        // eId scripts that run in frontend scope, but do not have a full blown frontend.
1020
        if (is_object($this->getTypoScriptFrontendController()) && property_exists($this->getTypoScriptFrontendController(), 'sys_page') && method_exists($this->getTypoScriptFrontendController()->sys_page, 'versionOL')) {
1021
            // Frontend
1022
            $this->getTypoScriptFrontendController()->sys_page->versionOL('sys_template', $row);
1023
        } else {
1024
            // Backend
1025
            BackendUtility::workspaceOL('sys_template', $row);
1026
        }
1027
    }
1028
1029
    /*******************************************************************
1030
     *
1031
     * Parsing TypoScript code text from Template Records into PHP array
1032
     *
1033
     *******************************************************************/
1034
    /**
1035
     * Generates the configuration array by replacing constants and parsing the whole thing.
1036
     * Depends on $this->config and $this->constants to be set prior to this! (done by processTemplate/runThroughTemplates)
1037
     *
1038
     * @see \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser, start()
1039
     */
1040
    public function generateConfig()
1041
    {
1042
        // Add default TS for all code types
1043
        $this->addDefaultTypoScript();
1044
1045
        // Parse the TypoScript code text for include-instructions!
1046
        $this->processIncludes();
1047
        // These vars are also set lateron...
1048
        $this->setup['sitetitle'] = $this->sitetitle;
1049
        // ****************************
1050
        // Parse TypoScript Constants
1051
        // ****************************
1052
        // Initialize parser and match-condition classes:
1053
        /** @var $constants Parser\TypoScriptParser */
1054
        $constants = GeneralUtility::makeInstance(Parser\TypoScriptParser::class);
1055
        $constants->breakPointLN = (int)$this->ext_constants_BRP;
1056
        $constants->setup = $this->mergeConstantsFromPageTSconfig([]);
1057
        /** @var $matchObj ConditionMatcher */
1058
        $matchObj = GeneralUtility::makeInstance(ConditionMatcher::class);
1059
        $matchObj->setSimulateMatchConditions($this->matchAlternative);
1060
        $matchObj->setSimulateMatchResult((bool)$this->matchAll);
1061
        // Traverse constants text fields and parse them
1062
        foreach ($this->constants as $str) {
1063
            $constants->parse($str, $matchObj);
1064
        }
1065
        // Read out parse errors if any
1066
        $this->parserErrors['constants'] = $constants->errors;
1067
        // Then flatten the structure from a multi-dim array to a single dim array with all constants listed as key/value pairs (ready for substitution)
1068
        $this->flatSetup = [];
1069
        $this->flattenSetup($constants->setup, '');
1070
        // ***********************************************
1071
        // Parse TypoScript Setup (here called "config")
1072
        // ***********************************************
1073
        // Initialize parser and match-condition classes:
1074
        /** @var $config Parser\TypoScriptParser */
1075
        $config = GeneralUtility::makeInstance(Parser\TypoScriptParser::class);
1076
        $config->breakPointLN = (int)$this->ext_config_BRP;
1077
        $config->regLinenumbers = $this->ext_regLinenumbers;
1078
        $config->regComments = $this->ext_regComments;
1079
        $config->setup = $this->setup;
1080
        // Transfer information about conditions found in "Constants" and which of them returned TRUE.
1081
        $config->sections = $constants->sections;
1082
        $config->sectionsMatch = $constants->sectionsMatch;
1083
        // Traverse setup text fields and concatenate them into one, single string separated by a [GLOBAL] condition
1084
        $all = '';
1085
        foreach ($this->config as $str) {
1086
            $all .= '
1087
[GLOBAL]
1088
' . $str;
1089
        }
1090
        // Substitute constants in the Setup code:
1091
        if ($this->tt_track) {
1092
            $this->getTimeTracker()->push('Substitute Constants (' . count($this->flatSetup) . ')');
1093
        }
1094
        $all = $this->substituteConstants($all);
1095
        if ($this->tt_track) {
1096
            $this->getTimeTracker()->pull();
1097
        }
1098
1099
        // Searching for possible unsubstituted constants left (only for information)
1100
        if ($this->verbose) {
1101
            if (preg_match_all('/\\{\\$.[^}]*\\}/', $all, $constantList) > 0) {
1102
                if ($this->tt_track) {
1103
                    $this->getTimeTracker()->setTSlogMessage(implode(', ', $constantList[0]) . ': Constants may remain un-substituted!!', 2);
1104
                }
1105
            }
1106
        }
1107
1108
        // Logging the textual size of the TypoScript Setup field text with all constants substituted:
1109
        if ($this->tt_track) {
1110
            $this->getTimeTracker()->setTSlogMessage('TypoScript template size as textfile: ' . strlen($all) . ' bytes');
1111
        }
1112
        // Finally parse the Setup field TypoScript code (where constants are now substituted)
1113
        $config->parse($all, $matchObj);
1114
        // Read out parse errors if any
1115
        $this->parserErrors['config'] = $config->errors;
1116
        // Transfer the TypoScript array from the parser object to the internal $this->setup array:
1117
        $this->setup = $config->setup;
1118
        // Do the same for the constants
1119
        $this->setup_constants = $constants->setup;
1120
        // ****************************************************************
1121
        // Final processing of the $this->setup TypoScript Template array
1122
        // Basically: This is unsetting/setting of certain reserved keys.
1123
        // ****************************************************************
1124
        // These vars are already set after 'processTemplate', but because $config->setup overrides them (in the line above!), we set them again. They are not changed compared to the value they had in the top of the page!
1125
        unset($this->setup['sitetitle']);
1126
        unset($this->setup['sitetitle.']);
1127
        $this->setup['sitetitle'] = $this->sitetitle;
1128
        // Unsetting some vars...
1129
        unset($this->setup['types.']);
1130
        unset($this->setup['types']);
1131
        if (is_array($this->setup)) {
1132
            foreach ($this->setup as $key => $value) {
1133
                if ($value === 'PAGE') {
1134
                    // Set the typeNum of the current page object:
1135
                    if (isset($this->setup[$key . '.']['typeNum'])) {
1136
                        $typeNum = $this->setup[$key . '.']['typeNum'];
1137
                        $this->setup['types.'][$typeNum] = $key;
1138
                    } elseif (!isset($this->setup['types.'][0]) || !$this->setup['types.'][0]) {
1139
                        $this->setup['types.'][0] = $key;
1140
                    }
1141
                }
1142
            }
1143
        }
1144
        unset($this->setup['temp.']);
1145
        unset($constants);
1146
        // Storing the conditions found/matched information:
1147
        $this->sections = $config->sections;
0 ignored issues
show
Documentation Bug introduced by
It seems like $config->sections of type array is incompatible with the declared type string of property $sections.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1148
        $this->sectionsMatch = $config->sectionsMatch;
1149
    }
1150
1151
    /**
1152
     * Searching TypoScript code text (for constants and config (Setup))
1153
     * for include instructions and does the inclusion of external TypoScript files
1154
     * if needed.
1155
     *
1156
     * @see \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser, generateConfig()
1157
     */
1158
    public function processIncludes()
1159
    {
1160
        if ($this->processIncludesHasBeenRun) {
1161
            return;
1162
        }
1163
1164
        $paths = $this->templateIncludePaths;
1165
        $files = [];
1166 View Code Duplication
        foreach ($this->constants as &$value) {
1167
            $includeData = Parser\TypoScriptParser::checkIncludeLines($value, 1, true, array_shift($paths));
1168
            $files = array_merge($files, $includeData['files']);
1169
            $value = $includeData['typoscript'];
1170
        }
1171
        unset($value);
1172
        $paths = $this->templateIncludePaths;
1173 View Code Duplication
        foreach ($this->config as &$value) {
1174
            $includeData = Parser\TypoScriptParser::checkIncludeLines($value, 1, true, array_shift($paths));
1175
            $files = array_merge($files, $includeData['files']);
1176
            $value = $includeData['typoscript'];
1177
        }
1178
        unset($value);
1179
1180
        if (!empty($files)) {
1181
            $files = array_unique($files);
1182
            foreach ($files as $file) {
1183
                $this->rowSum[] = [$file, filemtime($file)];
1184
            }
1185
        }
1186
1187
        $this->processIncludesHasBeenRun = true;
1188
    }
1189
1190
    /**
1191
     * Loads Page TSconfig until the outermost template record and parses the configuration - if TSFE.constants object path is found it is merged with the default data in here!
1192
     *
1193
     * @param array $constArray Constants array, default input.
1194
     * @return array Constants array, modified
1195
     * @todo Apply caching to the parsed Page TSconfig. This is done in the other similar functions for both frontend and backend. However, since this functions works for BOTH frontend and backend we will have to either write our own local caching function or (more likely) detect if we are in FE or BE and use caching functions accordingly. Not having caching affects mostly the backend modules inside the "Template" module since the overhead in the frontend is only seen when TypoScript templates are parsed anyways (after which point they are cached anyways...)
1196
     */
1197
    public function mergeConstantsFromPageTSconfig($constArray)
1198
    {
1199
        $TSdataArray = [];
1200
        // Setting default configuration:
1201
        $TSdataArray[] = $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPageTSconfig'];
1202
        for ($a = 0; $a <= $this->outermostRootlineIndexWithTemplate; $a++) {
1203
            if (trim($this->absoluteRootLine[$a]['tsconfig_includes'])) {
1204
                $includeTsConfigFileList = GeneralUtility::trimExplode(
1205
                    ',',
1206
                    $this->absoluteRootLine[$a]['tsconfig_includes'],
1207
                    true
1208
                );
1209
1210
                $TSdataArray = $this->mergeConstantsFromIncludedTsConfigFiles($includeTsConfigFileList, $TSdataArray);
1211
            }
1212
            $TSdataArray[] = $this->absoluteRootLine[$a]['TSconfig'];
1213
        }
1214
        // Parsing the user TS (or getting from cache)
1215
        $TSdataArray = Parser\TypoScriptParser::checkIncludeLines_array($TSdataArray);
1216
        $userTS = implode(LF . '[GLOBAL]' . LF, $TSdataArray);
1217
        /** @var $parseObj Parser\TypoScriptParser */
1218
        $parseObj = GeneralUtility::makeInstance(Parser\TypoScriptParser::class);
1219
        $parseObj->parse($userTS);
1220
        if (is_array($parseObj->setup['TSFE.']['constants.'])) {
1221
            ArrayUtility::mergeRecursiveWithOverrule($constArray, $parseObj->setup['TSFE.']['constants.']);
1222
        }
1223
1224
        return $constArray;
1225
    }
1226
1227
    /**
1228
     * Reads TSconfig defined in external files and appends it to the given TSconfig array (in this case only constants)
1229
     *
1230
     * @param array $filesToInclude The files to read constants from
1231
     * @param array $TSdataArray The TSconfig array the constants should be appended to
1232
     * @return array The TSconfig with the included constants appended
1233
     */
1234
    protected function mergeConstantsFromIncludedTsConfigFiles($filesToInclude, $TSdataArray)
1235
    {
1236
        foreach ($filesToInclude as $key => $file) {
1237
            if (strpos($file, 'EXT:') !== 0) {
1238
                continue;
1239
            }
1240
1241
            list($extensionKey, $filePath) = explode('/', substr($file, 4), 2);
0 ignored issues
show
Bug introduced by
It seems like substr($file, 4) can also be of type false; however, parameter $string of explode() does only seem to accept string, 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

1241
            list($extensionKey, $filePath) = explode('/', /** @scrutinizer ignore-type */ substr($file, 4), 2);
Loading history...
1242
1243
            if ((string)$extensionKey === '' || !ExtensionManagementUtility::isLoaded($extensionKey)) {
1244
                continue;
1245
            }
1246
            if ((string)$filePath == '') {
1247
                continue;
1248
            }
1249
1250
            $tsConfigFile = ExtensionManagementUtility::extPath($extensionKey) . $filePath;
1251
            if (file_exists($tsConfigFile)) {
1252
                $TSdataArray[] = file_get_contents($tsConfigFile);
1253
            }
1254
        }
1255
1256
        return $TSdataArray;
1257
    }
1258
1259
    /**
1260
     * This flattens a hierarchical TypoScript array to $this->flatSetup
1261
     *
1262
     * @param array $setupArray TypoScript array
1263
     * @param string $prefix Prefix to the object path. Used for recursive calls to this function.
1264
     * @see generateConfig()
1265
     */
1266
    public function flattenSetup($setupArray, $prefix)
1267
    {
1268 View Code Duplication
        if (is_array($setupArray)) {
1269
            foreach ($setupArray as $key => $val) {
1270
                if (is_array($val)) {
1271
                    $this->flattenSetup($val, $prefix . $key);
1272
                } else {
1273
                    $this->flatSetup[$prefix . $key] = $val;
1274
                }
1275
            }
1276
        }
1277
    }
1278
1279
    /**
1280
     * Substitutes the constants from $this->flatSetup in the text string $all
1281
     *
1282
     * @param string $all TypoScript code text string
1283
     * @return string The processed string with all constants found in $this->flatSetup as key/value pairs substituted.
1284
     * @see generateConfig(), flattenSetup()
1285
     */
1286
    public function substituteConstants($all)
1287
    {
1288
        if ($this->tt_track) {
1289
            $this->getTimeTracker()->setTSlogMessage('Constants to substitute: ' . count($this->flatSetup));
1290
        }
1291
        $noChange = false;
1292
        // Recursive substitution of constants (up to 10 nested levels)
1293
        for ($i = 0; $i < 10 && !$noChange; $i++) {
1294
            $old_all = $all;
1295
            $all = preg_replace_callback('/\\{\\$(.[^}]*)\\}/', [$this, 'substituteConstantsCallBack'], $all);
1296
            if ($old_all == $all) {
1297
                $noChange = true;
1298
            }
1299
        }
1300
        return $all;
1301
    }
1302
1303
    /**
1304
     * Call back method for preg_replace_callback in substituteConstants
1305
     *
1306
     * @param array $matches Regular expression matches
1307
     * @return string Replacement
1308
     * @see substituteConstants()
1309
     */
1310
    public function substituteConstantsCallBack($matches)
1311
    {
1312
        // Replace {$CONST} if found in $this->flatSetup, else leave unchanged
1313
        return isset($this->flatSetup[$matches[1]]) && !is_array($this->flatSetup[$matches[1]]) ? $this->flatSetup[$matches[1]] : $matches[0];
1314
    }
1315
1316
    /*******************************************************************
1317
     *
1318
     * Various API functions, used from elsewhere in the frontend classes
1319
     *
1320
     *******************************************************************/
1321
1322
    /**
1323
     * Returns the reference used for the frontend inclusion, checks against allowed paths for inclusion.
1324
     *
1325
     * @param string $fileFromSetup TypoScript "resource" data type value.
1326
     * @return string|null Resulting filename, is either a full absolute URL or a relative path. Returns NULL if invalid filename or a directory is given
1327
     */
1328
    public function getFileName($fileFromSetup)
1329
    {
1330
        $file = trim($fileFromSetup);
1331
        if (!$file) {
1332
            return null;
1333
        }
1334
        if (strpos($file, '../') !== false) {
1335
            if ($this->tt_track) {
1336
                $this->getTimeTracker()->setTSlogMessage('File path "' . $file . '" contained illegal string "../"!', 3);
1337
            }
1338
            return null;
1339
        }
1340
        // Cache
1341
        $hash = md5($file);
1342
        if (isset($this->fileCache[$hash])) {
1343
            return $this->fileCache[$hash];
1344
        }
1345
1346
        // if this is an URL, it can be returned directly
1347
        $urlScheme = parse_url($file, PHP_URL_SCHEME);
1348
        if ($urlScheme === 'https' || $urlScheme === 'http' || is_file(PATH_site . $file)) {
1349
            return $file;
1350
        }
1351
1352
        // this call also resolves EXT:myext/ files
1353
        $file = GeneralUtility::getFileAbsFileName($file);
1354
        if (!$file) {
1355
            if ($this->tt_track) {
1356
                $this->getTimeTracker()->setTSlogMessage('File "' . $fileFromSetup . '" was not found!', 3);
1357
            }
1358
            return null;
1359
        }
1360
1361
        $file = PathUtility::stripPathSitePrefix($file);
1362
1363
        // Check if the found file is in the allowed paths
1364
        foreach ($this->allowedPaths as $val) {
1365
            if (GeneralUtility::isFirstPartOfStr($file, $val)) {
1366
                $this->fileCache[$hash] = $file;
1367
                return $file;
1368
            }
1369
        }
1370
1371
        if ($this->tt_track) {
1372
            $this->getTimeTracker()->setTSlogMessage('"' . $file . '" was not located in the allowed paths: (' . implode(',', $this->allowedPaths) . ')', 3);
1373
        }
1374
        return null;
1375
    }
1376
1377
    /**
1378
     * Compiles the content for the page <title> tag.
1379
     *
1380
     * @param string $pageTitle The input title string, typically the "title" field of a page's record.
1381
     * @param bool $noTitle If set, then only the site title is outputted (from $this->setup['sitetitle'])
1382
     * @param bool $showTitleFirst If set, then "sitetitle" and $title is swapped
1383
     * @param string $pageTitleSeparator an alternative to the ": " as the separator between site title and page title
1384
     * @return string The page title on the form "[sitetitle]: [input-title]". Not htmlspecialchar()'ed.
1385
     * @see \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController::tempPageCacheContent(), \TYPO3\CMS\Frontend\Page\PageGenerator::renderContentWithHeader()
1386
     */
1387
    public function printTitle($pageTitle, $noTitle = false, $showTitleFirst = false, $pageTitleSeparator = '')
1388
    {
1389
        $siteTitle = trim($this->setup['sitetitle']);
1390
        $pageTitle = $noTitle ? '' : $pageTitle;
1391
        if ($showTitleFirst) {
1392
            $temp = $siteTitle;
1393
            $siteTitle = $pageTitle;
1394
            $pageTitle = $temp;
1395
        }
1396
        // only show a separator if there are both site title and page title
1397
        if ($pageTitle === '' || $siteTitle === '') {
1398
            $pageTitleSeparator = '';
1399
        } elseif (empty($pageTitleSeparator)) {
1400
            // use the default separator if non given
1401
            $pageTitleSeparator = ': ';
1402
        }
1403
        return $siteTitle . $pageTitleSeparator . $pageTitle;
1404
    }
1405
1406
    /**
1407
     * Returns the level of the given page in the rootline - Multiple pages can be given by separating the UIDs by comma.
1408
     *
1409
     * @param string $list A list of UIDs for which the rootline-level should get returned
1410
     * @return int The level in the rootline. If more than one page was given the lowest level will get returned.
1411
     */
1412
    public function getRootlineLevel($list)
1413
    {
1414
        $idx = 0;
1415
        foreach ($this->rootLine as $page) {
1416
            if (GeneralUtility::inList($list, $page['uid'])) {
1417
                return $idx;
1418
            }
1419
            $idx++;
1420
        }
1421
        return false;
1422
    }
1423
1424
    /*******************************************************************
1425
     *
1426
     * Functions for creating links
1427
     *
1428
     *******************************************************************/
1429
    /**
1430
     * The mother of all functions creating links/URLs etc in a TypoScript environment.
1431
     * See the references below.
1432
     * Basically this function takes care of issues such as type,id,alias and Mount Points, URL rewriting (through hooks), M5/B6 encoded parameters etc.
1433
     * It is important to pass all links created through this function since this is the guarantee that globally configured settings for link creating are observed and that your applications will conform to the various/many configuration options in TypoScript Templates regarding this.
1434
     *
1435
     * @param array $page The page record of the page to which we are creating a link. Needed due to fields like uid, alias, target, title and sectionIndex_uid.
1436
     * @param string $oTarget Default target string to use IF not $page['target'] is set.
1437
     * @param bool $no_cache If set, then the "&no_cache=1" parameter is included in the URL.
1438
     * @param string $_ not in use anymore
1439
     * @param array $overrideArray Array with overriding values for the $page array.
1440
     * @param string $addParams Additional URL parameters to set in the URL. Syntax is "&foo=bar&foo2=bar2" etc. Also used internally to add parameters if needed.
1441
     * @param string $typeOverride If you set this value to something else than a blank string, then the typeNumber used in the link will be forced to this value. Normally the typeNum is based on the target set OR on $this->getTypoScriptFrontendController()->config['config']['forceTypeValue'] if found.
1442
     * @param string $targetDomain The target Doamin, if any was detected in typolink
1443
     * @return array Contains keys like "totalURL", "url", "sectionIndex", "linkVars", "no_cache", "type", "target" of which "totalURL" is normally the value you would use while the other keys contains various parts that was used to construct "totalURL
1444
     * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::typoLink(), \TYPO3\CMS\Frontend\ContentObject\Menu\AbstractMenuContentObject::link()
1445
     */
1446
    public function linkData($page, $oTarget, $no_cache, $_ = null, $overrideArray = null, $addParams = '', $typeOverride = '', $targetDomain = '')
1447
    {
1448
        $LD = [];
1449
        // Overriding some fields in the page record and still preserves the values by adding them as parameters. Little strange function.
1450
        if (is_array($overrideArray)) {
1451
            foreach ($overrideArray as $theKey => $theNewVal) {
1452
                $addParams .= '&real_' . $theKey . '=' . rawurlencode($page[$theKey]);
1453
                $page[$theKey] = $theNewVal;
1454
            }
1455
        }
1456
        // Adding Mount Points, "&MP=", parameter for the current page if any is set:
1457
        if (!strstr($addParams, '&MP=')) {
1458
            // Looking for hardcoded defaults:
1459
            if (trim($this->getTypoScriptFrontendController()->MP_defaults[$page['uid']])) {
1460
                $addParams .= '&MP=' . rawurlencode(trim($this->getTypoScriptFrontendController()->MP_defaults[$page['uid']]));
1461
            } elseif ($this->getTypoScriptFrontendController()->config['config']['MP_mapRootPoints']) {
1462
                // Else look in automatically created map:
1463
                $m = $this->getFromMPmap($page['uid']);
1464
                if ($m) {
1465
                    $addParams .= '&MP=' . rawurlencode($m);
1466
                }
1467
            }
1468
        }
1469
        // Setting ID/alias:
1470
        $script = 'index.php';
1471
        if ($page['alias']) {
1472
            $LD['url'] = $script . '?id=' . rawurlencode($page['alias']);
1473
        } else {
1474
            $LD['url'] = $script . '?id=' . $page['uid'];
1475
        }
1476
        // Setting target
1477
        $LD['target'] = trim($page['target']) ?: $oTarget;
1478
        // typeNum
1479
        $typeNum = $this->setup[$LD['target'] . '.']['typeNum'];
1480
        if (!MathUtility::canBeInterpretedAsInteger($typeOverride) && (int)$this->getTypoScriptFrontendController()->config['config']['forceTypeValue']) {
1481
            $typeOverride = (int)$this->getTypoScriptFrontendController()->config['config']['forceTypeValue'];
1482
        }
1483
        if ((string)$typeOverride !== '') {
1484
            $typeNum = $typeOverride;
1485
        }
1486
        // Override...
1487
        if ($typeNum) {
1488
            $LD['type'] = '&type=' . (int)$typeNum;
1489
        } else {
1490
            $LD['type'] = '';
1491
        }
1492
        // Preserving the type number.
1493
        $LD['orig_type'] = $LD['type'];
1494
        // noCache
1495
        $LD['no_cache'] = $no_cache ? '&no_cache=1' : '';
1496
        // linkVars
1497
        if ($addParams) {
1498
            $LD['linkVars'] = GeneralUtility::implodeArrayForUrl('', GeneralUtility::explodeUrl2Array($this->getTypoScriptFrontendController()->linkVars . $addParams), '', false, true);
1499
        } else {
1500
            $LD['linkVars'] = $this->getTypoScriptFrontendController()->linkVars;
1501
        }
1502
        // Add absRefPrefix if exists.
1503
        $LD['url'] = $this->getTypoScriptFrontendController()->absRefPrefix . $LD['url'];
1504
        // If the special key 'sectionIndex_uid' (added 'manually' in tslib/menu.php to the page-record) is set, then the link jumps directly to a section on the page.
1505
        $LD['sectionIndex'] = $page['sectionIndex_uid'] ? '#c' . $page['sectionIndex_uid'] : '';
1506
        // Compile the normal total url
1507
        $LD['totalURL'] = rtrim($LD['url'] . $LD['type'] . $LD['no_cache'] . $LD['linkVars'] . $this->getTypoScriptFrontendController()->getMethodUrlIdToken, '?') . $LD['sectionIndex'];
1508
        // Call post processing function for link rendering:
1509
        $_params = [
1510
            'LD' => &$LD,
1511
            'args' => ['page' => $page, 'oTarget' => $oTarget, 'no_cache' => $no_cache, 'script' => $script, 'overrideArray' => $overrideArray, 'addParams' => $addParams, 'typeOverride' => $typeOverride, 'targetDomain' => $targetDomain],
1512
            'typeNum' => $typeNum
1513
        ];
1514
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tstemplate.php']['linkData-PostProc'] ?? [] as $_funcRef) {
1515
            GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1516
        }
1517
        // Return the LD-array
1518
        return $LD;
1519
    }
1520
1521
    /**
1522
     * Initializes the automatically created MPmap coming from the "config.MP_mapRootPoints" setting
1523
     * Can be called many times with overhead only the first time since then the map is generated and cached in memory.
1524
     *
1525
     * @param int $pageId Page id to return MPvar value for.
1526
     * @return string
1527
     * @see initMPmap_create()
1528
     * @todo Implement some caching of the result between hits. (more than just the memory caching used here)
1529
     */
1530
    public function getFromMPmap($pageId = 0)
1531
    {
1532
        // Create map if not found already:
1533
        if (!is_array($this->MPmap)) {
1534
            $this->MPmap = [];
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type string of property $MPmap.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1535
            $rootPoints = GeneralUtility::trimExplode(',', strtolower($this->getTypoScriptFrontendController()->config['config']['MP_mapRootPoints']), true);
1536
            // Traverse rootpoints:
1537
            foreach ($rootPoints as $p) {
1538
                $initMParray = [];
1539
                if ($p === 'root') {
1540
                    $p = $this->rootLine[0]['uid'];
1541
                    if ($this->rootLine[0]['_MOUNT_OL'] && $this->rootLine[0]['_MP_PARAM']) {
1542
                        $initMParray[] = $this->rootLine[0]['_MP_PARAM'];
1543
                    }
1544
                }
1545
                $this->initMPmap_create($p, $initMParray);
1546
            }
1547
        }
1548
        // Finding MP var for Page ID:
1549
        if ($pageId) {
1550
            if (is_array($this->MPmap[$pageId]) && !empty($this->MPmap[$pageId])) {
1551
                return implode(',', $this->MPmap[$pageId]);
1552
            }
1553
        }
1554
        return '';
1555
    }
1556
1557
    /**
1558
     * Creating MPmap for a certain ID root point.
1559
     *
1560
     * @param int $id Root id from which to start map creation.
1561
     * @param array $MP_array MP_array passed from root page.
1562
     * @param int $level Recursion brake. Incremented for each recursive call. 20 is the limit.
1563
     * @see getFromMPvar()
1564
     */
1565
    public function initMPmap_create($id, $MP_array = [], $level = 0)
1566
    {
1567
        $id = (int)$id;
1568
        if ($id <= 0) {
1569
            return;
1570
        }
1571
        // First level, check id
1572
        if (!$level) {
1573
            // Find mount point if any:
1574
            $mount_info = $this->getTypoScriptFrontendController()->sys_page->getMountPointInfo($id);
1575
            // Overlay mode:
1576 View Code Duplication
            if (is_array($mount_info) && $mount_info['overlay']) {
1577
                $MP_array[] = $mount_info['MPvar'];
1578
                $id = $mount_info['mount_pid'];
1579
            }
1580
            // Set mapping information for this level:
1581
            $this->MPmap[$id] = $MP_array;
1582
            // Normal mode:
1583 View Code Duplication
            if (is_array($mount_info) && !$mount_info['overlay']) {
1584
                $MP_array[] = $mount_info['MPvar'];
1585
                $id = $mount_info['mount_pid'];
1586
            }
1587
        }
1588
        if ($id && $level < 20) {
1589
            $nextLevelAcc = [];
1590
            // Select and traverse current level pages:
1591
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
1592
            $queryBuilder->getRestrictions()
1593
                ->removeAll()
1594
                ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1595
            $queryResult = $queryBuilder
1596
                ->select('uid', 'pid', 'doktype', 'mount_pid', 'mount_pid_ol')
1597
                ->from('pages')
1598
                ->where(
1599
                    $queryBuilder->expr()->eq(
1600
                        'pid',
1601
                        $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)
1602
                    ),
1603
                    $queryBuilder->expr()->neq(
1604
                        'doktype',
1605
                        $queryBuilder->createNamedParameter(PageRepository::DOKTYPE_RECYCLER, \PDO::PARAM_INT)
1606
                    ),
1607
                    $queryBuilder->expr()->neq(
1608
                        'doktype',
1609
                        $queryBuilder->createNamedParameter(PageRepository::DOKTYPE_BE_USER_SECTION, \PDO::PARAM_INT)
1610
                    )
1611
                )->execute();
1612
            while ($row = $queryResult->fetch()) {
1613
                // Find mount point if any:
1614
                $next_id = $row['uid'];
1615
                $next_MP_array = $MP_array;
1616
                $mount_info = $this->getTypoScriptFrontendController()->sys_page->getMountPointInfo($next_id, $row);
1617
                // Overlay mode:
1618 View Code Duplication
                if (is_array($mount_info) && $mount_info['overlay']) {
1619
                    $next_MP_array[] = $mount_info['MPvar'];
1620
                    $next_id = $mount_info['mount_pid'];
1621
                }
1622
                if (!isset($this->MPmap[$next_id])) {
1623
                    // Set mapping information for this level:
1624
                    $this->MPmap[$next_id] = $next_MP_array;
1625
                    // Normal mode:
1626 View Code Duplication
                    if (is_array($mount_info) && !$mount_info['overlay']) {
1627
                        $next_MP_array[] = $mount_info['MPvar'];
1628
                        $next_id = $mount_info['mount_pid'];
1629
                    }
1630
                    // Register recursive call
1631
                    // (have to do it this way since ALL of the current level should be registered BEFORE the sublevel at any time)
1632
                    $nextLevelAcc[] = [$next_id, $next_MP_array];
1633
                }
1634
            }
1635
            // Call recursively, if any:
1636
            foreach ($nextLevelAcc as $pSet) {
1637
                $this->initMPmap_create($pSet[0], $pSet[1], $level + 1);
1638
            }
1639
        }
1640
    }
1641
1642
    /**
1643
     * Adds the TypoScript from the global array.
1644
     * The class property isDefaultTypoScriptAdded ensures
1645
     * that the adding only happens once.
1646
     *
1647
     * @see isDefaultTypoScriptAdded
1648
     */
1649
    protected function addDefaultTypoScript()
1650
    {
1651
        // Add default TS for all code types, if not done already
1652
        if (!$this->isDefaultTypoScriptAdded) {
1653
            // adding default setup and constants
1654
            // defaultTypoScript_setup is *very* unlikely to be empty
1655
            // the count of elements in ->constants, ->config and ->templateIncludePaths have to be in sync
1656
            array_unshift($this->constants, (string)$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_constants']);
1657
            array_unshift($this->config, (string)$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup']);
1658
            array_unshift($this->templateIncludePaths, '');
1659
            // prepare a proper entry to hierachyInfo (used by TemplateAnalyzer in BE)
1660
            $rootTemplateId = $this->hierarchyInfo[count($this->hierarchyInfo)-1]['templateID'];
1661
            $defaultTemplateInfo = [
1662
                'root' => '',
1663
                'next' => '',
1664
                'clConst' => '',
1665
                'clConf' => '',
1666
                'templateID' => '_defaultTypoScript_',
1667
                'templateParent' => $rootTemplateId,
1668
                'title' => 'SYS:TYPO3_CONF_VARS:FE:defaultTypoScript',
1669
                'uid' => '_defaultTypoScript_',
1670
                'pid' => '',
1671
                'configLines' => substr_count((string)$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup'], LF) + 1
1672
            ];
1673
            // push info to information arrays used in BE by TemplateTools (Analyzer)
1674
            array_unshift($this->clearList_const, $defaultTemplateInfo['uid']);
1675
            array_unshift($this->clearList_setup, $defaultTemplateInfo['uid']);
1676
            array_unshift($this->hierarchyInfo, $defaultTemplateInfo);
1677
            $this->isDefaultTypoScriptAdded = true;
1678
        }
1679
    }
1680
1681
    /**
1682
     * @return TypoScriptFrontendController
1683
     */
1684
    protected function getTypoScriptFrontendController()
1685
    {
1686
        return $GLOBALS['TSFE'];
1687
    }
1688
1689
    /**
1690
     * @return TimeTracker
1691
     */
1692
    protected function getTimeTracker()
1693
    {
1694
        return GeneralUtility::makeInstance(TimeTracker::class);
1695
    }
1696
1697
    /**
1698
     * Returns data stored for the hash string in the cache "cache_hash"
1699
     * used to store the parsed TypoScript template structures.
1700
     *
1701
     * @param string $identifier The hash-string which was used to store the data value
1702
     * @return mixed The data from the cache
1703
     */
1704
    protected function getCacheEntry($identifier)
1705
    {
1706
        return GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash')->get($identifier);
1707
    }
1708
1709
    /**
1710
     * Stores $data in the 'cache_hash' cache with the hash key $identifier
1711
     *
1712
     * @param string $identifier 32 bit hash string (eg. a md5 hash of a serialized array identifying the data being stored)
1713
     * @param mixed $data The data to store
1714
     * @param string $tag Is just a textual identification in order to inform about the content
1715
     */
1716
    protected function setCacheEntry($identifier, $data, $tag)
1717
    {
1718
        GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash')->set($identifier, $data, ['ident_' . $tag], 0);
1719
    }
1720
}
1721