Completed
Pull Request — master (#808)
by
unknown
26:57 queued 23:22
created

ConnectionManager::getLanguageName()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3.0052

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 11
cts 12
cp 0.9167
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 11
nc 3
nop 1
crap 3.0052
1
<?php
2
namespace ApacheSolrForTypo3\Solr;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2010-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\NoSolrConnectionFoundException;
28
use ApacheSolrForTypo3\Solr\Site;
29
use ApacheSolrForTypo3\Solr\SolrService;
30
use ApacheSolrForTypo3\Solr\System\Page\Rootline;
31
use TYPO3\CMS\Backend\Routing\UriBuilder;
32
use TYPO3\CMS\Backend\Toolbar\ClearCacheActionsHookInterface;
33
use TYPO3\CMS\Core\Imaging\Icon;
34
use TYPO3\CMS\Core\Imaging\IconFactory;
35
use TYPO3\CMS\Core\Registry;
36
use TYPO3\CMS\Core\SingletonInterface;
37
use TYPO3\CMS\Core\TypoScript\ExtendedTemplateService;
38
use TYPO3\CMS\Core\Utility\GeneralUtility;
39
use TYPO3\CMS\Frontend\Page\PageRepository;
40
41
/**
42
 * A class to easily create a connection to a Solr server.
43
 *
44
 * Internally keeps track of already existing connections and makes sure that no
45
 * duplicate connections are created.
46
 *
47
 * @author Ingo Renner <[email protected]>
48
 */
49
class ConnectionManager implements SingletonInterface, ClearCacheActionsHookInterface
50
{
51
52
    /**
53
     * @var array
54
     */
55
    protected static $connections = [];
56
57
    /**
58
     * Gets a Solr connection.
59
     *
60
     * Instead of generating a new connection with each call, connections are
61
     * kept and checked whether the requested connection already exists. If a
62
     * connection already exists, it's reused.
63
     *
64
     * @param string $host Solr host (optional)
65
     * @param int $port Solr port (optional)
66
     * @param string $path Solr path (optional)
67
     * @param string $scheme Solr scheme, defaults to http, can be https (optional)
68
     * @return SolrService A solr connection.
69
     */
70 57
    public function getConnection($host = '', $port = 8983, $path = '/solr/', $scheme = 'http')
71
    {
72 57
        $connection = null;
0 ignored issues
show
Unused Code introduced by
$connection is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
73
74 57
        if (empty($host)) {
75 1
            GeneralUtility::devLog(
76
                'ApacheSolrForTypo3\Solr\ConnectionManager::getConnection() called with empty
77
                host parameter. Using configuration from TSFE, might be
78
                inaccurate. Always provide a host or use the getConnectionBy*
79 1
                methods.',
80 1
                'solr',
81
                2
82 1
            );
83
84 1
            $configuration = Util::getSolrConfiguration();
85 1
            $solrConfiguration = $configuration->getValueByPathOrDefaultValue('plugin.tx_solr.solr.', []);
86 1
            $host = $solrConfiguration['host'];
87 1
            $port = $solrConfiguration['port'];
88 1
            $path = $solrConfiguration['path'];
89 1
            $scheme = $solrConfiguration['scheme'];
90 1
        }
91
92 57
        $connectionHash = md5($scheme . '://' . $host . $port . $path);
93
94 57
        if (!isset(self::$connections[$connectionHash])) {
95 57
            $connection = GeneralUtility::makeInstance(SolrService::class, $host, $port, $path, $scheme);
96 56
            self::$connections[$connectionHash] = $connection;
97 56
        }
98
99 56
        return self::$connections[$connectionHash];
100
    }
101
102
    /**
103
     * Creates a solr configuration from the configuration array and returns it.
104
     *
105
     * @param array $config
106
     * @return SolrService
107
     */
108 54
    protected function getConnectionFromConfiguration(array $config)
109
    {
110 54
        return $this->getConnection($config['solrHost'], $config['solrPort'], $config['solrPath'], $config['solrScheme']);
111
    }
112
113
    /**
114
     * Gets a Solr configuration for a page ID.
115
     *
116
     * @param int $pageId A page ID.
117
     * @param int $language The language ID to get the connection for as the path may differ. Optional, defaults to 0.
118
     * @param string $mount Comma list of MountPoint parameters
119
     * @return array A solr configuration.
120
     * @throws NoSolrConnectionFoundException
121
     */
122 45
    public function getConfigurationByPageId($pageId, $language = 0, $mount = '')
123
    {
124
        // find the root page
125 45
        $pageSelect = GeneralUtility::makeInstance(PageRepository::class);
126
127
        /** @var Rootline $rootLine */
128 45
        $rootLine = GeneralUtility::makeInstance(Rootline::class, $pageSelect->getRootLine($pageId, $mount));
129 45
        $siteRootPageId = $rootLine->getRootPageId();
130
131
        try {
132 45
            $solrConfiguration = $this->getConfigurationByRootPageId($siteRootPageId, $language);
133 45
        } catch (NoSolrConnectionFoundException $nscfe) {
134
            /* @var $noSolrConnectionException NoSolrConnectionFoundException */
135 1
            $noSolrConnectionException = GeneralUtility::makeInstance(
136 1
                NoSolrConnectionFoundException::class,
137 1
                $nscfe->getMessage() . ' Initial page used was [' . $pageId . ']',
138
                1275399922
139 1
            );
140 1
            $noSolrConnectionException->setPageId($pageId);
141
142 1
            throw $noSolrConnectionException;
143
        }
144
145 44
        return $solrConfiguration;
146
    }
147
148
    /**
149
     * Gets a Solr connection for a page ID.
150
     *
151
     * @param int $pageId A page ID.
152
     * @param int $language The language ID to get the connection for as the path may differ. Optional, defaults to 0.
153
     * @param string $mount Comma list of MountPoint parameters
154
     * @return SolrService A solr connection.
155
     * @throws NoSolrConnectionFoundException
156
     */
157 45
    public function getConnectionByPageId($pageId, $language = 0, $mount = '')
158
    {
159 45
        $solrServer = $this->getConfigurationByPageId($pageId, $language, $mount);
160 44
        $solrConnection = $this->getConnectionFromConfiguration($solrServer);
161
162 44
        return $solrConnection;
163
    }
164
165
    /**
166
     * Gets a Solr configuration for a root page ID.
167
     *
168
     * @param int $pageId A root page ID.
169
     * @param int $language The language ID to get the configuration for as the path may differ. Optional, defaults to 0.
170
     * @return array A solr configuration.
171
     * @throws NoSolrConnectionFoundException
172
     */
173 46
    public function getConfigurationByRootPageId($pageId, $language = 0)
174
    {
175 46
        $connectionKey = $pageId . '|' . $language;
176 46
        $solrServers = $this->getAllConfigurations();
177
178 46
        if (isset($solrServers[$connectionKey])) {
179 44
            $solrConfiguration = $solrServers[$connectionKey];
180 44
        } else {
181
            /* @var $noSolrConnectionException NoSolrConnectionFoundException */
182 2
            $noSolrConnectionException = GeneralUtility::makeInstance(
183 2
                NoSolrConnectionFoundException::class,
184
                'Could not find a Solr connection for root page ['
185 2
                . $pageId . '] and language [' . $language . '].',
186
                1275396474
187 2
            );
188 2
            $noSolrConnectionException->setRootPageId($pageId);
189 2
            $noSolrConnectionException->setLanguageId($language);
190
191 2
            throw $noSolrConnectionException;
192
        }
193
194 44
        return $solrConfiguration;
195
    }
196
197
    /**
198
     * Gets a Solr connection for a root page ID.
199
     *
200
     * @param int $pageId A root page ID.
201
     * @param int $language The language ID to get the connection for as the path may differ. Optional, defaults to 0.
202
     * @return SolrService A solr connection.
203
     * @throws NoSolrConnectionFoundException
204
     */
205 2
    public function getConnectionByRootPageId($pageId, $language = 0)
206
    {
207 2
        $config = $this->getConfigurationByRootPageId($pageId, $language);
208 2
        $solrConnection = $this->getConnectionFromConfiguration($config);
209
210 2
        return $solrConnection;
211
    }
212
213
    /**
214
     * Gets all connection configurations found.
215
     *
216
     * @return array An array of connection configurations.
217
     */
218 56
    public function getAllConfigurations()
219
    {
220
        /** @var $registry Registry */
221 56
        $registry = GeneralUtility::makeInstance(Registry::class);
222 56
        $solrConfigurations = $registry->get('tx_solr', 'servers', []);
223
224 56
        return $solrConfigurations;
225
    }
226
227
    /**
228
     * Stores the connections in the registry.
229
     *
230
     * @param array $solrConfigurations
231
     */
232 3
    protected function setAllConfigurations(array $solrConfigurations)
233
    {
234
        /** @var $registry Registry */
235 3
        $registry = GeneralUtility::makeInstance(Registry::class);
236 3
        $registry->set('tx_solr', 'servers', $solrConfigurations);
237 3
    }
238
239
    /**
240
     * Gets all connections found.
241
     *
242
     * @return SolrService[] An array of initialized ApacheSolrForTypo3\Solr\SolrService connections
243
     */
244 6
    public function getAllConnections()
245
    {
246 6
        $connections = [];
247
248 6
        $solrConfigurations = $this->getAllConfigurations();
249 6
        foreach ($solrConfigurations as $solrConfiguration) {
250 6
            $connections[] = $this->getConnectionFromConfiguration($solrConfiguration);
251 6
        }
252
253 6
        return $connections;
254
    }
255
256
    /**
257
     * Gets all connection configurations for a given site.
258
     *
259
     * @param Site $site A TYPO3 site
260
     * @return array An array of Solr connection configurations for a site
261
     */
262 15
    public function getConfigurationsBySite(Site $site)
263
    {
264 15
        $solrConfigurations = [];
265
266 15
        $allConfigurations = $this->getAllConfigurations();
267 15
        foreach ($allConfigurations as $configuration) {
268 15
            if ($configuration['rootPageUid'] == $site->getRootPageId()) {
269 15
                $solrConfigurations[] = $configuration;
270 15
            }
271 15
        }
272
273 15
        return $solrConfigurations;
274
    }
275
276
    /**
277
     * Gets all connections configured for a given site.
278
     *
279
     * @param Site $site A TYPO3 site
280
     * @return SolrService[] An array of Solr connection objects (ApacheSolrForTypo3\Solr\SolrService)
281
     */
282 5
    public function getConnectionsBySite(Site $site)
283
    {
284 5
        $connections = [];
285
286 5
        $solrServers = $this->getConfigurationsBySite($site);
287 5
        foreach ($solrServers as $solrServer) {
288 5
            $connections[] = $this->getConnectionFromConfiguration($solrServer);
289 5
        }
290
291 5
        return $connections;
292
    }
293
294
    // updates
295
296
    /**
297
     * Adds a menu entry to the clear cache menu to detect Solr connections.
298
     *
299
     * @param array $cacheActions Array of CacheMenuItems
300
     * @param array $optionValues Array of AccessConfigurations-identifiers (typically  used by userTS with options.clearCache.identifier)
301
     */
302
    public function manipulateCacheActions(&$cacheActions, &$optionValues)
303
    {
304
        if ($GLOBALS['BE_USER']->isAdmin()) {
305
            $title = 'Initialize Solr connections';
306
            $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
307
308
            $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
309
            $cacheActions[] = [
310
                'id' => 'clearSolrConnectionCache',
311
                'title' => $title,
312
                'href' =>  $uriBuilder->buildUriFromRoute('ajax_solr_updateConnections'),
313
                'icon' => $iconFactory->getIcon('extensions-solr-module-initsolrconnections', Icon::SIZE_SMALL)
314
            ];
315
            $optionValues[] = 'clearSolrConnectionCache';
316
        }
317
    }
318
319
    /**
320
     * Updates the connections in the registry.
321
     *
322
     */
323 2
    public function updateConnections()
324
    {
325 2
        $solrConnections = $this->getConfiguredSolrConnections();
326 2
        $solrConnections = $this->filterDuplicateConnections($solrConnections);
327
328 2
        if (!empty($solrConnections)) {
329 2
            $this->setAllConfigurations($solrConnections);
330 2
        }
331 2
    }
332
333
    /**
334
     * Entrypoint for the ajax request
335
     */
336
    public function updateConnectionsInCacheMenu()
337
    {
338
        $this->updateConnections();
339
    }
340
341
    /**
342
     * Updates the Solr connections for a specific root page ID / site.
343
     *
344
     * @param int $rootPageId A site root page id
345
     */
346 1
    public function updateConnectionByRootPageId($rootPageId)
347
    {
348 1
        $systemLanguages = $this->getSystemLanguages();
349 1
        $rootPage = GeneralUtility::makeInstance(Site::class, $rootPageId)->getRootPage();
350
351 1
        $updatedSolrConnections = [];
352 1
        foreach ($systemLanguages as $languageId) {
353 1
            $connection = $this->getConfiguredSolrConnectionByRootPage($rootPage, $languageId);
354
355 1
            if (!empty($connection)) {
356 1
                $updatedSolrConnections[$connection['connectionKey']] = $connection;
357 1
            }
358 1
        }
359
360 1
        $solrConnections = $this->getAllConfigurations();
361 1
        $solrConnections = array_merge($solrConnections, $updatedSolrConnections);
362 1
        $solrConnections = $this->filterDuplicateConnections($solrConnections);
363 1
        $this->setAllConfigurations($solrConnections);
364 1
    }
365
366
    /**
367
     * Finds the configured Solr connections. Also respects multi-site
368
     * environments.
369
     *
370
     * @return array An array with connections, each connection with keys rootPageTitle, rootPageUid, solrHost, solrPort, solrPath
371
     */
372 2
    protected function getConfiguredSolrConnections()
373
    {
374 2
        $configuredSolrConnections = [];
375
376
        // find website roots and languages for this installation
377 2
        $rootPages = $this->getRootPages();
378 2
        $languages = $this->getSystemLanguages();
379
380
        // find solr configurations and add them as function menu entries
381 2
        foreach ($rootPages as $rootPage) {
382 2
            foreach ($languages as $languageId) {
383 2
                $connection = $this->getConfiguredSolrConnectionByRootPage($rootPage,
384 2
                    $languageId);
385
386 2
                if (!empty($connection)) {
387 2
                    $configuredSolrConnections[$connection['connectionKey']] = $connection;
388 2
                }
389 2
            }
390 2
        }
391
392 2
        return $configuredSolrConnections;
393
    }
394
395
    /**
396
     * Gets the configured Solr connection for a specific root page and language ID.
397
     *
398
     * @param array $rootPage A root page record with at least title and uid
399
     * @param int $languageId ID of a system language
400
     * @return array A solr connection configuration.
401
     */
402 3
    protected function getConfiguredSolrConnectionByRootPage(array $rootPage, $languageId)
403
    {
404 3
        $connection = [];
405
406 3
        $languageId = intval($languageId);
407 3
        GeneralUtility::_GETset($languageId, 'L');
408 3
        $connectionKey = $rootPage['uid'] . '|' . $languageId;
409
410 3
        $pageSelect = GeneralUtility::makeInstance(PageRepository::class);
411 3
        $rootLine = $pageSelect->getRootLine($rootPage['uid']);
412
413 3
        $tmpl = GeneralUtility::makeInstance(ExtendedTemplateService::class);
414 3
        $tmpl->tt_track = false; // Do not log time-performance information
415 3
        $tmpl->init();
416 3
        $tmpl->runThroughTemplates($rootLine); // This generates the constants/config + hierarchy info for the template.
417
418
        // fake micro TSFE to get correct condition parsing
419 3
        $GLOBALS['TSFE'] = new \stdClass();
420 3
        $GLOBALS['TSFE']->tmpl = new \stdClass();
421 3
        $GLOBALS['TSFE']->tmpl->rootLine = $rootLine;
422 3
        $GLOBALS['TSFE']->sys_page = $pageSelect;
423 3
        $GLOBALS['TSFE']->id = $rootPage['uid'];
424 3
        $GLOBALS['TSFE']->page = $rootPage;
425
426 3
        $tmpl->generateConfig();
427
428 3
        list($solrSetup) = $tmpl->ext_getSetup($tmpl->setup,
429 3
            'plugin.tx_solr.solr');
430 3
        list(, $solrEnabled) = $tmpl->ext_getSetup($tmpl->setup,
431 3
            'plugin.tx_solr.enabled');
432 3
        $solrEnabled = !empty($solrEnabled) ? true : false;
433
434 3
        if (!empty($solrSetup) && $solrEnabled) {
435 3
            $solrPath = trim($solrSetup['path'], '/');
436 3
            $solrPath = '/' . $solrPath . '/';
437
438
            $connection = [
439 3
                'connectionKey' => $connectionKey,
440
441 3
                'rootPageTitle' => $rootPage['title'],
442 3
                'rootPageUid' => $rootPage['uid'],
443
444 3
                'solrScheme' => $solrSetup['scheme'],
445 3
                'solrHost' => $solrSetup['host'],
446 3
                'solrPort' => $solrSetup['port'],
447 3
                'solrPath' => $solrPath,
448
449
                'language' => $languageId
450 3
            ];
451 3
            $connection['label'] = $this->buildConnectionLabel($connection);
452 3
        }
453
454 3
        return $connection;
455
    }
456
457
    /**
458
     * Gets the language name for a given language ID.
459
     *
460
     * @param int $languageId language ID
461
     * @return string Language name
462
     */
463 3
    protected function getLanguageName($languageId)
464
    {
465 3
        $languageName = '';
466
467 3
        $language = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
468 3
            'uid, title',
469 3
            'sys_language',
470
            'uid = ' . (integer)$languageId
471 3
        );
472
473 3
        if (count($language)) {
474
            $languageName = $language[0]['title'];
475 3
        } elseif ($languageId == 0) {
476 3
            $languageName = 'default';
477 3
        }
478
479 3
        return $languageName;
480
    }
481
482
    /**
483
     * Creates a human readable label from the connections' configuration.
484
     *
485
     * @param array $connection Connection configuration
486
     * @return string Connection label
487
     */
488 3
    protected function buildConnectionLabel(array $connection)
489
    {
490 3
        $connectionLabel = $connection['rootPageTitle']
491 3
            . ' (pid: ' . $connection['rootPageUid']
492 3
            . ', language: ' . $this->getLanguageName($connection['language'])
493 3
            . ') - '
494
#			. $connection['solrScheme'] . '://'
495 3
            . $connection['solrHost'] . ':'
496 3
            . $connection['solrPort']
497 3
            . $connection['solrPath'];
498
499 3
        return $connectionLabel;
500
    }
501
502
    /**
503
     * Filters duplicate connections. When detecting the configured connections
504
     * this is done with a little brute force by simply combining all root pages
505
     * with all languages, this method filters out the duplicates.
506
     *
507
     * @param array $connections An array of unfiltered connections, containing duplicates
508
     * @return array An array with connections, no duplicates.
509
     */
510 3
    protected function filterDuplicateConnections(array $connections)
511
    {
512 3
        $hashedConnections = [];
513 3
        $filteredConnections = [];
514
515
        // array_unique() doesn't work on multi dimensional arrays, so we need to flatten it first
516 3
        foreach ($connections as $key => $connection) {
517 3
            unset($connection['language']);
518 3
            $connectionHash = md5(implode('|', $connection));
519 3
            $hashedConnections[$key] = $connectionHash;
520 3
        }
521
522 3
        $hashedConnections = array_unique($hashedConnections);
523
524 3
        foreach ($hashedConnections as $key => $hash) {
525 3
            $filteredConnections[$key] = $connections[$key];
526 3
        }
527
528 3
        return $filteredConnections;
529
    }
530
531
    /**
532
     * Finds the system's configured languages.
533
     *
534
     * @return array An array of language IDs
535
     */
536 3
    protected function getSystemLanguages()
537
    {
538 3
        $languages = [0];
539
540 3
        $languageRecords = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
541 3
            'uid',
542 3
            'sys_language',
543
            'hidden = 0'
544 3
        );
545
546 3
        if (!is_array($languageRecords)) {
547
            return $languages;
548
        }
549
550 3
        foreach ($languageRecords as $languageRecord) {
551
            $languages[] = $languageRecord['uid'];
552 3
        }
553 3
        return $languages;
554
    }
555
556
    /**
557
     * Gets the site's root pages. The "Is root of website" flag must be set,
558
     * which usually is the case for pages with pid = 0.
559
     *
560
     * @return array An array of (partial) root page records, containing the uid and title fields
561
     */
562 2
    protected function getRootPages()
563
    {
564 2
        $rootPages = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
565 2
            'uid, title',
566 2
            'pages',
567
            'is_siteroot = 1 AND deleted = 0 AND hidden = 0 AND pid != -1'
568 2
        );
569
570 2
        return $rootPages;
571
    }
572
}
573