Passed
Push — master ( 20d9ae...2c204f )
by Timo
01:45
created

SearchResultSetService   D

Complexity

Total Complexity 88

Size/Duplication

Total Lines 608
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 12

Test Coverage

Coverage 85.45%

Importance

Changes 0
Metric Value
wmc 88
lcom 1
cbo 12
dl 0
loc 608
ccs 182
cts 213
cp 0.8545
rs 4.0182
c 0
b 0
f 0

28 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getIsSolrAvailable() 0 5 1
A getHasSearched() 0 4 1
A getSearch() 0 4 1
A setUseQueryAwareComponents() 0 4 1
A getUseQueryAwareComponents() 0 4 1
B getPreparedQuery() 0 37 6
A initializeRegisteredSearchComponents() 0 15 4
B getNumberOfResultsPerPage() 0 25 6
B processResponse() 0 21 5
D addExpandedDocumentsFromVariants() 0 39 10
A getInitialSearchIsConfigured() 0 4 4
A getRegisteredSearchComponents() 0 4 1
B wrapResultDocumentInResultObject() 0 33 5
A wrapApacheSolrDocumentInResultObject() 0 10 2
A getResultClassName() 0 5 2
A getResultSetClassName() 0 5 2
B shouldHideResultsFromInitialSearch() 0 4 5
A applyPageSectionsRootLineFilter() 0 13 3
C getAdditionalFilters() 0 36 7
B search() 0 51 6
A getDocumentById() 0 12 2
A handleSearchHook() 0 15 4
A getLastResultSet() 0 4 1
A getLastSearchWasExecutedWithEmptyQueryString() 0 9 2
A setPerPageInSession() 0 4 1
A getPerPageFromSession() 0 4 1
A addSearchResultsToResultSet() 0 10 3

How to fix   Complexity   

Complex Class

Complex classes like SearchResultSetService 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 SearchResultSetService, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace ApacheSolrForTypo3\Solr\Domain\Search\ResultSet;
4
5
/***************************************************************
6
 *  Copyright notice
7
 *
8
 *  (c) 2015-2016 Timo Schmidt <[email protected]>
9
 *  All rights reserved
10
 *
11
 *  This script is part of the TYPO3 project. The TYPO3 project is
12
 *  free software; you can redistribute it and/or modify
13
 *  it under the terms of the GNU General Public License as published by
14
 *  the Free Software Foundation; either version 2 of the License, or
15
 *  (at your option) any later version.
16
 *
17
 *  The GNU General Public License can be found at
18
 *  http://www.gnu.org/copyleft/gpl.html.
19
 *
20
 *  This script is distributed in the hope that it will be useful,
21
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 *  GNU General Public License for more details.
24
 *
25
 *  This copyright notice MUST APPEAR in all copies of the script!
26
 ***************************************************************/
27
28
use ApacheSolrForTypo3\Solr\Domain\Search\SearchRequest;
29
use ApacheSolrForTypo3\Solr\Query;
30
use ApacheSolrForTypo3\Solr\Response\Processor\ResponseProcessor;
31
use ApacheSolrForTypo3\Solr\Search;
32
use ApacheSolrForTypo3\Solr\Search\QueryAware;
33
use ApacheSolrForTypo3\Solr\Search\SearchComponentManager;
34
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
35
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
36
use TYPO3\CMS\Core\Utility\GeneralUtility;
37
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
38
use Apache_Solr_ParserException;
39
40
/**
41
 * The SearchResultSetService is responsible to build a SearchResultSet from a SearchRequest.
42
 * It encapsulates the logic to trigger a search in order to be able to reuse it in multiple places.
43
 *
44
 * @author Timo Schmidt <[email protected]>
45
 */
