PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 1.0.5
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v1.0.5
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 6 years ago Handler 6 years ago TableLogAction 6 years ago Visit 6 years ago Action.php 6 years ago ActionPageview.php 6 years ago Cache.php 6 years ago Db.php 6 years ago Failures.php 6 years ago GoalManager.php 6 years ago Handler.php 6 years ago IgnoreCookie.php 6 years ago LogTable.php 6 years ago Model.php 6 years ago PageUrl.php 6 years ago Request.php 6 years ago RequestProcessor.php 6 years ago RequestSet.php 6 years ago Response.php 6 years ago ScheduledTasksRunner.php 6 years ago Settings.php 6 years ago TableLogAction.php 6 years ago TrackerCodeGenerator.php 6 years ago TrackerConfig.php 6 years ago Visit.php 6 years ago VisitExcluded.php 6 years ago VisitInterface.php 6 years ago Visitor.php 6 years ago VisitorNotFoundInDb.php 6 years ago VisitorRecognizer.php 6 years ago
VisitorRecognizer.php
267 lines
1 <?php
2 /**
3 * Piwik - 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\Plugins\CustomVariables\CustomVariables;
15 use Piwik\Tracker\Visit\VisitProperties;
16
17 /**
18 * Tracker service that finds the last known visit for the visitor being tracked.
19 */
20 class VisitorRecognizer
21 {
22 /**
23 * Local variable cache for the getVisitFieldsPersist() method.
24 *
25 * @var array
26 */
27 private $visitFieldsToSelect;
28
29 /**
30 * See http://piwik.org/faq/how-to/faq_175/.
31 *
32 * @var bool
33 */
34 private $trustCookiesOnly;
35
36 /**
37 * Length of a visit in seconds.
38 *
39 * @var int
40 */
41 private $visitStandardLength;
42
43 /**
44 * Number of seconds that have to pass after an action before a new action from the same visitor is
45 * considered a new visit. Defaults to $visitStandardLength.
46 *
47 * @var int
48 */
49 private $lookBackNSecondsCustom;
50
51 /**
52 * @var Model
53 */
54 private $model;
55
56 /**
57 * @var EventDispatcher
58 */
59 private $eventDispatcher;
60
61 /**
62 * @var array
63 */
64 private $visitRow;
65
66 public function __construct($trustCookiesOnly, $visitStandardLength, $lookbackNSecondsCustom,
67 Model $model, EventDispatcher $eventDispatcher)
68 {
69 $this->trustCookiesOnly = $trustCookiesOnly;
70 $this->visitStandardLength = $visitStandardLength;
71 $this->lookBackNSecondsCustom = $lookbackNSecondsCustom;
72
73 $this->model = $model;
74 $this->eventDispatcher = $eventDispatcher;
75 }
76
77 public function setTrustCookiesOnly($trustCookiesOnly)
78 {
79 $this->trustCookiesOnly = $trustCookiesOnly;
80 }
81
82 public function findKnownVisitor($configId, VisitProperties $visitProperties, Request $request)
83 {
84 $idSite = $request->getIdSite();
85 $idVisitor = $request->getVisitorId();
86 $userId = $request->getForcedUserId();
87
88 $isVisitorIdToLookup = !empty($idVisitor);
89
90 if ($isVisitorIdToLookup) {
91 $visitProperties->setProperty('idvisitor', $idVisitor);
92 Common::printDebug("Matching visitors with: visitorId=" . bin2hex($idVisitor) . " OR configId=" . bin2hex($configId));
93 } else {
94 Common::printDebug("Visitor doesn't have the piwik cookie...");
95 }
96
97 $persistedVisitAttributes = $this->getVisitorFieldsPersist();
98
99 $shouldMatchOneFieldOnly = $this->shouldLookupOneVisitorFieldOnly($isVisitorIdToLookup, $request);
100 list($timeLookBack, $timeLookAhead) = $this->getWindowLookupThisVisit($request);
101
102 $visitRow = $this->model->findVisitor($idSite, $configId, $idVisitor, $userId, $persistedVisitAttributes, $shouldMatchOneFieldOnly, $isVisitorIdToLookup, $timeLookBack, $timeLookAhead);
103 $this->visitRow = $visitRow;
104
105 if ($visitRow
106 && count($visitRow) > 0
107 ) {
108 $visitProperties->setProperty('idvisitor', $visitRow['idvisitor']);
109 $visitProperties->setProperty('user_id', $visitRow['user_id']);
110
111 Common::printDebug("The visitor is known (idvisitor = " . bin2hex($visitProperties->getProperty('idvisitor')) . ",
112 config_id = " . bin2hex($configId) . ",
113 last action = " . date("r", $visitProperties->getProperty('visit_last_action_time')) . ",
114 first action = " . date("r", $visitProperties->getProperty('visit_first_action_time')) . ")");
115
116 return true;
117 } else {
118 Common::printDebug("The visitor was not matched with an existing visitor...");
119
120 return false;
121 }
122 }
123
124 public function updateVisitPropertiesFromLastVisitRow(VisitProperties $visitProperties)
125 {
126 // These values will be used throughout the request
127 foreach ($this->getVisitorFieldsPersist() as $field) {
128 $value = $this->visitRow[$field];
129 if ($field == 'visit_last_action_time' || $field == 'visit_first_action_time') {
130 $value = strtotime($value);
131 }
132
133 $visitProperties->setProperty($field, $value);
134 }
135
136 Common::printDebug("The visit is part of an existing visit (
137 idvisit = {$visitProperties->getProperty('idvisit')},
138 visit_goal_buyer' = " . $visitProperties->getProperty('visit_goal_buyer') . ")");
139 }
140
141 protected function shouldLookupOneVisitorFieldOnly($isVisitorIdToLookup, Request $request)
142 {
143 $isForcedUserIdMustMatch = (false !== $request->getForcedUserId());
144
145 // This setting would be enabled for Intranet websites, to ensure that visitors using all the same computer config, same IP
146 // are not counted as 1 visitor. In this case, we want to enforce and trust the visitor ID from the cookie.
147 if ($isVisitorIdToLookup && $this->trustCookiesOnly) {
148 return true;
149 }
150
151 if ($isForcedUserIdMustMatch) {
152 // if &iud was set, we must try and match both idvisitor and config_id
153 return false;
154 }
155
156 // If a &cid= was set, we force to select this visitor (or create a new one)
157 $isForcedVisitorIdMustMatch = ($request->getForcedVisitorId() != null);
158
159 if ($isForcedVisitorIdMustMatch) {
160 return true;
161 }
162
163 if (!$isVisitorIdToLookup) {
164 return true;
165 }
166
167 return false;
168 }
169
170 /**
171 * By default, we look back 30 minutes to find a previous visitor (for performance reasons).
172 * In some cases, it is useful to look back and count unique visitors more accurately. You can set custom lookback window in
173 * [Tracker] window_look_back_for_visitor
174 *
175 * The returned value is the window range (Min, max) that the matched visitor should fall within
176 *
177 * @return array( datetimeMin, datetimeMax )
178 */
179 protected function getWindowLookupThisVisit(Request $request)
180 {
181 $lookAheadNSeconds = $this->visitStandardLength;
182 $lookBackNSeconds = $this->visitStandardLength;
183 if ($this->lookBackNSecondsCustom > $lookBackNSeconds) {
184 $lookBackNSeconds = $this->lookBackNSecondsCustom;
185 }
186
187 $timeLookBack = date('Y-m-d H:i:s', $request->getCurrentTimestamp() - $lookBackNSeconds);
188 $timeLookAhead = date('Y-m-d H:i:s', $request->getCurrentTimestamp() + $lookAheadNSeconds);
189
190 return array($timeLookBack, $timeLookAhead);
191 }
192
193 /**
194 * @return array
195 */
196 private function getVisitorFieldsPersist()
197 {
198 if (is_null($this->visitFieldsToSelect)) {
199 $fields = array(
200 'idvisitor',
201 'idvisit',
202 'user_id',
203
204 'visit_exit_idaction_url',
205 'visit_exit_idaction_name',
206 'visitor_returning',
207 'visitor_days_since_first',
208 'visitor_days_since_order',
209 'visitor_count_visits',
210 'visit_goal_buyer',
211
212 'location_country',
213 'location_region',
214 'location_city',
215 'location_latitude',
216 'location_longitude',
217
218 'referer_name',
219 'referer_keyword',
220 'referer_type',
221 );
222
223 $dimensions = VisitDimension::getAllDimensions();
224
225 foreach ($dimensions as $dimension) {
226 if ($dimension->hasImplementedEvent('onExistingVisit') || $dimension->hasImplementedEvent('onAnyGoalConversion')) {
227 $fields[] = $dimension->getColumnName();
228 }
229
230 foreach ($dimension->getRequiredVisitFields() as $field) {
231 $fields[] = $field;
232 }
233 }
234
235 /**
236 * This event collects a list of [visit entity](/guides/persistence-and-the-mysql-backend#visits) properties that should be loaded when reading
237 * the existing visit. Properties that appear in this list will be available in other tracking
238 * events such as 'onExistingVisit'.
239 *
240 * Plugins can use this event to load additional visit entity properties for later use during tracking.
241 *
242 * This event is deprecated, use [Dimensions](http://developer.piwik.org/guides/dimensions) instead.
243 *
244 * @deprecated
245 */
246 $this->eventDispatcher->postEvent('Tracker.getVisitFieldsToPersist', array(&$fields));
247
248 array_unshift($fields, 'visit_first_action_time');
249 array_unshift($fields, 'visit_last_action_time');
250
251 for ($index = 1; $index <= CustomVariables::getNumUsableCustomVariables(); $index++) {
252 $fields[] = 'custom_var_k' . $index;
253 $fields[] = 'custom_var_v' . $index;
254 }
255
256 $this->visitFieldsToSelect = array_unique($fields);
257 }
258
259 return $this->visitFieldsToSelect;
260 }
261
262 public function getLastKnownVisit()
263 {
264 return $this->visitRow;
265 }
266 }
267