<?php
 declare(strict_types=1); namespace SpomkyLabs\Pki\X501\ASN1; use ArrayIterator; use Countable; use IteratorAggregate; use SpomkyLabs\Pki\ASN1\Type\Constructed\Set; use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType; use SpomkyLabs\Pki\X501\ASN1\AttributeValue\AttributeValue; use Stringable; use UnexpectedValueException; use function count; final class RDN implements Countable, IteratorAggregate, Stringable { private readonly array $_attribs; private function __construct(AttributeTypeAndValue ...$attribs) { if (count($attribs) === 0) { throw new UnexpectedValueException('RDN must have at least one AttributeTypeAndValue.'); } $this->_attribs = $attribs; } public function __toString(): string { return $this->toString(); } public static function create(AttributeTypeAndValue ...$attribs): self { return new self(...$attribs); } public static function fromAttributeValues(AttributeValue ...$values): self { $attribs = array_map( static fn (AttributeValue $value) => AttributeTypeAndValue::create(AttributeType::create( $value->oid() ), $value), $values ); return self::create(...$attribs); } public static function fromASN1(Set $set): self { $attribs = array_map( static fn (UnspecifiedType $el) => AttributeTypeAndValue::fromASN1($el->asSequence()), $set->elements() ); return self::create(...$attribs); } public function toASN1(): Set { $elements = array_map(static fn (AttributeTypeAndValue $tv) => $tv->toASN1(), $this->_attribs); return Set::create(...$elements)->sortedSetOf(); } public function toString(): string { $parts = array_map(static fn (AttributeTypeAndValue $tv) => $tv->toString(), $this->_attribs); return implode('+', $parts); } public function equals(self $other): bool { if (count($this) !== count($other)) { return false; } $attribs1 = $this->_attribs; $attribs2 = $other->_attribs; if (count($attribs1) > 1) { $attribs1 = self::fromASN1($this->toASN1())->_attribs; $attribs2 = self::fromASN1($other->toASN1())->_attribs; } for ($i = count($attribs1) - 1; $i >= 0; --$i) { $tv1 = $attribs1[$i]; $tv2 = $attribs2[$i]; if (! $tv1->equals($tv2)) { return false; } } return true; } public function all(): array { return $this->_attribs; } public function allOf(string $name): array { $oid = AttributeType::attrNameToOID($name); $attribs = array_filter($this->_attribs, static fn (AttributeTypeAndValue $tv) => $tv->oid() === $oid); return array_values($attribs); } public function count(): int { return count($this->_attribs); } public function getIterator(): ArrayIterator { return new ArrayIterator($this->_attribs); } } 