<?php
 declare(strict_types=1); namespace SpomkyLabs\Pki\X501\ASN1; use ArrayIterator; use Countable; use IteratorAggregate; use RangeException; use SpomkyLabs\Pki\ASN1\Element; use SpomkyLabs\Pki\ASN1\Type\Constructed\Sequence; use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType; use SpomkyLabs\Pki\X501\ASN1\AttributeValue\AttributeValue; use SpomkyLabs\Pki\X501\DN\DNParser; use Stringable; use function count; final class Name implements Countable, IteratorAggregate, Stringable { private readonly array $rdns; private function __construct(RDN ...$rdns) { $this->rdns = $rdns; } public function __toString(): string { return $this->toString(); } public static function create(RDN ...$rdns): self { return new self(...$rdns); } public static function fromASN1(Sequence $seq): self { $rdns = array_map(static fn (UnspecifiedType $el) => RDN::fromASN1($el->asSet()), $seq->elements()); return self::create(...$rdns); } public static function fromString(string $str): self { $rdns = []; foreach (DNParser::parseString($str) as $nameComponent) { $attribs = []; foreach ($nameComponent as [$name, $val]) { $type = AttributeType::fromName($name); if ($val instanceof Element) { $el = $val; } else { $el = AttributeType::asn1StringForType($type->oid(), $val); } $value = AttributeValue::fromASN1ByOID($type->oid(), $el->asUnspecified()); $attribs[] = AttributeTypeAndValue::create($type, $value); } $rdns[] = RDN::create(...$attribs); } return self::create(...$rdns); } public function toASN1(): Sequence { $elements = array_map(static fn (RDN $rdn) => $rdn->toASN1(), $this->rdns); return Sequence::create(...$elements); } public function toString(): string { $parts = array_map(static fn (RDN $rdn) => $rdn->toString(), array_reverse($this->rdns)); return implode(',', $parts); } public function equals(self $other): bool { if (count($this) !== count($other)) { return false; } for ($i = count($this) - 1; $i >= 0; --$i) { $rdn1 = $this->rdns[$i]; $rdn2 = $other->rdns[$i]; if (! $rdn1->equals($rdn2)) { return false; } } return true; } public function all(): array { return $this->rdns; } public function firstValueOf(string $name): AttributeValue { $oid = AttributeType::attrNameToOID($name); foreach ($this->rdns as $rdn) { $tvs = $rdn->allOf($oid); if (count($tvs) > 1) { throw new RangeException("RDN with multiple {$name} attributes."); } if (count($tvs) === 1) { return $tvs[0]->value(); } } throw new RangeException("Attribute {$name} not found."); } public function count(): int { return count($this->rdns); } public function countOfType(string $name): int { $oid = AttributeType::attrNameToOID($name); return array_sum(array_map(static fn (RDN $rdn): int => count($rdn->allOf($oid)), $this->rdns)); } public function getIterator(): ArrayIterator { return new ArrayIterator($this->rdns); } } 