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 / ArchiveProcessor / Loader.php
matomo / app / core / ArchiveProcessor Last commit date
ArchivingStatus.php 5 months ago Loader.php 4 months ago LoaderLock.php 1 year ago Parameters.php 1 month ago PluginsArchiver.php 1 year ago PluginsArchiverException.php 2 years ago Record.php 3 months ago RecordBuilder.php 1 month ago Rules.php 3 months ago
Loader.php
572 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\ArchiveProcessor;
10
11 use Piwik\Archive\ArchiveInvalidator;
12 use Piwik\ArchiveProcessor;
13 use Piwik\Cache;
14 use Piwik\CacheId;
15 use Piwik\Common;
16 use Piwik\Config;
17 use Piwik\Container\StaticContainer;
18 use Piwik\Context;
19 use Piwik\DataAccess\ArchiveSelector;
20 use Piwik\DataAccess\ArchiveWriter;
21 use Piwik\DataAccess\Model;
22 use Piwik\DataAccess\RawLogDao;
23 use Piwik\Date;
24 use Piwik\Period;
25 use Piwik\Piwik;
26 use Piwik\SettingsServer;
27 use Piwik\Site;
28 use Piwik\Log\LoggerInterface;
29 use Piwik\CronArchive\SegmentArchiving;
30 /**
31 * This class uses PluginsArchiver class to trigger data aggregation and create archives.
32 */
33 class Loader
34 {
35 private static $archivingDepth = 0;
36 /**
37 * Tracks whether the current prepareArchive run reused an existing archive instead of processing.
38 *
39 * @var boolean
40 */
41 private $didReuseArchive = \false;
42 /**
43 * @var Parameters
44 */
45 protected $params;
46 /**
47 * @var ArchiveInvalidator
48 */
49 private $invalidator;
50 /**
51 * @var \Matomo\Cache\Cache
52 */
53 private $cache;
54 /**
55 * @var LoggerInterface
56 */
57 private $logger;
58 /**
59 * @var RawLogDao
60 */
61 private $rawLogDao;
62 /**
63 * @var Model
64 */
65 private $dataAccessModel;
66 /**
67 * @var bool
68 */
69 private $invalidateBeforeArchiving;
70 public function __construct(\Piwik\ArchiveProcessor\Parameters $params, $invalidateBeforeArchiving = \false)
71 {
72 $this->params = $params;
73 $this->invalidateBeforeArchiving = $invalidateBeforeArchiving;
74 $this->invalidator = StaticContainer::get(ArchiveInvalidator::class);
75 $this->cache = Cache::getTransientCache();
76 $this->logger = StaticContainer::get(LoggerInterface::class);
77 $this->rawLogDao = new RawLogDao();
78 $this->dataAccessModel = new Model();
79 }
80 /**
81 * @return bool
82 */
83 protected function isThereSomeVisits($visits)
84 {
85 return $visits > 0;
86 }
87 /**
88 * @return bool
89 */
90 protected function mustProcessVisitCount($visits)
91 {
92 return $visits === \false;
93 }
94 public function prepareArchive($pluginName)
95 {
96 return Context::changeIdSite($this->params->getSite()->getId(), function () use($pluginName) {
97 try {
98 ++self::$archivingDepth;
99 if (self::$archivingDepth === 1) {
100 $this->didReuseArchive = \false;
101 }
102 return $this->prepareArchiveImpl($pluginName);
103 } finally {
104 --self::$archivingDepth;
105 }
106 });
107 }
108 /**
109 * @throws \Exception
110 */
111 private function prepareArchiveImpl($pluginName)
112 {
113 $this->params->setRequestedPlugin($pluginName);
114 if (SettingsServer::isArchivePhpTriggered()) {
115 $requestedReport = Common::getRequestVar('requestedReport', '', 'string');
116 if (!empty($requestedReport)) {
117 $this->params->setArchiveOnlyReport($requestedReport);
118 }
119 }
120 // invalidate existing archives before we start archiving in case data was tracked in the past. if the archive is
121 // made invalid, we will correctly re-archive below.
122 if ($this->invalidateBeforeArchiving && \Piwik\ArchiveProcessor\Rules::isBrowserTriggerEnabled()) {
123 $this->invalidatedReportsIfNeeded();
124 }
125 // load existing data from archive
126 $data = $this->loadArchiveData();
127 if (sizeof($data) == 2) {
128 $this->didReuseArchive = \true;
129 return $data;
130 }
131 [$idArchives, $visits, $visitsConverted, $foundRecords] = $data;
132 // only lock meet those conditions
133 if (ArchiveProcessor::$isRootArchivingRequest && !SettingsServer::isArchivePhpTriggered()) {
134 $lockId = $this->makeArchivingLockId();
135 //ini lock
136 $lock = new \Piwik\ArchiveProcessor\LoaderLock($lockId);
137 //set mysql lock the entire process if another process is running
138 $lock->setLock();
139 try {
140 $data = $this->loadArchiveData();
141 if (sizeof($data) == 2) {
142 $this->didReuseArchive = \true;
143 return $data;
144 }
145 [$idArchives, $visits, $visitsConverted, $foundRecords] = $data;
146 return $this->insertArchiveData($visits, $visitsConverted, $idArchives, $foundRecords);
147 } finally {
148 $lock->unlock();
149 }
150 } else {
151 return $this->insertArchiveData($visits, $visitsConverted, $idArchives, $foundRecords);
152 }
153 }
154 /**
155 * @param $visits
156 * @param $visitsConverted
157 * @return int[]
158 */
159 protected function insertArchiveData($visits, $visitsConverted, $existingArchives, $foundRecords)
160 {
161 if (SettingsServer::isArchivePhpTriggered()) {
162 $this->logger->info("initiating archiving via core:archive for " . $this->params);
163 }
164 if (!empty($foundRecords)) {
165 $this->params->setFoundRequestedReports($foundRecords);
166 }
167 [$visits, $visitsConverted] = $this->prepareCoreMetricsArchive($visits, $visitsConverted);
168 [$idArchive, $visits] = $this->prepareAllPluginsArchive($visits, $visitsConverted);
169 $idArchivesToQuery = [$idArchive];
170 if (!empty($foundRecords)) {
171 $idArchivesToQuery = array_merge($idArchivesToQuery, $existingArchives ?: []);
172 }
173 return [$idArchivesToQuery, $visits];
174 }
175 /**
176 * @return string
177 * @throws \Exception
178 */
179 private function makeArchivingLockId()
180 {
181 $doneFlag = \Piwik\ArchiveProcessor\Rules::getDoneStringFlagFor([$this->params->getSite()->getId()], $this->params->getSegment(), $this->params->getPeriod()->getLabel(), $this->params->getRequestedPlugin());
182 return $this->params->getPeriod()->getDateStart()->toString() . $this->params->getPeriod()->getDateEnd()->toString() . '.' . $doneFlag;
183 }
184 /**
185 * @return array|false[]
186 */
187 protected function loadArchiveData()
188 {
189 // this hack was used to check the main function goes to return or continue
190 // NOTE: $idArchives will contain the latest DONE_OK/DONE_INVALIDATED archive as well as any partial archives
191 // with a ts_archived >= the DONE_OK/DONE_INVALIDATED date.
192 $archiveInfo = $this->loadExistingArchiveIdFromDb();
193 $idArchives = $archiveInfo['idArchives'];
194 $visits = $archiveInfo['visits'];
195 $visitsConverted = $archiveInfo['visitsConverted'];
196 $tsArchived = $archiveInfo['tsArchived'];
197 $doneFlagValue = $archiveInfo['doneFlagValue'];
198 $existingArchives = $archiveInfo['existingRecords'];
199 $requestedRecords = $this->params->getArchiveOnlyReportAsArray();
200 $isMissingRequestedRecords = !empty($requestedRecords) && is_array($existingArchives) && count($requestedRecords) != count($existingArchives);
201 if (!empty($idArchives) && !\Piwik\ArchiveProcessor\Rules::isActuallyForceArchivingSinglePlugin() && !$this->shouldForceInvalidatedArchive($doneFlagValue, $tsArchived) && !$isMissingRequestedRecords) {
202 // we have a usable idarchive (it's not invalidated and it's new enough), and we are not archiving
203 // a single report
204 return [$idArchives, $visits];
205 }
206 // NOTE: this optimization helps when archiving large periods. eg, if archiving a year w/ a segment where
207 // there are not visits in the entire year, we don't have to go through and do anything. but, w/o this
208 // code, we will end up launching archiving for each month, week and day, even though we don't have to.
209 //
210 // we don't create an archive in this case, because the archive may be in progress in some way, so a 0
211 // visits archive can be inaccurate in the long run.
212 if ($this->canSkipThisArchive()) {
213 if (!empty($idArchives)) {
214 return [$idArchives, $visits];
215 } else {
216 return [\false, 0];
217 }
218 }
219 if (self::$archivingDepth > 1) {
220 $this->logger->debug(sprintf("Sub-period archive requires processing. Archiving depth: %d", self::$archivingDepth));
221 $this->params->logStatusDebug();
222 }
223 return [$idArchives, $visits, $visitsConverted, $existingArchives];
224 }
225 /**
226 * Prepares the core metrics if needed.
227 *
228 * @param $visits
229 * @return array
230 */
231 protected function prepareCoreMetricsArchive($visits, $visitsConverted)
232 {
233 $createSeparateArchiveForCoreMetrics = $this->mustProcessVisitCount($visits) && !$this->doesRequestedPluginIncludeVisitsSummary();
234 if ($createSeparateArchiveForCoreMetrics) {
235 $requestedPlugin = $this->params->getRequestedPlugin();
236 $requestedReport = $this->params->getArchiveOnlyReport();
237 $this->params->setRequestedPlugin('VisitsSummary');
238 $this->params->setArchiveOnlyReport(null);
239 $metrics = Context::executeWithQueryParameters(['requestedReport' => ''], function () {
240 $pluginsArchiver = new \Piwik\ArchiveProcessor\PluginsArchiver($this->params);
241 $metrics = $pluginsArchiver->callAggregateCoreMetrics();
242 $pluginsArchiver->finalizeArchive();
243 return $metrics;
244 });
245 $this->params->setRequestedPlugin($requestedPlugin);
246 $this->params->setArchiveOnlyReport($requestedReport);
247 $visits = $metrics['nb_visits'];
248 $visitsConverted = $metrics['nb_visits_converted'];
249 }
250 return array($visits, $visitsConverted);
251 }
252 protected function prepareAllPluginsArchive($visits, $visitsConverted)
253 {
254 $pluginsArchiver = new \Piwik\ArchiveProcessor\PluginsArchiver($this->params);
255 if ($this->mustProcessVisitCount($visits) || $this->doesRequestedPluginIncludeVisitsSummary()) {
256 $metrics = $pluginsArchiver->callAggregateCoreMetrics();
257 $visits = $metrics['nb_visits'];
258 $visitsConverted = $metrics['nb_visits_converted'];
259 }
260 $forceArchivingWithoutVisits = !$this->isThereSomeVisits($visits) && $this->shouldArchiveForSiteEvenWhenNoVisits();
261 $pluginsArchiver->callAggregateAllPlugins($visits, $visitsConverted, $forceArchivingWithoutVisits);
262 $idArchive = $pluginsArchiver->finalizeArchive();
263 return array($idArchive, $visits);
264 }
265 protected function doesRequestedPluginIncludeVisitsSummary()
266 {
267 $processAllReportsIncludingVisitsSummary = \Piwik\ArchiveProcessor\Rules::shouldProcessReportsAllPlugins(array($this->params->getSite()->getId()), $this->params->getSegment(), $this->params->getPeriod()->getLabel());
268 $doesRequestedPluginIncludeVisitsSummary = $processAllReportsIncludingVisitsSummary || $this->params->getRequestedPlugin() == 'VisitsSummary';
269 return $doesRequestedPluginIncludeVisitsSummary;
270 }
271 protected function isArchivingForcedToTrigger()
272 {
273 $period = $this->params->getPeriod()->getLabel();
274 $debugSetting = 'always_archive_data_period';
275 // default
276 if ($period == 'day') {
277 $debugSetting = 'always_archive_data_day';
278 } elseif ($period == 'range') {
279 $debugSetting = 'always_archive_data_range';
280 }
281 return (bool) Config::getInstance()->Debug[$debugSetting];
282 }
283 /**
284 * Returns the idArchive if the archive is available in the database for the requested plugin.
285 * Returns false if the archive needs to be processed.
286 *
287 * (public for tests)
288 *
289 * @return array
290 */
291 public function loadExistingArchiveIdFromDb()
292 {
293 if ($this->isArchivingForcedToTrigger()) {
294 $this->logger->debug("Archiving forced to trigger for {$this->params}.");
295 // return no usable archive found, and no existing archive. this will skip invalidation, which should
296 // be fine since we just force archiving.
297 return ['idArchives' => \false, 'visits' => \false, 'visitsConverted' => \false, 'archiveExists' => \false, 'tsArchived' => \false, 'doneFlagValue' => \false, 'existingRecords' => null];
298 }
299 $minDatetimeArchiveProcessedUTC = $this->getMinTimeArchiveProcessed();
300 $result = ArchiveSelector::getArchiveIdAndVisits($this->params, $minDatetimeArchiveProcessedUTC);
301 return $result;
302 }
303 /**
304 * Returns the minimum archive processed datetime to look at. Only public for tests.
305 *
306 * @return int|bool Datetime timestamp, or false if must look at any archive available
307 */
308 protected function getMinTimeArchiveProcessed()
309 {
310 // for range periods we can archive in a browser request request, make sure to check for the ttl no matter what
311 $isRangeArchiveAndArchivingEnabled = $this->params->getPeriod()->getLabel() == 'range' && \Piwik\ArchiveProcessor\Rules::isArchivingEnabledFor([$this->params->getSite()->getId()], $this->params->getSegment(), $this->params->getPeriod()->getLabel());
312 if (!$isRangeArchiveAndArchivingEnabled) {
313 $endDateTimestamp = self::determineIfArchivePermanent($this->params->getDateEnd());
314 if ($endDateTimestamp) {
315 // past archive
316 return $endDateTimestamp;
317 }
318 }
319 $dateStart = $this->params->getDateStart();
320 $period = $this->params->getPeriod();
321 $segment = $this->params->getSegment();
322 $site = $this->params->getSite();
323 // in-progress archive
324 return \Piwik\ArchiveProcessor\Rules::getMinTimeProcessedForInProgressArchive($dateStart, $period, $segment, $site);
325 }
326 protected static function determineIfArchivePermanent(Date $dateEnd)
327 {
328 $now = time();
329 $endTimestampUTC = strtotime($dateEnd->getDateEndUTC());
330 if ($endTimestampUTC <= $now) {
331 // - if the period we are looking for is finished, we look for a ts_archived that
332 // is greater than the last day of the archive
333 return $endTimestampUTC;
334 }
335 return \false;
336 }
337 private function shouldArchiveForSiteEvenWhenNoVisits()
338 {
339 $idSitesToArchive = $this->getIdSitesToArchiveWhenNoVisits();
340 return in_array($this->params->getSite()->getId(), $idSitesToArchive);
341 }
342 private function getIdSitesToArchiveWhenNoVisits()
343 {
344 $cacheKey = 'Archiving.getIdSitesToArchiveWhenNoVisits';
345 if (!$this->cache->contains($cacheKey)) {
346 $idSites = array();
347 // leaving undocumented unless decided otherwise
348 Piwik::postEvent('Archiving.getIdSitesToArchiveWhenNoVisits', array(&$idSites));
349 $this->cache->save($cacheKey, $idSites);
350 }
351 return $this->cache->fetch($cacheKey);
352 }
353 // public for tests
354 public function getReportsToInvalidate()
355 {
356 $sitesPerDays = $this->invalidator->getRememberedArchivedReportsThatShouldBeInvalidated();
357 foreach ($sitesPerDays as $dateStr => $siteIds) {
358 if (empty($siteIds) || !in_array($this->params->getSite()->getId(), $siteIds)) {
359 unset($sitesPerDays[$dateStr]);
360 }
361 $date = Date::factory($dateStr);
362 if ($date->isEarlier($this->params->getPeriod()->getDateStart()) || $date->isLater($this->params->getPeriod()->getDateEnd())) {
363 // date in list is not the current date, so ignore it
364 unset($sitesPerDays[$dateStr]);
365 }
366 }
367 return $sitesPerDays;
368 }
369 private function invalidatedReportsIfNeeded()
370 {
371 $sitesPerDays = $this->getReportsToInvalidate();
372 if (empty($sitesPerDays)) {
373 return;
374 }
375 foreach ($sitesPerDays as $date => $siteIds) {
376 try {
377 $this->invalidator->markArchivesAsInvalidated([$this->params->getSite()->getId()], array(Date::factory($date)), \false, $this->params->getSegment());
378 } catch (\Exception $e) {
379 Site::clearCache();
380 throw $e;
381 }
382 }
383 Site::clearCache();
384 }
385 public function canSkipThisArchive()
386 {
387 return $this->canSkipThisArchiveWithReason()[0];
388 }
389 /**
390 * @internal
391 *
392 * @return array{0: bool, 1: string}
393 */
394 public function canSkipThisArchiveWithReason() : array
395 {
396 $params = $this->params;
397 $idSite = $params->getSite()->getId();
398 $isWebsiteUsingTracker = $this->isWebsiteUsingTheTracker($idSite);
399 $isArchivingForcedWhenNoVisits = $this->shouldArchiveForSiteEvenWhenNoVisits();
400 $hasSiteVisitsBetweenTimeframe = $this->hasSiteVisitsBetweenTimeframe($idSite, $params->getPeriod());
401 $hasChildArchivesInPeriod = $this->hasChildArchivesInPeriod($idSite, $params->getPeriod());
402 $canSkipArchiveForSegment = $this->canSkipArchiveForSegmentWithReason();
403 if ($canSkipArchiveForSegment[0]) {
404 return [\true, 'Skip archive for segment: ' . $canSkipArchiveForSegment[1]];
405 }
406 if (!$isWebsiteUsingTracker) {
407 return [\false, 'Site is not using the JavaScript tracker'];
408 }
409 if ($isArchivingForcedWhenNoVisits) {
410 return [\false, 'Archiving is forced when no visits'];
411 }
412 if ($hasSiteVisitsBetweenTimeframe) {
413 return [\false, 'Site has visits between start and end date'];
414 }
415 if ($hasChildArchivesInPeriod) {
416 return [\false, 'There are child archives in the period'];
417 }
418 return [\true, 'Site is using tracker & archiving is not forced when no visits & site has has no visits between start and end date & there are no child archives in the period'];
419 }
420 public function didReuseArchive() : bool
421 {
422 return $this->didReuseArchive;
423 }
424 private function hasChildArchivesInPeriod($idSite, Period $period) : bool
425 {
426 $cacheKey = CacheId::siteAware('Archiving.hasChildArchivesInPeriod.' . $period->getRangeString(), [$idSite]);
427 if ($this->cache->contains($cacheKey)) {
428 $hasChildArchivesInPeriod = $this->cache->fetch($cacheKey);
429 } else {
430 $hasChildArchivesInPeriod = $this->dataAccessModel->hasChildArchivesInPeriod($idSite, $period);
431 $this->cache->save($cacheKey, $hasChildArchivesInPeriod);
432 }
433 return $hasChildArchivesInPeriod;
434 }
435 /**
436 * @return array{0: bool, 1: string}
437 */
438 private function canSkipArchiveForSegmentWithReason() : array
439 {
440 $params = $this->params;
441 if ($params->getSegment()->isEmpty()) {
442 return [\false, 'Segment is empty'];
443 }
444 if (!empty($params->getRequestedPlugin()) && \Piwik\ArchiveProcessor\Rules::isSegmentPluginArchivingDisabled($params->getRequestedPlugin(), $params->getSite()->getId())) {
445 return [\true, 'Plugin provided and segment plugin archiving disabled'];
446 }
447 // For better understanding of the next check please have a look at Rules::shouldProcessReportsAllPlugins implementation
448 // and what conditions it returns false on. For our use here, we need to ensure that:
449 // - we are not running CLI archiving
450 // - we are not dealing with a range period
451 // - we don't have an empty segment
452 // - we don't have a segment that should be preprocessed
453 // - we are not forcing a single plugin archiving
454 if (!\Piwik\ArchiveProcessor\Rules::shouldProcessReportsAllPlugins($params->getIdSites(), $params->getSegment(), $params->getPeriod()->getLabel())) {
455 return [\false, 'shouldProcessReportsAllPlugins reported false'];
456 }
457 /** @var SegmentArchiving */
458 $segmentArchiving = StaticContainer::get(SegmentArchiving::class);
459 $segmentInfo = $segmentArchiving->findSegmentForHash($params->getSegment()->getHash(), $params->getSite()->getId());
460 if (!$segmentInfo) {
461 return [\false, 'segment not found for hash'];
462 }
463 $segmentArchiveStartDate = $segmentArchiving->getReArchiveSegmentStartDate($segmentInfo);
464 if ($segmentArchiveStartDate !== null && $segmentArchiveStartDate->isLater($params->getPeriod()->getDateEnd()->getEndOfDay())) {
465 $doneFlag = \Piwik\ArchiveProcessor\Rules::getDoneStringFlagFor([$params->getSite()->getId()], $params->getSegment(), $params->getPeriod()->getLabel(), $params->getRequestedPlugin());
466 // if there is no invalidation where the report is null, we can skip
467 // if we have invalidations for the period and name, but only for a specific reports, we can skip
468 // if the report is not null we only want to rearchive if we have invalidation for that report
469 // if we don't find invalidation for that report, we can skip
470 $hasInvalidationsForPeriodAndName = $this->dataAccessModel->hasInvalidationForPeriodAndName($params->getSite()->getId(), $params->getPeriod(), $doneFlag, $params->getArchiveOnlyReport());
471 if ($hasInvalidationsForPeriodAndName) {
472 return [\false, 'Has invalidations for period and name'];
473 } else {
474 return [\true, 'No invalidations for period and name'];
475 }
476 }
477 return [\false, 'Segment archive date set or segment archive start date is earlier than period end of day'];
478 }
479 public function canSkipArchiveForSegment()
480 {
481 return $this->canSkipArchiveForSegmentWithReason()[0];
482 }
483 private function isWebsiteUsingTheTracker($idSite)
484 {
485 $idSitesNotUsingTracker = self::getSitesNotUsingTracker();
486 $isUsingTracker = !in_array($idSite, $idSitesNotUsingTracker);
487 return $isUsingTracker;
488 }
489 public static function getSitesNotUsingTracker()
490 {
491 $cache = Cache::getTransientCache();
492 $cacheKey = 'Archiving.isWebsiteUsingTheTracker';
493 $idSitesNotUsingTracker = $cache->fetch($cacheKey);
494 if ($idSitesNotUsingTracker === \false || !isset($idSitesNotUsingTracker)) {
495 // we want to trigger event only once
496 $idSitesNotUsingTracker = array();
497 /**
498 * This event is triggered when detecting whether there are sites that do not use the tracker.
499 *
500 * By default we only archive a site when there was actually any visit since the last archiving.
501 * However, some plugins do import data from another source instead of using the tracker and therefore
502 * will never have any visits for this site. To make sure we still archive data for such a site when
503 * archiving for this site is requested, you can listen to this event and add the idSite to the list of
504 * sites that do not use the tracker.
505 *
506 * @param bool $idSitesNotUsingTracker The list of idSites that rather import data instead of using the tracker
507 */
508 Piwik::postEvent('CronArchive.getIdSitesNotUsingTracker', array(&$idSitesNotUsingTracker));
509 $cache->save($cacheKey, $idSitesNotUsingTracker);
510 }
511 return $idSitesNotUsingTracker;
512 }
513 private function hasSiteVisitsBetweenTimeframe($idSite, Period $period) : bool
514 {
515 $cacheKeyStr = 'Archiving.hasSiteVisitsBetweenTimeframe.%s.%s';
516 $cacheKey = CacheId::siteAware(sprintf($cacheKeyStr, $period->getLabel(), $period->getRangeString()), [$idSite]);
517 if ($this->cache->contains($cacheKey)) {
518 return $this->cache->fetch($cacheKey);
519 }
520 $timezone = Site::getTimezoneFor($idSite);
521 /** @var Date $date1 */
522 /** @var Date $date2 */
523 [$date1, $date2] = $period->getBoundsInTimezone($timezone);
524 $hasSiteVisitsBetweenTimeframe = $this->rawLogDao->hasSiteVisitsBetweenTimeframe($date1->getDatetime(), $date2->getDatetime(), $idSite);
525 $this->cache->save($cacheKey, $hasSiteVisitsBetweenTimeframe);
526 if ($hasSiteVisitsBetweenTimeframe) {
527 $currentPeriod = $period;
528 do {
529 $parentPeriodLabel = $currentPeriod->getParentPeriodLabel();
530 if (!Period\Factory::isPeriodEnabledForAPI($parentPeriodLabel)) {
531 $parentPeriodLabel = null;
532 }
533 if ($parentPeriodLabel) {
534 $parentPeriod = Period\Factory::build($parentPeriodLabel, $date1);
535 $cacheKey = CacheId::siteAware(sprintf($cacheKeyStr, $parentPeriod->getLabel(), $parentPeriod->getRangeString()), [$idSite]);
536 $this->cache->save($cacheKey, \true);
537 $currentPeriod = $parentPeriod;
538 }
539 } while ($parentPeriodLabel);
540 }
541 return $hasSiteVisitsBetweenTimeframe;
542 }
543 public static function getArchivingDepth()
544 {
545 return self::$archivingDepth;
546 }
547 private function shouldForceInvalidatedArchive($value, $tsArchived)
548 {
549 $params = $this->params;
550 // the archive is invalidated and we are in a browser request that is allowed archive it
551 if ($value == ArchiveWriter::DONE_INVALIDATED && \Piwik\ArchiveProcessor\Rules::isArchivingEnabledFor([$params->getSite()->getId()], $params->getSegment(), $params->getPeriod()->getLabel())) {
552 // if coming from core:archive, force rearchiving, since if we don't the entry will be removed from archive_invalidations
553 // w/o being rearchived
554 if (SettingsServer::isArchivePhpTriggered()) {
555 return \true;
556 }
557 // if coming from a browser request, and period does not contain today, force rearchiving
558 $timezone = $params->getSite()->getTimezone();
559 if (!$params->getPeriod()->isDateInPeriod(Date::factoryInTimezone('today', $timezone))) {
560 return \true;
561 }
562 // if coming from a browser request, and period does contain today, check the ttl for the period (done just below this)
563 $minDatetimeArchiveProcessedUTC = \Piwik\ArchiveProcessor\Rules::getMinTimeProcessedForInProgressArchive($params->getDateStart(), $params->getPeriod(), $params->getSegment(), $params->getSite());
564 $minDatetimeArchiveProcessedUTC = Date::factory($minDatetimeArchiveProcessedUTC);
565 if ($minDatetimeArchiveProcessedUTC && Date::factory($tsArchived)->isEarlier($minDatetimeArchiveProcessedUTC)) {
566 return \true;
567 }
568 }
569 return \false;
570 }
571 }
572