Passed
Push — task/2976_TYPO3.11_compatibili... ( 803400...b598e4 )
by Rafael
33:58 queued 15:41
created

SiteRepository::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 15
ccs 7
cts 7
cp 1
rs 10
cc 1
nc 1
nop 6
crap 1
1
<?php
2
3
namespace ApacheSolrForTypo3\Solr\Domain\Site;
4
5
/***************************************************************
6
 *  Copyright notice
7
 *
8
 *  (c) 2017 - Thomas Hohn <[email protected]>
9
 *  All rights reserved
10
 *
11
 *  This script is part of the TYPO3 project. The TYPO3 project is
12
 *  free software; you can redistribute it and/or modify
13
 *  it under the terms of the GNU General Public License as published by
14
 *  the Free Software Foundation; either version 3 of the License, or
15
 *  (at your option) any later version.
16
 *
17
 *  The GNU General Public License can be found at
18
 *  http://www.gnu.org/copyleft/gpl.html.
19
 *
20
 *  This script is distributed in the hope that it will be useful,
21
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 *  GNU General Public License for more details.
24
 *
25
 *  This copyright notice MUST APPEAR in all copies of the script!
26
 ***************************************************************/
27
28
use ApacheSolrForTypo3\Solr\Domain\Index\Queue\RecordMonitor\Helper\RootPageResolver;
29
use ApacheSolrForTypo3\Solr\FrontendEnvironment;
30
use ApacheSolrForTypo3\Solr\System\Cache\TwoLevelCache;
31
use ApacheSolrForTypo3\Solr\System\Configuration\ExtensionConfiguration;
32
use ApacheSolrForTypo3\Solr\System\Records\Pages\PagesRepository;
33
use ApacheSolrForTypo3\Solr\System\Util\SiteUtility;
34
use Doctrine\DBAL\Driver\Exception as DBALDriverException;
35
use Exception;
36
use InvalidArgumentException;
37
use Throwable;
38
use TYPO3\CMS\Backend\Utility\BackendUtility;
39
use TYPO3\CMS\Core\Registry;
40
use TYPO3\CMS\Core\Site\Entity\Site as CoreSite;
41
use TYPO3\CMS\Core\Site\SiteFinder;
42
use TYPO3\CMS\Core\Utility\GeneralUtility;
43
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
44
45
/**
46
 * SiteRepository
47
 *
48
 * Responsible to retrieve instances of Site objects
49
 *
50
 * @author Thomas Hohn <[email protected]>
51
 */