46
class SearchResultSetService
47
{
48
    /**
49
     * Additional filters, which will be added to the query, as well as to
50
     * suggest queries.
51
     *
52
     * @var array
53
     */
54
    protected $additionalFilters = [];
55
56
    /**
57
     * Track, if the number of results per page has been changed by the current request
58
     *
59
     * @var bool
60
     */
61
    protected $resultsPerPageChanged = false;
62
63
    /**
64
     * @var \ApacheSolrForTypo3\Solr\Search
65
     */
66
    protected $search;
67
68
    /**
69
     * @var SearchResultSet
70
     */
71
    protected $lastResultSet = null;
72
73
    /**
74
     * @var bool
75
     */
76
    protected $useQueryAwareComponents = true;
77
78
    /**
79
     * @var
80
     */
81
    protected $isSolrAvailable = false;
82
83
    /**
84
     * @var TypoScriptConfiguration
85
     */
86
    protected $typoScriptConfiguration;
87
88
    /**
89
     * @var \ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
90
     */
91
    protected $logger = null;
92
93
    /**
94
     * @param TypoScriptConfiguration $configuration
95
     * @param Search $search
96
     */
97 41
    public function __construct(TypoScriptConfiguration $configuration, Search $search)
98
    {
99 41
        $this->logger = GeneralUtility::makeInstance(SolrLogManager::class, __CLASS__);
100 41
        $this->search = $search;
101 41
        $this->typoScriptConfiguration = $configuration;
102 41
    }
103
104
    /**
105
     * @param bool $useCache
106
     * @return bool
107
     */
108 25
    public function getIsSolrAvailable($useCache = true)
109
    {
110 25
        $this->isSolrAvailable = $this->search->ping($useCache);
111 25
        return $this->isSolrAvailable;
112
    }
113
114
    /**
115
     * @return bool
116
     */
117 25
    public function getHasSearched()
118
    {
119 25
        return $this->search->hasSearched();
120
    }
121
122
    /**
123
     * Retrieves the used search instance.
124
     *
125
     * @return Search
126
     */
127 2
    public function getSearch()
128
    {
129 2
        return $this->search;
130
    }
131
132
    /**
133
     * @param bool $useQueryAwareComponents
134
     */
135
    public function setUseQueryAwareComponents($useQueryAwareComponents)
136
    {
137
        $this->useQueryAwareComponents = $useQueryAwareComponents;
138
    }
139
140
    /**
141
     * @return bool
142
     */
143
    public function getUseQueryAwareComponents()
144
    {
145
        return $this->useQueryAwareComponents;
146
    }
147
148
    /**
149
     * Initializes the Query object and SearchComponents and returns
150
     * the initialized query object, when a search should be executed.
151
     *
152
     * @param string $rawQuery
153
     * @param int $resultsPerPage
154
     * @return Query
155
     */
156 33
    protected function getPreparedQuery($rawQuery, $resultsPerPage)
157
    {
158
        /* @var $query Query */
159 33
        $query = GeneralUtility::makeInstance(Query::class, $rawQuery);
160
161 33
        $this->applyPageSectionsRootLineFilter($query);
162
163 33
        if ($this->typoScriptConfiguration->getLoggingQuerySearchWords()) {
164
            $this->logger->log(
165
                SolrLogManager::INFO,
166
                'Received search query',
167
                [
168
                    $rawQuery
169
                ]
170
            );
171
        }
172
173 33
        $query->setResultsPerPage($resultsPerPage);
174
175 33
        $this->initializeRegisteredSearchComponents($query);
176
177 33
        if ($this->typoScriptConfiguration->getSearchInitializeWithEmptyQuery() || $this->typoScriptConfiguration->getSearchQueryAllowEmptyQuery()) {
178
            // empty main query, but using a "return everything"
179
            // alternative query in q.alt
180 26
            $query->setAlternativeQuery('*:*');
181
        }
182
183 33
        if ($this->typoScriptConfiguration->getSearchInitializeWithQuery()) {
184 1
            $query->setAlternativeQuery($this->typoScriptConfiguration->getSearchInitializeWithQuery());
185
        }
186
187 33
        foreach ($this->getAdditionalFilters() as $additionalFilter) {
188 2
            $query->addFilter($additionalFilter);
189
        }
190
191 33
        return $query;
192
    }
193
194
    /**
195
     * @param Query $query
196
     */
197 33
    protected function initializeRegisteredSearchComponents(Query $query)
198
    {
199 33
        $searchComponents = $this->getRegisteredSearchComponents();
200
201 33
        foreach ($searchComponents as $searchComponent) {
202
            /** @var Search\SearchComponent $searchComponent */
203 27
            $searchComponent->setSearchConfiguration($this->typoScriptConfiguration->getSearchConfiguration());
204
205 27
            if ($searchComponent instanceof QueryAware && $this->useQueryAwareComponents) {
206 27
                $searchComponent->setQuery($query);
207
            }
208
209 27
            $searchComponent->initializeSearchComponent();
210
        }
211 33
    }
212
213
    /**
214
     * Returns the number of results per Page.
215
     *
216
     * Also influences how many result documents are returned by the Solr
217
     * server as the return value is used in the Solr "rows" GET parameter.
218
     *
219
     * @param string $rawQuery
220
     * @param int|null $requestedPerPage
221
     * @return int number of results to show per page
222
     */
223 33
    protected function getNumberOfResultsPerPage($rawQuery, $requestedPerPage = null)
224
    {
225 33
        $perPageSwitchOptions = $this->typoScriptConfiguration->getSearchResultsPerPageSwitchOptionsAsArray();
226 33
        if (isset($requestedPerPage) && in_array($requestedPerPage, $perPageSwitchOptions)) {
227 4
            $this->setPerPageInSession($requestedPerPage);
228 4
            $this->resultsPerPageChanged = true;
229
        }
230
231 33
        $defaultResultsPerPage = $this->typoScriptConfiguration->getSearchResultsPerPage();
232 33
        $sessionResultPerPage = $this->getPerPageFromSession();
233
234 33
        $currentNumberOfResultsShown = $defaultResultsPerPage;
235 33
        if (!is_null($sessionResultPerPage) && in_array($sessionResultPerPage, $perPageSwitchOptions)) {
236 3
            $currentNumberOfResultsShown = (int)$sessionResultPerPage;
237
        }
238
239 33
        if ($this->shouldHideResultsFromInitialSearch($rawQuery)) {
240
            // initialize search with an empty query, which would by default return all documents
241
            // anyway, tell Solr to not return any result documents
242
            // Solr will still return facets though
243
            $currentNumberOfResultsShown = 0;
244
        }
245
246 33
        return $currentNumberOfResultsShown;
247
    }
248
249
    /**
250
     * Provides a hook for other classes to process the search's response.
251
     *
252
     * @param string $rawQuery
253
     * @param Query $query The query that has been searched for.
254
     * @param \Apache_Solr_Response $response The search's response.
255
     */
256 35
    protected function processResponse($rawQuery, Query $query, \Apache_Solr_Response $response)
257
    {
258 35
        if ($this->shouldHideResultsFromInitialSearch($rawQuery)) {
259
            // explicitly set number of results to 0 as we just wanted
260
            // facets and the like according to configuration
261
            // @see getNumberOfResultsPerPage()
262
            $response->response->numFound = 0;
0 ignored issues
show
Bug introduced by
The property response does not seem to exist. Did you mean _response?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
263
        }
264
265 35
        $this->wrapResultDocumentInResultObject($response);
266 35
        $this->addExpandedDocumentsFromVariants($response);
267
268 35
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['processSearchResponse'])) {
269 23
            foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['processSearchResponse'] as $classReference) {
270 23
                $responseProcessor = GeneralUtility::getUserObj($classReference);
271 23
                if ($responseProcessor instanceof ResponseProcessor) {
272 23
                    $responseProcessor->processResponse($query, $response);
273
                }
274
            }
