site-accueil-insa/matomo/core/CronArchive/SegmentArchiving.php

258 lines
8.8 KiB
PHP

<?php
/**
* Matomo - free/libre analytics platform
*
* @link https://matomo.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\CronArchive;
use Doctrine\Common\Cache\Cache;
use Matomo\Cache\Transient;
use Piwik\Access;
use Piwik\Archive\ArchiveInvalidator;
use Piwik\ArchiveProcessor\Rules;
use Piwik\Common;
use Piwik\Container\StaticContainer;
use Piwik\Date;
use Piwik\Db;
use Piwik\Period\Range;
use Piwik\Plugins\SegmentEditor\Model;
use Piwik\Segment;
use Piwik\Site;
use Psr\Log\LoggerInterface;
/**
* Provides URLs that initiate archiving during cron archiving for segments.
*
* Handles the `[General] process_new_segments_from` INI option.
*/
class SegmentArchiving
{
const BEGINNING_OF_TIME = 'beginning_of_time';
const CREATION_TIME = 'segment_creation_time';
const LAST_EDIT_TIME = 'segment_last_edit_time';
const DEFAULT_BEGINNING_OF_TIME_LAST_N_YEARS = 7;
/**
* @var Model
*/
private $segmentEditorModel;
/**
* @var Transient
*/
private $segmentListCache;
/**
* @var Date
*/
private $now;
private $processNewSegmentsFrom;
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var int
*/
private $beginningOfTimeLastNInYears;
/**
* @var bool
*/
private $forceArchiveAllSegments;
public function __construct($beginningOfTimeLastNInYears = self::DEFAULT_BEGINNING_OF_TIME_LAST_N_YEARS,
Model $segmentEditorModel = null, Cache $segmentListCache = null, Date $now = null,
LoggerInterface $logger = null)
{
$this->processNewSegmentsFrom = StaticContainer::get('ini.General.process_new_segments_from');
$this->beginningOfTimeLastNInYears = $beginningOfTimeLastNInYears;
$this->segmentEditorModel = $segmentEditorModel ?: new Model();
$this->segmentListCache = $segmentListCache ?: new Transient();
$this->now = $now ?: Date::factory('now');
$this->logger = $logger ?: StaticContainer::get('Psr\Log\LoggerInterface');
$this->forceArchiveAllSegments = self::getShouldForceArchiveAllSegments();
}
public function findSegmentForHash($hash, $idSite)
{
foreach ($this->getAllSegments() as $segment) {
if (!$this->isAutoArchivingEnabledFor($segment)
|| !self::isSegmentForSite($segment, $idSite)
) {
continue;
}
try {
$segmentObj = new Segment($segment['definition'], [$idSite]);
} catch (\Exception $ex) {
$this->logger->debug("Could not process segment {$segment['definition']} for site {$idSite}. Segment should not exist for the site, but does.");
continue;
}
if ($segmentObj->getHash() == $hash) {
return $segment;
}
}
return null;
}
public function getReArchiveSegmentStartDate($segmentInfo)
{
/**
* @var Date $segmentCreatedTime
* @var Date $segmentLastEditedTime
*/
list($segmentCreatedTime, $segmentLastEditedTime) = $this->getCreatedTimeOfSegment($segmentInfo);
if ($this->processNewSegmentsFrom == SegmentArchiving::CREATION_TIME) {
if (empty($segmentCreatedTime)) {
return null;
}
$this->logger->debug("process_new_segments_from set to segment_creation_time, oldest date to process is {time}", array('time' => $segmentCreatedTime));
return $segmentCreatedTime;
} else if ($this->processNewSegmentsFrom == SegmentArchiving::LAST_EDIT_TIME) {
if (empty($segmentLastEditedTime)) {
return null;
}
$this->logger->debug("process_new_segments_from set to segment_last_edit_time, segment last edit time is {time}",
array('time' => $segmentLastEditedTime));
return $segmentLastEditedTime;
} else if (preg_match("/^editLast([0-9]+)$/", $this->processNewSegmentsFrom, $matches)) {
if (empty($segmentLastEditedTime)) {
return null;
}
$lastN = $matches[1];
list($lastDate, $lastPeriod) = Range::getDateXPeriodsAgo($lastN, $segmentLastEditedTime, 'day');
$result = Date::factory($lastDate);
$this->logger->debug("process_new_segments_from set to editLast{N}, oldest date to process is {time}", array('N' => $lastN, 'time' => $result));
return $result;
} else if (preg_match("/^last([0-9]+)$/", $this->processNewSegmentsFrom, $matches)) {
if (empty($segmentCreatedTime)) {
return null;
}
$lastN = $matches[1];
list($lastDate, $lastPeriod) = Range::getDateXPeriodsAgo($lastN, $segmentCreatedTime, 'day');
$result = Date::factory($lastDate);
$this->logger->debug("process_new_segments_from set to last{N}, oldest date to process is {time}", array('N' => $lastN, 'time' => $result));
return $result;
} else {
$this->logger->debug("process_new_segments_from set to beginning_of_time or cannot recognize value");
$result = Date::factory('today')->subYear($this->beginningOfTimeLastNInYears);
$idSite = $segmentInfo['enable_only_idsite'] ?? null;
if (!empty($idSite)) {
$siteCreationDate = Date::factory(Site::getCreationDateFor($idSite));
if ($result->isEarlier($siteCreationDate)) {
$result = $siteCreationDate;
}
}
$earliestVisitTime = $this->getEarliestVisitTimeFor($idSite);
if (!empty($earliestVisitTime)
&& $result->isEarlier($earliestVisitTime)
) {
$result = $earliestVisitTime;
}
return $result;
}
}
private function getCreatedTimeOfSegment($storedSegment)
{
// check for an earlier ts_created timestamp
$createdTime = empty($storedSegment['ts_created']) ? null : Date::factory($storedSegment['ts_created']);
// if there is no ts_last_edit timestamp, initialize it to ts_created
if (empty($storedSegment['ts_last_edit'])) {
$storedSegment['ts_last_edit'] = empty($storedSegment['ts_created']) ? null : $storedSegment['ts_created'];
}
// check for a later ts_last_edit timestamp
$lastEditTime = empty($storedSegment['ts_last_edit']) ? null : Date::factory($storedSegment['ts_last_edit']);
return array($createdTime, $lastEditTime);
}
private function getEarliestVisitTimeFor($idSite)
{
$earliestIdVisit = Db::fetchOne('SELECT idvisit FROM ' . Common::prefixTable('log_visit')
. ' WHERE idsite = ? ORDER BY visit_last_action_time ASC LIMIT 1', [$idSite]);
$earliestStartTime = Db::fetchOne('SELECT visit_first_action_time FROM ' . Common::prefixTable('log_visit') . ' WHERE idvisit = ?', [
$earliestIdVisit,
]);
if (empty($earliestStartTime)) {
return null;
}
return Date::factory($earliestStartTime);
}
public function getAllSegments()
{
if (!$this->segmentListCache->contains('all')) {
$segments = $this->segmentEditorModel->getAllSegmentsAndIgnoreVisibility();
$this->segmentListCache->save('all', $segments);
}
return $this->segmentListCache->fetch('all');
}
public function getAllSegmentsToArchive($idSite)
{
return Rules::getSegmentsToProcess([$idSite]);
}
public static function isSegmentForSite($segment, $idSite)
{
return $segment['enable_only_idsite'] == 0
|| $segment['enable_only_idsite'] == $idSite;
}
public function isAutoArchivingEnabledFor($storedSegment)
{
return $this->forceArchiveAllSegments || !empty($storedSegment['auto_archive']);
}
public static function getShouldForceArchiveAllSegments()
{
return !Rules::isBrowserTriggerEnabled() && !Rules::isBrowserArchivingAvailableForSegments();
}
public function reArchiveSegment($segmentInfo)
{
if (empty($segmentInfo['definition'])) { // sanity check
return;
}
$definition = $segmentInfo['definition'];
$idSite = !empty($segmentInfo['enable_only_idsite']) ? $segmentInfo['enable_only_idsite'] : 'all';
$idSites = Access::doAsSuperUser(function () use ($idSite) {
return Site::getIdSitesFromIdSitesString($idSite);
});
$startDate = $this->getReArchiveSegmentStartDate($segmentInfo);
$invalidator = StaticContainer::get(ArchiveInvalidator::class);
$invalidator->scheduleReArchiving($idSites, null, null, $startDate, new Segment($definition, $idSites));
}
}