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, |
|
|
|
|
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
|
|
|
|
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.