275
        }
276 35
    }
277
278
    /**
279
     * This method is used to add documents to the expanded documents of the SearchResult
280
     * when collapsing is configured.
281
     *
282
     * @param \Apache_Solr_Response $response
283
     */
284 35
    protected function addExpandedDocumentsFromVariants(\Apache_Solr_Response $response)
285
    {
286 35
        if (!is_array($response->response->docs)) {
0 ignored issues
show
Bug introduced by
The property response does not seem to exist. Did you mean _response?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
287 5
            return;
288
        }
289
290 30
        if (!$this->typoScriptConfiguration->getSearchVariants()) {
291 27
            return;
292
        }
293
294 3
        $variantsField = $this->typoScriptConfiguration->getSearchVariantsField();
295 3
        foreach ($response->response->docs as $key => $resultDocument) {
0 ignored issues
show
Bug introduced by
The property response does not seem to exist. Did you mean _response?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
296
            /** @var $resultDocument SearchResult */
297 2
            $variantField = $resultDocument->getField($variantsField);
298 2
            $variantId = isset($variantField['value']) ? $variantField['value'] : null;
299
300
                // when there is no value in the collapsing field, we can return
301 2
            if ($variantId === null) {
302
                continue;
303
            }
304
305 2
            $variantAccessKey = strtolower($variantId);
306 2
            if (!isset($response->{'expanded'}) || !isset($response->{'expanded'}->{$variantAccessKey})) {
307
                continue;
308
            }
309
310 2
            foreach ($response->{'expanded'}->{$variantAccessKey}->{'docs'} as $variantDocumentArray) {
311 2
                $variantDocument = new \Apache_Solr_Document();
312 2
                foreach (get_object_vars($variantDocumentArray) as $propertyName => $propertyValue) {
313 2
                    $variantDocument->{$propertyName} = $propertyValue;
314
                }
315 2
                $variantSearchResult = $this->wrapApacheSolrDocumentInResultObject($variantDocument);
316 2
                $variantSearchResult->setIsVariant(true);
317 2
                $variantSearchResult->setVariantParent($resultDocument);
318
319 2
                $resultDocument->addVariant($variantSearchResult);
320
            }
321
        }
322 3
    }
