<?php
declare(strict_types=1);
namespace ReallySimplePlugins\RSS\Core\Features\Vulnerability\Dtos;
use ReallySimplePlugins\RSS\Core\Support\Helpers\Storage;
/**
* Immutable-style data transfer object representing all vulnerabilities
* for a single installed component.
*
* A component can be a plugin, theme, or WordPress core. This DTO groups:
* - Component identity (name, slug, type).
* - Known vulnerability ranges affecting this component.
* - Runtime-enriched data such as installed version, update availability,
* and latest known version.
*
* It is primarily used to:
* - Transport vulnerability data between API, storage, and domain layers.
* - Enrich vulnerability data with local installation context.
*
* This object holds state only:
* - No persistence logic
* - No policy or decision logic
* - No scheduling or WordPress hook concerns
*/
final class ComponentVulnerabilitiesDto
{
/**
* Human-readable component name.
*/
private string $name;
/**
* Unique component identifier used for lookups.
*/
private string $slug;
/**
* Component type (`plugin`, `theme`, or `core`).
*/
private string $type;
/**
* Latest known available version (null if unknown).
*/
private ?string $latestVersion = null;
/**
* List of vulnerability ranges affecting this component.
*
* @var list<VulnerabilityRangeDto>
*/
public array $vulnerabilities = [];
/**
* Currently installed version on the site.
*/
public string $installedVersion = '';
/**
* Whether an update is available for the installed version.
*/
private ?bool $updateAvailable = null;
/**
* Constructor.
*
* @param string $name Component name from the API.
* @param string $slug Component slug from the API.
* @param string $type Component type from the API.
* @param list<VulnerabilityRangeDto> $vulnerabilities Vulnerability ranges from the API.
*
* Runtime fields like installed version and update availability
* are populated later via setters.
*/
public function __construct(
string $name,
string $slug,
string $type,
array $vulnerabilities
) {
$this->vulnerabilities = $vulnerabilities;
$this->type = $type;
$this->slug = $slug;
$this->name = $name;
}
/**
* Normalize a raw API component payload into a strongly typed DTO.
*
* Invalid or unexpected vulnerability entries are skipped defensively.
*
* @param array<string, mixed> $payload Raw API component data.
*
* @return self
*/
public static function fromApiComponentArray(array $payload): self
{
$storage = new Storage($payload);
$name = $storage->getString('name');
$slug = $storage->getString('slug');
$type = strtolower($storage->getString('type'));
$vulnerabilities = [];
$rawVulnerabilities = $payload['vulnerabilities'] ?? [];
if (is_array($rawVulnerabilities)) {
foreach ($rawVulnerabilities as $rawVulnerability) {
if (!is_array($rawVulnerability)) {
continue;
}
$vulnerabilities[] = VulnerabilityRangeDto::fromApiArray($rawVulnerability);
}
}
return new self(
$name,
$slug,
$type,
$vulnerabilities,
);
}
/**
* Return a storage/serialization-friendly representation of this component and its vulnerabilities.
*
* Nested vulnerabilities are converted using their own DTOs.
*
* @return array{
* name: string,
* slug: string,
* type: string,
* latestVersion: string|null,
* vulnerabilities: list<array<string, mixed>>
* }
*/
public function toArray(): array
{
$list = [];
foreach ($this->vulnerabilities as $vulnerability) {
$list[] = $vulnerability->toArray();
}
return [
'name' => $this->name,
'slug' => $this->slug,
'type' => $this->type,
'latestVersion' => $this->latestVersion,
'vulnerabilities' => $list,
];
}
/**
* Set the latest known available version for this component.
*
* Used to enrich the DTO with runtime update information.
*/
public function setLatestVersion(?string $latestVersion): void
{
$this->latestVersion = $latestVersion;
}
/**
* Get a unique storage key for this component.
*
* Combines type and slug for indexing.
*/
public function getStorageKey(): string
{
return $this->type . ':' . $this->slug;
}
/**
* Update the component type.
*
* Used to enrich the DTO with runtime data.
*/
public function setType(string $type): void
{
$this->type = $type;
}
/**
* Update the component slug.
*
* Used to enrich the DTO with runtime data.
*/
public function setSlug(string $installedSlug): void
{
$this->slug = $installedSlug;
}
/**
* Update the component name.
*
* Used to enrich the DTO with runtime data.
*/
public function setName(string $installedName): void
{
$this->name = $installedName;
}
/**
* Set the currently installed version on the site.
*
* Used to enrich the DTO with runtime installation data.
*/
public function setInstalledVersion(string $installedVersion): void
{
$this->installedVersion = $installedVersion;
}
/**
* Set whether an update is available for the installed version.
*
* Used to enrich the DTO with runtime update status.
*/
public function setUpdateAvailable(?bool $updateAvailable): void
{
$this->updateAvailable = $updateAvailable;
}
}