Completed
Branch master (b22b71)
by Timo
13:13 queued 21s
created

ConnectionManager::getSiteRootPageIdFromRootLine()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

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