Failed Conditions
Push — master ( 5f60a5...9b80eb )
by Rafael
21:42
created

Classes/ConnectionManager.php (2 issues)

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 3 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\SiteRepository;
28
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
29
use ApacheSolrForTypo3\Solr\System\Page\Rootline;
30
use ApacheSolrForTypo3\Solr\System\Records\Pages\PagesRepository as PagesRepositoryAtExtSolr;
31
use ApacheSolrForTypo3\Solr\System\Records\SystemLanguage\SystemLanguageRepository;
32
use ApacheSolrForTypo3\Solr\System\Solr\SolrConnection;
33
use TYPO3\CMS\Backend\Routing\UriBuilder;
0 ignored issues
show
The type TYPO3\CMS\Backend\Routing\UriBuilder was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
34
use TYPO3\CMS\Backend\Toolbar\ClearCacheActionsHookInterface;
0 ignored issues
show
The type TYPO3\CMS\Backend\Toolba...cheActionsHookInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

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