Completed
Push — master ( 1ccb29...3a83fd )
by Timo
47s
created

Site   B

Complexity

Total Complexity 41

Size/Duplication

Total Lines 420
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 5

Test Coverage

Coverage 79.23%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 41
c 2
b 0
f 0
lcom 2
cbo 5
dl 0
loc 420
ccs 103
cts 130
cp 0.7923
rs 8.2769

20 Methods

Rating   Name   Duplication   Size   Complexity  
A getSiteByPageId() 0 11 2
B getAvailableSitesSelector() 0 22 4
A getFirstAvailableSite() 0 5 1
A clearSitePagesCache() 0 4 1
A isRootPage() 0 8 2
A getRootPageId() 0 4 1
A getLabel() 0 12 2
A getSolrServersFromRegistry() 0 7 1
A getSolrConfiguration() 0 4 1
A getLanguages() 0 17 3
A getDefaultLanguage() 0 17 1
B getPages() 0 29 4
B getPageIdsFromCurrentDepthAndCallRecursive() 0 25 4
A getSiteHash() 0 6 1
A getDomain() 0 7 1
A getRootPage() 0 4 1
A getTitle() 0 4 1
A getSysLanguageMode() 0 9 2
A __construct() 0 14 2
B getAvailableSites() 0 31 6

How to fix   Complexity   

Complex Class

Complex classes like Site often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Site, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace ApacheSolrForTypo3\Solr;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2011-2015 Ingo Renner <[email protected]>
8
 *  All rights reserved
9
 *
10
 *  This script is part of the TYPO3 project. The TYPO3 project is
11
 *  free software; you can redistribute it and/or modify
12
 *  it under the terms of the GNU General Public License as published by
13
 *  the Free Software Foundation; either version 2 of the License, or
14
 *  (at your option) any later version.
15
 *
16
 *  The GNU General Public License can be found at
17
 *  http://www.gnu.org/copyleft/gpl.html.
18
 *
19
 *  This script is distributed in the hope that it will be useful,
20
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 *  GNU General Public License for more details.
23
 *
24
 *  This copyright notice MUST APPEAR in all copies of the script!
25
 ***************************************************************/
26
27
use ApacheSolrForTypo3\Solr\Domain\Site\SiteHashService;
28
use ApacheSolrForTypo3\Solr\Domain\Index\Queue\RecordMonitor\Helper\ConfigurationAwareRecordService;
29
use TYPO3\CMS\Backend\Utility\BackendUtility;
30
use TYPO3\CMS\Core\Registry;
31
use TYPO3\CMS\Core\Utility\GeneralUtility;
32
use TYPO3\CMS\Frontend\Page\PageRepository;
33
34
/**
35
 * A site is a branch in a TYPO3 installation. Each site's root page is marked
36
 * by the "Use as Root Page" flag.
37
 *
38
 * @author Ingo Renner <[email protected]>
39
 */