52
class SiteRepository
53
{
54
    /**
55
     * Rootpage resolver
56
     *
57
     * @var RootPageResolver
58
     */
59
    protected $rootPageResolver;
60
61
    /**
62
     * @var TwoLevelCache
63
     */
64
    protected $runtimeCache;
65
66
    /**
67
     * @var Registry
68
     */
69
    protected $registry;
70
71
    /**
72
     * @var SiteFinder
73
     */
74
    protected $siteFinder;
75
76
    /**
77
     * @var ExtensionConfiguration
78
     */
79
    protected $extensionConfiguration;
80
81
    /**
82
     * @var FrontendEnvironment
83
     */
84
    protected $frontendEnvironment = null;
85
86
    /**
87
     * SiteRepository constructor.
88
     *
89
     * @param RootPageResolver|null $rootPageResolver
90
     * @param TwoLevelCache|null $twoLevelCache
91
     * @param Registry|null $registry
92
     * @param SiteFinder|null $siteFinder
93
     * @param ExtensionConfiguration|null $extensionConfiguration
94
     * @param FrontendEnvironment|null $frontendEnvironment
95
     */
96 238
    public function __construct(
97
        RootPageResolver $rootPageResolver = null,
98
        TwoLevelCache $twoLevelCache = null,
99
        Registry $registry = null,
100
        SiteFinder $siteFinder = null,
101
        ExtensionConfiguration $extensionConfiguration = null,
102
        FrontendEnvironment $frontendEnvironment = null
103
    )
104
    {
105 238
        $this->rootPageResolver = $rootPageResolver ?? GeneralUtility::makeInstance(RootPageResolver::class);
106 238
        $this->runtimeCache = $twoLevelCache ?? GeneralUtility::makeInstance(TwoLevelCache::class, /** @scrutinizer ignore-type */'runtime');
107 238
        $this->registry = $registry ?? GeneralUtility::makeInstance(Registry::class);
108 238
        $this->siteFinder = $siteFinder ?? GeneralUtility::makeInstance(SiteFinder::class);
109 238
        $this->extensionConfiguration = $extensionConfiguration ?? GeneralUtility::makeInstance(ExtensionConfiguration::class);
110 238
        $this->frontendEnvironment = $frontendEnvironment ?? GeneralUtility::makeInstance(FrontendEnvironment::class);
111 238
    }
112
113
    /**
114
     * Gets the Site for a specific page ID.
115
     *
116
     * @param int $pageId The page ID to get a Site object for.
117
     * @param string $mountPointIdentifier
118
     * @return SiteInterface Site for the given page ID.
119
     */
120 162
    public function getSiteByPageId(int $pageId, string $mountPointIdentifier = '')
121
    {
122 162
        $rootPageId = $this->rootPageResolver->getRootPageId($pageId, false, $mountPointIdentifier);
123 162
        return $this->getSiteByRootPageId($rootPageId);
124
    }
125
126
    /**
127
     * Gets the Site for a specific root page Id.
128
     *
129
     * @param int $rootPageId Root page Id to get a Site object for.
130
     * @return SiteInterface Site for the given page Id.
131
     */
132 189
    public function getSiteByRootPageId(int $rootPageId)
133
    {
134 189
        $cacheId = 'SiteRepository' . '_' . 'getSiteByPageId' . '_' . $rootPageId;
135
136 189
        $methodResult = $this->runtimeCache->get($cacheId);
137 189
        if (!empty($methodResult)) {
138 148
            return $methodResult;
139
        }
140
141 189
        $methodResult = $this->buildSite($rootPageId);
142 182
        $this->runtimeCache->set($cacheId, $methodResult);
143
144 182
        return $methodResult;
145
    }
146
147
    /**
148
     * Returns the first available Site.
149
     *
150
     * @param bool $stopOnInvalidSite
151
     * @throws Exception
152
     * @return Site
153
     */
154 21
    public function getFirstAvailableSite($stopOnInvalidSite = false)
155
    {
156 21
        $sites = $this->getAvailableSites($stopOnInvalidSite);
157 21
        return array_shift($sites);
158
    }
159
160
    /**
161
     * Gets all available TYPO3 sites with Solr configured.
162
     *
163
     * @param bool $stopOnInvalidSite
164
     * @return Site[] An array of available sites
165
     * @throws DBALDriverException
166
     * @throws Throwable
167
     */
168 104
    public function getAvailableSites($stopOnInvalidSite = false)
169
    {
170 104
        $cacheId = 'SiteRepository' . '_' . 'getAvailableSites';
171
172 104
        $sites = $this->runtimeCache->get($cacheId);
173 104
        if (!empty($sites)) {
174 22
            return $sites;
175
        }
176
177 104
        $sites = $this->getAvailableTYPO3ManagedSites($stopOnInvalidSite);
178 104
        $this->runtimeCache->set($cacheId, $sites);
179
180 104
        return $sites;
181
    }
182
183
    /**
184
     * @param bool $stopOnInvalidSite
185
     * @return array
186
     * @throws DBALDriverException
187
     * @throws Throwable
188
     */
189 104
    protected function getAvailableTYPO3ManagedSites(bool $stopOnInvalidSite): array
190
    {
191 104
        $typo3ManagedSolrSites = [];
192 104
        $typo3Sites = $this->siteFinder->getAllSites();
193 104
        foreach ($typo3Sites as $typo3Site) {
194
            try {
195 104
                $rootPageId = $typo3Site->getRootPageId();
196 104
                if (isset($typo3ManagedSolrSites[$rootPageId])) {
197
                    //get each site only once
198
                    continue;
199
                }
200 104
                $typo3ManagedSolrSite = $this->buildSite($rootPageId);
201 104
                if ($typo3ManagedSolrSite->isEnabled()) {
202 104
                    $typo3ManagedSolrSites[$rootPageId] = $typo3ManagedSolrSite;
203
                }
204
205 22
            } catch (Throwable $e) {
206 22
                if ($stopOnInvalidSite) {
207
                    throw $e;
208
                }
209
            }
210
        }
211 104
        return $typo3ManagedSolrSites;
212
    }
213
214
    /**
215
     * Creates an instance of the Site object.
216
     *
217
     * @param int $rootPageId
218
     * @return SiteInterface
219
     * @throws DBALDriverException
220
     */
221 213
    protected function buildSite(int $rootPageId)
222
    {
223 213
        $rootPageRecord = BackendUtility::getRecord('pages', $rootPageId);
224 213
        if (empty($rootPageRecord)) {
225 27
            throw new InvalidArgumentException(
226 27
                "The rootPageRecord for the given rootPageRecord ID '{$rootPageId}' could not be found in the database and can therefore not be used as site root rootPageRecord.",
227 27
                1487326416
228
            );
229
        }
230
231 207
        $this->validateRootPageRecord($rootPageId, $rootPageRecord);
232
233 206
        return $this->buildTypo3ManagedSite($rootPageRecord);
234
    }
235
236
    /**
237
     * @param string $domain
238
     * @return string
239
     */
240 206
    protected function getSiteHashForDomain(string $domain): string
241
    {
242
        /** @var $siteHashService SiteHashService */
243 206
        $siteHashService = GeneralUtility::makeInstance(SiteHashService::class);
244 206
        return $siteHashService->getSiteHashForDomain($domain);
245
    }
246
247
    /**
248
     * @param int $rootPageId
249
     * @param array $rootPageRecord
250
     * @throws InvalidArgumentException
251
     */
252 207
    protected function validateRootPageRecord(int $rootPageId, array $rootPageRecord)
253
    {
254 207
        if (!Site::isRootPage($rootPageRecord)) {
255 3
            throw new InvalidArgumentException(
256 3
                "The rootPageRecord for the given rootPageRecord ID '{$rootPageId}' is not marked as root rootPageRecord and can therefore not be used as site root rootPageRecord.",
257 3
                1309272922
258
            );
259
        }
260 206
    }
261
262
    /**
263
     * Builds a TYPO3 managed site with TypoScript configuration.
264
     *
265
     * @param array $rootPageRecord
266
     *
267
     * @return Site
268
     *
269
     * @throws DBALDriverException
270
     */
271 206
    protected function buildTypo3ManagedSite(array $rootPageRecord): ?Site
272
    {
273 206
        $typo3Site = $this->getTypo3Site($rootPageRecord['uid']);
274 206
        if (!$typo3Site instanceof CoreSite) {
275
            return null;
276
        }
277
278 206
        $domain = $typo3Site->getBase()->getHost();
279
280 206
        $siteHash = $this->getSiteHashForDomain($domain);
281 206
        $defaultLanguage = $typo3Site->getDefaultLanguage()->getLanguageId();
282 206
        $pageRepository = GeneralUtility::makeInstance(PagesRepository::class);
283 206
        $availableLanguageIds = array_map(function($language) {
284 206
            return $language->getLanguageId();
285 206
        }, $typo3Site->getLanguages());
286
287
        // Try to get first instantiable TSFE for one of site languages, to get TypoScript with `plugin.tx_solr.index.*`,
288
        // to be able to collect indexing configuration,
289
        // which are required for BE-Modules/CLI-Commands or RecordMonitor within BE/TCE-commands.
290
        // If TSFE for none of languages can be initialized, then the \ApacheSolrForTypo3\Solr\Domain\Site\Site object unusable at all,
291
        // so the rest of the steps in this method are not necessary, and therefore the null will be returned.
292 206
        $tsfeFactory = GeneralUtility::makeInstance(FrontendEnvironment\Tsfe::class);
293 206
        $tsfeToUseForTypoScriptConfiguration = $tsfeFactory->getTsfeByPageIdAndLanguageFallbackChain($typo3Site->getRootPageId(), ...$availableLanguageIds);
294 206
        if (!$tsfeToUseForTypoScriptConfiguration instanceof TypoScriptFrontendController) {
295
            return null;
296
        }
297
298 206
        $solrConnectionConfigurations = [];
299
300 206
        foreach ($availableLanguageIds as $languageUid) {
301 206
            $solrEnabled = SiteUtility::getConnectionProperty($typo3Site, 'enabled', $languageUid, 'read', true);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type null|string expected by parameter $defaultValue of ApacheSolrForTypo3\Solr\...getConnectionProperty(). ( Ignorable by Annotation )

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

301
            $solrEnabled = SiteUtility::getConnectionProperty($typo3Site, 'enabled', $languageUid, 'read', /** @scrutinizer ignore-type */ true);
Loading history...
302 206
            if ($solrEnabled) {
303 206
                $solrConnectionConfigurations[$languageUid] = [
304 206
                    'connectionKey' =>  $rootPageRecord['uid'] . '|' . $languageUid,
305 206
                    'rootPageTitle' => $rootPageRecord['title'],
306 206
                    'rootPageUid' => $rootPageRecord['uid'],
307
                    'read' => [
308 206
                        'scheme' => SiteUtility::getConnectionProperty($typo3Site, 'scheme', $languageUid, 'read', 'http'),
309 206
                        'host' => SiteUtility::getConnectionProperty($typo3Site, 'host', $languageUid, 'read', 'localhost'),
310 206
                        'port' => (int)SiteUtility::getConnectionProperty($typo3Site, 'port', $languageUid, 'read', 8983),
311
                        // @todo: transform core to path
312
                        'path' =>
313 206
                            SiteUtility::getConnectionProperty($typo3Site, 'path', $languageUid, 'read', '/solr/') .
314 206
                            SiteUtility::getConnectionProperty($typo3Site, 'core', $languageUid, 'read', 'core_en') . '/' ,
315 206
                        'username' => SiteUtility::getConnectionProperty($typo3Site, 'username', $languageUid, 'read', ''),
316 206
                        'password' => SiteUtility::getConnectionProperty($typo3Site, 'password', $languageUid, 'read', '')
317
                    ],
318
                    'write' => [
319 206
                        'scheme' => SiteUtility::getConnectionProperty($typo3Site, 'scheme', $languageUid, 'write', 'http'),
320 206
                        'host' => SiteUtility::getConnectionProperty($typo3Site, 'host', $languageUid, 'write', 'localhost'),
321 206
                        'port' => (int)SiteUtility::getConnectionProperty($typo3Site, 'port', $languageUid, 'write', 8983),
322
                        // @todo: transform core to path
323
                        'path' =>
324 206
                            SiteUtility::getConnectionProperty($typo3Site, 'path', $languageUid, 'read', '/solr/') .
325 206
                            SiteUtility::getConnectionProperty($typo3Site, 'core', $languageUid, 'read', 'core_en') . '/' ,
326 206
                        'username' => SiteUtility::getConnectionProperty($typo3Site, 'username', $languageUid, 'write', ''),
327 206
                        'password' => SiteUtility::getConnectionProperty($typo3Site, 'password', $languageUid, 'write', '')
328
                    ],
329
330 206
                    'language' => $languageUid
331
                ];
332
            }
333
        }
334
335 206
        $solrConfiguration = $this->frontendEnvironment->getSolrConfigurationFromPageId(
336 206
            $rootPageRecord['uid'],
337 206
            $tsfeToUseForTypoScriptConfiguration->getLanguage()->getLanguageId()
338
        );
339
340 206
        return GeneralUtility::makeInstance(
341 206
            Site::class,
342
            /** @scrutinizer ignore-type */
343 206
            $solrConfiguration,
344
            /** @scrutinizer ignore-type */
345 206
            $rootPageRecord,
346
            /** @scrutinizer ignore-type */
347 206
            $domain,
348
            /** @scrutinizer ignore-type */
349 206
            $siteHash,
350
            /** @scrutinizer ignore-type */
351 206
            $pageRepository,
352
            /** @scrutinizer ignore-type */
353 206
            $defaultLanguage,
354
            /** @scrutinizer ignore-type */
355 206
            $availableLanguageIds,
356
            /** @scrutinizer ignore-type */
357 206
            $solrConnectionConfigurations,
358
            /** @scrutinizer ignore-type */
359 206
            $typo3Site
360
        );
361
    }
362
363
    /**
364
     * Returns {@link \TYPO3\CMS\Core\Site\Entity\Site}.
365
     *
366
     * @param int $pageUid
367
     * @return CoreSite|null
368
     */
369 206
    protected function getTypo3Site(int $pageUid): ?CoreSite
370
    {
371
        try {
372 206
            return $this->siteFinder->getSiteByPageId($pageUid);
373
        } catch (Throwable $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
374
        return null;
375
    }
376
377
}
378