Attribute   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 157
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 15
eloc 41
dl 0
loc 157
ccs 45
cts 45
cp 1
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A first() 0 6 2
A castValues() 0 17 3
A values() 0 3 1
A toASN1() 0 8 1
A getIterator() 0 3 1
A count() 0 3 1
A fromASN1() 0 8 1
A __construct() 0 10 3
A fromAttributeValues() 0 8 2
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Sop\X501\ASN1;
6
7
use Sop\ASN1\Type\Constructed\Sequence;
8
use Sop\ASN1\Type\Constructed\Set;
9
use Sop\ASN1\Type\UnspecifiedType;
10
use Sop\X501\ASN1\AttributeValue\AttributeValue;
11
use Sop\X501\ASN1\Feature\TypedAttribute;
12
13
/**
14
 * Implements *Attribute* ASN.1 type.
15
 *
16
 * @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x501/2012/InformationFramework.html#InformationFramework.Attribute
17
 */
18
class Attribute implements \Countable, \IteratorAggregate
19
{
20
    use TypedAttribute;
21
22
    /**
23
     * Attribute values.
24
     *
25
     * @var AttributeValue[]
26
     */
27
    protected $_values;
28
29
    /**
30
     * Constructor.
31
     *
32
     * @param AttributeType  $type      Attribute type
33
     * @param AttributeValue ...$values Attribute values
34
     */
35 14
    public function __construct(AttributeType $type, AttributeValue ...$values)
36
    {
37
        // check that attribute values have correct oid
38 14
        foreach ($values as $value) {
39 13
            if ($value->oid() !== $type->oid()) {
40 13
                throw new \LogicException('Attribute OID mismatch.');
41
            }
42
        }
43 13
        $this->_type = $type;
44 13
        $this->_values = $values;
45 13
    }
46
47
    /**
48
     * Initialize from ASN.1.
49
     *
50
     * @param Sequence $seq
51
     *
52
     * @return self
53
     */
54 4
    public static function fromASN1(Sequence $seq): self
55
    {
56 4
        $type = AttributeType::fromASN1($seq->at(0)->asObjectIdentifier());
57 4
        $values = array_map(
58
            function (UnspecifiedType $el) use ($type) {
59 4
                return AttributeValue::fromASN1ByOID($type->oid(), $el);
60 4
            }, $seq->at(1)->asSet()->elements());
61 4
        return new self($type, ...$values);
62
    }
63
64
    /**
65
     * Convenience method to initialize from attribute values.
66
     *
67
     * @param AttributeValue ...$values One or more values
68
     *
69
     * @throws \LogicException
70
     *
71
     * @return self
72
     */
73 11
    public static function fromAttributeValues(AttributeValue ...$values): self
74
    {
75
        // we need at least one value to determine OID
76 11
        if (!count($values)) {
77 1
            throw new \LogicException('No values.');
78
        }
79 10
        $oid = reset($values)->oid();
80 10
        return new self(new AttributeType($oid), ...$values);
81
    }
82
83
    /**
84
     * Get first value of the attribute.
85
     *
86
     * @throws \LogicException
87
     *
88
     * @return AttributeValue
89
     */
90 10
    public function first(): AttributeValue
91
    {
92 10
        if (!count($this->_values)) {
93 1
            throw new \LogicException('Attribute contains no values.');
94
        }
95 9
        return $this->_values[0];
96
    }
97
98
    /**
99
     * Get all values.
100
     *
101
     * @return AttributeValue[]
102
     */
103 1
    public function values(): array
104
    {
105 1
        return $this->_values;
106
    }
107
108
    /**
109
     * Generate ASN.1 structure.
110
     *
111
     * @return Sequence
112
     */
113 5
    public function toASN1(): Sequence
114
    {
115 5
        $values = array_map(
116
            function (AttributeValue $value) {
117 5
                return $value->toASN1();
118 5
            }, $this->_values);
119 5
        $valueset = new Set(...$values);
120 5
        return new Sequence($this->_type->toASN1(), $valueset->sortedSetOf());
121
    }
122
123
    /**
124
     * Cast attribute values to another AttributeValue class.
125
     *
126
     * This method is generally used to cast UnknownAttributeValue values
127
     * to specific objects when class is declared outside this package.
128
     *
129
     * The new class must be derived from AttributeValue and have the same OID
130
     * as current attribute values.
131
     *
132
     * @param string $cls AttributeValue class name
133
     *
134
     * @throws \LogicException
135
     *
136
     * @return self
137
     */
138 4
    public function castValues(string $cls): self
139
    {
140
        // check that target class derives from AttributeValue
141 4
        if (!is_subclass_of($cls, AttributeValue::class)) {
142 1
            throw new \LogicException(sprintf(
143 1
                '%s must be derived from %s.', $cls, AttributeValue::class));
144
        }
145 3
        $values = array_map(
146
            function (AttributeValue $value) use ($cls) {
147
                /** @var AttributeValue $cls Class name as a string */
148 3
                $value = $cls::fromSelf($value);
149 3
                if ($value->oid() !== $this->oid()) {
150 1
                    throw new \LogicException('Attribute OID mismatch.');
151
                }
152 2
                return $value;
153 3
            }, $this->_values);
154 2
        return self::fromAttributeValues(...$values);
155
    }
156
157
    /**
158
     * @see \Countable::count()
159
     *
160
     * @return int
161
     */
162 1
    public function count(): int
163
    {
164 1
        return count($this->_values);
165
    }
166
167
    /**
168
     * @see \IteratorAggregate::getIterator()
169
     *
170
     * @return \ArrayIterator
171
     */
172 1
    public function getIterator(): \ArrayIterator
173
    {
174 1
        return new \ArrayIterator($this->_values);
175
    }
176
}
177