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