Failed Conditions
Push — master ( 93b1b5...fcd324 )
by Rafael
04:07
created

StatisticsWriterProcessor::applyIpMask()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 24
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.6845
c 0
b 0
f 0
cc 4
eloc 15
nc 5
nop 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
     * @param StatisticsRepository $statisticsRepository
50
     */
51
    public function __construct(StatisticsRepository $statisticsRepository = null)
52
    {
53
        $this->statisticsRepository = isset($statisticsRepository) ? $statisticsRepository : GeneralUtility::makeInstance(StatisticsRepository::class);
54
    }
55
56
    /**
57
     * @param SearchResultSet $resultSet
58
     * @return SearchResultSet
59
     */
60
    public function process(SearchResultSet $resultSet) {
61
        $searchRequest = $resultSet->getUsedSearchRequest();
62
        $response = $resultSet->getResponse();
63
        $configuration = $searchRequest->getContextTypoScriptConfiguration();
64
        $keywords = $this->getProcessedKeywords($resultSet->getUsedQuery(), $configuration->getSearchFrequentSearchesUseLowercaseKeywords());
65
66
        if (empty($keywords)) {
67
            // do not track empty queries
68
            return $resultSet;
69
        }
70
71
        $filters = $searchRequest->getActiveFacets();
72
        $sorting = $this->sanitizeString($searchRequest->getSorting());
73
        $page = (int)$searchRequest->getPage();
74
        $ipMaskLength = $configuration->getStatisticsAnonymizeIP();
75
76
        $TSFE = $this->getTSFE();
77
        $statisticData = [
78
            'pid' => $TSFE->id,
79
            'root_pid' => $TSFE->tmpl->rootLine[0]['uid'],
80
            'tstamp' => $this->getTime(),
81
            'language' => $TSFE->sys_language_uid,
82
            '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
            '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
            'time_processing' => isset($response->debug->timing->process->time) ? $response->debug->timing->process->time : 0,
87
            'feuser_id' => (int)$TSFE->fe_user->user['uid'],
88
            '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
97
        $this->statisticsRepository->saveStatisticsRecord($statisticData);
98
99
        return $resultSet;
100
    }
101
102
    /**
103
     * @param Query $query
104
     * @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
        }
114
115
        return $keywords;
116
    }
117
118
    /**
119
     * Sanitizes a string
120
     *
121
     * @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
132
        return $string;
133
    }
134
135
    /**
136
     * 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
     * @return string
141
     */
142
    protected function applyIpMask($ip, $maskLength)
143
    {
144
        // IPv4 or mapped IPv4 in IPv6
145
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
146
            $i = strlen($ip);
147
            if ($maskLength > $i) {
148
                $maskLength = $i;
149
            }
150
151
            while ($maskLength-- > 0) {
152
                $ip[--$i] = chr(0);
153
            }
154
        } else {
155
            $masks = [
156
                'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
157
                'ffff:ffff:ffff:ffff::',
158
                'ffff:ffff:ffff:0000::',
159
                'ffff:ff00:0000:0000::'
160
            ];
161
            return $ip & pack('a16', inet_pton($masks[$maskLength]));
162
        }
163
164
        return $ip;
165
    }
166
167
    /**
168
     * @return TypoScriptFrontendController
169
     */
170
    protected function getTSFE()
171
    {
172
        return $GLOBALS['TSFE'];
173
    }
174
175
    /**
176
     * @return string
177
     */
178
    protected function getUserIp()
179
    {
180
        return GeneralUtility::getIndpEnv('REMOTE_ADDR');
181
    }
182
183
    /**
184
     * @return mixed
185
     */
186
    protected function getTime()
187
    {
188
        return $GLOBALS['EXEC_TIME'];
189
    }
190
}
191