Willkommen bei WordPress. Dies ist dein erster Beitrag. Bearbeite oder lösche ihn und beginne mit dem Schreiben!
Hallo Welt!
von raredesign | Dez 3, 2019 | Allgemein | 0 Kommentare
Cokiee Shell
Current Path : /var/www/web28/html/wp-content/plugins/matomo/app/core/Tracker/ |
Current File : //var/www/web28/html/wp-content/plugins/matomo/app/core/Tracker/Visit.php |
<?php /** * Matomo - free/libre analytics platform * * @link https://matomo.org * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later */ namespace Piwik\Tracker; use Piwik\Archive\ArchiveInvalidator; use Piwik\Common; use Piwik\Config; use Piwik\Container\StaticContainer; use Piwik\Date; use Piwik\Exception\UnexpectedWebsiteFoundException; use Matomo\Network\IPUtils; use Piwik\Plugin\Dimension\VisitDimension; use Piwik\Plugins\Actions\Tracker\ActionsRequestProcessor; use Piwik\Plugins\UserCountry\Columns\Base; use Piwik\Tracker; use Piwik\Tracker\Visit\VisitProperties; /** * Class used to handle a Visit. * A visit is either NEW or KNOWN. * - If a visit is NEW then we process the visitor information (settings, referrers, etc.) and save * a new line in the log_visit table. * - If a visit is KNOWN then we update the visit row in the log_visit table, updating the number of pages * views, time spent, etc. * * Whether a visit is NEW or KNOWN we also save the action in the DB. * One request to the matomo.php script is associated to one action. * */ class Visit implements \Piwik\Tracker\VisitInterface { public const UNKNOWN_CODE = 'xx'; /** * @var GoalManager */ protected $goalManager; /** * @var Request */ protected $request; /** * @var Settings */ protected $userSettings; public static $dimensions; /** * @var RequestProcessor[] */ protected $requestProcessors; /** * @var VisitProperties */ protected $visitProperties; /** * @var VisitProperties */ protected $previousVisitProperties; /** * @var ArchiveInvalidator */ private $invalidator; protected $fieldsThatRequireAuth = array('city', 'region', 'country', 'lat', 'long'); public function __construct() { $requestProcessors = StaticContainer::get('Piwik\\Plugin\\RequestProcessors'); $this->requestProcessors = $requestProcessors->getRequestProcessors(); $this->visitProperties = null; $this->userSettings = StaticContainer::get('Piwik\\Tracker\\Settings'); $this->invalidator = StaticContainer::get('Piwik\\Archive\\ArchiveInvalidator'); } /** * @param Request $request */ public function setRequest(\Piwik\Tracker\Request $request) { $this->request = $request; } private function checkSiteExists(\Piwik\Tracker\Request $request) { try { $request->getIdSite(); } catch (UnexpectedWebsiteFoundException $e) { // we allow 0... the request will fail anyway as the site won't exist... allowing 0 will help us // reporting this tracking problem as it is a common issue. Otherwise we would not be able to report // this problem in tracking failures StaticContainer::get(\Piwik\Tracker\Failures::class)->logFailure(\Piwik\Tracker\Failures::FAILURE_ID_INVALID_SITE, $request); throw $e; } } private function validateRequest(\Piwik\Tracker\Request $request) { // Check for params that aren't allowed to be included unless the request is authenticated foreach ($this->fieldsThatRequireAuth as $field) { Base::getValueFromUrlParamsIfAllowed($field, $request); } // Special logic for timestamp as some overrides are OK without auth and others aren't $request->getCurrentTimestamp(); } /** * Main algorithm to handle the visit. * * Once we have the visitor information, we have to determine if the visit is a new or a known visit. * * 1) When the last action was done more than 30min ago, * or if the visitor is new, then this is a new visit. * * 2) If the last action is less than 30min ago, then the same visit is going on. * Because the visit goes on, we can get the time spent during the last action. * * NB: * - In the case of a new visit, then the time spent * during the last action of the previous visit is unknown. * * - In the case of a new visit but with a known visitor, * we can set the 'returning visitor' flag. * * In all the cases we set a cookie to the visitor with the new information. */ public function handle() { $this->checkSiteExists($this->request); foreach ($this->requestProcessors as $processor) { Common::printDebug("Executing " . get_class($processor) . "::manipulateRequest()..."); $processor->manipulateRequest($this->request); } $this->validateRequest($this->request); $this->visitProperties = new VisitProperties(); foreach ($this->requestProcessors as $processor) { Common::printDebug("Executing " . get_class($processor) . "::processRequestParams()..."); $abort = $processor->processRequestParams($this->visitProperties, $this->request); if ($abort) { Common::printDebug("-> aborting due to processRequestParams method"); return; } } $isNewVisit = $this->request->getMetadata('CoreHome', 'isNewVisit'); if (!$isNewVisit) { $isNewVisit = $this->triggerPredicateHookOnDimensions($this->getAllVisitDimensions(), 'shouldForceNewVisit'); $this->request->setMetadata('CoreHome', 'isNewVisit', $isNewVisit); } foreach ($this->requestProcessors as $processor) { Common::printDebug("Executing " . get_class($processor) . "::afterRequestProcessed()..."); $abort = $processor->afterRequestProcessed($this->visitProperties, $this->request); if ($abort) { Common::printDebug("-> aborting due to afterRequestProcessed method"); return; } } $isNewVisit = $this->request->getMetadata('CoreHome', 'isNewVisit'); $this->previousVisitProperties = new VisitProperties($this->request->getMetadata('CoreHome', 'lastKnownVisit') ?: []); // Known visit when: // ( - the visitor has the Piwik cookie with the idcookie ID used by Piwik to match the visitor // OR // - the visitor doesn't have the Piwik cookie but could be match using heuristics @see recognizeTheVisitor() // ) // AND // - the last page view for this visitor was less than 30 minutes ago @see isLastActionInTheSameVisit() if (!$isNewVisit) { try { $this->handleExistingVisit($this->request->getMetadata('Goals', 'visitIsConverted')); } catch (\Piwik\Tracker\VisitorNotFoundInDb $e) { $this->request->setMetadata('CoreHome', 'visitorNotFoundInDb', true); // TODO: perhaps we should just abort here? } } // New visit when: // - the visitor has the Piwik cookie but the last action was performed more than 30 min ago @see isLastActionInTheSameVisit() // - the visitor doesn't have the Piwik cookie, and couldn't be matched in @see recognizeTheVisitor() // - the visitor does have the Piwik cookie but the idcookie and idvisit found in the cookie didn't match to any existing visit in the DB if ($isNewVisit) { $this->handleNewVisit($this->request->getMetadata('Goals', 'visitIsConverted')); } // update the cookie with the new visit information $this->request->setThirdPartyCookie($this->request->getVisitorIdForThirdPartyCookie()); foreach ($this->requestProcessors as $processor) { if (!$isNewVisit && $processor instanceof ActionsRequestProcessor) { // already processed earlier when handling exisitng visit see {@link self::handleExistingVisit()} continue; } Common::printDebug("Executing " . get_class($processor) . "::recordLogs()..."); $processor->recordLogs($this->visitProperties, $this->request); } $this->markArchivedReportsAsInvalidIfArchiveAlreadyFinished(); } /** * In the case of a known visit, we have to do the following actions: * * 1) Insert the new action * 2) Update the visit information * * @param Visitor $visitor * @param Action $action * @param $visitIsConverted * @throws VisitorNotFoundInDb */ protected function handleExistingVisit($visitIsConverted) { Common::printDebug("Visit is known (IP = " . IPUtils::binaryToStringIP($this->getVisitorIp()) . ")"); // TODO it should be its own dimension $this->visitProperties->setProperty('time_spent_ref_action', $this->getTimeSpentReferrerAction()); $valuesToUpdate = $this->getExistingVisitFieldsToUpdate($visitIsConverted); // update visitorInfo foreach ($valuesToUpdate as $name => $value) { $this->visitProperties->setProperty($name, $value); } foreach ($this->requestProcessors as $processor) { // for improving performance we create a log_link_visit_action entry before updating the visit. // this way we save one extra update on log_visit in custom dimensions. // Refs https://github.com/matomo-org/matomo/issues/17173 if ($processor instanceof ActionsRequestProcessor) { Common::printDebug("Executing " . get_class($processor) . "::recordLogs()..."); $processor->recordLogs($this->visitProperties, $this->request); } } foreach ($this->requestProcessors as $processor) { $processor->onExistingVisit($valuesToUpdate, $this->visitProperties, $this->request); } // we we remove values that haven't actually changed and are still the same when comparing to the initially // selected visit row. In best case this avoids the update completely. Eg when there is a bulk tracking request // of many content impressions. Then it will update the visit in the first request of the bulk request, and // all other visits that have same visit_last_action_time etc will be ignored and won't issue an update SQL // statement at all avoiding potential lock wait time when too many requests try to update the same visit at // same time $visitorRecognizer = StaticContainer::get(\Piwik\Tracker\VisitorRecognizer::class); $valuesToUpdate = $visitorRecognizer->removeUnchangedValues($valuesToUpdate, $this->previousVisitProperties); $this->updateExistingVisit($valuesToUpdate); $this->visitProperties->setProperty('visit_last_action_time', $this->request->getCurrentTimestamp()); } /** * @return int Time in seconds */ protected function getTimeSpentReferrerAction() { $timeSpent = $this->request->getCurrentTimestamp() - $this->visitProperties->getProperty('visit_last_action_time'); if ($timeSpent < 0) { $timeSpent = 0; } $visitStandardLength = $this->getVisitStandardLength(); if ($timeSpent > $visitStandardLength) { $timeSpent = $visitStandardLength; } return $timeSpent; } /** * In the case of a new visit, we have to do the following actions: * * 1) Insert the new action * * 2) Insert the visit information * * @param Visitor $visitor * @param Action $action * @param bool $visitIsConverted */ protected function handleNewVisit($visitIsConverted) { Common::printDebug("New Visit (IP = " . IPUtils::binaryToStringIP($this->getVisitorIp()) . ")"); $this->setNewVisitorInformation(); $dimensions = $this->getAllVisitDimensions(); $this->triggerHookOnDimensions($dimensions, 'onNewVisit'); if ($visitIsConverted) { $this->triggerHookOnDimensions($dimensions, 'onConvertedVisit'); } foreach ($this->requestProcessors as $processor) { $processor->onNewVisit($this->visitProperties, $this->request); } $this->printVisitorInformation(); $idVisit = $this->insertNewVisit($this->visitProperties->getProperties()); $this->visitProperties->setProperty('idvisit', $idVisit); $this->visitProperties->setProperty('visit_first_action_time', $this->request->getCurrentTimestamp()); $this->visitProperties->setProperty('visit_last_action_time', $this->request->getCurrentTimestamp()); } private function getModel() { return new \Piwik\Tracker\Model(); } /** * Returns visitor cookie * * @return string binary */ protected function getVisitorIdcookie() { $isKnown = $this->request->getMetadata('CoreHome', 'isVisitorKnown'); if ($isKnown) { return $this->visitProperties->getProperty('idvisitor'); } // If the visitor had a first party ID cookie, then we use this value $idVisitor = $this->visitProperties->getProperty('idvisitor'); if (!empty($idVisitor) && Tracker::LENGTH_BINARY_ID == strlen($this->visitProperties->getProperty('idvisitor'))) { return $this->visitProperties->getProperty('idvisitor'); } return Common::hex2bin($this->generateUniqueVisitorId()); } /** * @return string returns random 16 chars hex string */ public static function generateUniqueVisitorId() { return substr(Common::generateUniqId(), 0, Tracker::LENGTH_HEX_ID_STRING); } /** * Returns the visitor's IP address * * @return string */ protected function getVisitorIp() { return $this->visitProperties->getProperty('location_ip'); } /** * Gets the UserSettings object * * @return Settings */ protected function getSettingsObject() { return $this->userSettings; } // is the host any of the registered URLs for this website? public static function isHostKnownAliasHost($urlHost, $idSite) { $websiteData = \Piwik\Tracker\Cache::getCacheWebsiteAttributes($idSite); if (isset($websiteData['hosts'])) { $canonicalHosts = array(); foreach ($websiteData['hosts'] as $host) { $canonicalHosts[] = self::toCanonicalHost($host); } $canonicalHost = self::toCanonicalHost($urlHost); if (in_array($canonicalHost, $canonicalHosts)) { return true; } } return false; } private static function toCanonicalHost($host) { $hostLower = mb_strtolower($host); return str_replace('www.', '', $hostLower); } /** * @param $valuesToUpdate * @throws VisitorNotFoundInDb */ protected function updateExistingVisit($valuesToUpdate) { if (empty($valuesToUpdate)) { Common::printDebug('There are no values to be updated for this visit'); return; } $idSite = $this->request->getIdSite(); $idVisit = $this->visitProperties->getProperty('idvisit'); $wasInserted = $this->getModel()->updateVisit($idSite, $idVisit, $valuesToUpdate); // Debug output if (isset($valuesToUpdate['idvisitor'])) { $valuesToUpdate['idvisitor'] = bin2hex($valuesToUpdate['idvisitor']); } if ($wasInserted) { Common::printDebug('Updated existing visit: ' . var_export($valuesToUpdate, true)); } elseif (!$this->getModel()->hasVisit($idSite, $idVisit)) { // mostly for WordPress. see https://github.com/matomo-org/matomo/pull/15587 // as WP doesn't set `MYSQLI_CLIENT_FOUND_ROWS` and therefore when the update succeeded but no value changed // it would still return 0 vs OnPremise would return 1 or 2. throw new \Piwik\Tracker\VisitorNotFoundInDb("The visitor with idvisitor=" . bin2hex($this->visitProperties->getProperty('idvisitor')) . " and idvisit=" . @$this->visitProperties->getProperty('idvisit') . " wasn't found in the DB, we fallback to a new visitor"); } } private function printVisitorInformation() { $debugVisitInfo = $this->visitProperties->getProperties(); $debugVisitInfo['idvisitor'] = isset($debugVisitInfo['idvisitor']) ? bin2hex($debugVisitInfo['idvisitor']) : ''; $debugVisitInfo['config_id'] = isset($debugVisitInfo['config_id']) ? bin2hex($debugVisitInfo['config_id']) : ''; $debugVisitInfo['location_ip'] = IPUtils::binaryToStringIP($debugVisitInfo['location_ip']); Common::printDebug($debugVisitInfo); } private function setNewVisitorInformation() { $idVisitor = $this->getVisitorIdcookie(); $visitorIp = $this->getVisitorIp(); $configId = $this->request->getMetadata('CoreHome', 'visitorId'); $this->visitProperties->clearProperties(); $this->visitProperties->setProperty('idvisitor', $idVisitor); $this->visitProperties->setProperty('config_id', $configId); $this->visitProperties->setProperty('location_ip', $visitorIp); } /** * Gather fields=>values that needs to be updated for the existing visit in log_visit * * @param $visitIsConverted * @return array */ private function getExistingVisitFieldsToUpdate($visitIsConverted) { $valuesToUpdate = array(); $valuesToUpdate = $this->setIdVisitorForExistingVisit($valuesToUpdate); $dimensions = $this->getAllVisitDimensions(); $valuesToUpdate = $this->triggerHookOnDimensions($dimensions, 'onExistingVisit', $valuesToUpdate); if ($visitIsConverted) { $valuesToUpdate = $this->triggerHookOnDimensions($dimensions, 'onConvertedVisit', $valuesToUpdate); } // Custom Variables overwrite previous values on each page view return $valuesToUpdate; } /** * @param VisitDimension[] $dimensions * @param string $hook * @param Visitor $visitor * @param Action|null $action * @param array|null $valuesToUpdate If null, $this->visitorInfo will be updated * * @return array|null The updated $valuesToUpdate or null if no $valuesToUpdate given */ private function triggerHookOnDimensions($dimensions, $hook, $valuesToUpdate = null) { $visitor = $this->makeVisitorFacade(); /** @var Action $action */ $action = $this->request->getMetadata('Actions', 'action'); foreach ($dimensions as $dimension) { $value = $dimension->{$hook}($this->request, $visitor, $action); if ($value !== false) { $fieldName = $dimension->getColumnName(); $visitor->setVisitorColumn($fieldName, $value); if (is_float($value)) { $value = Common::forceDotAsSeparatorForDecimalPoint($value); } if ($valuesToUpdate !== null) { $valuesToUpdate[$fieldName] = $value; } else { $this->visitProperties->setProperty($fieldName, $value); } } } return $valuesToUpdate; } private function triggerPredicateHookOnDimensions($dimensions, $hook) { $visitor = $this->makeVisitorFacade(); /** @var Action $action */ $action = $this->request->getMetadata('Actions', 'action'); foreach ($dimensions as $dimension) { if ($dimension->{$hook}($this->request, $visitor, $action)) { return true; } } return false; } protected function getAllVisitDimensions() { if (is_null(self::$dimensions)) { self::$dimensions = VisitDimension::getAllDimensions(); $dimensionNames = array(); foreach (self::$dimensions as $dimension) { $dimensionNames[] = $dimension->getColumnName(); } Common::printDebug("Following dimensions have been collected from plugins: " . implode(", ", $dimensionNames)); } return self::$dimensions; } private function getVisitStandardLength() { return Config::getInstance()->Tracker['visit_standard_length']; } /** * @param $visitor * @param $valuesToUpdate * @return mixed */ private function setIdVisitorForExistingVisit($valuesToUpdate) { if (strlen($this->visitProperties->getProperty('idvisitor')) == Tracker::LENGTH_BINARY_ID) { $valuesToUpdate['idvisitor'] = $this->visitProperties->getProperty('idvisitor'); } $visitorId = $this->request->getVisitorId(); if ($visitorId && strlen($visitorId) === Tracker::LENGTH_BINARY_ID) { // Might update the idvisitor when it was forced or overwritten for this visit $valuesToUpdate['idvisitor'] = $this->request->getVisitorId(); } if (\Piwik\Tracker\TrackerConfig::getConfigValue('enable_userid_overwrites_visitorid', $this->request->getIdSiteIfExists())) { // User ID takes precedence and overwrites idvisitor value $userId = $this->request->getForcedUserId(); if ($userId) { $userIdHash = $this->request->getUserIdHashed($userId); $binIdVisitor = Common::hex2bin($userIdHash); $this->visitProperties->setProperty('idvisitor', $binIdVisitor); $valuesToUpdate['idvisitor'] = $binIdVisitor; } } return $valuesToUpdate; } protected function insertNewVisit($visit) { return $this->getModel()->createVisit($visit); } private function markArchivedReportsAsInvalidIfArchiveAlreadyFinished() { $idSite = (int) $this->request->getIdSite(); $time = $this->request->getCurrentTimestamp(); $timezone = $this->getTimezoneForSite($idSite); if (!isset($timezone)) { return; } $date = Date::factory((int) $time, $timezone); // $date->isToday() is buggy when server and website timezones don't match - so we'll do our own checking $startOfToday = Date::factoryInTimezone('yesterday', $timezone)->addDay(1); $isLaterThanYesterday = $date->getTimestamp() >= $startOfToday->getTimestamp(); if ($isLaterThanYesterday) { return; // don't try to invalidate archives for today or later } $this->invalidator->rememberToInvalidateArchivedReportsLater($idSite, $date); } private function getTimezoneForSite($idSite) { try { $site = \Piwik\Tracker\Cache::getCacheWebsiteAttributes($idSite); } catch (UnexpectedWebsiteFoundException $e) { return null; } if (!empty($site['timezone'])) { return $site['timezone']; } } private function makeVisitorFacade() { return \Piwik\Tracker\Visitor::makeFromVisitProperties($this->visitProperties, $this->request, $this->previousVisitProperties); } }
Cokiee Shell Web 1.0, Coded By Razor
Neueste Kommentare