<?php
 declare(strict_types=1); namespace SpomkyLabs\Pki\X501\ASN1\Collection; use ArrayIterator; use Countable; use IteratorAggregate; use SpomkyLabs\Pki\ASN1\Type\Structure; use SpomkyLabs\Pki\ASN1\Type\UnspecifiedType; use SpomkyLabs\Pki\X501\ASN1\Attribute; use SpomkyLabs\Pki\X501\ASN1\AttributeType; use UnexpectedValueException; use function count; abstract class AttributeCollection implements Countable, IteratorAggregate { protected array $_attributes; final private function __construct(Attribute ...$attribs) { $this->_attributes = $attribs; } public static function create(Attribute ...$attribs): static { return new static(...$attribs); } public function has(string $name): bool { return $this->_findFirst($name) !== null; } public function firstOf(string $name): Attribute { $attr = $this->_findFirst($name); if ($attr === null) { throw new UnexpectedValueException("No {$name} attribute."); } return $attr; } public function allOf(string $name): array { $oid = AttributeType::attrNameToOID($name); return array_values(array_filter($this->_attributes, fn (Attribute $attr) => $attr->oid() === $oid)); } public function all(): array { return $this->_attributes; } public function withAdditional(Attribute ...$attribs): self { $obj = clone $this; foreach ($attribs as $attr) { $obj->_attributes[] = $attr; } return $obj; } public function withUnique(Attribute $attr): static { $attribs = array_values(array_filter($this->_attributes, fn (Attribute $a) => $a->oid() !== $attr->oid())); $attribs[] = $attr; $obj = clone $this; $obj->_attributes = $attribs; return $obj; } public function count(): int { return count($this->_attributes); } public function getIterator(): ArrayIterator { return new ArrayIterator($this->_attributes); } protected function _findFirst(string $name): ?Attribute { $oid = AttributeType::attrNameToOID($name); foreach ($this->_attributes as $attr) { if ($attr->oid() === $oid) { return $attr; } } return null; } protected static function _fromASN1Structure(Structure $struct): static { return static::create(...array_map( static fn (UnspecifiedType $el) => static::_castAttributeValues( Attribute::fromASN1($el->asSequence()) ), $struct->elements() )); } protected static function _castAttributeValues(Attribute $attribute): Attribute { return $attribute; } } 