Passed
Pull Request — main (#3354)
by
unknown
44:14
created

StatisticsWriterProcessor   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 134
Duplicated Lines 0 %

Test Coverage

Coverage 89.58%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 12
eloc 48
c 2
b 0
f 0
dl 0
loc 134
ccs 43
cts 48
cp 0.8958
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A getProcessedKeywords() 0 13 2
A sanitizeString() 0 6 1
A getTime() 0 3 1
A getTSFE() 0 3 1
A __construct() 0 6 1
A getUserIp() 0 3 1
A process() 0 46 5
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace ApacheSolrForTypo3\Solr\Domain\Search\Statistics;
17
18
use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query;
19
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\SearchResultSet;
20
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\SearchResultSetProcessor;
21
use ApacheSolrForTypo3\Solr\Domain\Site\SiteRepository;
22
use ApacheSolrForTypo3\Solr\HtmlContentExtractor;
23
use ApacheSolrForTypo3\Solr\Util;
24
use function inet_pton;
25
use function pack;
26
use TYPO3\CMS\Core\Context\Context;
27
use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException;
28
use TYPO3\CMS\Core\Utility\GeneralUtility;
29
use TYPO3\CMS\Core\Utility\IpAnonymizationUtility;
30
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
31
32
/**
33
 * Writes statistics after searches have been conducted.
34
 *
35
 * @author Ingo Renner <[email protected]>
36
 * @author Dimitri Ebert <[email protected]>
37
 * @author Timo Hund <[email protected]>
38
 */
39
class StatisticsWriterProcessor implements SearchResultSetProcessor
40
{
41
    /**
42
     * @var StatisticsRepository
43
     */
44
    protected $statisticsRepository;
45
46
    /**
47
     * @var SiteRepository
48
     */
49
    protected $siteRepository;
50
51
    /**
52
     * @param StatisticsRepository|null $statisticsRepository
53
     * @param SiteRepository|null $siteRepository
54 1
     */
55
    public function __construct(
56
        StatisticsRepository $statisticsRepository = null,
57
        SiteRepository $siteRepository = null
58 1
    ) {
59 1
        $this->statisticsRepository = $statisticsRepository ?? GeneralUtility::makeInstance(StatisticsRepository::class);
60
        $this->siteRepository = $siteRepository ?? GeneralUtility::makeInstance(SiteRepository::class);
61
    }
62
63
    /**
64
     * @param SearchResultSet $resultSet
65
     * @return SearchResultSet
66
     * @throws AspectNotFoundException
67 1
     */
68
    public function process(SearchResultSet $resultSet): SearchResultSet
69 1
    {
70 1
        $searchRequest = $resultSet->getUsedSearchRequest();
71 1
        $response = $resultSet->getResponse();
72 1
        $configuration = $searchRequest->getContextTypoScriptConfiguration();
73
        $keywords = $this->getProcessedKeywords($resultSet->getUsedQuery(), $configuration->getSearchFrequentSearchesUseLowercaseKeywords());
0 ignored issues
show
Bug introduced by
It seems like $resultSet->getUsedQuery() can also be of type null; however, parameter $query of ApacheSolrForTypo3\Solr\...:getProcessedKeywords() does only seem to accept ApacheSolrForTypo3\Solr\Domain\Search\Query\Query, maybe add an additional type check? ( Ignorable by Annotation )

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

73
        $keywords = $this->getProcessedKeywords(/** @scrutinizer ignore-type */ $resultSet->getUsedQuery(), $configuration->getSearchFrequentSearchesUseLowercaseKeywords());
Loading history...
74 1
75
        if (empty($keywords)) {
76
            // do not track empty queries
77
            return $resultSet;
78
        }
79 1
80 1
        $filters = $searchRequest->getActiveFacets();
81 1
        $sorting = $this->sanitizeString($searchRequest->getSorting());
82 1
        $page = (int)$searchRequest->getPage();
83
        $ipMaskLength = $configuration->getStatisticsAnonymizeIP();
84 1
85 1
        $TSFE = $this->getTSFE();
86 1
        $root_pid = $this->siteRepository->getSiteByPageId($TSFE->id)->getRootPageId();
0 ignored issues
show
Bug introduced by
It seems like $TSFE->id can also be of type string; however, parameter $pageId of ApacheSolrForTypo3\Solr\...tory::getSiteByPageId() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

86
        $root_pid = $this->siteRepository->getSiteByPageId(/** @scrutinizer ignore-type */ $TSFE->id)->getRootPageId();
Loading history...
87 1
        $statisticData = [
88
            'pid' => $TSFE->id,
89 1
            'root_pid' => $root_pid,
90 1
            'tstamp' => $this->getTime(),
91
            'language' => Util::getLanguageUid(),
92 1
            // @extensionScannerIgnoreLine
93 1
            'num_found' => $resultSet->getAllResultCount(),
94
            'suggestions_shown' => is_object($response->spellcheck->suggestions ?? null) ? (int)get_object_vars($response->spellcheck->suggestions) : 0,
95 1
            // @extensionScannerIgnoreLine
96
            'time_total' => $response->debug->timing->time ?? 0,
97 1
            // @extensionScannerIgnoreLine
98
            'time_preparation' => $response->debug->timing->prepare->time ?? 0,
99 1
            // @extensionScannerIgnoreLine
100 1
            'time_processing' => $response->debug->timing->process->time ?? 0,
101 1
            'feuser_id' => isset($TSFE->fe_user->user) ? (int)$TSFE->fe_user->user['uid'] ?? 0 : 0,
102 1
            'cookie' => $TSFE->fe_user->id ?? '',
103
            'ip' => IpAnonymizationUtility::anonymizeIp($this->getUserIp(), $ipMaskLength),
104
            'page' => $page,
105 1
            'keywords' => $keywords,
106
            'filters' => serialize($filters),
107 1
            'sorting' => $sorting,
108
            'parameters' => isset($response->responseHeader->params) ? serialize($response->responseHeader->params) : '',
109
        ];
110 1
111
        $this->statisticsRepository->saveStatisticsRecord($statisticData);
112 1
113
        return $resultSet;
114
    }
115
116
    /**
117
     * @param Query $query
118
     * @param bool $lowerCaseQuery
119
     * @return string
120 1
     */
121
    protected function getProcessedKeywords(
122
        Query $query,
123
        bool $lowerCaseQuery = false
124 1
    ): string {
125 1
        $keywords = $query->getQuery();
126 1
        $keywords = $this->sanitizeString($keywords);
0 ignored issues
show
Bug introduced by
It seems like $keywords can also be of type null; however, parameter $string of ApacheSolrForTypo3\Solr\...essor::sanitizeString() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

126
        $keywords = $this->sanitizeString(/** @scrutinizer ignore-type */ $keywords);
Loading history...
127
        // Ensure string does not exceed database field length
128
        $keywords = substr($keywords, 0, 128);
129
        if ($lowerCaseQuery) {
130 1
            $keywords = mb_strtolower($keywords);
131
        }
132
133
        return $keywords;
134
    }
135
136
    /**
137
     * Sanitizes a string
138
     *
139 1
     * @param $string String to sanitize
140
     * @return string Sanitized string
141
     */
142 1
    protected function sanitizeString(string $string): string
143 1
    {
144 1
        // clean content
145
        $string = HtmlContentExtractor::cleanContent($string);
146
        $string = htmlspecialchars(strip_tags($string), ENT_QUOTES); // after entity decoding we might have tags again
147
        return trim($string);
148
    }
149
150
    /**
151
     * @return TypoScriptFrontendController
152
     */
153
    protected function getTSFE(): ?TypoScriptFrontendController
154 1
    {
155
        return $GLOBALS['TSFE'];
156 1
    }
157 1
158
    /**
159
     * @return string
160
     */
161
    protected function getUserIp(): string
162
    {
163
        return GeneralUtility::getIndpEnv('REMOTE_ADDR');
164
    }
165
166
    /**
167
     * @return mixed
168
     * @throws AspectNotFoundException
169
     */
170
    protected function getTime()
171
    {
172
        return GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('date', 'timestamp');
173
    }
174
}
175