PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.3.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.3.0
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 5 years ago Loader.php 5 years ago Parameters.php 5 years ago PluginsArchiver.php 5 years ago PluginsArchiverException.php 5 years ago Rules.php 5 years ago
PluginsArchiver.php
339 lines
1 <?php
2 /**
3 * Matomo - 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
76 $shouldAggregateFromRawData = $this->params->isSingleSiteDayArchive();
77
78 /**
79 * Triggered to detect if the archiver should aggregate from raw data by using MySQL queries (when true)
80 * or by aggregate archives (when false). Typically, data is aggregated from raw data for "day" period, and
81 * aggregregated from archives for all other periods.
82 *
83 * @param bool $shouldAggregateFromRawData Set to true, to aggregate from raw data, or false to aggregate multiple reports.
84 * @param Parameters $params
85 */
86 Piwik::postEvent('ArchiveProcessor.shouldAggregateFromRawData', array(&$shouldAggregateFromRawData, $this->params));
87
88 $this->shouldAggregateFromRawData = $shouldAggregateFromRawData;
89 }
90
91 /**
92 * If period is day, will get the core metrics (including visits) from the logs.
93 * If period is != day, will sum the core metrics from the existing archives.
94 * @return array Core metrics
95 */
96 public function callAggregateCoreMetrics()
97 {
98 $this->logAggregator->cleanup();
99 $this->logAggregator->setQueryOriginHint('Core');
100
101 if ($this->shouldAggregateFromRawData) {
102 $metrics = $this->aggregateDayVisitsMetrics();
103 } else {
104 $metrics = $this->aggregateMultipleVisitsMetrics();
105 }
106
107 if (empty($metrics)) {
108 return array(
109 'nb_visits' => false,
110 'nb_visits_converted' => false
111 );
112 }
113 return array(
114 'nb_visits' => $metrics['nb_visits'],
115 'nb_visits_converted' => $metrics['nb_visits_converted']
116 );
117 }
118
119 /**
120 * Instantiates the Archiver class in each plugin that defines it,
121 * and triggers Aggregation processing on these plugins.
122 */
123 public function callAggregateAllPlugins($visits, $visitsConverted, $forceArchivingWithoutVisits = false)
124 {
125 Log::debug("PluginsArchiver::%s: Initializing archiving process for all plugins [visits = %s, visits converted = %s]",
126 __FUNCTION__, $visits, $visitsConverted);
127
128 /** @var Logger $performanceLogger */
129 $performanceLogger = StaticContainer::get(Logger::class);
130
131 $this->archiveProcessor->setNumberOfVisits($visits, $visitsConverted);
132
133 $archivers = static::getPluginArchivers();
134
135 foreach ($archivers as $pluginName => $archiverClass) {
136 // We clean up below all tables created during this function call (and recursive calls)
137 $latestUsedTableId = Manager::getInstance()->getMostRecentTableId();
138
139 /** @var Archiver $archiver */
140 $archiver = $this->makeNewArchiverObject($archiverClass, $pluginName);
141
142 if (!$archiver->isEnabled()) {
143 Log::debug("PluginsArchiver::%s: Skipping archiving for plugin '%s' (disabled).", __FUNCTION__, $pluginName);
144 continue;
145 }
146
147 if (!$forceArchivingWithoutVisits && !$visits && !$archiver->shouldRunEvenWhenNoVisits()) {
148 Log::debug("PluginsArchiver::%s: Skipping archiving for plugin '%s' (no visits).", __FUNCTION__, $pluginName);
149 continue;
150 }
151
152 if ($this->shouldProcessReportsForPlugin($pluginName)) {
153 $this->logAggregator->setQueryOriginHint($pluginName);
154
155 try {
156 self::$currentPluginBeingArchived = $pluginName;
157
158 $period = $this->params->getPeriod()->getLabel();
159
160 $timer = new Timer();
161 if ($this->shouldAggregateFromRawData) {
162 Log::debug("PluginsArchiver::%s: Archiving $period reports for plugin '%s' from raw data.", __FUNCTION__, $pluginName);
163
164 $archiver->callAggregateDayReport();
165 } else {
166 Log::debug("PluginsArchiver::%s: Archiving $period reports for plugin '%s' using reports for smaller periods.", __FUNCTION__, $pluginName);
167
168 $archiver->callAggregateMultipleReports();
169 }
170
171 $this->logAggregator->setQueryOriginHint('');
172
173 $performanceLogger->logMeasurement('plugin', $pluginName, $this->params, $timer);
174
175 Log::debug("PluginsArchiver::%s: %s while archiving %s reports for plugin '%s' %s.",
176 __FUNCTION__,
177 $timer->getMemoryLeak(),
178 $this->params->getPeriod()->getLabel(),
179 $pluginName,
180 $this->params->getSegment() ? sprintf("(for segment = '%s')", $this->params->getSegment()->getString()) : ''
181 );
182 } catch (Exception $e) {
183 throw new PluginsArchiverException($e->getMessage() . " - in plugin $pluginName.", $e->getCode(), $e);
184 } finally {
185 self::$currentPluginBeingArchived = null;
186 }
187 } else {
188 Log::debug("PluginsArchiver::%s: Not archiving reports for plugin '%s'.", __FUNCTION__, $pluginName);
189 }
190
191 Manager::getInstance()->deleteAll($latestUsedTableId);
192 unset($archiver);
193 }
194
195 $this->logAggregator->cleanup();
196 }
197
198 public function finalizeArchive()
199 {
200 $this->params->logStatusDebug();
201 $this->archiveWriter->finalizeArchive();
202 $idArchive = $this->archiveWriter->getIdArchive();
203
204 return $idArchive;
205 }
206
207 /**
208 * Returns if any plugin archiver archives without visits
209 */
210 public static function doesAnyPluginArchiveWithoutVisits()
211 {
212 $archivers = static::getPluginArchivers();
213
214 foreach ($archivers as $pluginName => $archiverClass) {
215 if ($archiverClass::shouldRunEvenWhenNoVisits()) {
216 return true;
217 }
218 }
219
220 return false;
221 }
222
223 /**
224 * Loads Archiver class from any plugin that defines one.
225 *
226 * @return \Piwik\Plugin\Archiver[]
227 */
228 protected static function getPluginArchivers()
229 {
230 if (empty(static::$archivers)) {
231 $pluginNames = \Piwik\Plugin\Manager::getInstance()->getActivatedPlugins();
232 $archivers = array();
233 foreach ($pluginNames as $pluginName) {
234 $archivers[$pluginName] = self::getPluginArchiverClass($pluginName);
235 }
236 static::$archivers = array_filter($archivers);
237 }
238 return static::$archivers;
239 }
240
241 private static function getPluginArchiverClass($pluginName)
242 {
243 $klassName = 'Piwik\\Plugins\\' . $pluginName . '\\Archiver';
244 if (class_exists($klassName)
245 && is_subclass_of($klassName, 'Piwik\\Plugin\\Archiver')) {
246 return $klassName;
247 }
248 return false;
249 }
250
251 /**
252 * Whether the specified plugin's reports should be archived
253 * @param string $pluginName
254 * @return bool
255 */
256 protected function shouldProcessReportsForPlugin($pluginName)
257 {
258 if ($this->params->getRequestedPlugin() == $pluginName) {
259 return true;
260 }
261
262 if ($this->params->shouldOnlyArchiveRequestedPlugin()) {
263 return false;
264 }
265
266 if (Rules::shouldProcessReportsAllPlugins(
267 array($this->params->getSite()->getId()),
268 $this->params->getSegment(),
269 $this->params->getPeriod()->getLabel())
270 ) {
271 return true;
272 }
273
274 if ($this->params->getRequestedPlugin() &&
275 !\Piwik\Plugin\Manager::getInstance()->isPluginLoaded($this->params->getRequestedPlugin())
276 ) {
277 return false;
278 }
279
280 return false;
281 }
282
283 protected function aggregateDayVisitsMetrics()
284 {
285 $query = $this->archiveProcessor->getLogAggregator()->queryVisitsByDimension();
286 $data = $query->fetch();
287
288 $metrics = $this->convertMetricsIdToName($data);
289 $this->archiveProcessor->insertNumericRecords($metrics);
290 return $metrics;
291 }
292
293 protected function convertMetricsIdToName($data)
294 {
295 $metrics = array();
296 foreach ($data as $metricId => $value) {
297 $readableMetric = Metrics::$mappingFromIdToName[$metricId];
298 $metrics[$readableMetric] = $value;
299 }
300 return $metrics;
301 }
302
303 protected function aggregateMultipleVisitsMetrics()
304 {
305 $toSum = Metrics::getVisitsMetricNames();
306 $metrics = $this->archiveProcessor->aggregateNumericMetrics($toSum);
307 return $metrics;
308 }
309
310
311 /**
312 * @param $archiverClass
313 * @return Archiver
314 */
315 private function makeNewArchiverObject($archiverClass, $pluginName)
316 {
317 $archiver = new $archiverClass($this->archiveProcessor);
318
319 /**
320 * Triggered right after a new **plugin archiver instance** is created.
321 * Subscribers to this event can configure the plugin archiver, for example prevent the archiving of a plugin's data
322 * by calling `$archiver->disable()` method.
323 *
324 * @param \Piwik\Plugin\Archiver &$archiver The newly created plugin archiver instance.
325 * @param string $pluginName The name of plugin of which archiver instance was created.
326 * @param array $this->params Array containing archive parameters (Site, Period, Date and Segment)
327 * @param bool false This parameter is deprecated and will be removed.
328 */
329 Piwik::postEvent('Archiving.makeNewArchiverObject', array($archiver, $pluginName, $this->params, false));
330
331 return $archiver;
332 }
333
334 public static function isArchivingProcessActive()
335 {
336 return self::$currentPluginBeingArchived !== null;
337 }
338 }
339