40
class Site
41
{
42
    /**
43
     * Cache for ApacheSolrForTypo3\Solr\Site objects
44
     *
45
     * @var array
46
     */
47
    protected static $sitesCache = [];
48
49
    /**
50
     * Small cache for the list of pages in a site, so that the results of this
51
     * rather expensive operation can be used by all initializers without having
52
     * each initializer do it again.
53
     *
54
     * TODO Move to caching framework once TYPO3 4.6 is the minimum required
55
     * version.
56
     *
57
     * @var array
58
     */
59
    protected static $sitePagesCache = [];
60
61
    /**
62
     * Root page record.
63
     *
64
     * @var array
65
     */
66
    protected $rootPage = [];
67
68
    /**
69
     * The site's sys_language_mode
70
     *
71
     * @var string
72
     */
73
    protected $sysLanguageMode = null;
74
75
    /**
76
     * Constructor.
77
     *
78
     * @param int $rootPageId Site root page ID (uid). The page must be marked as site root ("Use as Root Page" flag).
79
     */
80 97
    public function __construct($rootPageId)
81
    {
82 97
        $page = BackendUtility::getRecord('pages', $rootPageId);
83
84 97
        if (!self::isRootPage($page)) {
0 ignored issues
show
Bug introduced by
It seems like $page defined by \TYPO3\CMS\Backend\Utili...d('pages', $rootPageId) on line 82 can also be of type null; however, ApacheSolrForTypo3\Solr\Site::isRootPage() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
85 4
            throw new \InvalidArgumentException(
86 4
                'The page for the given page ID \'' . $rootPageId
87 4
                . '\' is not marked as root page and can therefore not be used as site root page.',
88 4
                1309272922
89
            );
90
        }
91
92 93
        $this->rootPage = $page;
0 ignored issues
show
Documentation Bug introduced by
It seems like $page can be null. However, the property $rootPage is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

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

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
93 93
    }
94
95
    /**
96
     * Gets the Site for a specific page Id.
97
     *
98
     * @param int $pageId The page Id to get a Site object for.
99
     * @return Site Site for the given page Id.
100
     */
101 57
    public static function getSiteByPageId($pageId)
102
    {
103 57
        $rootPageId = Util::getRootPageId($pageId);
104
105 57
        if (!isset(self::$sitesCache[$rootPageId])) {
106 57
            self::$sitesCache[$rootPageId] = GeneralUtility::makeInstance(__CLASS__,
107
                $rootPageId);
108
        }
109
110 57
        return self::$sitesCache[$rootPageId];
111
    }
112
113
    /**
114
     * Creates a dropdown selector of available TYPO3 sites with Solr
115
     * configured.
116
     *
117
     * @param string $selectorName Name to be used in the select's name attribute
118
     * @param Site $selectedSite Optional, currently selected site
119
     * @return string Site selector HTML code
120
     * @todo Extract into own class like indexing configuration selector
121
     */
122
    public static function getAvailableSitesSelector(
123
        $selectorName,
124
        Site $selectedSite = null
125
    ) {
126
        $sites = self::getAvailableSites();
127
        $selector = '<select name="' . $selectorName . '" class="form-control">';
128
129
        foreach ($sites as $site) {
130
            $selectedAttribute = '';
131
            if ($selectedSite !== null && $site->getRootPageId() == $selectedSite->getRootPageId()) {
132
                $selectedAttribute = ' selected="selected"';
133
            }
134
135
            $selector .= '<option value="' . $site->getRootPageId() . '"' . $selectedAttribute . '>'
136
                . $site->getLabel()
137
                . '</option>';
138
        }
139
140
        $selector .= '</select>';
141
142
        return $selector;
143
    }
144
145
    /**
146
     * Gets all available TYPO3 sites with Solr configured.
147
     *
148
     * @param bool $stopOnInvalidSite
149
     *
150
     * @return Site[] An array of available sites
151
     */
152 59
    public static function getAvailableSites($stopOnInvalidSite = false)
153
    {
154 59
        static $sitesCached;
155 59
        $sites = [];
156
157
        // Check if $sites has been cached
158 59
        if (isset($sitesCached)) {
159 15
            return $sitesCached;
160
        }
161
162 59
        $servers = self::getSolrServersFromRegistry();
163
164 59
        foreach ($servers as $server) {
165 59
            if (isset($sites[$server['rootPageUid']])) {
166
                //get each site only once
167 1
                continue;
168
            }
169
170
            try {
171 59
                $sites[$server['rootPageUid']] = GeneralUtility::makeInstance(__CLASS__, $server['rootPageUid']);
172 4
            } catch (\InvalidArgumentException $e) {
173 4
                if ($stopOnInvalidSite) {
174 59
                    throw $e;
175
                }
176
            }
177
        }
178
179 59
        $sitesCached = $sites;
180
181 59
        return $sitesCached;
182
    }
183
184
    /**
185
     * Returns the first available Site.
186
     *
187
     * @param bool $stopOnInvalidSite
188
     *
189
     * @return Site
190
     */
191 20
    public static function getFirstAvailableSite($stopOnInvalidSite = false)
192
    {
193 20
        $sites = self::getAvailableSites($stopOnInvalidSite);
194 20
        return array_shift($sites);
195
    }
196
197
    /**
198
     * Clears the $sitePagesCache
199
     *
200
     */
201
    public static function clearSitePagesCache()
202
    {
203
        self::$sitePagesCache = [];
204
    }
205
206
    /**
207
     * Takes an pagerecord and checks whether the page is marked as root page.
208
     *
209
     * @param array $page pagerecord
210
     * @return bool true if the page is marked as root page, false otherwise
211
     */
212 103
    public static function isRootPage($page)
213
    {
214 103
        if ($page['is_siteroot']) {
215 103
            return true;
216
        }
217
218 55
        return false;
219
    }
220
221
    /**
222
     * Gets the site's root page ID (uid).
223
     *
224
     * @return int The site's root page ID.
225
     */
226 32
    public function getRootPageId()
227
    {
228 32
        return $this->rootPage['uid'];
229
    }
230
231
    /**
232
     * Gets the site's label. The label is build from the the site title and root
233
     * page ID (uid).
234
     *
235
     * @return string The site's label.
236
     */
237 9
    public function getLabel()
238
    {
239 9
        $rootlineTitles = [];
240 9
        $rootLine = BackendUtility::BEgetRootLine($this->rootPage['uid']);
241
        // Remove last
242 9
        array_pop($rootLine);
243 9
        $rootLine = array_reverse($rootLine);
244 9
        foreach ($rootLine as $rootLineItem) {
245 9
            $rootlineTitles[] = $rootLineItem['title'];
246
        }
247 9
        return implode(' - ', $rootlineTitles) . ', Root Page ID: ' . $this->rootPage['uid'];
248
    }
249
250
    /**
251
     * Retrieves the configured solr servers from the registry.
252
     *
253
     * @return array
254
     */
255 59
    protected static function getSolrServersFromRegistry()
256
    {
257
        /** @var $registry Registry */
258 59
        $registry = GeneralUtility::makeInstance(Registry::class);
259 59
        $servers = (array) $registry->get('tx_solr', 'servers', []);
260 59
        return $servers;
261
    }
262
263
    /**
264
     * Gets the site's Solr TypoScript configuration (plugin.tx_solr.*)
265
     *
266
     * @return  \ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration The Solr TypoScript configuration
267
     */
268 49
    public function getSolrConfiguration()
269
    {
270 49
        return Util::getSolrConfigurationFromPageId($this->rootPage['uid']);
271
    }
272
273
    /**
274
     * Gets the system languages (IDs) for which Solr connections have been
275
     * configured.
276
     *
277
     * @return array Array of system language IDs for which connections have been configured on this site.
278
     */
279
    public function getLanguages()
280
    {
281
        $siteLanguages = [];
282
283
        $servers = self::getSolrServersFromRegistry();
284
285
        foreach ($servers as $connectionKey => $solrConnection) {
286
            list($siteRootPageId, $systemLanguageId) = explode('|',
287
                $connectionKey);
288
289
            if ($siteRootPageId == $this->rootPage['uid']) {
290
                $siteLanguages[] = $systemLanguageId;
291
            }
292
        }
293
294
        return $siteLanguages;
295
    }
296
297
    /**
298
     * Gets the site's default language as configured in
299
     * config.sys_language_uid. If sys_language_uid is not set, 0 is assumed to
300
     * be the default.
301
     *
302
     * @return int The site's default language.
303
     */
304 1
    public function getDefaultLanguage()
305
    {
306 1
        $siteDefaultLanguage = 0;
307
308 1
        $configuration = Util::getConfigurationFromPageId(
309 1
            $this->rootPage['uid'],
310 1
            'config',
311 1
            false,
312 1
            false
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
313
        );
314
315 1
        $siteDefaultLanguage = $configuration->getValueByPathOrDefaultValue('sys_language_uid', $siteDefaultLanguage);
316
        // default language is set through default L GET parameter -> overruling config.sys_language_uid
317 1
        $siteDefaultLanguage = $configuration->getValueByPathOrDefaultValue('defaultGetVars.L', $siteDefaultLanguage);
318
319 1
        return $siteDefaultLanguage;
320
    }
321
322
    /**
323
     * Generates a list of page IDs in this site. Attention, this includes
324
     * all page types! Deleted pages are not included.
325
     *
326
     * @param int|string $rootPageId Page ID from where to start collection sub pages
327
     * @param int $maxDepth Maximum depth to descend into the site tree
328
     * @return array Array of pages (IDs) in this site
329
     */
330 8
    public function getPages($rootPageId = 'SITE_ROOT', $maxDepth = 999)
331
    {
332
        // when we have a cached value, we can return it.
333 8
        if (!empty(self::$sitePagesCache[$rootPageId])) {
334 7
            return self::$sitePagesCache[$rootPageId];
335
        }
336
337 8
        $pageIds = [];
338 8
        $maxDepth = intval($maxDepth);
339
340 8
        $recursionRootPageId = intval($rootPageId);
341 8
        if ($rootPageId == 'SITE_ROOT') {
342 8
            $recursionRootPageId = $this->rootPage['uid'];
343 8
            $pageIds[] = (int) $this->rootPage['uid'];
344
        }
345
346 8
        if ($maxDepth <= 0) {
347
            // exiting the recursion loop, may write to cache now
348
            self::$sitePagesCache[$rootPageId] = $pageIds;
349
            return $pageIds;
350
        }
351
352
        // get the page ids of the current level and if needed call getPages recursive
353 8
        $pageIds = $this->getPageIdsFromCurrentDepthAndCallRecursive($maxDepth, $recursionRootPageId, $pageIds);
354
355
        // exiting the recursion loop, may write to cache now
356 8
        self::$sitePagesCache[$rootPageId] = $pageIds;
357 8
        return $pageIds;
358
    }
359
360
    /**
361
     * This method retrieves the pages ids from the current tree level an calls getPages recursive,
362
     * when the maxDepth has not been reached.
363
     *
364
     * @param int $maxDepth
365
     * @param int $recursionRootPageId
366
     * @param array $pageIds
367
     * @return array
368
     */
369 8
    protected function getPageIdsFromCurrentDepthAndCallRecursive($maxDepth, $recursionRootPageId, $pageIds)
370
    {
371 8
        static $initialPagesAdditionalWhereClause;
372
373
        // Only fetch $initialPagesAdditionalWhereClause on first call
374 8
        if (empty($initialPagesAdditionalWhereClause)) {
375 8
            $configurationAwareRecordService = GeneralUtility::makeInstance(ConfigurationAwareRecordService::class);
376
            // Fetch configuration in order to be able to read initialPagesAdditionalWhereClause
377 8
            $solrConfiguration = $this->getSolrConfiguration();
378 8
            $indexQueueConfigurationName = $configurationAwareRecordService->getIndexingConfigurationName('pages', $this->rootPage['uid'], $solrConfiguration);
379 8
            $initialPagesAdditionalWhereClause = $solrConfiguration->getInitialPagesAdditionalWhereClause($indexQueueConfigurationName);
380
        }
381
382 8
        $result = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', 'pages', 'pid = ' . $recursionRootPageId . ' ' . BackendUtility::deleteClause('pages') . $initialPagesAdditionalWhereClause);
383
384 8
        while ($page = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($result)) {
385 6
            $pageIds[] = (int) $page['uid'];
386
387 6
            if ($maxDepth > 1) {
388 6
                $pageIds = array_merge($pageIds, $this->getPages($page['uid'], $maxDepth - 1));
389
            }
390
        }
391 8
        $GLOBALS['TYPO3_DB']->sql_free_result($result);
392 8
        return $pageIds;
393
    }
394
395
    /**
396
     * Generates the site's unique Site Hash.
397
     *
398
     * The Site Hash is build from the site's main domain, the system encryption
399
     * key, and the extension "tx_solr". These components are concatenated and
400
     * sha1-hashed.
401
     *
402
     * @return string Site Hash.
403
     */
404 52
    public function getSiteHash()
405
    {
406
        /** @var $siteHashService SiteHashService */
407 52
        $siteHashService = GeneralUtility::makeInstance(SiteHashService::class);
408 52
        return $siteHashService->getSiteHashForDomain($this->getDomain());
409
    }
410
411
    /**
412
     * Gets the site's main domain. More specifically the first domain record in
413
     * the site tree.
414
     *
415
     * @return string The site's main domain.
416
     */
417 56
    public function getDomain()
418
    {
419 56
        $pageSelect = GeneralUtility::makeInstance(PageRepository::class);
420 56
        $rootLine = $pageSelect->getRootLine($this->rootPage['uid']);
421
422 56
        return BackendUtility::firstDomainRecord($rootLine);
423
    }
424
425
    /**
426
     * Gets the site's root page.
427
     *
428
     * @return array The site's root page.
429
     */
430 1
    public function getRootPage()
431
    {
432 1
        return $this->rootPage;
433
    }
434
435
    /**
436
     * Gets the site's root page's title.
437
     *
438
     * @return string The site's root page's title
439
     */
440
    public function getTitle()
441
    {
442
        return $this->rootPage['title'];
443
    }
444
445
    /**
446
     * Gets the site's config.sys_language_mode setting
447
     *
448
     * @return string The site's config.sys_language_mode
449
     */
450 11
    public function getSysLanguageMode()
451
    {
452 11
        if (is_null($this->sysLanguageMode)) {
453 11
            Util::initializeTsfe($this->getRootPageId());
454 11
            $this->sysLanguageMode = $GLOBALS['TSFE']->sys_language_mode;
455
        }
456
457 11
        return $this->sysLanguageMode;
458
    }
459
}
460