oid() !== $type->oid()) { throw new LogicException('Attribute OID mismatch.'); } } $this->type = $type; $this->values = $values; } public static function create(AttributeType $type, AttributeValue ...$values): self { return new self($type, ...$values); } /** * Initialize from ASN.1. */ public static function fromASN1(Sequence $seq): self { $type = AttributeType::fromASN1($seq->at(0)->asObjectIdentifier()); $values = array_map( static fn (UnspecifiedType $el) => AttributeValue::fromASN1ByOID($type->oid(), $el), $seq->at(1) ->asSet() ->elements() ); return self::create($type, ...$values); } /** * Convenience method to initialize from attribute values. * * @param AttributeValue ...$values One or more values */ public static function fromAttributeValues(AttributeValue ...$values): self { // we need at least one value to determine OID if (count($values) === 0) { throw new LogicException('No values.'); } $oid = reset($values) ->oid(); return self::create(AttributeType::create($oid), ...$values); } /** * Get first value of the attribute. */ public function first(): AttributeValue { if (count($this->values) === 0) { throw new LogicException('Attribute contains no values.'); } return $this->values[0]; } /** * Get all values. * * @return AttributeValue[] */ public function values(): array { return $this->values; } /** * Generate ASN.1 structure. */ public function toASN1(): Sequence { $values = array_map(static fn (AttributeValue $value) => $value->toASN1(), $this->values); $valueset = Set::create(...$values); return Sequence::create($this->type->toASN1(), $valueset->sortedSetOf()); } /** * Cast attribute values to another AttributeValue class. * * This method is generally used to cast UnknownAttributeValue values to specific objects when class is declared * outside this package. * * The new class must be derived from AttributeValue and have the same OID as current attribute values. * * @param string $cls AttributeValue class name */ public function castValues(string $cls): self { // check that target class derives from AttributeValue if (! is_subclass_of($cls, AttributeValue::class)) { throw new LogicException(sprintf('%s must be derived from %s.', $cls, AttributeValue::class)); } $values = array_map( function (AttributeValue $value) use ($cls) { /** @var AttributeValue $cls Class name as a string */ $value = $cls::fromSelf($value); if ($value->oid() !== $this->oid()) { throw new LogicException('Attribute OID mismatch.'); } return $value; }, $this->values ); return self::fromAttributeValues(...$values); } /** * @see \Countable::count() */ public function count(): int { return count($this->values); } /** * @see \IteratorAggregate::getIterator() */ public function getIterator(): ArrayIterator { return new ArrayIterator($this->values); } /** * Get attribute type. */ public function type(): AttributeType { return $this->type; } /** * Get OID of the attribute. */ public function oid(): string { return $this->type->oid(); } }