Passed
Push — master ( 3ce2af...702ea2 )
by Timo
24:17
created

Search   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 450
Duplicated Lines 0 %

Test Coverage

Coverage 37.59%

Importance

Changes 0
Metric Value
wmc 43
dl 0
loc 450
ccs 53
cts 141
cp 0.3759
rs 8.96
c 0
b 0
f 0

25 Methods

Rating   Name   Duplication   Size   Complexity  
A getQueryTime() 0 3 1
A getRawResponse() 0 3 1
A getDebugResponse() 0 3 1
A __construct() 0 13 2
A getResponse() 0 3 1
A getResponseHeader() 0 3 1
A setSolrConnection() 0 3 1
A hasSearched() 0 5 1
A ping() 0 23 4
A getHighlightedContent() 0 9 2
A getResultOffset() 0 3 1
A getNumberOfResults() 0 3 1
A getFacetQueryOptions() 0 22 3
A getFacetCounts() 0 31 5
A getSolrConnection() 0 3 1
A getResponseBody() 0 3 1
A getQuery() 0 3 1
A getFacetFieldOptions() 0 11 2
A getMaximumResultScore() 0 3 1
A getFacetRangeOptions() 0 4 1
A getSpellcheckingSuggestions() 0 18 3
A getResultDocumentsEscaped() 0 6 1
A getResultDocumentsRaw() 0 4 1
B search() 0 51 5
A getResultsPerPage() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Search often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

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

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

1
<?php
2
namespace ApacheSolrForTypo3\Solr;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2009-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\Search\ResultSet\Result\Parser\DocumentEscapeService;
28
use ApacheSolrForTypo3\Solr\Search\FacetsModifier;
29
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
30
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
31
use ApacheSolrForTypo3\Solr\System\Solr\SolrCommunicationException;
32
use ApacheSolrForTypo3\Solr\System\Solr\SolrConnection;
33
use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query as NewQuery;
34
use TYPO3\CMS\Core\Utility\GeneralUtility;
35
36
/**
37
 * Class to handle solr search requests
38
 *
39
 * @author Ingo Renner <[email protected]>
40
 */