323
324
    /**
325
     * Wrap all results document it a custom EXT:solr SearchResult object.
326
     *
327
     * Can be overwritten:
328
     *
329
     * $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultClassName '] = ''
330
     *
331
     * to use a custom result object.
332
     *
333
     * @param \Apache_Solr_Response $response
334
     * @throws \Apache_Solr_ParserException
335
     */
336 35
    protected function wrapResultDocumentInResultObject(\Apache_Solr_Response $response)
337
    {
338
        try {
339 35
            $documents = $response->response->docs;
0 ignored issues
show
Bug introduced by
The property response does not seem to exist. Did you mean _response?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
340 1
        } catch (Apache_Solr_ParserException $e) {
0 ignored issues
show
Unused Code introduced by
catch (\Apache_Solr_Pars...$documents = array(); } does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
341
            // when variant are enable and the index is empty, we get a parse exception, because of a
342
            // Apache Solr Bug.
343
            // see: https://github.com/TYPO3-Solr/ext-solr/issues/668
344
            // @todo this try/catch block can be removed after upgrading to Apache Solr 6.4
345 1
            if (!$this->typoScriptConfiguration->getSearchVariants()) {
346
                throw $e;
347
            }
348
349 1
            $response->response = new \stdClass();
350 1
            $response->spellcheck = [];
351 1
            $response->debug = [];
352 1
            $response->responseHeader = [];
353 1
            $response->facet_counts = [];
354
355 1
            $documents = [];
356
        }
357
358 35
        if (!is_array($documents)) {
359 5
            return;
360
        }
361
362 30
        foreach ($documents as $key => $originalDocument) {
363 27
            $result = $this->wrapApacheSolrDocumentInResultObject($originalDocument);
364 27
            $documents[$key] = $result;
365
        }
366
367 30
        $response->response->docs = $documents;
0 ignored issues
show
Bug introduced by
The property response does not seem to exist. Did you mean _response?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
368 30
    }
