<?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; } }