41
class Search
42
{
43
44
    /**
45
     * An instance of the Solr service
46
     *
47
     * @var SolrConnection
48
     */
49
    protected $solr = null;
50
51
    /**
52
     * The search query
53
     *
54
     * @var NewQuery
55
     */
56
    protected $query = null;
57
58
    /**
59
     * The search response
60
     *
61
     * @var \Apache_Solr_Response
62
     */
63
    protected $response = null;
64
65
    /**
66
     * Flag for marking a search
67
     *
68
     * @deprecated will be removed in EXT:solr 9.0.0 use SearchResultSet::getHasSearched instead
69
     * @var bool
70
     */
71
    protected $hasSearched = false;
72
73
    /**
74
     * @var TypoScriptConfiguration
75
     */
76
    protected $configuration;
77
78
    // TODO Override __clone to reset $response and $hasSearched
79
80
    /**
81
     * @var \ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager
82
     */
83
    protected $logger = null;
84
85
    /**
86
     * Constructor
87
     *
88
     * @param SolrConnection $solrConnection The Solr connection to use for searching
89
     */
90 54
    public function __construct(SolrConnection $solrConnection = null)
91
    {
92 54
        $this->logger = GeneralUtility::makeInstance(SolrLogManager::class, /** @scrutinizer ignore-type */ __CLASS__);
93
94 54
        $this->solr = $solrConnection;
95
96 54
        if (is_null($solrConnection)) {
97
            /** @var $connectionManager ConnectionManager */
98 7
            $connectionManager = GeneralUtility::makeInstance(ConnectionManager::class);
99 7
            $this->solr = $connectionManager->getConnectionByPageId($GLOBALS['TSFE']->id, $GLOBALS['TSFE']->sys_language_uid);
100
        }
101
102 54
        $this->configuration = Util::getSolrConfiguration();
103 54
    }
104
105
    /**
106
     * Gets the Solr connection used by this search.
107
     *
108
     * @return SolrConnection Solr connection
109
     */
110
    public function getSolrConnection()
111
    {
112
        return $this->solr;
113
    }
114
115
    /**
116
     * Sets the Solr connection used by this search.
117
     *
118
     * Since ApacheSolrForTypo3\Solr\Search is a \TYPO3\CMS\Core\SingletonInterface, this is needed to
119
     * be able to switch between multiple cores/connections during
120
     * one request
121
     *
122
     * @param SolrConnection $solrConnection
123
     */
124
    public function setSolrConnection(SolrConnection $solrConnection)
125
    {
126
        $this->solr = $solrConnection;
127
    }
128
129
    /**
130
     * Executes a query against a Solr server.
131
     *
132
     * 1) Gets the query string
133
     * 2) Conducts the actual search
134
     * 3) Checks debug settings
135
     *
136
     * @param Query $query The query with keywords, filters, and so on.
137
     * @param int $offset Result offset for pagination.
138
     * @param int $limit Maximum number of results to return. If set to NULL, this value is taken from the query object.
139
     * @return \Apache_Solr_Response Solr response
140
     * @throws \Exception
141
     */
142 48
    public function search(NewQuery $query, $offset = 0, $limit = 10)
143
    {
144 48
        $this->query = $query;
145
146 48
        if (empty($limit)) {
147 37
            $limit = $query->getRows();
148
        }
149
150
        try {
151 48
            $response = $this->solr->getReadService()->search(
152 48
                (string)$query->getQueryStringContainer()->getQueryString(),
153 48
                $offset,
154 48
                $limit,
155 48
                $query->getQueryParameters()
156
            );
157
158 46
            if ($this->configuration->getLoggingQueryQueryString()) {
159
                $this->logger->log(
160
                    SolrLogManager::INFO,
161
                    'Querying Solr, getting result',
162
                    [
163
                        'query string' => $query->getQueryStringContainer()->getQueryString(),
164
                        'query parameters' => $query->getQueryParameters(),
165
                        'response' => json_decode($response->getRawResponse(),
166 46
                            true)
167
                    ]
168
                );
169
            }
170 2
        } catch (SolrCommunicationException $e) {
171 2
            if ($this->configuration->getLoggingExceptions()) {
172 2
                $this->logger->log(
173 2
                    SolrLogManager::ERROR,
174 2
                    'Exception while querying Solr',
175
                    [
176 2
                        'exception' => $e->__toString(),
177 2
                        'query' => (array)$query,
178 2
                        'offset' => $offset,
179 2
                        'limit' => $limit
180
                    ]
181
                );
182
            }
183
184 2
            throw $e;
185
        }
186
187 46
        $this->response = $response;
188
189
        //@todo can be dropped in EXT:solr 9.0.0
190 46
        $this->hasSearched = true;
0 ignored issues
show
Deprecated Code introduced by
The property ApacheSolrForTypo3\Solr\Search::$hasSearched has been deprecated: will be removed in EXT:solr 9.0.0 use SearchResultSet::getHasSearched instead ( Ignorable by Annotation )

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

190
        /** @scrutinizer ignore-deprecated */ $this->hasSearched = true;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
191
192 46
        return $this->response;
193
    }
194
195
    /**
196
     * Sends a ping to the solr server to see whether it is available.
197
     *
198
     * @param bool $useCache Set to true if the cache should be used.
199
     * @return bool Returns TRUE on successful ping.
200
     * @throws \Exception Throws an exception in case ping was not successful.
201
     */
202
    public function ping($useCache = true)
203
    {
204
        $solrAvailable = false;
205
206
        try {
207
            if (!$this->solr->getReadService()->ping(2, $useCache)) {
208
                throw new \Exception('Solr Server not responding.', 1237475791);
209
            }
210
211
            $solrAvailable = true;
212
        } catch (\Exception $e) {
213
            if ($this->configuration->getLoggingExceptions()) {
214
                $this->logger->log(
215
                    SolrLogManager::ERROR,
216
                    'Exception while trying to ping the solr server',
217
                    [
218
                        $e->__toString()
219
                    ]
220
                );
221
            }
222
        }
223
224
        return $solrAvailable;
225
    }
226
227
    /**
228
     * checks whether a search has been executed.
229
     *
230
     * @deprecated Since 8.1.0 will be removed in 9.0.0. This method is deprecated. Use SearchResultSet::getHasSearched instead.
231
     * @return bool    TRUE if there was a search, FALSE otherwise (if the user just visited the search page f.e.)
232
     */
233
    public function hasSearched()
234
    {
235
        trigger_error('Call deprecated method Search::hasSearched, deprecated since 8.1.0 will be removed in 9.0.0 use SearchResultSet::getHasSearched instead', E_USER_DEPRECATED);
236
237
        return $this->hasSearched;
0 ignored issues
show
Deprecated Code introduced by
The property ApacheSolrForTypo3\Solr\Search::$hasSearched has been deprecated: will be removed in EXT:solr 9.0.0 use SearchResultSet::getHasSearched instead ( Ignorable by Annotation )

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

237
        return /** @scrutinizer ignore-deprecated */ $this->hasSearched;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
238
    }
239
240
    /**
241
     * Gets the query object.
242
     *
243
     * @return NewQuery Query
244
     */
245 31
    public function getQuery()
246
    {
247 31
        return $this->query;
248
    }
249
250
    /**
251
     * Gets the Solr response
252
     *
253
     * @return \Apache_Solr_Response
254
     */
255 26
    public function getResponse()
256
    {
257 26
        return $this->response;
258
    }
259
260
    public function getRawResponse()
261
    {
262
        return $this->response->getRawResponse();
263
    }
264
265 26
    public function getResponseHeader()
266
    {
267 26
        return $this->getResponse()->responseHeader;
268
    }
269
270 26
    public function getResponseBody()
271
    {
272 26
        return $this->getResponse()->response;
273
    }
274
275
    /**
276
     * Returns all results documents raw. Use with caution!
277
     *
278
     * @deprecated Since 8.0.0 will be removed in 9.0.0. Use $resultSet->getSearchResults() this will be initialized by the parser depending on the settings
279
     * @return \Apache_Solr_Document[]
280
     */
281
    public function getResultDocumentsRaw()
282
    {
283
        trigger_error('Call deprecated method Search::getResultDocumentsRaw, deprecated since 8.0.0 will be removed in 9.0.0', E_USER_DEPRECATED);
284
        return $this->getResponseBody()->docs;
285
    }
286
287
    /**
288
     * Returns all result documents but applies htmlspecialchars() on all fields retrieved
289
     * from solr except the configured fields in plugin.tx_solr.search.trustedFields
290
     *
291
     * @deprecated Since 8.0.0 will be removed in 9.0.0. Use DocumentEscapeService or
292
     * $resultSet->getSearchResults() this will be initialized by the parser depending on the settings.
293
     * @return \Apache_Solr_Document[]
294
     */
295
    public function getResultDocumentsEscaped()
296
    {
297
        trigger_error('Call deprecated method Search::getResultDocumentsEscaped, deprecated since 8.0.0 will be removed in 9.0.0', E_USER_DEPRECATED);
298
        /** @var $escapeService DocumentEscapeService */
299
        $escapeService = GeneralUtility::makeInstance(DocumentEscapeService::class, /** @scrutinizer ignore-type */ $this->configuration);
300
        return $escapeService->applyHtmlSpecialCharsOnAllFields($this->getResponseBody()->docs);
301
    }
302
303
    /**
304
     * Gets the time Solr took to execute the query and return the result.
305
     *
306
     * @return int Query time in milliseconds
307
     */
308 26
    public function getQueryTime()
309
    {
310 26
        return $this->getResponseHeader()->QTime;
311
    }
312
313
    /**
314
     * Gets the number of results per page.
315
     *
316
     * @return int Number of results per page
317
     */
318
    public function getResultsPerPage()
319
    {
320
        return $this->getResponseHeader()->params->rows;
321
    }
322
323
    /**
324
     * Gets all facets with their fields, options, and counts.
325
     *
326
     * @deprecated Since 8.0.0 will be removed in 9.0.0. This method is deprecated. Use SearchResultSet::getFacets instead.
327
     * The parsing of facets count's is now done in the parser of the corresponding facet type
328
     * @see \ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\
329
     *
330
     * @return array
331
     */
332
    public function getFacetCounts()
333
    {
334
        trigger_error('Call deprecated method Search::getFacetCounts, deprecated since 8.0.0 will be removed in 9.0.0', E_USER_DEPRECATED);
335
        static $facetCountsModified = false;
336
        static $facetCounts = null;
337
338
        $unmodifiedFacetCounts = $this->response->facet_counts;
339
340
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifyFacets'])) {
341
            if (!$facetCountsModified) {
342
                $facetCounts = $unmodifiedFacetCounts;
343
344
                foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifyFacets'] as $classReference) {
345
                    $facetsModifier = GeneralUtility::makeInstance($classReference);
346
347
                    if ($facetsModifier instanceof FacetsModifier) {
348
                        $facetCounts = $facetsModifier->modifyFacets($facetCounts);
349
                        $facetCountsModified = true;
350
                    } else {
351
                        throw new \UnexpectedValueException(
352
                            get_class($facetsModifier) . ' must implement interface ' . FacetsModifier::class,
353
                            1310387526
354
                        );
355
                    }
356
                }
357
            }
358
        } else {
359
            $facetCounts = $unmodifiedFacetCounts;
360
        }