369
370
    /**
371
     * This method is used to wrap the \Apache_Solr_Document instance in an instance of the configured SearchResult
372
     * class.
373
     *
374
     * @param \Apache_Solr_Document $originalDocument
375
     * @throws \InvalidArgumentException
376
     * @return SearchResult
377
     */
378 27
    protected function wrapApacheSolrDocumentInResultObject(\Apache_Solr_Document $originalDocument)
379
    {
380 27
        $searchResultClassName = $this->getResultClassName();
381 27
        $result = GeneralUtility::makeInstance($searchResultClassName, $originalDocument);
382 27
        if (!$result instanceof SearchResult) {
383
            throw new \InvalidArgumentException('Could not create result object with class: ' . (string)$searchResultClassName, 1470037679);
384
        }
385
386 27
        return $result;
387
    }
388
389
    /**
390
     * @return string
391
     */
392 27
    protected function getResultClassName()
393
    {
394 27
        return isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultClassName ']) ?
395 27
            $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultClassName '] : SearchResult::class;
396
    }
397
398
    /**
399
     * @return string
400
     */
401 35
    protected function getResultSetClassName()
402
    {
403 35
        return isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultSetClassName ']) ?
404 35
            $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultSetClassName '] : SearchResultSet::class;
405
    }
406
407
    /**
408
     * Checks it the results should be hidden in the response.
409
     *
410
     * @param string $rawQuery
411
     * @return bool
412
     */
413 35
    protected function shouldHideResultsFromInitialSearch($rawQuery)
414
    {
415 35
        return ($this->typoScriptConfiguration->getSearchInitializeWithEmptyQuery() || $this->typoScriptConfiguration->getSearchInitializeWithQuery()) && !$this->typoScriptConfiguration->getSearchShowResultsOfInitialEmptyQuery() && !$this->typoScriptConfiguration->getSearchShowResultsOfInitialQuery() && $rawQuery === null;
416
    }
417
418
    /**
419
     * Initializes additional filters configured through TypoScript and
420
     * Flexforms for use in regular queries and suggest queries.
421
     *
422
     * @param Query $query
423
     * @return void
424
     */
425 33
    protected function applyPageSectionsRootLineFilter(Query $query)
426
    {
427 33
        $searchQueryFilters = $this->typoScriptConfiguration->getSearchQueryFilterConfiguration();
428 33
        if (count($searchQueryFilters) <= 0) {
429 31
            return;
430
        }
431
432
        // special filter to limit search to specific page tree branches
433 2
        if (array_key_exists('__pageSections', $searchQueryFilters)) {
434
            $query->setRootlineFilter($searchQueryFilters['__pageSections']);
435
            $this->typoScriptConfiguration->removeSearchQueryFilterForPageSections();
436
        }
437 2
    }
438
439
    /**
440
     * Retrieves the configuration filters from the TypoScript configuration, except the __pageSections filter.
441
     *
442
     * @return array
443
     */
444 38
    public function getAdditionalFilters()
445
    {
446
        // when we've build the additionalFilter once, we could return them
447 38
        if (count($this->additionalFilters) > 0) {
448 2
            return $this->additionalFilters;
449
        }
450
451 38
        $searchQueryFilters = $this->typoScriptConfiguration->getSearchQueryFilterConfiguration();
452 38
        if (count($searchQueryFilters) <= 0) {
453 36
            return [];
454
        }
455
456 2
        $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
457
458
        // all other regular filters
459 2
        foreach ($searchQueryFilters as $filterKey => $filter) {
460
            // the __pageSections filter should not be handled as additional filter
461 2
            if ($filterKey === '__pageSections') {
462
                continue;
463
            }
464
465 2
            $filterIsArray = is_array($searchQueryFilters[$filterKey]);
466 2
            if ($filterIsArray) {
467
                continue;
468
            }
469
470 2
            $hasSubConfiguration = is_array($searchQueryFilters[$filterKey . '.']);
471 2
            if ($hasSubConfiguration) {
472
                $filter = $cObj->stdWrap($searchQueryFilters[$filterKey], $searchQueryFilters[$filterKey . '.']);
473
            }
474
475 2
            $this->additionalFilters[$filterKey] = $filter;
476
        }
477
478 2
        return $this->additionalFilters;
479
    }
