PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.4.2
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.4.2
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.php
matomo / app / core Last commit date
API 4 years ago Access 5 years ago Application 5 years ago Archive 4 years ago ArchiveProcessor 4 years ago Archiver 5 years ago AssetManager 4 years ago Auth 5 years ago Category 5 years ago CliMulti 4 years ago Columns 5 years ago Composer 5 years ago Concurrency 4 years ago Config 5 years ago Container 5 years ago CronArchive 5 years ago DataAccess 4 years ago DataFiles 5 years ago DataTable 4 years ago Db 4 years ago DeviceDetector 4 years ago Email 5 years ago Exception 4 years ago Http 5 years ago Intl 5 years ago Mail 5 years ago Measurable 5 years ago Menu 5 years ago Metrics 5 years ago Notification 5 years ago Period 5 years ago Plugin 4 years ago ProfessionalServices 5 years ago Report 5 years ago ReportRenderer 5 years ago Scheduler 5 years ago Segment 5 years ago Session 5 years ago Settings 5 years ago Tracker 4 years ago Translation 5 years ago UpdateCheck 5 years ago Updater 5 years ago Updates 4 years ago Validators 4 years ago View 5 years ago ViewDataTable 5 years ago Visualization 5 years ago Widget 5 years ago .htaccess 6 years ago Access.php 5 years ago Archive.php 5 years ago ArchiveProcessor.php 4 years ago AssetManager.php 5 years ago Auth.php 5 years ago AuthResult.php 5 years ago BaseFactory.php 5 years ago Cache.php 5 years ago CacheId.php 5 years ago CliMulti.php 5 years ago Common.php 4 years ago Config.php 4 years ago Console.php 5 years ago Context.php 5 years ago Cookie.php 5 years ago CronArchive.php 4 years ago DataArray.php 5 years ago DataTable.php 4 years ago Date.php 4 years ago Db.php 4 years ago DbHelper.php 5 years ago Development.php 5 years ago ErrorHandler.php 5 years ago EventDispatcher.php 4 years ago ExceptionHandler.php 5 years ago FileIntegrity.php 5 years ago Filechecks.php 5 years ago Filesystem.php 4 years ago FrontController.php 4 years ago Http.php 4 years ago IP.php 4 years ago Log.php 4 years ago LogDeleter.php 4 years ago Mail.php 4 years ago Metrics.php 5 years ago NoAccessException.php 5 years ago Nonce.php 5 years ago Notification.php 5 years ago NumberFormatter.php 5 years ago Option.php 4 years ago Period.php 5 years ago Piwik.php 4 years ago Plugin.php 5 years ago Profiler.php 4 years ago ProxyHeaders.php 5 years ago ProxyHttp.php 5 years ago QuickForm2.php 5 years ago RankingQuery.php 5 years ago ReportRenderer.php 5 years ago Segment.php 5 years ago Sequence.php 5 years ago Session.php 5 years ago SettingsPiwik.php 5 years ago SettingsServer.php 5 years ago Singleton.php 5 years ago Site.php 4 years ago SupportedBrowser.php 5 years ago TCPDF.php 5 years ago Theme.php 5 years ago Timer.php 5 years ago Tracker.php 4 years ago Twig.php 4 years ago Unzip.php 5 years ago UpdateCheck.php 5 years ago Updater.php 5 years ago UpdaterErrorException.php 5 years ago Updates.php 5 years ago Url.php 4 years ago UrlHelper.php 5 years ago Version.php 4 years ago View.php 5 years ago bootstrap.php 5 years ago dispatch.php 5 years ago testMinimumPhpVersion.php 5 years ago
Tracker.php
359 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;
10
11 use Exception;
12 use Piwik\Container\StaticContainer;
13 use Piwik\Plugins\BulkTracking\Tracker\Requests;
14 use Piwik\Plugins\PrivacyManager\Config as PrivacyManagerConfig;
15 use Piwik\Tracker\Db as TrackerDb;
16 use Piwik\Tracker\Db\DbException;
17 use Piwik\Tracker\Handler;
18 use Piwik\Tracker\Request;
19 use Piwik\Tracker\RequestSet;
20 use Piwik\Tracker\TrackerConfig;
21 use Piwik\Tracker\Visit;
22 use Piwik\Plugin\Manager as PluginManager;
23 use Psr\Log\LoggerInterface;
24
25 /**
26 * Class used by the logging script piwik.php called by the javascript tag.
27 * Handles the visitor and their actions on the website, saves the data in the DB,
28 * saves information in the cookie, etc.
29 *
30 * We try to include as little files as possible (no dependency on 3rd party modules).
31 */
32 class Tracker
33 {
34 /**
35 * @var Db
36 */
37 private static $db = null;
38
39 // We use hex ID that are 16 chars in length, ie. 64 bits IDs
40 const LENGTH_HEX_ID_STRING = 16;
41 const LENGTH_BINARY_ID = 8;
42
43 public static $initTrackerMode = false;
44
45 private $countOfLoggedRequests = 0;
46 protected $isInstalled = null;
47
48 /**
49 * @var LoggerInterface
50 */
51 private $logger;
52
53 public function __construct()
54 {
55 $this->logger = StaticContainer::get(LoggerInterface::class);
56 }
57
58 public function isDebugModeEnabled()
59 {
60 return array_key_exists('PIWIK_TRACKER_DEBUG', $GLOBALS) && $GLOBALS['PIWIK_TRACKER_DEBUG'] === true;
61 }
62
63 public function shouldRecordStatistics()
64 {
65 $record = TrackerConfig::getConfigValue('record_statistics') != 0;
66
67 if (!$record) {
68 $this->logger->debug('Tracking is disabled in the config.ini.php via record_statistics=0');
69 }
70
71 return $record && $this->isInstalled();
72 }
73
74 public static function loadTrackerEnvironment()
75 {
76 SettingsServer::setIsTrackerApiRequest();
77 if (empty($GLOBALS['PIWIK_TRACKER_DEBUG'])) {
78 $GLOBALS['PIWIK_TRACKER_DEBUG'] = self::isDebugEnabled();
79 }
80 if (!empty($GLOBALS['PIWIK_TRACKER_DEBUG']) && !Common::isPhpCliMode()) {
81 Common::sendHeader('Content-Type: text/plain');
82 }
83 PluginManager::getInstance()->loadTrackerPlugins();
84 }
85
86 private function init()
87 {
88 $this->handleFatalErrors();
89
90 if ($this->isDebugModeEnabled()) {
91 ErrorHandler::registerErrorHandler();
92 ExceptionHandler::setUp();
93
94 $this->logger->debug("Debug enabled - Input parameters: {params}", [
95 'params' => var_export($_GET + $_POST, true),
96 ]);
97 }
98 }
99
100 public function isInstalled()
101 {
102 if (is_null($this->isInstalled)) {
103 $this->isInstalled = SettingsPiwik::isMatomoInstalled();
104 }
105
106 return $this->isInstalled;
107 }
108
109 public function main(Handler $handler, RequestSet $requestSet)
110 {
111 try {
112 $this->init();
113 $handler->init($this, $requestSet);
114
115 $this->track($handler, $requestSet);
116 } catch (Exception $e) {
117 StaticContainer::get(LoggerInterface::class)->debug("Tracker encountered an exception: {ex}", [$e]);
118
119 $handler->onException($this, $requestSet, $e);
120 }
121
122 Piwik::postEvent('Tracker.end');
123 $response = $handler->finish($this, $requestSet);
124
125 $this->disconnectDatabase();
126
127 return $response;
128 }
129
130 public function track(Handler $handler, RequestSet $requestSet)
131 {
132 if (!$this->shouldRecordStatistics()) {
133 return;
134 }
135
136 $requestSet->initRequestsAndTokenAuth();
137
138 if ($requestSet->hasRequests()) {
139 $handler->onStartTrackRequests($this, $requestSet);
140 $handler->process($this, $requestSet);
141 $handler->onAllRequestsTracked($this, $requestSet);
142 }
143 }
144
145 /**
146 * @param Request $request
147 * @return array
148 */
149 public function trackRequest(Request $request)
150 {
151 if ($request->isEmptyRequest()) {
152 $this->logger->debug('The request is empty');
153 } else {
154 $this->logger->debug('Current datetime: {date}', [
155 'date' => date("Y-m-d H:i:s", $request->getCurrentTimestamp()),
156 ]);
157
158 $visit = Visit\Factory::make();
159 $visit->setRequest($request);
160 $visit->handle();
161 }
162
163 // increment successfully logged request count. make sure to do this after try-catch,
164 // since an excluded visit is considered 'successfully logged'
165 ++$this->countOfLoggedRequests;
166 }
167
168 /**
169 * Used to initialize core Piwik components on a piwik.php request
170 * Eg. when cache is missed and we will be calling some APIs to generate cache
171 */
172 public static function initCorePiwikInTrackerMode()
173 {
174 if (SettingsServer::isTrackerApiRequest()
175 && self::$initTrackerMode === false
176 ) {
177 self::$initTrackerMode = true;
178 require_once PIWIK_INCLUDE_PATH . '/core/Option.php';
179
180 Access::getInstance();
181 Config::getInstance();
182
183 try {
184 Db::get();
185 } catch (Exception $e) {
186 Db::createDatabaseObject();
187 }
188
189 PluginManager::getInstance()->loadCorePluginsDuringTracker();
190 }
191 }
192
193 public static function restoreTrackerPlugins()
194 {
195 if (SettingsServer::isTrackerApiRequest() && Tracker::$initTrackerMode) {
196 Plugin\Manager::getInstance()->loadTrackerPlugins();
197 }
198 }
199
200 public function getCountOfLoggedRequests()
201 {
202 return $this->countOfLoggedRequests;
203 }
204
205 public function setCountOfLoggedRequests($numLoggedRequests)
206 {
207 $this->countOfLoggedRequests = $numLoggedRequests;
208 }
209
210 public function hasLoggedRequests()
211 {
212 return 0 !== $this->countOfLoggedRequests;
213 }
214
215 public function isDatabaseConnected()
216 {
217 return !is_null(self::$db);
218 }
219
220 public static function getDatabase()
221 {
222 if (is_null(self::$db)) {
223 try {
224 self::$db = TrackerDb::connectPiwikTrackerDb();
225 } catch (Exception $e) {
226 $code = $e->getCode();
227 // Note: PDOException might return a string as code, but we can't use this for DbException
228 throw new DbException($e->getMessage(), is_int($code) ? $code : 0);
229 }
230 }
231
232 return self::$db;
233 }
234
235 protected function disconnectDatabase()
236 {
237 if ($this->isDatabaseConnected()) { // note: I think we do this only for the tests
238 self::$db->disconnect();
239 self::$db = null;
240 }
241 }
242
243 // for tests
244 public static function disconnectCachedDbConnection()
245 {
246 // code redundancy w/ above is on purpose; above disconnectDatabase depends on method that can potentially be overridden
247 if (!is_null(self::$db)) {
248 self::$db->disconnect();
249 self::$db = null;
250 }
251 }
252
253 public static function setTestEnvironment($args = null, $requestMethod = null)
254 {
255 if (is_null($args)) {
256 $requests = new Requests();
257 $args = $requests->getRequestsArrayFromBulkRequest($requests->getRawBulkRequest());
258 $args = $_GET + $args;
259 }
260
261 if (is_null($requestMethod) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
262 $requestMethod = $_SERVER['REQUEST_METHOD'];
263 } elseif (is_null($requestMethod)) {
264 $requestMethod = 'GET';
265 }
266
267 // Do not run scheduled tasks during tests
268 if (!defined('DEBUG_FORCE_SCHEDULED_TASKS')) {
269 TrackerConfig::setConfigValue('scheduled_tasks_min_interval', 0);
270 }
271
272 // if nothing found in _GET/_POST and we're doing a POST, assume bulk request. in which case,
273 // we have to bypass authentication
274 if (empty($args) && $requestMethod == 'POST') {
275 TrackerConfig::setConfigValue('tracking_requests_require_authentication', 0);
276 }
277
278 // Tests can force the use of 3rd party cookie for ID visitor
279 if (Common::getRequestVar('forceEnableFingerprintingAcrossWebsites', false, null, $args) == 1) {
280 TrackerConfig::setConfigValue('enable_fingerprinting_across_websites', 1);
281 }
282
283 // Tests can simulate the tracker API maintenance mode
284 if (Common::getRequestVar('forceEnableTrackerMaintenanceMode', false, null, $args) == 1) {
285 TrackerConfig::setConfigValue('record_statistics', 0);
286 }
287
288 // Tests can force the use of 3rd party cookie for ID visitor
289 if (Common::getRequestVar('forceUseThirdPartyCookie', false, null, $args) == 1) {
290 TrackerConfig::setConfigValue('use_third_party_id_cookie', 1);
291 }
292
293 // Tests using window_look_back_for_visitor
294 if (Common::getRequestVar('forceLargeWindowLookBackForVisitor', false, null, $args) == 1
295 // also look for this in bulk requests (see fake_logs_replay.log)
296 || strpos(json_encode($args, true), '"forceLargeWindowLookBackForVisitor":"1"') !== false
297 ) {
298 TrackerConfig::setConfigValue('window_look_back_for_visitor', 2678400);
299 }
300
301 // Tests can force the enabling of IP anonymization
302 if (Common::getRequestVar('forceIpAnonymization', false, null, $args) == 1) {
303 self::getDatabase(); // make sure db is initialized
304
305 $privacyConfig = new PrivacyManagerConfig();
306 $privacyConfig->ipAddressMaskLength = 2;
307
308 \Piwik\Plugins\PrivacyManager\IPAnonymizer::activate();
309
310 \Piwik\Tracker\Cache::deleteTrackerCache();
311 Filesystem::clearPhpCaches();
312 }
313 }
314
315 protected function loadTrackerPlugins()
316 {
317 try {
318 $pluginManager = PluginManager::getInstance();
319 $pluginsTracker = $pluginManager->loadTrackerPlugins();
320
321 $this->logger->debug("Loading plugins: { {plugins} }", [
322 'plugins' => implode(", ", $pluginsTracker),
323 ]);
324 } catch (Exception $e) {
325 $this->logger->error('Error loading tracker plugins: {exception}', [
326 'exception' => $e,
327 ]);
328 }
329 }
330
331 private function handleFatalErrors()
332 {
333 register_shutdown_function(function () { // TODO: add a log here
334 $lastError = error_get_last();
335 if (!empty($lastError) && $lastError['type'] == E_ERROR) {
336 Common::sendResponseCode(500);
337 }
338 });
339 }
340
341 private static function isDebugEnabled()
342 {
343 try {
344 $debug = (bool) TrackerConfig::getConfigValue('debug');
345 if ($debug) {
346 return true;
347 }
348
349 $debugOnDemand = (bool) TrackerConfig::getConfigValue('debug_on_demand');
350 if ($debugOnDemand) {
351 return (bool) Common::getRequestVar('debug', false);
352 }
353 } catch (Exception $e) {
354 }
355
356 return false;
357 }
358 }
359