PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.0.3
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.0.3
5.13.0 5.12.1 5.12.0 5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / Tracker / VisitorRecognizer.php
matomo / app / core / Tracker Last commit date
Db 5 years ago Handler 5 years ago TableLogAction 5 years ago Visit 5 years ago Action.php 5 years ago ActionPageview.php 5 years ago Cache.php 5 years ago Db.php 5 years ago Failures.php 6 years ago FingerprintSalt.php 6 years ago GoalManager.php 5 years ago Handler.php 5 years ago IgnoreCookie.php 5 years ago LogTable.php 5 years ago Model.php 5 years ago PageUrl.php 5 years ago Request.php 5 years ago RequestProcessor.php 5 years ago RequestSet.php 5 years ago Response.php 5 years ago ScheduledTasksRunner.php 5 years ago Settings.php 5 years ago TableLogAction.php 5 years ago TrackerCodeGenerator.php 5 years ago TrackerConfig.php 5 years ago Visit.php 5 years ago VisitExcluded.php 5 years ago VisitInterface.php 5 years ago Visitor.php 5 years ago VisitorNotFoundInDb.php 5 years ago VisitorRecognizer.php 5 years ago
VisitorRecognizer.php
295 lines
1 <?php
2 /**
3 * Matomo - free/libre analytics platform
4 *
5 * @link https://matomo.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 */
8
9 namespace Piwik\Tracker;
10
11 use Piwik\Common;
12 use Piwik\EventDispatcher;
13 use Piwik\Plugin\Dimension\VisitDimension;
14 use Piwik\Tracker\Visit\VisitProperties;
15
16 /**
17 * Tracker service that finds the last known visit for the visitor being tracked.
18 */
19 class VisitorRecognizer
20 {
21 /**
22 * Set when a visit was found. Stores the original values of the row that is currently stored in the DB when
23 * the visit was selected.
24 */
25 const KEY_ORIGINAL_VISIT_ROW = 'originalVisit';
26
27 /**
28 * Local variable cache for the getVisitFieldsPersist() method.
29 *
30 * @var array
31 */
32 private $visitFieldsToSelect;
33
34 /**
35 * See http://piwik.org/faq/how-to/faq_175/.
36 *
37 * @var bool
38 */
39 private $trustCookiesOnly;
40
41 /**
42 * Length of a visit in seconds.
43 *
44 * @var int
45 */
46 private $visitStandardLength;
47
48 /**
49 * Number of seconds that have to pass after an action before a new action from the same visitor is
50 * considered a new visit. Defaults to $visitStandardLength.
51 *
52 * @var int
53 */
54 private $lookBackNSecondsCustom;
55
56 /**
57 * @var Model
58 */
59 private $model;
60
61 /**
62 * @var EventDispatcher
63 */
64 private $eventDispatcher;
65
66 /**
67 * @var array
68 */
69 private $visitRow;
70
71 public function __construct($trustCookiesOnly, $visitStandardLength, $lookbackNSecondsCustom,
72 Model $model, EventDispatcher $eventDispatcher)
73 {
74 $this->trustCookiesOnly = $trustCookiesOnly;
75 $this->visitStandardLength = $visitStandardLength;
76 $this->lookBackNSecondsCustom = $lookbackNSecondsCustom;
77
78 $this->model = $model;
79 $this->eventDispatcher = $eventDispatcher;
80 }
81
82 public function setTrustCookiesOnly($trustCookiesOnly)
83 {
84 $this->trustCookiesOnly = $trustCookiesOnly;
85 }
86
87 public function findKnownVisitor($configId, VisitProperties $visitProperties, Request $request)
88 {
89 $idSite = $request->getIdSite();
90 $idVisitor = $request->getVisitorId();
91 $userId = $request->getForcedUserId();
92
93 $isVisitorIdToLookup = !empty($idVisitor);
94
95 if ($isVisitorIdToLookup) {
96 $visitProperties->setProperty('idvisitor', $idVisitor);
97 Common::printDebug("Matching visitors with: visitorId=" . bin2hex($idVisitor) . " OR configId=" . bin2hex($configId));
98 } else {
99 Common::printDebug("Visitor doesn't have the piwik cookie...");
100 }
101
102 $persistedVisitAttributes = $this->getVisitorFieldsPersist();
103
104 $shouldMatchOneFieldOnly = $this->shouldLookupOneVisitorFieldOnly($isVisitorIdToLookup, $request);
105 list($timeLookBack, $timeLookAhead) = $this->getWindowLookupThisVisit($request);
106
107 $visitRow = $this->model->findVisitor($idSite, $configId, $idVisitor, $userId, $persistedVisitAttributes, $shouldMatchOneFieldOnly, $isVisitorIdToLookup, $timeLookBack, $timeLookAhead);
108 $this->visitRow = $visitRow;
109
110 if ($visitRow
111 && count($visitRow) > 0
112 ) {
113 $visitProperties->setProperty('idvisitor', $visitRow['idvisitor']);
114 $visitProperties->setProperty('user_id', $visitRow['user_id']);
115
116 Common::printDebug("The visitor is known (idvisitor = " . bin2hex($visitProperties->getProperty('idvisitor')) . ",
117 config_id = " . bin2hex($configId) . ",
118 last action = " . date("r", $visitProperties->getProperty('visit_last_action_time')) . ",
119 first action = " . date("r", $visitProperties->getProperty('visit_first_action_time')) . ")");
120
121 return true;
122 } else {
123 Common::printDebug("The visitor was not matched with an existing visitor...");
124
125 return false;
126 }
127 }
128
129 public function removeUnchangedValues($visit, VisitProperties $originalVisit = null)
130 {
131 if (empty($originalVisit)) {
132 return $visit;
133 }
134
135 $originalRow = $originalVisit->getProperties();
136 if (!empty($originalRow['idvisitor'])
137 && !empty($visit['idvisitor'])
138 && bin2hex($originalRow['idvisitor']) === bin2hex($visit['idvisitor'])) {
139 unset($visit['idvisitor']);
140 }
141
142 $fieldsToCompareValue = array('user_id', 'visit_last_action_time', 'visit_total_time');
143 foreach ($fieldsToCompareValue as $field) {
144 if (!empty($originalRow[$field])
145 && !empty($visit[$field])
146 && $visit[$field] == $originalRow[$field]) {
147 // we can't use === eg for visit_total_time which may be partially an integer and sometimes a string
148 // because we check for !empty things should still work as expected though
149 // (eg we wouldn't compare false with 0)
150 unset($visit[$field]);
151 }
152 }
153
154 return $visit;
155 }
156
157 public function updateVisitPropertiesFromLastVisitRow(VisitProperties $visitProperties)
158 {
159 // These values will be used throughout the request
160 foreach ($this->getVisitorFieldsPersist() as $field) {
161 $value = $this->visitRow[$field];
162 if ($field == 'visit_last_action_time' || $field == 'visit_first_action_time') {
163 $value = strtotime($value);
164 }
165
166 $visitProperties->setProperty($field, $value);
167 }
168
169 Common::printDebug("The visit is part of an existing visit (
170 idvisit = {$visitProperties->getProperty('idvisit')},
171 visit_goal_buyer' = " . $visitProperties->getProperty('visit_goal_buyer') . ")");
172 }
173
174 protected function shouldLookupOneVisitorFieldOnly($isVisitorIdToLookup, Request $request)
175 {
176 $isForcedUserIdMustMatch = (false !== $request->getForcedUserId());
177
178 // This setting would be enabled for Intranet websites, to ensure that visitors using all the same computer config, same IP
179 // are not counted as 1 visitor. In this case, we want to enforce and trust the visitor ID from the cookie.
180 if ($isVisitorIdToLookup && $this->trustCookiesOnly) {
181 return true;
182 }
183
184 if ($isForcedUserIdMustMatch) {
185 // if &iud was set, we must try and match both idvisitor and config_id
186 return false;
187 }
188
189 // If a &cid= was set, we force to select this visitor (or create a new one)
190 $isForcedVisitorIdMustMatch = ($request->getForcedVisitorId() != null);
191
192 if ($isForcedVisitorIdMustMatch) {
193 return true;
194 }
195
196 if (!$isVisitorIdToLookup) {
197 return true;
198 }
199
200 return false;
201 }
202
203 /**
204 * By default, we look back 30 minutes to find a previous visitor (for performance reasons).
205 * In some cases, it is useful to look back and count unique visitors more accurately. You can set custom lookback window in
206 * [Tracker] window_look_back_for_visitor
207 *
208 * The returned value is the window range (Min, max) that the matched visitor should fall within
209 *
210 * @return array( datetimeMin, datetimeMax )
211 */
212 protected function getWindowLookupThisVisit(Request $request)
213 {
214 $lookAheadNSeconds = $this->visitStandardLength;
215 $lookBackNSeconds = $this->visitStandardLength;
216 if ($this->lookBackNSecondsCustom > $lookBackNSeconds) {
217 $lookBackNSeconds = $this->lookBackNSecondsCustom;
218 }
219
220 $timeLookBack = date('Y-m-d H:i:s', $request->getCurrentTimestamp() - $lookBackNSeconds);
221 $timeLookAhead = date('Y-m-d H:i:s', $request->getCurrentTimestamp() + $lookAheadNSeconds);
222
223 return array($timeLookBack, $timeLookAhead);
224 }
225
226 /**
227 * @return array
228 */
229 private function getVisitorFieldsPersist()
230 {
231 if (is_null($this->visitFieldsToSelect)) {
232 $fields = array(
233 'idvisitor',
234 'idvisit',
235 'user_id',
236
237 'visit_exit_idaction_url',
238 'visit_exit_idaction_name',
239 'visitor_returning',
240 'visitor_seconds_since_first',
241 'visitor_seconds_since_order',
242 'visitor_count_visits',
243 'visit_goal_buyer',
244
245 'location_country',
246 'location_region',
247 'location_city',
248 'location_latitude',
249 'location_longitude',
250
251 'referer_name',
252 'referer_keyword',
253 'referer_type',
254 );
255
256 $dimensions = VisitDimension::getAllDimensions();
257
258 foreach ($dimensions as $dimension) {
259 if ($dimension->hasImplementedEvent('onExistingVisit') || $dimension->hasImplementedEvent('onAnyGoalConversion')) {
260 $fields[] = $dimension->getColumnName();
261 }
262
263 foreach ($dimension->getRequiredVisitFields() as $field) {
264 $fields[] = $field;
265 }
266 }
267
268 /**
269 * This event collects a list of [visit entity](/guides/persistence-and-the-mysql-backend#visits) properties that should be loaded when reading
270 * the existing visit. Properties that appear in this list will be available in other tracking
271 * events such as 'onExistingVisit'.
272 *
273 * Plugins can use this event to load additional visit entity properties for later use during tracking.
274 *
275 * This event is deprecated, use [Dimensions](http://developer.piwik.org/guides/dimensions) instead.
276 *
277 * @deprecated
278 */
279 $this->eventDispatcher->postEvent('Tracker.getVisitFieldsToPersist', array(&$fields));
280
281 array_unshift($fields, 'visit_first_action_time');
282 array_unshift($fields, 'visit_last_action_time');
283
284 $this->visitFieldsToSelect = array_unique($fields);
285 }
286
287 return $this->visitFieldsToSelect;
288 }
289
290 public function getLastKnownVisit()
291 {
292 return $this->visitRow;
293 }
294 }
295