480
481
    /**
482
     * Performs a search and returns a SearchResultSet.
483
     *
484
     * @param SearchRequest $searchRequest
485
     * @return SearchResultSet
486
     */
487 35
    public function search(SearchRequest $searchRequest)
488
    {
489
        /** @var $resultSet SearchResultSet */
490 35
        $resultSetClass = $this->getResultSetClassName();
491 35
        $resultSet = GeneralUtility::makeInstance($resultSetClass);
492 35
        $resultSet->setUsedSearchRequest($searchRequest);
493 35
        $this->lastResultSet = $resultSet;
494
495 35
        $resultSet = $this->handleSearchHook('beforeSearch', $resultSet);
496
497 35
        if ($searchRequest->getRawUserQueryIsNull() && !$this->getInitialSearchIsConfigured()) {
498
            // when no rawQuery was passed or no initialSearch is configured, we pass an empty result set
499 2
            return $resultSet;
500
        }
501
502 33
        if ($searchRequest->getRawUserQueryIsEmptyString() && !$this->typoScriptConfiguration->getSearchQueryAllowEmptyQuery()) {
503
            // the user entered an empty query string "" or "  " and empty querystring is not allowed
504
            return $resultSet;
505
        }
506
507 33
        $rawQuery = $searchRequest->getRawUserQuery();
508 33
        $resultsPerPage = $this->getNumberOfResultsPerPage($rawQuery, $searchRequest->getResultsPerPage());
0 ignored issues
show
Documentation introduced by
$rawQuery is of type array|null, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug introduced by
It seems like $searchRequest->getResultsPerPage() targeting ApacheSolrForTypo3\Solr\...st::getResultsPerPage() can also be of type array; however, ApacheSolrForTypo3\Solr\...umberOfResultsPerPage() does only seem to accept integer|null, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
509 33
        $query = $this->getPreparedQuery($rawQuery, $resultsPerPage);
0 ignored issues
show
Documentation introduced by
$rawQuery is of type array|null, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
510
511 33
        $resultSet->setUsedQuery($query);
512
513 33
        $currentPage = max(0, $searchRequest->getPage());
514
        // if the number of results per page has been changed by the current request, reset the pagebrowser
515 33
        if ($this->resultsPerPageChanged) {
516 4
            $currentPage = 0;
517
        }
518
519 33
        $offSet = $currentPage * $resultsPerPage;
520
        // performing the actual search, sending the query to the Solr server
521 33
        $response = $this->search->search($query, $offSet, null);
522
523 33
        $this->processResponse($rawQuery, $query, $response);
0 ignored issues
show
Documentation introduced by
$rawQuery is of type array|null, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
524 33
        $this->addSearchResultsToResultSet($response, $resultSet);
525
526 33
        $resultSet->setResponse($response);
527 33
        $resultSet->setUsedPage($currentPage);
528 33
        $resultSet->setUsedResultsPerPage($resultsPerPage);
529 33
        $resultSet->setUsedAdditionalFilters($this->getAdditionalFilters());
530 33
        $resultSet->setUsedSearch($this->search);
531
532
        /** @var $searchResultReconstitutionProcessor ResultSetReconstitutionProcessor */
533 33
        $searchResultReconstitutionProcessor = GeneralUtility::makeInstance(ResultSetReconstitutionProcessor::class);
534 33
        $searchResultReconstitutionProcessor->process($resultSet);
535
536 33
        return $this->handleSearchHook('afterSearch', $resultSet);
537
    }
538
539
    /**
540
     * Retrieves a single document from solr by document id.
541
     *
542
     * @param string $documentId
543
     * @return SearchResult
544
     */
545 2
    public function getDocumentById($documentId)
