TenantAtlas/app/Support/Baselines/BaselineScope.php
Ahmed Darrazi 74ab2d1404 feat: Phase 2 foundational - capabilities, migrations, models, factories, badges, support classes
T003-T018b: Add workspace_baselines.view/manage capabilities, role mappings,
baseline_capture/baseline_compare operation labels, severity summary keys,
5 migrations, 4 models, 4 factories, BaselineScope, BaselineReasonCodes,
BaselineProfileStatus badge domain + mapper.
2026-02-19 14:15:46 +01:00

114 lines
2.8 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Support\Baselines;
/**
* Value object for baseline scope resolution.
*
* A scope defines which policy types are included in a baseline profile.
* An empty policy_types array means "all types" (no filter).
*/
final class BaselineScope
{
/**
* @param array<string> $policyTypes
*/
public function __construct(
public readonly array $policyTypes = [],
) {}
/**
* Create from the scope_jsonb column value.
*
* @param array<string, mixed>|null $scopeJsonb
*/
public static function fromJsonb(?array $scopeJsonb): self
{
if ($scopeJsonb === null) {
return new self;
}
$policyTypes = $scopeJsonb['policy_types'] ?? [];
return new self(
policyTypes: is_array($policyTypes) ? array_values(array_filter($policyTypes, 'is_string')) : [],
);
}
/**
* Normalize the effective scope by intersecting profile scope with an optional override.
*
* Override can only narrow the profile scope (subset enforcement).
* If the profile scope is empty (all types), the override becomes the effective scope.
* If the override is empty or null, the profile scope is used as-is.
*/
public static function effective(self $profileScope, ?self $overrideScope): self
{
if ($overrideScope === null || $overrideScope->isEmpty()) {
return $profileScope;
}
if ($profileScope->isEmpty()) {
return $overrideScope;
}
$intersected = array_values(array_intersect($profileScope->policyTypes, $overrideScope->policyTypes));
return new self(policyTypes: $intersected);
}
/**
* An empty scope means "all types".
*/
public function isEmpty(): bool
{
return $this->policyTypes === [];
}
/**
* Check if a policy type is included in this scope.
*/
public function includes(string $policyType): bool
{
if ($this->isEmpty()) {
return true;
}
return in_array($policyType, $this->policyTypes, true);
}
/**
* Validate that override is a subset of the profile scope.
*/
public static function isValidOverride(self $profileScope, self $overrideScope): bool
{
if ($overrideScope->isEmpty()) {
return true;
}
if ($profileScope->isEmpty()) {
return true;
}
foreach ($overrideScope->policyTypes as $type) {
if (! in_array($type, $profileScope->policyTypes, true)) {
return false;
}
}
return true;
}
/**
* @return array<string, mixed>
*/
public function toJsonb(): array
{
return [
'policy_types' => $this->policyTypes,
];
}
}