|
Server : Apache System : Linux s1230 5.15.0-139-generic #149~20.04.1 SMP Tue Jul 14 11:21:49 UTC 2026 x86_64 User : p141464 ( 418825) PHP Version : 7.4.33.12 Disable Function : NONE Directory : /html/KMU-CSR-PLANER/wp-content/plugins/matomo/app/plugins/ArchivingMetrics/ |
Upload File : |
<?php
/**
* Matomo - free/libre analytics platform
*
* @link https://matomo.org
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
declare (strict_types=1);
namespace Piwik\Plugins\ArchivingMetrics;
use Piwik\ArchiveProcessor\Rules;
use Piwik\Date;
use Piwik\Plugins\ArchivingMetrics\Clock\Clock;
use Piwik\Plugins\ArchivingMetrics\Clock\ClockInterface;
use Piwik\Plugins\ArchivingMetrics\Writer\DbWriter;
use Piwik\Plugins\ArchivingMetrics\Writer\WriterInterface;
final class Timer
{
/**
* @var bool
*/
private $isArchivePhpTriggered;
/**
* @var ClockInterface
*/
private $clock;
/**
* @var WriterInterface
*/
private $writer;
/**
* @var array<string, array<string, mixed>>
*/
private $runs = [];
/**
* @var ?Timer
*/
private static $instance;
public function __construct(bool $isArchivePhpTriggered, ClockInterface $clock, WriterInterface $writer)
{
$this->isArchivePhpTriggered = $isArchivePhpTriggered;
$this->clock = $clock;
$this->writer = $writer;
}
public static function getInstance(bool $isArchivePhpTriggered, ?ClockInterface $clock = null, ?WriterInterface $writer = null) : self
{
if (self::$instance !== null) {
return self::$instance;
}
if ($clock === null) {
$clock = new Clock();
}
if ($writer === null) {
$writer = new DbWriter();
}
self::$instance = new self($isArchivePhpTriggered, $clock, $writer);
return self::$instance;
}
/**
* @internal For tests only.
*/
public static function resetInstanceForTests() : void
{
self::$instance = null;
}
public function start(\Piwik\Plugins\ArchivingMetrics\Context $context) : void
{
if (\false === $this->isApplicableForTiming($context)) {
return;
}
$this->runs[$context->getKey()] = ['context' => $context, 'timeStarted' => $this->clock->microtime()];
}
public function complete(\Piwik\Plugins\ArchivingMetrics\Context $context, array $idArchives, bool $wasCached) : void
{
if (\false === $this->isApplicableForTiming($context)) {
return;
}
if (\true === $wasCached) {
return;
}
if (empty($idArchives) || count($idArchives) !== 1) {
return;
}
$idArchive = reset($idArchives);
$key = $context->getKey();
if (!isset($this->runs[$key]['timeStarted'])) {
return;
}
$finishedAt = $this->clock->microtime();
$totalTimeMs = $finishedAt - $this->runs[$key]['timeStarted'];
$this->runs[$key]['ts_started'] = date('Y-m-d H:i:s', (int) $this->runs[$key]['timeStarted']);
$this->runs[$key]['totalTime'] = $totalTimeMs;
$this->runs[$key]['timeFinished'] = $finishedAt;
$this->runs[$key]['ts_finished'] = date('Y-m-d H:i:s', (int) $this->runs[$key]['timeFinished']);
$exclusiveTimeMs = $this->calculateExclusiveTime($key);
$this->runs[$key]['exclusiveTime'] = $exclusiveTimeMs;
/** @var Context $storedContext */
$storedContext = $this->runs[$key]['context'];
$archiveName = Rules::getDoneStringFlagFor([$storedContext->idSite], $storedContext->segment, $storedContext->period->getLabel(), $storedContext->plugin);
$this->writer->write($storedContext, ['idarchive' => $idArchive, 'archive_name' => $archiveName, 'ts_started' => $this->runs[$key]['ts_started'], 'ts_finished' => $this->runs[$key]['ts_finished'], 'total_time' => (int) round($totalTimeMs * 1000), 'total_time_exclusive' => (int) round($exclusiveTimeMs * 1000), 'is_temporary' => $this->isTemporaryArchive($storedContext, (int) $this->runs[$key]['timeStarted'])]);
}
private function isTemporaryArchive(\Piwik\Plugins\ArchivingMetrics\Context $context, int $archiveStartTimestamp) : int
{
$archiveStartInSiteTimezone = Date::factory($archiveStartTimestamp, $context->siteTimezone);
$periodEndInSiteTimezone = $context->period->getDateTimeEnd()->setTimezone($context->siteTimezone);
return (int) $archiveStartInSiteTimezone->isEarlier($periodEndInSiteTimezone);
}
private function isApplicableForTiming(\Piwik\Plugins\ArchivingMetrics\Context $context) : bool
{
if (\false === $this->isArchivePhpTriggered) {
return \false;
}
$doneFlag = Rules::getDoneStringFlagFor([$context->idSite], $context->segment, $context->period->getLabel(), $context->plugin);
if (strpos($doneFlag, '.') !== \false) {
return \false;
}
return \true;
}
private function calculateExclusiveTime(string $currentKey) : float
{
if (empty($this->runs[$currentKey])) {
return 0.0;
}
$current = $this->runs[$currentKey];
$totalTimeMs = $current['totalTime'];
// If the key is last in the array then it's probably not a nested archive so this calculation doesn't matter
if ($currentKey === array_key_last($this->runs)) {
return $totalTimeMs;
}
$childTotalMs = 0.0;
foreach ($this->runs as $otherKey => $run) {
if ($otherKey === $currentKey) {
continue;
}
if (empty($run['exclusiveTime'])) {
continue;
}
$childFinished = $run['timeFinished'] ?? null;
$childStarted = $run['timeStarted'] ?? null;
if ($childFinished === null || $childStarted === null) {
continue;
}
if ($childFinished <= $current['timeFinished'] && $childStarted >= $current['timeStarted']) {
$childTotalMs += $run['exclusiveTime'];
}
}
$exclusive = $totalTimeMs - $childTotalMs;
if ($exclusive < 0) {
return $totalTimeMs;
}
return $exclusive;
}
}