PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.0.3
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.0.3
5.13.0 5.12.1 5.12.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 2 years ago Loader.php 2 years ago LoaderLock.php 2 years ago Parameters.php 2 years ago PluginsArchiver.php 2 years ago PluginsArchiverException.php 2 years ago Record.php 2 years ago RecordBuilder.php 2 years ago Rules.php 2 years ago
PluginsArchiver.php
282 lines
1 <?php
2
3 /**
4 * Matomo - free/libre analytics platform
5 *
6 * @link https://matomo.org
7 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
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 * This class creates the Archiver objects found in plugins and will trigger aggregation,
26 * so each plugin can process their reports.
27 */
28 class PluginsArchiver
29 {
30 /**
31 * @var string|null
32 */
33 private static $currentPluginBeingArchived = null;
34 /**
35 * @param ArchiveProcessor $archiveProcessor
36 */
37 public $archiveProcessor;
38 /**
39 * @var Parameters
40 */
41 protected $params;
42 /**
43 * @var LogAggregator
44 */
45 private $logAggregator;
46 /**
47 * Public only for tests. Won't be necessary after DI changes are complete.
48 *
49 * @var Archiver[] $archivers
50 */
51 public static $archivers = array();
52 /**
53 * Defines if we should aggregate from raw data by using MySQL queries (when true) or aggregate archives (when false)
54 * @var bool
55 */
56 private $shouldAggregateFromRawData;
57 /**
58 * @var ArchiveWriter
59 */
60 private $archiveWriter;
61 public function __construct(\Piwik\ArchiveProcessor\Parameters $params, ArchiveWriter $archiveWriter = null)
62 {
63 $this->params = $params;
64 $this->archiveWriter = $archiveWriter ?: new ArchiveWriter($this->params);
65 $this->archiveWriter->initNewArchive();
66 $this->logAggregator = new LogAggregator($params);
67 $this->logAggregator->allowUsageSegmentCache();
68 $this->archiveProcessor = new ArchiveProcessor($this->params, $this->archiveWriter, $this->logAggregator);
69 $shouldAggregateFromRawData = $this->params->isSingleSiteDayArchive();
70 /**
71 * Triggered to detect if the archiver should aggregate from raw data by using MySQL queries (when true)
72 * or by aggregate archives (when false). Typically, data is aggregated from raw data for "day" period, and
73 * aggregregated from archives for all other periods.
74 *
75 * @param bool $shouldAggregateFromRawData Set to true, to aggregate from raw data, or false to aggregate multiple reports.
76 * @param Parameters $params
77 */
78 Piwik::postEvent('ArchiveProcessor.shouldAggregateFromRawData', array(&$shouldAggregateFromRawData, $this->params));
79 $this->shouldAggregateFromRawData = $shouldAggregateFromRawData;
80 }
81 /**
82 * If period is day, will get the core metrics (including visits) from the logs.
83 * If period is != day, will sum the core metrics from the existing archives.
84 * @return array Core metrics
85 */
86 public function callAggregateCoreMetrics()
87 {
88 $this->logAggregator->cleanup();
89 $this->logAggregator->setQueryOriginHint('Core');
90 if ($this->shouldAggregateFromRawData) {
91 $metrics = $this->aggregateDayVisitsMetrics();
92 } else {
93 $metrics = $this->aggregateMultipleVisitsMetrics();
94 }
95 if (empty($metrics)) {
96 return array('nb_visits' => false, 'nb_visits_converted' => false);
97 }
98 return array('nb_visits' => $metrics['nb_visits'], 'nb_visits_converted' => $metrics['nb_visits_converted']);
99 }
100 /**
101 * Instantiates the Archiver class in each plugin that defines it,
102 * and triggers Aggregation processing on these plugins.
103 */
104 public function callAggregateAllPlugins($visits, $visitsConverted, $forceArchivingWithoutVisits = false)
105 {
106 Log::debug("PluginsArchiver::%s: Initializing archiving process for all plugins [visits = %s, visits converted = %s]", __FUNCTION__, $visits, $visitsConverted);
107 /** @var Logger $performanceLogger */
108 $performanceLogger = StaticContainer::get(Logger::class);
109 $this->archiveProcessor->setNumberOfVisits($visits, $visitsConverted);
110 $archivers = static::getPluginArchivers();
111 $archiveOnlyPlugin = $this->params->getRequestedPlugin();
112 $archiveOnlyReports = $this->params->getArchiveOnlyReport();
113 foreach ($archivers as $pluginName => $archiverClass) {
114 // if we are archiving specific reports for a single plugin then we don't need or want to create
115 // Archiver instances, since they will set the archive to partial even if the requested reports aren't
116 // handled by the Archiver
117 if (!empty($archiveOnlyReports) && $archiveOnlyPlugin != $pluginName) {
118 continue;
119 }
120 // We clean up below all tables created during this function call (and recursive calls)
121 $latestUsedTableId = Manager::getInstance()->getMostRecentTableId();
122 /** @var Archiver $archiver */
123 $archiver = $this->makeNewArchiverObject($archiverClass, $pluginName);
124 if (!$archiver->isEnabled()) {
125 Log::debug("PluginsArchiver::%s: Skipping archiving for plugin '%s' (disabled).", __FUNCTION__, $pluginName);
126 continue;
127 }
128 if (!$forceArchivingWithoutVisits && !$visits && !$archiver->shouldRunEvenWhenNoVisits()) {
129 Log::debug("PluginsArchiver::%s: Skipping archiving for plugin '%s' (no visits).", __FUNCTION__, $pluginName);
130 continue;
131 }
132 if ($this->shouldProcessReportsForPlugin($pluginName)) {
133 $this->logAggregator->setQueryOriginHint($pluginName);
134 try {
135 self::$currentPluginBeingArchived = $pluginName;
136 $period = $this->params->getPeriod()->getLabel();
137 $timer = new Timer();
138 if ($this->shouldAggregateFromRawData) {
139 Log::debug("PluginsArchiver::%s: Archiving {$period} reports for plugin '%s' from raw data.", __FUNCTION__, $pluginName);
140 $archiver->callAggregateDayReport();
141 } else {
142 Log::debug("PluginsArchiver::%s: Archiving {$period} reports for plugin '%s' using reports for smaller periods.", __FUNCTION__, $pluginName);
143 $archiver->callAggregateMultipleReports();
144 }
145 $this->logAggregator->setQueryOriginHint('');
146 $performanceLogger->logMeasurement('plugin', $pluginName, $this->params, $timer);
147 Log::debug("PluginsArchiver::%s: %s while archiving %s reports for plugin '%s' %s.", __FUNCTION__, $timer->getMemoryLeak(), $this->params->getPeriod()->getLabel(), $pluginName, $this->params->getSegment() ? sprintf("(for segment = '%s')", $this->params->getSegment()->getString()) : '');
148 } catch (Exception $e) {
149 throw new \Piwik\ArchiveProcessor\PluginsArchiverException($e->getMessage() . " - in plugin {$pluginName}.", $e->getCode(), $e);
150 } finally {
151 self::$currentPluginBeingArchived = null;
152 }
153 } else {
154 Log::debug("PluginsArchiver::%s: Not archiving reports for plugin '%s'.", __FUNCTION__, $pluginName);
155 }
156 Manager::getInstance()->deleteAll($latestUsedTableId);
157 unset($archiver);
158 }
159 $this->logAggregator->cleanup();
160 }
161 public function finalizeArchive()
162 {
163 $this->params->logStatusDebug();
164 $this->archiveWriter->finalizeArchive();
165 $idArchive = $this->archiveWriter->getIdArchive();
166 return $idArchive;
167 }
168 /**
169 * Returns if any plugin archiver archives without visits
170 */
171 public static function doesAnyPluginArchiveWithoutVisits()
172 {
173 $archivers = static::getPluginArchivers();
174 foreach ($archivers as $pluginName => $archiverClass) {
175 if ($archiverClass::shouldRunEvenWhenNoVisits()) {
176 return true;
177 }
178 }
179 return false;
180 }
181 /**
182 * Loads Archiver class from any plugin that defines one.
183 *
184 * @return \Piwik\Plugin\Archiver[]
185 */
186 protected static function getPluginArchivers()
187 {
188 if (empty(static::$archivers)) {
189 $pluginNames = \Piwik\Plugin\Manager::getInstance()->getActivatedPlugins();
190 $archivers = array();
191 foreach ($pluginNames as $pluginName) {
192 $archivers[$pluginName] = self::getPluginArchiverClass($pluginName);
193 }
194 static::$archivers = array_filter($archivers);
195 }
196 return static::$archivers;
197 }
198 private static function getPluginArchiverClass(string $pluginName) : ?string
199 {
200 $klassName = 'Piwik\\Plugins\\' . $pluginName . '\\Archiver';
201 if (class_exists($klassName) && is_subclass_of($klassName, 'Piwik\\Plugin\\Archiver')) {
202 return $klassName;
203 }
204 if (Archiver::doesPluginHaveRecordBuilders($pluginName)) {
205 return Archiver::class;
206 }
207 return null;
208 }
209 /**
210 * Whether the specified plugin's reports should be archived
211 * @param string $pluginName
212 * @return bool
213 */
214 protected function shouldProcessReportsForPlugin($pluginName)
215 {
216 if ($this->params->getRequestedPlugin() == $pluginName) {
217 return true;
218 }
219 if ($this->params->shouldOnlyArchiveRequestedPlugin()) {
220 return false;
221 }
222 if (\Piwik\ArchiveProcessor\Rules::shouldProcessReportsAllPlugins(array($this->params->getSite()->getId()), $this->params->getSegment(), $this->params->getPeriod()->getLabel())) {
223 return true;
224 }
225 if ($this->params->getRequestedPlugin() && !\Piwik\Plugin\Manager::getInstance()->isPluginLoaded($this->params->getRequestedPlugin())) {
226 return false;
227 }
228 return false;
229 }
230 protected function aggregateDayVisitsMetrics()
231 {
232 $query = $this->archiveProcessor->getLogAggregator()->queryVisitsByDimension();
233 $data = $query->fetch();
234 $metrics = $this->convertMetricsIdToName($data);
235 $this->archiveProcessor->insertNumericRecords($metrics);
236 return $metrics;
237 }
238 protected function convertMetricsIdToName($data)
239 {
240 $metrics = array();
241 foreach ($data as $metricId => $value) {
242 $readableMetric = Metrics::$mappingFromIdToName[$metricId];
243 $metrics[$readableMetric] = $value;
244 }
245 return $metrics;
246 }
247 protected function aggregateMultipleVisitsMetrics()
248 {
249 $toSum = Metrics::getVisitsMetricNames();
250 $metrics = $this->archiveProcessor->aggregateNumericMetrics($toSum);
251 return $metrics;
252 }
253 /**
254 * @param $archiverClass
255 * @return Archiver
256 */
257 private function makeNewArchiverObject($archiverClass, $pluginName)
258 {
259 if ($archiverClass === Archiver::class) {
260 $archiver = new Archiver($this->archiveProcessor, $pluginName);
261 } else {
262 $archiver = new $archiverClass($this->archiveProcessor);
263 }
264 /**
265 * Triggered right after a new **plugin archiver instance** is created.
266 * Subscribers to this event can configure the plugin archiver, for example prevent the archiving of a plugin's data
267 * by calling `$archiver->disable()` method.
268 *
269 * @param \Piwik\Plugin\Archiver &$archiver The newly created plugin archiver instance.
270 * @param string $pluginName The name of plugin of which archiver instance was created.
271 * @param array $this->params Array containing archive parameters (Site, Period, Date and Segment)
272 * @param bool false This parameter is deprecated and will be removed.
273 */
274 Piwik::postEvent('Archiving.makeNewArchiverObject', array($archiver, $pluginName, $this->params, false));
275 return $archiver;
276 }
277 public static function isArchivingProcessActive()
278 {
279 return self::$currentPluginBeingArchived !== null;
280 }
281 }
282