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 / PluginsArchiver.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
PluginsArchiver.php
340 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
10 namespace Piwik\ArchiveProcessor;
11
12 use Piwik\ArchiveProcessor;
13 use Piwik\Container\StaticContainer;
14 use Piwik\CronArchive\Performance\Logger;
15 use Piwik\DataAccess\ArchiveWriter;
16 use Piwik\DataAccess\LogAggregator;
17 use Piwik\DataTable\Manager;
18 use Piwik\Metrics;
19 use Piwik\Piwik;
20 use Piwik\Plugin\Archiver;
21 use Piwik\Log;
22 use Piwik\Timer;
23 use Exception;
24
25 /**
26 * This class creates the Archiver objects found in plugins and will trigger aggregation,
27 * so each plugin can process their reports.
28 */
29 class PluginsArchiver
30 {
31 /**
32 * @var string|null
33 */
34 private static $currentPluginBeingArchived = null;
35
36 /**
37 * @param ArchiveProcessor $archiveProcessor
38 */
39 public $archiveProcessor;
40
41 /**
42 * @var Parameters
43 */
44 protected $params;
45
46 /**
47 * @var LogAggregator
48 */
49 private $logAggregator;
50
51 /**
52 * Public only for tests. Won't be necessary after DI changes are complete.
53 *
54 * @var Archiver[] $archivers
55 */
56 public static $archivers = array();
57
58 /**
59 * Defines if we should aggregate from raw data by using MySQL queries (when true) or aggregate archives (when false)
60 * @var bool
61 */
62 private $shouldAggregateFromRawData;
63
64 public function __construct(Parameters $params, ArchiveWriter $archiveWriter = null)
65 {
66 $this->params = $params;
67 $this->archiveWriter = $archiveWriter ?: new ArchiveWriter($this->params);
68 $this->archiveWriter->initNewArchive();
69
70 $this->logAggregator = new LogAggregator($params);
71 $this->logAggregator->allowUsageSegmentCache();
72
73 $this->archiveProcessor = new ArchiveProcessor($this->params, $this->archiveWriter, $this->logAggregator);
74
75 $shouldAggregateFromRawData = $this->params->isSingleSiteDayArchive();
76
77 /**
78 * Triggered to detect if the archiver should aggregate from raw data by using MySQL queries (when true)
79 * or by aggregate archives (when false). Typically, data is aggregated from raw data for "day" period, and
80 * aggregregated from archives for all other periods.
81 *
82 * @param bool $shouldAggregateFromRawData Set to true, to aggregate from raw data, or false to aggregate multiple reports.
83 * @param Parameters $params
84 * @ignore
85 * @deprecated
86 *
87 * In Matomo 4.0 we should maybe remove this event, and instead maybe always archive from raw data when it is daily archive,
88 * no matter if single site or not. We cannot do this in Matomo 3.X as some custom plugin archivers may not be able to handle multiple sites.
89 */
90 Piwik::postEvent('ArchiveProcessor.shouldAggregateFromRawData', array(&$shouldAggregateFromRawData, $this->params));
91
92 $this->shouldAggregateFromRawData = $shouldAggregateFromRawData;
93 }
94
95 /**
96 * If period is day, will get the core metrics (including visits) from the logs.
97 * If period is != day, will sum the core metrics from the existing archives.
98 * @return array Core metrics
99 */
100 public function callAggregateCoreMetrics()
101 {
102 $this->logAggregator->cleanup();
103 $this->logAggregator->setQueryOriginHint('Core');
104
105 if ($this->shouldAggregateFromRawData) {
106 $metrics = $this->aggregateDayVisitsMetrics();
107 } else {
108 $metrics = $this->aggregateMultipleVisitsMetrics();
109 }
110
111 if (empty($metrics)) {
112 return array(
113 'nb_visits' => false,
114 'nb_visits_converted' => false
115 );
116 }
117 return array(
118 'nb_visits' => $metrics['nb_visits'],
119 'nb_visits_converted' => $metrics['nb_visits_converted']
120 );
121 }
122
123 /**
124 * Instantiates the Archiver class in each plugin that defines it,
125 * and triggers Aggregation processing on these plugins.
126 */
127 public function callAggregateAllPlugins($visits, $visitsConverted, $forceArchivingWithoutVisits = false)
128 {
129 Log::debug("PluginsArchiver::%s: Initializing archiving process for all plugins [visits = %s, visits converted = %s]",
130 __FUNCTION__, $visits, $visitsConverted);
131
132 /** @var Logger $performanceLogger */
133 $performanceLogger = StaticContainer::get(Logger::class);
134
135 $this->archiveProcessor->setNumberOfVisits($visits, $visitsConverted);
136
137 $archivers = static::getPluginArchivers();
138
139 foreach ($archivers as $pluginName => $archiverClass) {
140 // We clean up below all tables created during this function call (and recursive calls)
141 $latestUsedTableId = Manager::getInstance()->getMostRecentTableId();
142
143 /** @var Archiver $archiver */
144 $archiver = $this->makeNewArchiverObject($archiverClass, $pluginName);
145
146 if (!$archiver->isEnabled()) {
147 Log::debug("PluginsArchiver::%s: Skipping archiving for plugin '%s' (disabled).", __FUNCTION__, $pluginName);
148 continue;
149 }
150
151 if (!$forceArchivingWithoutVisits && !$visits && !$archiver->shouldRunEvenWhenNoVisits()) {
152 Log::debug("PluginsArchiver::%s: Skipping archiving for plugin '%s' (no visits).", __FUNCTION__, $pluginName);
153 continue;
154 }
155
156 if ($this->shouldProcessReportsForPlugin($pluginName)) {
157
158 $this->logAggregator->setQueryOriginHint($pluginName);
159
160 try {
161 self::$currentPluginBeingArchived = $pluginName;
162
163 $period = $this->params->getPeriod()->getLabel();
164
165 $timer = new Timer();
166 if ($this->shouldAggregateFromRawData) {
167 Log::debug("PluginsArchiver::%s: Archiving $period reports for plugin '%s' from raw data.", __FUNCTION__, $pluginName);
168
169 $archiver->callAggregateDayReport();
170 } else {
171 Log::debug("PluginsArchiver::%s: Archiving $period reports for plugin '%s' using reports for smaller periods.", __FUNCTION__, $pluginName);
172
173 $archiver->callAggregateMultipleReports();
174 }
175
176 $this->logAggregator->setQueryOriginHint('');
177
178 $performanceLogger->logMeasurement('plugin', $pluginName, $this->params, $timer);
179
180 Log::debug("PluginsArchiver::%s: %s while archiving %s reports for plugin '%s' %s.",
181 __FUNCTION__,
182 $timer->getMemoryLeak(),
183 $this->params->getPeriod()->getLabel(),
184 $pluginName,
185 $this->params->getSegment() ? sprintf("(for segment = '%s')", $this->params->getSegment()->getString()) : ''
186 );
187 } catch (Exception $e) {
188 throw new PluginsArchiverException($e->getMessage() . " - in plugin $pluginName", $e->getCode(), $e);
189 } finally {
190 self::$currentPluginBeingArchived = null;
191 }
192 } else {
193 Log::debug("PluginsArchiver::%s: Not archiving reports for plugin '%s'.", __FUNCTION__, $pluginName);
194 }
195
196 Manager::getInstance()->deleteAll($latestUsedTableId);
197 unset($archiver);
198 }
199
200 $this->logAggregator->cleanup();
201 }
202
203 public function finalizeArchive()
204 {
205 $this->params->logStatusDebug();
206 $this->archiveWriter->finalizeArchive();
207 $idArchive = $this->archiveWriter->getIdArchive();
208
209 return $idArchive;
210 }
211
212 /**
213 * Returns if any plugin archiver archives without visits
214 */
215 public static function doesAnyPluginArchiveWithoutVisits()
216 {
217 $archivers = static::getPluginArchivers();
218
219 foreach ($archivers as $pluginName => $archiverClass) {
220 if ($archiverClass::shouldRunEvenWhenNoVisits()) {
221 return true;
222 }
223 }
224
225 return false;
226 }
227
228 /**
229 * Loads Archiver class from any plugin that defines one.
230 *
231 * @return \Piwik\Plugin\Archiver[]
232 */
233 protected static function getPluginArchivers()
234 {
235 if (empty(static::$archivers)) {
236 $pluginNames = \Piwik\Plugin\Manager::getInstance()->getActivatedPlugins();
237 $archivers = array();
238 foreach ($pluginNames as $pluginName) {
239 $archivers[$pluginName] = self::getPluginArchiverClass($pluginName);
240 }
241 static::$archivers = array_filter($archivers);
242 }
243 return static::$archivers;
244 }
245
246 private static function getPluginArchiverClass($pluginName)
247 {
248 $klassName = 'Piwik\\Plugins\\' . $pluginName . '\\Archiver';
249 if (class_exists($klassName)
250 && is_subclass_of($klassName, 'Piwik\\Plugin\\Archiver')) {
251 return $klassName;
252 }
253 return false;
254 }
255
256 /**
257 * Whether the specified plugin's reports should be archived
258 * @param string $pluginName
259 * @return bool
260 */
261 protected function shouldProcessReportsForPlugin($pluginName)
262 {
263 if ($this->params->getRequestedPlugin() == $pluginName) {
264 return true;
265 }
266
267 if ($this->params->shouldOnlyArchiveRequestedPlugin()) {
268 return false;
269 }
270
271 if (Rules::shouldProcessReportsAllPlugins(
272 array($this->params->getSite()->getId()),
273 $this->params->getSegment(),
274 $this->params->getPeriod()->getLabel())) {
275 return true;
276 }
277
278 if (!\Piwik\Plugin\Manager::getInstance()->isPluginLoaded($this->params->getRequestedPlugin())) {
279 return true;
280 }
281 return false;
282 }
283
284 protected function aggregateDayVisitsMetrics()
285 {
286 $query = $this->archiveProcessor->getLogAggregator()->queryVisitsByDimension();
287 $data = $query->fetch();
288
289 $metrics = $this->convertMetricsIdToName($data);
290 $this->archiveProcessor->insertNumericRecords($metrics);
291 return $metrics;
292 }
293
294 protected function convertMetricsIdToName($data)
295 {
296 $metrics = array();
297 foreach ($data as $metricId => $value) {
298 $readableMetric = Metrics::$mappingFromIdToName[$metricId];
299 $metrics[$readableMetric] = $value;
300 }
301 return $metrics;
302 }
303
304 protected function aggregateMultipleVisitsMetrics()
305 {
306 $toSum = Metrics::getVisitsMetricNames();
307 $metrics = $this->archiveProcessor->aggregateNumericMetrics($toSum);
308 return $metrics;
309 }
310
311
312 /**
313 * @param $archiverClass
314 * @return Archiver
315 */
316 private function makeNewArchiverObject($archiverClass, $pluginName)
317 {
318 $archiver = new $archiverClass($this->archiveProcessor);
319
320 /**
321 * Triggered right after a new **plugin archiver instance** is created.
322 * Subscribers to this event can configure the plugin archiver, for example prevent the archiving of a plugin's data
323 * by calling `$archiver->disable()` method.
324 *
325 * @param \Piwik\Plugin\Archiver &$archiver The newly created plugin archiver instance.
326 * @param string $pluginName The name of plugin of which archiver instance was created.
327 * @param array $this->params Array containing archive parameters (Site, Period, Date and Segment)
328 * @param bool false This parameter is deprecated and will be removed.
329 */
330 Piwik::postEvent('Archiving.makeNewArchiverObject', array($archiver, $pluginName, $this->params, false));
331
332 return $archiver;
333 }
334
335 public static function isArchivingProcessActive()
336 {
337 return self::$currentPluginBeingArchived !== null;
338 }
339 }
340