Issues (216)

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\Site;
28
use ApacheSolrForTypo3\Solr\Domain\Site\SiteRepository;
29
use ApacheSolrForTypo3\Solr\System\Records\Pages\PagesRepository as PagesRepositoryAtExtSolr;
30
use ApacheSolrForTypo3\Solr\System\Records\SystemLanguage\SystemLanguageRepository;
31
use ApacheSolrForTypo3\Solr\System\Solr\Node;
32
use ApacheSolrForTypo3\Solr\System\Solr\SolrConnection;
33
use InvalidArgumentException;
34
use TYPO3\CMS\Core\Registry;
35
use TYPO3\CMS\Core\SingletonInterface;
36
use TYPO3\CMS\Core\Utility\GeneralUtility;
37
use function json_encode;
38
39
/**
40
 * ConnectionManager is responsible to create SolrConnection objects.
41
 *
42
 * @author Ingo Renner <[email protected]>
43
 */
44
class ConnectionManager implements SingletonInterface
45
{
46
47
    /**
48
     * @var array
49
     */
50
    protected static $connections = [];
51
52
    /**
53
     * @var SystemLanguageRepository
54
     */
55
    protected $systemLanguageRepository;
56
57
    /**
58
     * @var PagesRepositoryAtExtSolr
59
     */
60
    protected $pagesRepositoryAtExtSolr;
61
62
    /**
63
     * @var SiteRepository
64
     */
65
    protected $siteRepository;
66
67
68
    /**
69
     * @param SystemLanguageRepository $systemLanguageRepository
70
     * @param PagesRepositoryAtExtSolr|null $pagesRepositoryAtExtSolr
71
     * @param SiteRepository $siteRepository
72
     */
73
    public function __construct(
74
        SystemLanguageRepository $systemLanguageRepository = null,
75
        PagesRepositoryAtExtSolr $pagesRepositoryAtExtSolr = null,
76
        SiteRepository $siteRepository = null
77
    )
78 118
    {
79
        $this->systemLanguageRepository = $systemLanguageRepository ?? GeneralUtility::makeInstance(SystemLanguageRepository::class);
80 118
        $this->siteRepository           = $siteRepository ?? GeneralUtility::makeInstance(SiteRepository::class);
81 118
        $this->pagesRepositoryAtExtSolr = $pagesRepositoryAtExtSolr ?? GeneralUtility::makeInstance(PagesRepositoryAtExtSolr::class);
82 118
    }
83 118
84
    /**
85
     * Creates a solr connection for read and write endpoints
86
     *
87
     * @param array $readNodeConfiguration
88
     * @param array $writeNodeConfiguration
89
     * @return SolrConnection|object
90
     */
91
    public function getSolrConnectionForNodes(array $readNodeConfiguration, array $writeNodeConfiguration)
92
    {
93
        $connectionHash = md5(json_encode($readNodeConfiguration) .  json_encode($writeNodeConfiguration));
94
        if (!isset(self::$connections[$connectionHash])) {
95
            $readNode = Node::fromArray($readNodeConfiguration);
96
            $writeNode = Node::fromArray($writeNodeConfiguration);
97
            self::$connections[$connectionHash] = GeneralUtility::makeInstance(SolrConnection::class, $readNode, $writeNode);
0 ignored issues
show
$readNode of type ApacheSolrForTypo3\Solr\System\Solr\Node is incompatible with the type array|array<mixed,mixed> expected by parameter $constructorArguments of TYPO3\CMS\Core\Utility\G...Utility::makeInstance(). ( Ignorable by Annotation )

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

97
            self::$connections[$connectionHash] = GeneralUtility::makeInstance(SolrConnection::class, /** @scrutinizer ignore-type */ $readNode, $writeNode);
Loading history...
98
        }
99
        return self::$connections[$connectionHash];
100
    }
101
102
    /**
103
     * Creates a solr configuration from the configuration array and returns it.
104
     *
105
     * @param array $config The solr configuration array
106
     * @return SolrConnection
107
     */
108
    public function getConnectionFromConfiguration(array $config)
109
    {
110
        if(empty($config['read']) && !empty($config['solrHost'])) {
111
            throw new InvalidArgumentException('Invalid registry data please re-initialize your solr connections');
112
        }
113
114
        return $this->getSolrConnectionForNodes($config['read'], $config['write']);
115
    }
116
117
    /**
118
     * Gets a Solr configuration for a page ID.
119
     *
120
     * @param int $pageId A page ID.
121
     * @param int $language The language ID to get the connection for as the path may differ. Optional, defaults to 0.
122 109
     * @param string $mount Comma list of MountPoint parameters
123
     * @deprecated will be removed in v11, use Site object/SiteRepository directly
124 109
     * @return array A solr configuration.
125 109
     * @throws NoSolrConnectionFoundException
126 109
     */
127 109
    public function getConfigurationByPageId(int $pageId, int $language = 0, string $mount = '')
128 109
    {
129
        trigger_error('solr:deprecation: Method getConfigurationByPageId is deprecated since EXT:solr 10 and will be removed in v11, use Site object/SiteRepository directly.', E_USER_DEPRECATED);
130 109
131
        try {
132
            $site = $this->siteRepository->getSiteByPageId($pageId, $mount);
133
            $this->throwExceptionOnInvalidSite($site, 'No site for pageId ' . $pageId);
134
            return $site->getSolrConnectionConfiguration($language);
135
        } catch(InvalidArgumentException $e) {
136
            /* @var NoSolrConnectionFoundException $noSolrConnectionException */
137
            $noSolrConnectionException = $this->buildNoConnectionExceptionForPageAndLanguage($pageId, $language);
138
            throw $noSolrConnectionException;
139 109
        }
140
    }
141 109
142
    /**
143
     * Gets a Solr connection for a page ID.
144
     *
145 109
     * @param int $pageId A page ID.
146
     * @param int $language The language ID to get the connection for as the path may differ. Optional, defaults to 0.
147
     * @param string $mount Comma list of MountPoint parameters
148
     * @return SolrConnection A solr connection.
149
     * @throws NoSolrConnectionFoundException
150
     */
151
    public function getConnectionByPageId($pageId, $language = 0, $mount = '')
152
    {
153
        try {
154
            $site = $this->siteRepository->getSiteByPageId($pageId, $mount);
155
            $this->throwExceptionOnInvalidSite($site, 'No site for pageId ' . $pageId);
156
            $config = $site->getSolrConnectionConfiguration($language);
157 97
            $solrConnection = $this->getConnectionFromConfiguration($config);
158
            return $solrConnection;
159
        } catch(InvalidArgumentException $e) {
160 97
            $noSolrConnectionException = $this->buildNoConnectionExceptionForPageAndLanguage($pageId, $language);
161
            throw $noSolrConnectionException;
162
        }
163 97
    }
164 97
165
    /**
166
     * Gets a Solr configuration for a root page ID.
167 97
     *
168 4
     * @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 4
     * @return array A solr configuration.
171 4
     * @throws NoSolrConnectionFoundException
172 4
     * @deprecated will be removed in v11, use Site object/SiteRepository directly
173 4
     */
174
    public function getConfigurationByRootPageId($pageId, $language = 0)
175 4
    {
176
        trigger_error('solr:deprecation: Method getConfigurationByRootPageId is deprecated since EXT:solr 10 and will be removed in v11, use Site object/SiteRepository directly.', E_USER_DEPRECATED);
177 4
178
        try {
179
            $site = $this->siteRepository->getSiteByRootPageId($pageId);
180 94
            $this->throwExceptionOnInvalidSite($site, 'No site for pageId ' . $pageId);
181
182
            return $site->getSolrConnectionConfiguration($language);
183
        } catch(InvalidArgumentException $e) {
184
            /* @var NoSolrConnectionFoundException $noSolrConnectionException */
185
            $noSolrConnectionException = $this->buildNoConnectionExceptionForPageAndLanguage($pageId, $language);
186
            throw $noSolrConnectionException;
187
        }
188
    }
189
190
    /**
191
     * Gets a Solr connection for a root page ID.
192 97
     *
193
     * @param int $pageId A root page ID.
194 97
     * @param int $language The language ID to get the connection for as the path may differ. Optional, defaults to 0.
195 94
     * @return SolrConnection A solr connection.
196 94
     * @throws NoSolrConnectionFoundException
197
     */
198
    public function getConnectionByRootPageId($pageId, $language = 0)
199
    {
200
        try {
201
            $site = $this->siteRepository->getSiteByRootPageId($pageId);
202
            $this->throwExceptionOnInvalidSite($site, 'No site for pageId ' . $pageId);
203
            $config = $site->getSolrConnectionConfiguration($language);
204
            $solrConnection = $this->getConnectionFromConfiguration($config);
205
            return $solrConnection;
206
        } catch (InvalidArgumentException $e) {
207 98
            /* @var NoSolrConnectionFoundException $noSolrConnectionException */
208
            $noSolrConnectionException = $this->buildNoConnectionExceptionForPageAndLanguage($pageId, $language);
209 98
            throw $noSolrConnectionException;
210 98
        }
211
    }
212 98
213 96
    /**
214
     * Gets all connection configurations found.
215
     *
216 5
     * @return array An array of connection configurations.
217 5
     * @throws NoSolrConnectionFoundException
218 5
     * @deprecated will be removed in v11, use SiteRepository
219 5
     */
220
    public function getAllConfigurations()
221 5
    {
222 5
        trigger_error('solr:deprecation: Method getAllConfigurations is deprecated since EXT:solr 10 and will be removed in v11, use Site object/SiteRepository directly.', E_USER_DEPRECATED);
223
224 5
        $solrConfigurations = [];
225
        foreach ($this->siteRepository->getAvailableSites() as $site) {
226
            foreach ($site->getAllSolrConnectionConfigurations() as $solrConfiguration) {
227 96
                $solrConfigurations[] = $solrConfiguration;
228
            }
229
        }
230
231
        return $solrConfigurations;
232
    }
233
234
    /**
235
     * Stores the connections in the registry.
236
     *
237
     * @param array $solrConfigurations
238 10
     * @deprecated will be removed in v11, use SiteRepository
239
     */
240 10
    protected function setAllConfigurations(array $solrConfigurations)
241 10
    {
242
        trigger_error('solr:deprecation: Method setAllConfigurations is deprecated since EXT:solr 10 and will be removed in v11, use Site object/SiteRepository directly.', E_USER_DEPRECATED);
243 10
244
        /** @var $registry Registry */
245
        $registry = GeneralUtility::makeInstance(Registry::class);
246
        $registry->set('tx_solr', 'servers', $solrConfigurations);
247
    }
248
249
    /**
250
     * Gets all connections found.
251 111
     *
252
     * @return SolrConnection[] An array of initialized ApacheSolrForTypo3\Solr\System\Solr\SolrConnection connections
253
     * @throws NoSolrConnectionFoundException
254 111
     */
255 111
    public function getAllConnections()
256
    {
257 111
        $solrConnections = [];
258
        foreach ($this->siteRepository->getAvailableSites() as $site) {
259
            foreach ($site->getAllSolrConnectionConfigurations() as $solrConfiguration) {
260
                $solrConnections[] = $this->getConnectionFromConfiguration($solrConfiguration);
261
            }
262
        }
263
264
        return $solrConnections;
265 3
    }
266
267
    /**
268 3
     * Gets all connection configurations for a given site.
269 3
     *
270 3
     * @param Site $site A TYPO3 site
271
     * @return array An array of Solr connection configurations for a site
272
     * @throws NoSolrConnectionFoundException
273
     * @deprecated will be removed in v11, use $site->getAllSolrConnectionConfigurations()
274
     */
275
    public function getConfigurationsBySite(Site $site)
276
    {
277 7
        trigger_error('solr:deprecation: Method getConfigurationsBySite is deprecated since EXT:solr 10 and will be removed in v11, use $site->getAllSolrConnectionConfigurations()', E_USER_DEPRECATED);
278
279 7
        return $site->getAllSolrConnectionConfigurations();
280
    }
281 7
282 7
    /**
283 7
     * Gets all connections configured for a given site.
284
     *
285
     * @param Site $site A TYPO3 site
286 7
     * @return SolrConnection[] An array of Solr connection objects (ApacheSolrForTypo3\Solr\System\Solr\SolrConnection)
287
     * @throws NoSolrConnectionFoundException
288
     */
289
    public function getConnectionsBySite(Site $site)
290
    {
291
        $connections = [];
292
293
        foreach ($site->getAllSolrConnectionConfigurations() as $solrConnectionConfiguration) {
294
            $connections[] = $this->getConnectionFromConfiguration($solrConnectionConfiguration);
295 31
        }
296
297 31
        return $connections;
298
    }
299 31
300 31
    // updates
301 31
302 31
    /**
303
     * Updates the connections in the registry.
304
     *
305
     * @deprecated will be removed in v11, use SiteRepository
306 31
     */
307
    public function updateConnections()
308
    {
309
        trigger_error('solr:deprecation: Method updateConnections is deprecated since EXT:solr 10 and will be removed in v11, use sitehandling instead', E_USER_DEPRECATED);
310
311
        $solrConnections = $this->getConfiguredSolrConnections();
312
        $solrConnections = $this->filterDuplicateConnections($solrConnections);
313
314
        if (!empty($solrConnections)) {
315 16
            $this->setAllConfigurations($solrConnections);
316
        }
317 16
    }
318
319 16
    /**
320 16
     * Updates the Solr connections for a specific root page ID / site.
321 16
     *
322
     * @param int $rootPageId A site root page id
323
     * @throws NoSolrConnectionFoundException
324 16
     * @deprecated Use TYPO3 site config to configure site/connection info
325
     */
326
    public function updateConnectionByRootPageId($rootPageId)
327
    {
328
        trigger_error('solr:deprecation: Method updateConnectionByRootPageId is deprecated since EXT:solr 10 and will be removed in v11, use sitehandling instead', E_USER_DEPRECATED);
329
330
        $systemLanguages = $this->systemLanguageRepository->findSystemLanguages();
331
        /* @var SiteRepository $siteRepository */
332
        $siteRepository = GeneralUtility::makeInstance(SiteRepository::class);
333
        $site = $siteRepository->getSiteByRootPageId($rootPageId);
334
        $rootPage = $site->getRootPage();
335
336
        $updatedSolrConnections = [];
337
        foreach ($systemLanguages as $languageId) {
338
            $connection = $this->getConfiguredSolrConnectionByRootPage($rootPage, $languageId);
0 ignored issues
show
The method getConfiguredSolrConnectionByRootPage() does not exist on ApacheSolrForTypo3\Solr\ConnectionManager. Did you maybe mean getConfiguredSolrConnections()? ( Ignorable by Annotation )

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

338
            /** @scrutinizer ignore-call */ 
339
            $connection = $this->getConfiguredSolrConnectionByRootPage($rootPage, $languageId);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
339
340
            if (!empty($connection)) {
341
                $updatedSolrConnections[$connection['connectionKey']] = $connection;
342
            }
343
        }
344
345
        $solrConnections = $this->getAllConfigurations();
346
        $solrConnections = array_merge($solrConnections, $updatedSolrConnections);
347
        $solrConnections = $this->filterDuplicateConnections($solrConnections);
348
        $this->setAllConfigurations($solrConnections);
349
    }
350
351
    /**
352
     * Finds the configured Solr connections. Also respects multi-site
353
     * environments.
354
     *
355
     * @return array An array with connections, each connection with keys rootPageTitle, rootPageUid, solrHost, solrPort, solrPath
356
     * @deprecated will be removed in v11, use SiteRepository
357 2
     */
358
    protected function getConfiguredSolrConnections()
359 2
    {
360 2
        trigger_error('solr:deprecation: Method getConfiguredSolrConnections is deprecated since EXT:solr 10 and will be removed in v11, use sitehandling instead', E_USER_DEPRECATED);
361
362 2
        $configuredSolrConnections = [];
363 2
        // find website roots and languages for this installation
364
        $rootPages = $this->pagesRepositoryAtExtSolr->findAllRootPages();
365 2
        $languages = $this->systemLanguageRepository->findSystemLanguages();
366
367
        // find solr configurations and add them as function menu entries
368
        foreach ($rootPages as $rootPage) {
369
            foreach ($languages as $languageId) {
370
                $connection = $this->getConfiguredSolrConnectionByRootPage($rootPage, $languageId);
371
372 1
                if (!empty($connection)) {
373
                    $configuredSolrConnections[$connection['connectionKey']] = $connection;
374 1
                }
375 1
            }
376 1
        }
377 1
378
        return $configuredSolrConnections;
379 1
    }
380 1
381 1
    /**
382
     * Creates a human readable label from the connections' configuration.
383 1
     *
384 1
     * @param array $connection Connection configuration
385
     * @return string Connection label
386
     */
387
    protected function buildConnectionLabel(array $connection)
388 1
    {
389 1
        return $connection['rootPageTitle']
390 1
            . ' (pid: ' . $connection['rootPageUid']
391 1
            . ', language: ' . $this->systemLanguageRepository->findOneLanguageTitleByLanguageId($connection['language'])
392 1
            . ') - Read node: '
393
            . $connection['read']['host'] . ':'
394
            . $connection['read']['port']
395
            . $connection['read']['path']
396
            .' - Write node: '
397
            . $connection['write']['host'] . ':'
398
            . $connection['write']['port']
399
            . $connection['write']['path'];
400 2
    }
401
402 2
    /**
403
     * Filters duplicate connections. When detecting the configured connections
404 2
     * this is done with a little brute force by simply combining all root pages
405 2
     * with all languages, this method filters out the duplicates.
406
     *
407
     * @param array $connections An array of unfiltered connections, containing duplicates
408 2
     * @return array An array with connections, no duplicates.
409 2
     * @deprecated will be removed in v11, use SiteRepository
410 2
     */
411
    protected function filterDuplicateConnections(array $connections)
412 2
    {
413 2
        trigger_error('solr:deprecation: Method filterDuplicateConnections is deprecated since EXT:solr 10 and will be removed in v11, use sitehandling instead', E_USER_DEPRECATED);
414
415
        $hashedConnections = [];
416
        $filteredConnections = [];
417
418 2
        // array_unique() doesn't work on multi dimensional arrays, so we need to flatten it first
419
        foreach ($connections as $key => $connection) {
420
            unset($connection['language']);
421
            $connectionHash = md5(implode('|', $connection));
422
            $hashedConnections[$key] = $connectionHash;
423
        }
424
425
        $hashedConnections = array_unique($hashedConnections);
426
427
        foreach ($hashedConnections as $key => $hash) {
428 3
            $filteredConnections[$key] = $connections[$key];
429
        }
430 3
431
        return $filteredConnections;
432 3
    }
433 3
434 3
    /**
435
     * @param $pageId
436 3
     * @param $language
437 3
     * @return NoSolrConnectionFoundException
438
     */
439 3
    protected function buildNoConnectionExceptionForPageAndLanguage($pageId, $language): NoSolrConnectionFoundException
440 3
    {
441 3
        $message = 'Could not find a Solr connection for page [' . $pageId . '] and language [' . $language . '].';
442 3
        $noSolrConnectionException = $this->buildNoConnectionException($message);
443
444
        $noSolrConnectionException->setLanguageId($language);
445 3
        return $noSolrConnectionException;
446 3
    }
447 3
448 3
    /**
449 3
     * Throws a no connection exception when no site was passed.
450 3
     *
451 3
     * @param Site|null $site
452
     * @param $message
453 3
     * @throws NoSolrConnectionFoundException
454 3
     */
455
    protected function throwExceptionOnInvalidSite(?Site $site, string $message)
456 3
    {
457
        if (!is_null($site)) {
458 3
            return;
459 3
        }
460
461
        throw $this->buildNoConnectionException($message);
462
    }
463
464 3
    /**
465 3
     * Build a NoSolrConnectionFoundException with the passed message.
466 3
     * @param string $message
467
     * @return NoSolrConnectionFoundException
468 3
     */
469 3
    protected function buildNoConnectionException(string $message): NoSolrConnectionFoundException
470 3
    {
471 3
        /* @var NoSolrConnectionFoundException $noSolrConnectionException */
472 3
        $noSolrConnectionException = GeneralUtility::makeInstance(
473 3
            NoSolrConnectionFoundException::class,
474 3
            /** @scrutinizer ignore-type */
475
            $message,
476
            /** @scrutinizer ignore-type */
477 3
            1575396474
478 3
        );
479 3
        return $noSolrConnectionException;
480 3
    }
481
}
482