361
362
        return $facetCounts;
363
    }
364
365
    /**
366
     * @deprecated Since 8.0.0 will be removed in 9.0.0. This method is deprecated. Use SearchResultSet::getFacets instead.
367
     * The parsing of the "options" is now done in the facet parser of the OptionsFacets
368
     * @see \ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\OptionBased\Options\OptionsFacetParser
369
     *
370
     * @param $facetField
371
     * @return array|null
372
     */
373
    public function getFacetFieldOptions($facetField)
374
    {
375
        trigger_error('Call deprecated method Search::getFacetFieldOptions, deprecated since 8.0.0 will be removed in 9.0.0', E_USER_DEPRECATED);
376
        $facetOptions = null;
377
378
        if (property_exists($this->getFacetCounts()->facet_fields,
0 ignored issues
show
Deprecated Code introduced by
The function ApacheSolrForTypo3\Solr\Search::getFacetCounts() has been deprecated: Since 8.0.0 will be removed in 9.0.0. This method is deprecated. Use SearchResultSet::getFacets instead. The parsing of facets count's is now done in the parser of the corresponding facet type ( Ignorable by Annotation )

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

378
        if (property_exists(/** @scrutinizer ignore-deprecated */ $this->getFacetCounts()->facet_fields,

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
379
            $facetField)) {
380
            $facetOptions = get_object_vars($this->getFacetCounts()->facet_fields->$facetField);
0 ignored issues
show
Deprecated Code introduced by
The function ApacheSolrForTypo3\Solr\Search::getFacetCounts() has been deprecated: Since 8.0.0 will be removed in 9.0.0. This method is deprecated. Use SearchResultSet::getFacets instead. The parsing of facets count's is now done in the parser of the corresponding facet type ( Ignorable by Annotation )

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

380
            $facetOptions = get_object_vars(/** @scrutinizer ignore-deprecated */ $this->getFacetCounts()->facet_fields->$facetField);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
381
        }
382
383
        return $facetOptions;
384
    }
385
386
    /**
387
     * @deprecated Since 8.0.0 will be removed in 9.0.0. This method is deprecated. Use SearchResultSet::getFacets instead.
388
     * The parsing of the "query options" is now done in the facet parser of the QueryFacets
389
     * @see \ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\OptionBased\QueryGroup\QueryGroupFacetParser
390
     *
391
     * @param string $facetField
392
     * @return array
393
     */
394
    public function getFacetQueryOptions($facetField)
395
    {
396
        trigger_error('Call deprecated method Search::getFacetQueryOptions, deprecated since 8.0.0 will be removed in 9.0.0', E_USER_DEPRECATED);
397
398
        $options = [];
399
400
        $facetQueries = get_object_vars($this->getFacetCounts()->facet_queries);
0 ignored issues
show
Deprecated Code introduced by
The function ApacheSolrForTypo3\Solr\Search::getFacetCounts() has been deprecated: Since 8.0.0 will be removed in 9.0.0. This method is deprecated. Use SearchResultSet::getFacets instead. The parsing of facets count's is now done in the parser of the corresponding facet type ( Ignorable by Annotation )

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

400
        $facetQueries = get_object_vars(/** @scrutinizer ignore-deprecated */ $this->getFacetCounts()->facet_queries);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
401
        foreach ($facetQueries as $facetQuery => $numberOfResults) {
402
            // remove tags from the facet.query response, for facet.field
403
            // and facet.range Solr does that on its own automatically
404
            $facetQuery = preg_replace('/^\{!ex=[^\}]*\}(.*)/', '\\1',
405
                $facetQuery);
406
407
            if (GeneralUtility::isFirstPartOfStr($facetQuery, $facetField)) {
408
                $options[$facetQuery] = $numberOfResults;
409
            }
410
        }
411
412
        // filter out queries with no results
413
        $options = array_filter($options);
414
415
        return $options;
416
    }
417
418
    /**
419
     * @deprecated Since 8.0.0 will be removed in 9.0.0. This method is deprecated. Use SearchResultSet::getFacets instead.
420
     * The parsing of the range options is now done in the facet parser of the RangeFacets
421
     * @see \ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\RangeBased\AbstractRangeFacetParser
422
     *
423
     * @param string $rangeFacetField
424
     * @return array
425
     */
426
    public function getFacetRangeOptions($rangeFacetField)
427
    {
428
        trigger_error('Call deprecated method Search::getFacetRangeOptions, deprecated since 8.0.0 will be removed in 9.0.0', E_USER_DEPRECATED);
429
        return get_object_vars($this->getFacetCounts()->facet_ranges->$rangeFacetField);
0 ignored issues
show
Deprecated Code introduced by
The function ApacheSolrForTypo3\Solr\Search::getFacetCounts() has been deprecated: Since 8.0.0 will be removed in 9.0.0. This method is deprecated. Use SearchResultSet::getFacets instead. The parsing of facets count's is now done in the parser of the corresponding facet type ( Ignorable by Annotation )

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

429
        return get_object_vars(/** @scrutinizer ignore-deprecated */ $this->getFacetCounts()->facet_ranges->$rangeFacetField);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
430
    }
431
432 33
    public function getNumberOfResults()
433
    {
434 33
        return $this->response->response->numFound;
435
    }
436
437
    /**
438
     * Gets the result offset.
439
     *
440
     * @return int Result offset
441
     */
442
    public function getResultOffset()
443
    {
444
        return $this->response->response->start;
445
    }
446
447 26
    public function getMaximumResultScore()
448
    {
449 26
        return $this->response->response->maxScore;
450
    }
451
452 2
    public function getDebugResponse()
453
    {
454 2
        return $this->response->debug;
455
    }
456
457 26
    public function getHighlightedContent()
458
    {
459 26
        $highlightedContent = false;
460
461 26
        if ($this->response->highlighting) {
462 26
            $highlightedContent = $this->response->highlighting;
463
        }
464
465 26
        return $highlightedContent;
466
    }
467
468
    /**
469
     * @deprecated Since 8.0.0 will be removed in 9.0.0. This method is deprecated. Use SearchResultSet::getSpellcheckingSuggestions
470
     * and the domain model instead
471
     * @return array|bool
472
     */
473
    public function getSpellcheckingSuggestions()
474
    {
475
        trigger_error('Call deprecated method Search::getSpellcheckingSuggestions, deprecated since 8.0.0 will be removed in 9.0.0', E_USER_DEPRECATED);
476
477
        $spellcheckingSuggestions = false;
478
479
        $suggestions = (array)$this->response->spellcheck->suggestions;
480
481
        if (!empty($suggestions)) {
482
            $spellcheckingSuggestions = $suggestions;
483
484
            if (isset($this->response->spellcheck->collations)) {
485
                $collections = (array)$this->response->spellcheck->collations;
486
                $spellcheckingSuggestions['collation'] = $collections['collation'];
487
            }
488
        }
489
490
        return $spellcheckingSuggestions;
491
    }
492
}
493