546
    {
547
        /* @var $query Query */
548 2
        $query = GeneralUtility::makeInstance(Query::class, $documentId);
549 2
        $query->setQueryFieldsFromString('id');
550
551 2
        $response = $this->search->search($query, 0, 1);
552 2
        $this->processResponse($documentId, $query, $response);
553
554 2
        $resultDocument = isset($response->response->docs[0]) ? $response->response->docs[0] : null;
0 ignored issues
show
Bug introduced by
The property response does not seem to exist. Did you mean _response?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
555 2
        return $resultDocument;
556
    }
557
558
    /**
559
     * This method is used to call the registered hooks during the search execution.
560
     *
561
     * @param string $eventName
562
     * @param SearchResultSet $resultSet
563
     * @return SearchResultSet
564
     */
565 35
    private function handleSearchHook($eventName, SearchResultSet $resultSet)
566
    {
567 35
        if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr'][$eventName])) {
568 35
            return $resultSet;
569
        }
570
571
        foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr'][$eventName] as $classReference) {
572
            $afterSearchProcessor = GeneralUtility::getUserObj($classReference);
573
            if ($afterSearchProcessor instanceof SearchResultSetProcessor) {
574
                $afterSearchProcessor->process($resultSet);
575
            }
576
        }
577
578
        return $resultSet;
579
    }
580
581
    /**
582
     * @return SearchResultSet
583
     */
584 24
    public function getLastResultSet()
585
    {
586 24
        return $this->lastResultSet;
587
    }
588
589
    /**
590
     * This method returns true when the last search was executed with an empty query
591
     * string or whitespaces only. When no search was triggered it will return false.
592
     *
593
     * @return bool
594
     */
595
    public function getLastSearchWasExecutedWithEmptyQueryString()
596
    {
597
        $wasEmptyQueryString = false;
598
        if ($this->lastResultSet != null) {
599
            $wasEmptyQueryString = $this->lastResultSet->getUsedSearchRequest()->getRawUserQueryIsEmptyString();
600
        }
601
602
        return $wasEmptyQueryString;
603
    }
604
605
    /**
606
     * @param int $requestedPerPage
607
     */
608 3
    protected function setPerPageInSession($requestedPerPage)
609
    {
610 3
        $GLOBALS['TSFE']->fe_user->setKey('ses', 'tx_solr_resultsPerPage', intval($requestedPerPage));
611 3
    }
612
613
    /**
614
     * @return mixed
615
     */
616 26
    protected function getPerPageFromSession()
617
    {
618 26
        return $GLOBALS['TSFE']->fe_user->getKey('ses', 'tx_solr_resultsPerPage');
619
    }
620
621
    /**
622
     * @return bool
623
     */
624 2
    protected function getInitialSearchIsConfigured()
625
    {
626 2
        return $this->typoScriptConfiguration->getSearchInitializeWithEmptyQuery() || $this->typoScriptConfiguration->getSearchShowResultsOfInitialEmptyQuery() || $this->typoScriptConfiguration->getSearchInitializeWithQuery() || $this->typoScriptConfiguration->getSearchShowResultsOfInitialQuery();
627
    }
628
629
    /**
630
     * @return mixed
631
     */
632 26
    protected function getRegisteredSearchComponents()
633
    {
634 26
        return GeneralUtility::makeInstance(SearchComponentManager::class)->getSearchComponents();
635
    }
636
637
    /**
638
     * This method is used to reference the SearchResult object from the response in the SearchResultSet object.
639
     *
640
     * @param \Apache_Solr_Response $response
641
     * @param SearchResultSet $resultSet
642
     */
643 33
    protected function addSearchResultsToResultSet($response, $resultSet)
644
    {
645 33
        if (!is_array($response->response->docs)) {
0 ignored issues
show
Bug introduced by
The property response does not seem to exist. Did you mean _response?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
646 5
            return;
647
        }
648
649 28
        foreach ($response->response->docs as $searchResult) {
0 ignored issues
show
Bug introduced by
The property response does not seem to exist. Did you mean _response?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
650 25
            $resultSet->addSearchResult($searchResult);
651
        }
652 28
    }
653
}
654