PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / trunk
Matomo Analytics – Powerful, Privacy-First Insights for WordPress vtrunk
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
Config 4 months ago Db 3 months ago Handler 2 years ago Visit 1 month ago Action.php 3 months ago ActionPageview.php 2 years ago BotRequest.php 3 months ago BotRequestProcessor.php 1 month ago Cache.php 6 months ago Db.php 1 year ago Failures.php 6 months ago FingerprintSalt.php 1 year ago GoalManager.php 1 month ago Handler.php 2 years ago IgnoreCookie.php 1 year ago LogTable.php 1 year ago Model.php 6 months ago PageUrl.php 2 weeks ago Request.php 1 month ago RequestHandlerTrait.php 4 months ago RequestProcessor.php 1 month ago RequestSet.php 6 months ago Response.php 3 months ago ScheduledTasksRunner.php 1 year ago Settings.php 3 months ago TableLogAction.php 6 months ago TrackerCodeGenerator.php 1 year ago TrackerConfig.php 1 month ago Visit.php 3 months ago VisitExcluded.php 3 months ago VisitInterface.php 3 months ago Visitor.php 1 month ago VisitorNotFoundInDb.php 1 month ago VisitorRecognizer.php 1 year ago
VisitorRecognizer.php
220 lines
1 <?php
2
3 /**
4 * Matomo - free/libre analytics platform
5 *
6 * @link https://matomo.org
7 * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
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 * Tracker service that finds the last known visit for the visitor being tracked.
17 */
18 class VisitorRecognizer
19 {
20 /**
21 * Set when a visit was found. Stores the original values of the row that is currently stored in the DB when
22 * the visit was selected.
23 */
24 public const KEY_ORIGINAL_VISIT_ROW = 'originalVisit';
25 /**
26 * Local variable cache for the getVisitFieldsPersist() method.
27 *
28 * @var array
29 */
30 private $visitFieldsToSelect;
31 /**
32 * See https://piwik.org/faq/how-to/faq_175/.
33 *
34 * @var bool
35 */
36 private $trustCookiesOnly;
37 /**
38 * Length of a visit in seconds.
39 *
40 * @var int
41 */
42 private $visitStandardLength;
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 * @var Model
52 */
53 private $model;
54 /**
55 * @var EventDispatcher
56 */
57 private $eventDispatcher;
58 /**
59 * @var array
60 */
61 private $visitRow;
62 public function __construct($trustCookiesOnly, $visitStandardLength, $lookbackNSecondsCustom, \Piwik\Tracker\Model $model, EventDispatcher $eventDispatcher)
63 {
64 $this->trustCookiesOnly = $trustCookiesOnly;
65 $this->visitStandardLength = $visitStandardLength;
66 $this->lookBackNSecondsCustom = $lookbackNSecondsCustom;
67 $this->model = $model;
68 $this->eventDispatcher = $eventDispatcher;
69 }
70 public function setTrustCookiesOnly($trustCookiesOnly)
71 {
72 $this->trustCookiesOnly = $trustCookiesOnly;
73 }
74 public function findKnownVisitor($configId, VisitProperties $visitProperties, \Piwik\Tracker\Request $request)
75 {
76 $idSite = $request->getIdSite();
77 $idVisitor = $request->getVisitorId();
78 $userId = $request->getForcedUserId();
79 $isVisitorIdToLookup = !empty($idVisitor);
80 if ($isVisitorIdToLookup) {
81 $visitProperties->setProperty('idvisitor', $idVisitor);
82 Common::printDebug("Matching visitors with: visitorId=" . bin2hex($idVisitor) . " OR configId=" . bin2hex($configId));
83 } else {
84 Common::printDebug("Visitor doesn't have the piwik cookie...");
85 }
86 $persistedVisitAttributes = $this->getVisitorFieldsPersist();
87 $shouldMatchOneFieldOnly = $this->shouldLookupOneVisitorFieldOnly($isVisitorIdToLookup, $request);
88 list($timeLookBack, $timeLookAhead) = $this->getWindowLookupThisVisit($request);
89 $maxActions = \Piwik\Tracker\TrackerConfig::getConfigValue('create_new_visit_after_x_actions', $request->getIdSiteIfExists());
90 $visitRow = $this->model->findVisitor($idSite, $configId, $idVisitor, $userId, $persistedVisitAttributes, $shouldMatchOneFieldOnly, $isVisitorIdToLookup, $timeLookBack, $timeLookAhead);
91 if (!empty($maxActions) && $maxActions > 0 && !empty($visitRow['visit_total_actions']) && $maxActions <= $visitRow['visit_total_actions']) {
92 $this->visitRow = \false;
93 return \false;
94 }
95 $this->visitRow = $visitRow;
96 if ($visitRow && count($visitRow) > 0) {
97 $visitProperties->setProperty('idvisitor', $visitRow['idvisitor']);
98 $visitProperties->setProperty('user_id', $visitRow['user_id']);
99 Common::printDebug("The visitor is known (idvisitor = " . bin2hex($visitProperties->getProperty('idvisitor')) . ",\n config_id = " . bin2hex($configId) . ",\n last action = " . date("r", $visitProperties->getProperty('visit_last_action_time')) . ",\n first action = " . date("r", $visitProperties->getProperty('visit_first_action_time')) . ")");
100 return \true;
101 } else {
102 Common::printDebug("The visitor was not matched with an existing visitor...");
103 return \false;
104 }
105 }
106 public function removeUnchangedValues($visit, ?VisitProperties $originalVisit = null)
107 {
108 if (empty($originalVisit)) {
109 return $visit;
110 }
111 $originalRow = $originalVisit->getProperties();
112 if (!empty($originalRow['idvisitor']) && !empty($visit['idvisitor']) && bin2hex($originalRow['idvisitor']) === bin2hex($visit['idvisitor'])) {
113 unset($visit['idvisitor']);
114 }
115 $fieldsToCompareValue = array('user_id', 'visit_last_action_time', 'visit_total_time');
116 foreach ($fieldsToCompareValue as $field) {
117 if (!empty($originalRow[$field]) && !empty($visit[$field]) && $visit[$field] == $originalRow[$field]) {
118 // we can't use === eg for visit_total_time which may be partially an integer and sometimes a string
119 // because we check for !empty things should still work as expected though
120 // (eg we wouldn't compare false with 0)
121 unset($visit[$field]);
122 }
123 }
124 return $visit;
125 }
126 public function updateVisitPropertiesFromLastVisitRow(VisitProperties $visitProperties)
127 {
128 // These values will be used throughout the request
129 foreach ($this->getVisitorFieldsPersist() as $field) {
130 $value = $this->visitRow[$field];
131 if ($field == 'visit_last_action_time' || $field == 'visit_first_action_time') {
132 $value = strtotime($value);
133 }
134 // Set the immutable and mutable properties initial value
135 $visitProperties->initializeProperty($field, $value);
136 }
137 Common::printDebug("The visit is part of an existing visit (\n idvisit = {$visitProperties->getProperty('idvisit')},\n visit_goal_buyer' = " . $visitProperties->getProperty('visit_goal_buyer') . ")");
138 }
139 protected function shouldLookupOneVisitorFieldOnly($isVisitorIdToLookup, \Piwik\Tracker\Request $request)
140 {
141 $isForcedUserIdMustMatch = \false !== $request->getForcedUserId();
142 // This setting would be enabled for Intranet websites, to ensure that visitors using all the same computer config, same IP
143 // are not counted as 1 visitor. In this case, we want to enforce and trust the visitor ID from the cookie.
144 if ($isVisitorIdToLookup && $this->trustCookiesOnly) {
145 return \true;
146 }
147 if ($isForcedUserIdMustMatch) {
148 // if &iud was set, we must try and match both idvisitor and config_id
149 return \false;
150 }
151 // If a &cid= was set, we force to select this visitor (or create a new one)
152 $isForcedVisitorIdMustMatch = $request->getForcedVisitorId() != null;
153 if ($isForcedVisitorIdMustMatch) {
154 return \true;
155 }
156 if (!$isVisitorIdToLookup) {
157 return \true;
158 }
159 return \false;
160 }
161 /**
162 * By default, we look back 30 minutes to find a previous visitor (for performance reasons).
163 * In some cases, it is useful to look back and count unique visitors more accurately. You can set custom lookback window in
164 * [Tracker] window_look_back_for_visitor
165 *
166 * The returned value is the window range (Min, max) that the matched visitor should fall within
167 *
168 * @return array( datetimeMin, datetimeMax )
169 */
170 protected function getWindowLookupThisVisit(\Piwik\Tracker\Request $request)
171 {
172 $lookAheadNSeconds = $this->visitStandardLength;
173 $lookBackNSeconds = $this->visitStandardLength;
174 if ($this->lookBackNSecondsCustom > $lookBackNSeconds) {
175 $lookBackNSeconds = $this->lookBackNSecondsCustom;
176 }
177 $timeLookBack = date('Y-m-d H:i:s', $request->getCurrentTimestamp() - $lookBackNSeconds);
178 $timeLookAhead = date('Y-m-d H:i:s', $request->getCurrentTimestamp() + $lookAheadNSeconds);
179 return array($timeLookBack, $timeLookAhead);
180 }
181 /**
182 * @return array
183 */
184 private function getVisitorFieldsPersist()
185 {
186 if (is_null($this->visitFieldsToSelect)) {
187 $fields = array('idvisitor', 'idvisit', 'user_id', 'visit_exit_idaction_url', 'visit_exit_idaction_name', 'visitor_returning', 'visitor_seconds_since_first', 'visitor_seconds_since_order', 'visitor_count_visits', 'visit_goal_buyer', 'location_country', 'location_region', 'location_city', 'location_latitude', 'location_longitude', 'referer_name', 'referer_keyword', 'referer_type');
188 $dimensions = VisitDimension::getAllDimensions();
189 foreach ($dimensions as $dimension) {
190 if ($dimension->hasImplementedEvent('onExistingVisit') || $dimension->hasImplementedEvent('onAnyGoalConversion')) {
191 $fields[] = $dimension->getColumnName();
192 }
193 foreach ($dimension->getRequiredVisitFields() as $field) {
194 $fields[] = $field;
195 }
196 }
197 /**
198 * This event collects a list of [visit entity](/guides/persistence-and-the-mysql-backend#visits) properties that should be loaded when reading
199 * the existing visit. Properties that appear in this list will be available in other tracking
200 * events such as 'onExistingVisit'.
201 *
202 * Plugins can use this event to load additional visit entity properties for later use during tracking.
203 *
204 * This event is deprecated, use [Dimensions](https://developer.matomo.org/guides/dimensions) instead.
205 *
206 * @deprecated
207 */
208 $this->eventDispatcher->postEvent('Tracker.getVisitFieldsToPersist', array(&$fields));
209 array_unshift($fields, 'visit_first_action_time');
210 array_unshift($fields, 'visit_last_action_time');
211 $this->visitFieldsToSelect = array_unique($fields);
212 }
213 return $this->visitFieldsToSelect;
214 }
215 public function getLastKnownVisit()
216 {
217 return $this->visitRow;
218 }
219 }
220