Passed
Push — master ( 93b1b5...cf5d08 )
by Timo
04:01
created

StatisticsWriterProcessor::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 1
crap 2
1
<?php
2
namespace ApacheSolrForTypo3\Solr\Domain\Search\Statistics;
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 2 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\SearchResultSet;
28
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\SearchResultSetProcessor;
29
use ApacheSolrForTypo3\Solr\Query;
30
use ApacheSolrForTypo3\Solr\HtmlContentExtractor;
31
use TYPO3\CMS\Core\Utility\GeneralUtility;
32
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
33
34
/**
35
 * Writes statistics after searches have been conducted.
36
 *
37
 * @author Ingo Renner <[email protected]>
38
 * @author Dimitri Ebert <[email protected]>
39
 * @author Timo Hund <[email protected]>
40
 */
41
class StatisticsWriterProcessor implements SearchResultSetProcessor
42
{
43
    /**
44
     * @var StatisticsRepository
45
     */
46
    protected $statisticsRepository;
47
48
    /**
49 28
     * @param StatisticsRepository $statisticsRepository
50 28
     */
51 28
    public function __construct(StatisticsRepository $statisticsRepository = null)
52 28
    {
53 28
        $this->statisticsRepository = isset($statisticsRepository) ? $statisticsRepository : GeneralUtility::makeInstance(StatisticsRepository::class);
54
    }
55 28
56
    /**
57 4
     * @param SearchResultSet $resultSet
58
     * @return SearchResultSet
59
     */
60 24
    public function process(SearchResultSet $resultSet) {
61 24
        $searchRequest = $resultSet->getUsedSearchRequest();
62 24
        $response = $resultSet->getResponse();
63 24
        $configuration = $searchRequest->getContextTypoScriptConfiguration();
64
        $keywords = $this->getProcessedKeywords($resultSet->getUsedQuery(), $configuration->getSearchFrequentSearchesUseLowercaseKeywords());
65 24
66
        if (empty($keywords)) {
67 24
            // do not track empty queries
68 24
            return $resultSet;
69 24
        }
70 24
71 24
        $filters = $searchRequest->getActiveFacets();
72 24
        $sorting = $this->sanitizeString($searchRequest->getSorting());
73 24
        $page = (int)$searchRequest->getPage();
74 24
        $ipMaskLength = $configuration->getStatisticsAnonymizeIP();
75 24
76 24
        $TSFE = $this->getTSFE();
77 24
        $statisticData = [
78 24
            'pid' => $TSFE->id,
79 24
            'root_pid' => $TSFE->tmpl->rootLine[0]['uid'],
80 24
            'tstamp' => $this->getTime(),
81 24
            'language' => $TSFE->sys_language_uid,
82 24
            'num_found' => isset($response->response->numFound) ? (int)$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...
83 24
            'suggestions_shown' => is_object($response->spellcheck->suggestions) ? (int)get_object_vars($response->spellcheck->suggestions) : 0,
84
            'time_total' => isset($response->debug->timing->time) ? $response->debug->timing->time : 0,
85
            'time_preparation' => isset($response->debug->timing->prepare->time) ? $response->debug->timing->prepare->time : 0,
86 24
            'time_processing' => isset($response->debug->timing->process->time) ? $response->debug->timing->process->time : 0,
87
            'feuser_id' => (int)$TSFE->fe_user->user['uid'],
88 24
            'cookie' => $TSFE->fe_user->id,
89
            'ip' => $this->applyIpMask($this->getUserIp(), $ipMaskLength),
90
            'page' => (int)$page,
91
            'keywords' => $keywords,
92
            'filters' => serialize($filters),
93
            'sorting' => $sorting,
94
            'parameters' => serialize($response->responseHeader->params)
95
        ];
96 28
97
        $this->statisticsRepository->saveStatisticsRecord($statisticData);
98 28
99 28
        return $resultSet;
100 28
    }
101
102
    /**
103
     * @param Query $query
104 28
     * @param boolean $lowerCaseQuery
105
     * @return string
106
     */
107
    protected function getProcessedKeywords(Query $query, $lowerCaseQuery = false)
108
    {
109
        $keywords = $query->getKeywords();
110
        $keywords = $this->sanitizeString($keywords);
111
        if ($lowerCaseQuery) {
112
            $keywords = mb_strtolower($keywords);
113 28
        }
114
115
        return $keywords;
116 28
    }
117 28
118 28
    /**
119 28
     * Sanitizes a string
120
     *
121 28
     * @param $string String to sanitize
122
     * @return string Sanitized string
123
     */
124
    protected function sanitizeString($string)
125
    {
126
        // clean content
127
        $string = HtmlContentExtractor::cleanContent($string);
128
        $string = html_entity_decode($string, ENT_QUOTES, 'UTF-8');
129
        $string = filter_var(strip_tags($string), FILTER_SANITIZE_STRING); // after entity decoding we might have tags again
130
        $string = trim($string);
131 24
132
        return $string;
133
    }
134 24
135 1
    /**
136 1
     * Internal function to mask portions of the visitor IP address
137
     *
138
     * @param string $ip IP address in network address format
139
     * @param int $maskLength Number of octets to reset
140 1
     * @return string
141
     */
142
    protected function applyIpMask($ip, $maskLength)
143
    {
144
        // IPv4 or mapped IPv4 in IPv6
145 23
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
146
            $i = strlen($ip);
147
            if ($maskLength > $i) {
148
                $maskLength = $i;
149
            }
150 23
151
            while ($maskLength-- > 0) {
152
                $ip[--$i] = chr(0);
153 1
            }
154
        } else {
155
            $masks = [
156
                'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
157
                'ffff:ffff:ffff:ffff::',
158
                'ffff:ffff:ffff:0000::',
159 23
                'ffff:ff00:0000:0000::'
160
            ];
161 23
            return $ip & pack('a16', inet_pton($masks[$maskLength]));
162 23
        }
163
164
        return $ip;
165
    }
166
167 23
    /**
168
     * @return TypoScriptFrontendController
169 23
     */
170
    protected function getTSFE()
171
    {
172
        return $GLOBALS['TSFE'];
173
    }
174
175 23
    /**
176
     * @return string
177 23
     */
178
    protected function getUserIp()
179
    {
180
        return GeneralUtility::getIndpEnv('REMOTE_ADDR');
181
    }
182
183 23
    /**
184
     * @return mixed
185 23
     */
186
    protected function getTime()
187
    {
188
        return $GLOBALS['EXEC_TIME'];
189
    }
190
}
191