Passed
Push — master ( 0ba0e3...644d9b )
by Timo
19:06
created

StatisticsWriterProcessor::getTSFE()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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