params = $params; $this->archiveWriter = $archiveWriter ?: new ArchiveWriter($this->params); $this->archiveWriter->initNewArchive(); $this->logAggregator = new LogAggregator($params); $this->logAggregator->allowUsageSegmentCache(); $this->archiveProcessor = new ArchiveProcessor($this->params, $this->archiveWriter, $this->logAggregator); $shouldAggregateFromRawData = $this->params->isSingleSiteDayArchive(); /** * Triggered to detect if the archiver should aggregate from raw data by using MySQL queries (when true) * or by aggregate archives (when false). Typically, data is aggregated from raw data for "day" period, and * aggregregated from archives for all other periods. * * @param bool $shouldAggregateFromRawData Set to true, to aggregate from raw data, or false to aggregate multiple reports. * @param Parameters $params */ Piwik::postEvent('ArchiveProcessor.shouldAggregateFromRawData', array(&$shouldAggregateFromRawData, $this->params)); $this->shouldAggregateFromRawData = $shouldAggregateFromRawData; } /** * If period is day, will get the core metrics (including visits) from the logs. * If period is != day, will sum the core metrics from the existing archives. * @return array Core metrics */ public function callAggregateCoreMetrics() { $this->logAggregator->cleanup(); $this->logAggregator->setQueryOriginHint('Core'); if ($this->shouldAggregateFromRawData) { $metrics = $this->aggregateDayVisitsMetrics(); } else { $metrics = $this->aggregateMultipleVisitsMetrics(); } if (empty($metrics)) { return array( 'nb_visits' => false, 'nb_visits_converted' => false ); } return array( 'nb_visits' => $metrics['nb_visits'], 'nb_visits_converted' => $metrics['nb_visits_converted'] ); } /** * Instantiates the Archiver class in each plugin that defines it, * and triggers Aggregation processing on these plugins. */ public function callAggregateAllPlugins($visits, $visitsConverted, $forceArchivingWithoutVisits = false) { Log::debug("PluginsArchiver::%s: Initializing archiving process for all plugins [visits = %s, visits converted = %s]", __FUNCTION__, $visits, $visitsConverted); /** @var Logger $performanceLogger */ $performanceLogger = StaticContainer::get(Logger::class); $this->archiveProcessor->setNumberOfVisits($visits, $visitsConverted); $archivers = static::getPluginArchivers(); foreach ($archivers as $pluginName => $archiverClass) { // We clean up below all tables created during this function call (and recursive calls) $latestUsedTableId = Manager::getInstance()->getMostRecentTableId(); /** @var Archiver $archiver */ $archiver = $this->makeNewArchiverObject($archiverClass, $pluginName); if (!$archiver->isEnabled()) { Log::debug("PluginsArchiver::%s: Skipping archiving for plugin '%s' (disabled).", __FUNCTION__, $pluginName); continue; } if (!$forceArchivingWithoutVisits && !$visits && !$archiver->shouldRunEvenWhenNoVisits()) { Log::debug("PluginsArchiver::%s: Skipping archiving for plugin '%s' (no visits).", __FUNCTION__, $pluginName); continue; } if ($this->shouldProcessReportsForPlugin($pluginName)) { $this->logAggregator->setQueryOriginHint($pluginName); try { self::$currentPluginBeingArchived = $pluginName; $period = $this->params->getPeriod()->getLabel(); $timer = new Timer(); if ($this->shouldAggregateFromRawData) { Log::debug("PluginsArchiver::%s: Archiving $period reports for plugin '%s' from raw data.", __FUNCTION__, $pluginName); $archiver->callAggregateDayReport(); } else { Log::debug("PluginsArchiver::%s: Archiving $period reports for plugin '%s' using reports for smaller periods.", __FUNCTION__, $pluginName); $archiver->callAggregateMultipleReports(); } $this->logAggregator->setQueryOriginHint(''); $performanceLogger->logMeasurement('plugin', $pluginName, $this->params, $timer); 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()) : '' ); } catch (Exception $e) { throw new PluginsArchiverException($e->getMessage() . " - in plugin $pluginName.", $e->getCode(), $e); } finally { self::$currentPluginBeingArchived = null; } } else { Log::debug("PluginsArchiver::%s: Not archiving reports for plugin '%s'.", __FUNCTION__, $pluginName); } Manager::getInstance()->deleteAll($latestUsedTableId); unset($archiver); } $this->logAggregator->cleanup(); } public function finalizeArchive() { $this->params->logStatusDebug(); $this->archiveWriter->finalizeArchive(); $idArchive = $this->archiveWriter->getIdArchive(); return $idArchive; } /** * Returns if any plugin archiver archives without visits */ public static function doesAnyPluginArchiveWithoutVisits() { $archivers = static::getPluginArchivers(); foreach ($archivers as $pluginName => $archiverClass) { if ($archiverClass::shouldRunEvenWhenNoVisits()) { return true; } } return false; } /** * Loads Archiver class from any plugin that defines one. * * @return \Piwik\Plugin\Archiver[] */ protected static function getPluginArchivers() { if (empty(static::$archivers)) { $pluginNames = \Piwik\Plugin\Manager::getInstance()->getActivatedPlugins(); $archivers = array(); foreach ($pluginNames as $pluginName) { $archivers[$pluginName] = self::getPluginArchiverClass($pluginName); } static::$archivers = array_filter($archivers); } return static::$archivers; } private static function getPluginArchiverClass($pluginName) { $klassName = 'Piwik\\Plugins\\' . $pluginName . '\\Archiver'; if (class_exists($klassName) && is_subclass_of($klassName, 'Piwik\\Plugin\\Archiver')) { return $klassName; } return false; } /** * Whether the specified plugin's reports should be archived * @param string $pluginName * @return bool */ protected function shouldProcessReportsForPlugin($pluginName) { if ($this->params->getRequestedPlugin() == $pluginName) { return true; } if ($this->params->shouldOnlyArchiveRequestedPlugin()) { return false; } if (Rules::shouldProcessReportsAllPlugins( array($this->params->getSite()->getId()), $this->params->getSegment(), $this->params->getPeriod()->getLabel()) ) { return true; } if ($this->params->getRequestedPlugin() && !\Piwik\Plugin\Manager::getInstance()->isPluginLoaded($this->params->getRequestedPlugin()) ) { return false; } return false; } protected function aggregateDayVisitsMetrics() { $query = $this->archiveProcessor->getLogAggregator()->queryVisitsByDimension(); $data = $query->fetch(); $metrics = $this->convertMetricsIdToName($data); $this->archiveProcessor->insertNumericRecords($metrics); return $metrics; } protected function convertMetricsIdToName($data) { $metrics = array(); foreach ($data as $metricId => $value) { $readableMetric = Metrics::$mappingFromIdToName[$metricId]; $metrics[$readableMetric] = $value; } return $metrics; } protected function aggregateMultipleVisitsMetrics() { $toSum = Metrics::getVisitsMetricNames(); $metrics = $this->archiveProcessor->aggregateNumericMetrics($toSum); return $metrics; } /** * @param $archiverClass * @return Archiver */ private function makeNewArchiverObject($archiverClass, $pluginName) { $archiver = new $archiverClass($this->archiveProcessor); /** * Triggered right after a new **plugin archiver instance** is created. * Subscribers to this event can configure the plugin archiver, for example prevent the archiving of a plugin's data * by calling `$archiver->disable()` method. * * @param \Piwik\Plugin\Archiver &$archiver The newly created plugin archiver instance. * @param string $pluginName The name of plugin of which archiver instance was created. * @param array $this->params Array containing archive parameters (Site, Period, Date and Segment) * @param bool false This parameter is deprecated and will be removed. */ Piwik::postEvent('Archiving.makeNewArchiverObject', array($archiver, $pluginName, $this->params, false)); return $archiver; } public static function isArchivingProcessActive() { return self::$currentPluginBeingArchived !== null; } }