<?php
 declare(strict_types=1); namespace SpomkyLabs\Pki\X509\Certificate\Extension; use ArrayIterator; use Countable; use IteratorAggregate; use LogicException; use SpomkyLabs\Pki\ASN1\Element; use SpomkyLabs\Pki\ASN1\Type\Constructed\Sequence; use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType; use SpomkyLabs\Pki\X509\Certificate\Extension\CertificatePolicy\PolicyInformation; use SpomkyLabs\Pki\X509\Certificate\Extension\PolicyMappings\PolicyMapping; use UnexpectedValueException; use function count; final class PolicyMappingsExtension extends Extension implements Countable, IteratorAggregate { private readonly array $mappings; private function __construct(bool $critical, PolicyMapping ...$mappings) { parent::__construct(self::OID_POLICY_MAPPINGS, $critical); $this->mappings = $mappings; } public static function create(bool $critical, PolicyMapping ...$mappings): self { return new self($critical, ...$mappings); } public function mappings(): array { return $this->mappings; } public function flattenedMappings(): array { $mappings = []; foreach ($this->mappings as $mapping) { $idp = $mapping->issuerDomainPolicy(); if (! isset($mappings[$idp])) { $mappings[$idp] = []; } array_push($mappings[$idp], $mapping->subjectDomainPolicy()); } return $mappings; } public function issuerMappings(string $oid): array { $oids = []; foreach ($this->mappings as $mapping) { if ($mapping->issuerDomainPolicy() === $oid) { $oids[] = $mapping->subjectDomainPolicy(); } } return $oids; } public function issuerDomainPolicies(): array { $idps = array_map(static fn (PolicyMapping $mapping) => $mapping->issuerDomainPolicy(), $this->mappings); return array_values(array_unique($idps)); } public function hasAnyPolicyMapping(): bool { foreach ($this->mappings as $mapping) { if ($mapping->issuerDomainPolicy() === PolicyInformation::OID_ANY_POLICY) { return true; } if ($mapping->subjectDomainPolicy() === PolicyInformation::OID_ANY_POLICY) { return true; } } return false; } public function count(): int { return count($this->mappings); } public function getIterator(): ArrayIterator { return new ArrayIterator($this->mappings); } protected static function fromDER(string $data, bool $critical): static { $mappings = array_map( static fn (UnspecifiedType $el) => PolicyMapping::fromASN1($el->asSequence()), UnspecifiedType::fromDER($data)->asSequence()->elements() ); if (count($mappings) === 0) { throw new UnexpectedValueException('PolicyMappings must have at least one mapping.'); } return self::create($critical, ...$mappings); } protected function valueASN1(): Element { if (count($this->mappings) === 0) { throw new LogicException('No mappings.'); } $elements = array_map(static fn (PolicyMapping $mapping) => $mapping->toASN1(), $this->mappings); return Sequence::create(...$elements); } } 