PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 1.3.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v1.3.1
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 6 years ago Loader.php 6 years ago Parameters.php 6 years ago PluginsArchiver.php 6 years ago PluginsArchiverException.php 6 years ago Rules.php 6 years ago
Loader.php
317 lines
1 <?php
2 /**
3 * Piwik - 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\ArchiveProcessor;
10
11 use Piwik\Archive\ArchiveInvalidator;
12 use Piwik\Cache;
13 use Piwik\Config;
14 use Piwik\Container\StaticContainer;
15 use Piwik\Context;
16 use Piwik\DataAccess\ArchiveSelector;
17 use Piwik\DataAccess\ArchiveTableCreator;
18 use Piwik\Date;
19 use Piwik\Db;
20 use Piwik\Piwik;
21 use Piwik\Site;
22 use Psr\Log\LoggerInterface;
23
24 /**
25 * This class uses PluginsArchiver class to trigger data aggregation and create archives.
26 */
27 class Loader
28 {
29 /**
30 * Idarchive in the DB for the requested archive
31 *
32 * @var int
33 */
34 protected $idArchive;
35
36 /**
37 * @var Parameters
38 */
39 protected $params;
40
41 /**
42 * @var ArchiveInvalidator
43 */
44 private $invalidator;
45
46 /**
47 * @var \Piwik\Cache\Cache
48 */
49 private $cache;
50
51 /**
52 * @var LoggerInterface
53 */
54 private $logger;
55
56 public function __construct(Parameters $params, $invalidateBeforeArchiving = false)
57 {
58 $this->params = $params;
59 $this->invalidateBeforeArchiving = $invalidateBeforeArchiving;
60 $this->invalidator = StaticContainer::get(ArchiveInvalidator::class);
61 $this->cache = Cache::getTransientCache();
62 $this->logger = StaticContainer::get(LoggerInterface::class);
63 }
64
65 /**
66 * @return bool
67 */
68 protected function isThereSomeVisits($visits)
69 {
70 return $visits > 0;
71 }
72
73 /**
74 * @return bool
75 */
76 protected function mustProcessVisitCount($visits)
77 {
78 return $visits === false;
79 }
80
81 public function prepareArchive($pluginName)
82 {
83 return Context::changeIdSite($this->params->getSite()->getId(), function () use ($pluginName) {
84 return $this->prepareArchiveImpl($pluginName);
85 });
86 }
87
88 private function prepareArchiveImpl($pluginName)
89 {
90 $this->params->setRequestedPlugin($pluginName);
91
92 list($idArchive, $visits, $visitsConverted, $isAnyArchiveExists) = $this->loadExistingArchiveIdFromDb();
93 if (!empty($idArchive)) { // we have a usable idarchive (it's not invalidated and it's new enough)
94 return $idArchive;
95 }
96
97 // if there is an archive, but we can't use it for some reason, invalidate existing archives before
98 // we start archiving. if the archive is made invalid, we will correctly re-archive below.
99 if ($this->invalidateBeforeArchiving
100 && $isAnyArchiveExists
101 ) {
102 $this->invalidatedReportsIfNeeded();
103 }
104
105 /** @var ArchivingStatus $archivingStatus */
106 $archivingStatus = StaticContainer::get(ArchivingStatus::class);
107 $archivingStatus->archiveStarted($this->params);
108
109 try {
110 list($visits, $visitsConverted) = $this->prepareCoreMetricsArchive($visits, $visitsConverted);
111 list($idArchive, $visits) = $this->prepareAllPluginsArchive($visits, $visitsConverted);
112 } finally {
113 $archivingStatus->archiveFinished();
114 }
115
116 if ($this->isThereSomeVisits($visits) || PluginsArchiver::doesAnyPluginArchiveWithoutVisits()) {
117 return $idArchive;
118 }
119
120 return false;
121 }
122
123 /**
124 * Prepares the core metrics if needed.
125 *
126 * @param $visits
127 * @return array
128 */
129 protected function prepareCoreMetricsArchive($visits, $visitsConverted)
130 {
131 $createSeparateArchiveForCoreMetrics = $this->mustProcessVisitCount($visits)
132 && !$this->doesRequestedPluginIncludeVisitsSummary();
133
134 if ($createSeparateArchiveForCoreMetrics) {
135 $requestedPlugin = $this->params->getRequestedPlugin();
136
137 $this->params->setRequestedPlugin('VisitsSummary');
138
139 $pluginsArchiver = new PluginsArchiver($this->params);
140 $metrics = $pluginsArchiver->callAggregateCoreMetrics();
141 $pluginsArchiver->finalizeArchive();
142
143 $this->params->setRequestedPlugin($requestedPlugin);
144
145 $visits = $metrics['nb_visits'];
146 $visitsConverted = $metrics['nb_visits_converted'];
147 }
148
149 return array($visits, $visitsConverted);
150 }
151
152 protected function prepareAllPluginsArchive($visits, $visitsConverted)
153 {
154 $pluginsArchiver = new PluginsArchiver($this->params);
155
156 if ($this->mustProcessVisitCount($visits)
157 || $this->doesRequestedPluginIncludeVisitsSummary()
158 ) {
159 $metrics = $pluginsArchiver->callAggregateCoreMetrics();
160 $visits = $metrics['nb_visits'];
161 $visitsConverted = $metrics['nb_visits_converted'];
162 }
163
164 $forceArchivingWithoutVisits = !$this->isThereSomeVisits($visits) && $this->shouldArchiveForSiteEvenWhenNoVisits();
165 $pluginsArchiver->callAggregateAllPlugins($visits, $visitsConverted, $forceArchivingWithoutVisits);
166
167 $idArchive = $pluginsArchiver->finalizeArchive();
168
169 return array($idArchive, $visits);
170 }
171
172 protected function doesRequestedPluginIncludeVisitsSummary()
173 {
174 $processAllReportsIncludingVisitsSummary =
175 Rules::shouldProcessReportsAllPlugins(array($this->params->getSite()->getId()), $this->params->getSegment(), $this->params->getPeriod()->getLabel());
176 $doesRequestedPluginIncludeVisitsSummary = $processAllReportsIncludingVisitsSummary
177 || $this->params->getRequestedPlugin() == 'VisitsSummary';
178 return $doesRequestedPluginIncludeVisitsSummary;
179 }
180
181 protected function isArchivingForcedToTrigger()
182 {
183 $period = $this->params->getPeriod()->getLabel();
184 $debugSetting = 'always_archive_data_period'; // default
185
186 if ($period == 'day') {
187 $debugSetting = 'always_archive_data_day';
188 } elseif ($period == 'range') {
189 $debugSetting = 'always_archive_data_range';
190 }
191
192 return (bool) Config::getInstance()->Debug[$debugSetting];
193 }
194
195 /**
196 * Returns the idArchive if the archive is available in the database for the requested plugin.
197 * Returns false if the archive needs to be processed.
198 *
199 * (public for tests)
200 *
201 * @return array
202 */
203 public function loadExistingArchiveIdFromDb()
204 {
205 if ($this->isArchivingForcedToTrigger()) {
206 $this->logger->debug("Archiving forced to trigger for {$this->params}.");
207
208 // return no usable archive found, and no existing archive. this will skip invalidation, which should
209 // be fine since we just force archiving.
210 return [false, false, false, false];
211 }
212
213 $minDatetimeArchiveProcessedUTC = $this->getMinTimeArchiveProcessed();
214 $result = ArchiveSelector::getArchiveIdAndVisits($this->params, $minDatetimeArchiveProcessedUTC);
215 return $result;
216 }
217
218 /**
219 * Returns the minimum archive processed datetime to look at. Only public for tests.
220 *
221 * @return int|bool Datetime timestamp, or false if must look at any archive available
222 */
223 protected function getMinTimeArchiveProcessed()
224 {
225 $endDateTimestamp = self::determineIfArchivePermanent($this->params->getDateEnd());
226 if ($endDateTimestamp) {
227 // past archive
228 return $endDateTimestamp;
229 }
230 $dateStart = $this->params->getDateStart();
231 $period = $this->params->getPeriod();
232 $segment = $this->params->getSegment();
233 $site = $this->params->getSite();
234 // in-progress archive
235 return Rules::getMinTimeProcessedForInProgressArchive($dateStart, $period, $segment, $site);
236 }
237
238 protected static function determineIfArchivePermanent(Date $dateEnd)
239 {
240 $now = time();
241 $endTimestampUTC = strtotime($dateEnd->getDateEndUTC());
242
243 if ($endTimestampUTC <= $now) {
244 // - if the period we are looking for is finished, we look for a ts_archived that
245 // is greater than the last day of the archive
246 return $endTimestampUTC;
247 }
248
249 return false;
250 }
251
252 private function shouldArchiveForSiteEvenWhenNoVisits()
253 {
254 $idSitesToArchive = $this->getIdSitesToArchiveWhenNoVisits();
255 return in_array($this->params->getSite()->getId(), $idSitesToArchive);
256 }
257
258 private function getIdSitesToArchiveWhenNoVisits()
259 {
260 $cache = Cache::getTransientCache();
261 $cacheKey = 'Archiving.getIdSitesToArchiveWhenNoVisits';
262
263 if (!$cache->contains($cacheKey)) {
264 $idSites = array();
265
266 // leaving undocumented unless decided otherwise
267 Piwik::postEvent('Archiving.getIdSitesToArchiveWhenNoVisits', array(&$idSites));
268
269 $cache->save($cacheKey, $idSites);
270 }
271
272 return $cache->fetch($cacheKey);
273 }
274
275 // public for tests
276 public function getReportsToInvalidate()
277 {
278 $sitesPerDays = $this->invalidator->getRememberedArchivedReportsThatShouldBeInvalidated();
279
280 foreach ($sitesPerDays as $dateStr => $siteIds) {
281 if (empty($siteIds)
282 || !in_array($this->params->getSite()->getId(), $siteIds)
283 ) {
284 unset($sitesPerDays[$dateStr]);
285 }
286
287 $date = Date::factory($dateStr);
288 if ($date->isEarlier($this->params->getPeriod()->getDateStart())
289 || $date->isLater($this->params->getPeriod()->getDateEnd())
290 ) { // date in list is not the current date, so ignore it
291 unset($sitesPerDays[$dateStr]);
292 }
293 }
294
295 return $sitesPerDays;
296 }
297
298 private function invalidatedReportsIfNeeded()
299 {
300 $sitesPerDays = $this->getReportsToInvalidate();
301 if (empty($sitesPerDays)) {
302 return;
303 }
304
305 foreach ($sitesPerDays as $date => $siteIds) {
306 try {
307 $this->invalidator->markArchivesAsInvalidated([$this->params->getSite()->getId()], array(Date::factory($date)), false, $this->params->getSegment());
308 } catch (\Exception $e) {
309 Site::clearCache();
310 throw $e;
311 }
312 }
313
314 Site::clearCache();
315 }
316 }
317