<?php
declare(strict_types=1);
namespace ReallySimplePlugins\RSS\Core\Features\Vulnerability\Repositories;
use ReallySimplePlugins\RSS\Core\Features\Vulnerability\Dtos\VulnerabilitySnapshotDto;
/**
* Persists and reads the latest vulnerability snapshot from WordPress option storage.
*
* Constraints: No external I/O and no business rules beyond basic shape validation.
*/
final class VulnerabilitySnapshotRepository
{
private const OPTION_KEY = 'rsssl_vulnerability_snapshot';
public function getLatest(): ?VulnerabilitySnapshotDto
{
$raw = get_option(self::OPTION_KEY, null);
if (!is_array($raw)) {
return null;
}
return VulnerabilitySnapshotDto::fromArray($raw);
}
public function storeLatest(VulnerabilitySnapshotDto $vulnerabilitySnapshotDto): void
{
$this->save($vulnerabilitySnapshotDto);
}
public function save(VulnerabilitySnapshotDto $vulnerabilitySnapshotDto): void
{
update_option(self::OPTION_KEY, $vulnerabilitySnapshotDto->toArray(), false);
}
public function clear(): void
{
delete_option(self::OPTION_KEY);
}
/**
* Builds a snapshot from the repository's aggregated snapshot data.
*/
public function buildCurrentSnapshot(VulnerabilityStorageRepository $vulnerabilityStorageRepository): VulnerabilitySnapshotDto
{
$data = $vulnerabilityStorageRepository->getSnapshotData();
$updatableComponents = (int)($data['updatableComponents'] ?? 0);
$vulnerableCount = (int)($data['vulnerablePlugins'] ?? 0);
$highestSeverity = isset($data['highestSeverity']) && is_string($data['highestSeverity'])
? $data['highestSeverity']
: null;
$severityScore = (int)($data['severityScore'] ?? 0);
return new VulnerabilitySnapshotDto(
$highestSeverity,
$severityScore,
$vulnerableCount,
$updatableComponents,
time()
);
}
public function buildFromStorage(
VulnerabilityStorageRepository $vulnerabilityStorageRepository
): VulnerabilitySnapshotDto {
return $this->buildCurrentSnapshot($vulnerabilityStorageRepository);
}
}