Passed
Branch php72 (a3eac9)
by Joni
03:37
created

Attribute   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 160
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 14
eloc 46
dl 0
loc 160
ccs 50
cts 50
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 10 1
A __construct() 0 11 2
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 <i>Attribute</i> 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 6
    public function __construct(AttributeType $type, AttributeValue ...$values)
36
    {
37
        // check that attribute values have correct oid
38 6
        array_walk($values,
39
            function (AttributeValue $value) use ($type) {
40 5
                if ($value->oid() != $type->oid()) {
41 1
                    throw new \LogicException('Attribute OID mismatch.');
42
                }
43 6
            });
44 5
        $this->_type = $type;
45 5
        $this->_values = $values;
46 5
    }
47
48
    /**
49
     * Initialize from ASN.1.
50
     *
51
     * @param Sequence $seq
52
     *
53
     * @return self
54
     */
55 1
    public static function fromASN1(Sequence $seq): self
56
    {
57 1
        $type = AttributeType::fromASN1($seq->at(0)->asObjectIdentifier());
58 1
        $values = array_map(
59
            function (UnspecifiedType $el) use ($type) {
60 1
                return AttributeValue::fromASN1ByOID($type->oid(), $el);
61 1
            }, $seq->at(1)
62 1
                ->asSet()
63 1
                ->elements());
64 1
        return new self($type, ...$values);
65
    }
66
67
    /**
68
     * Convenience method to initialize from attribute values.
69
     *
70
     * @param AttributeValue ...$values One or more values
71
     *
72
     * @throws \LogicException
73
     *
74
     * @return self
75
     */
76 5
    public static function fromAttributeValues(AttributeValue ...$values): self
77
    {
78
        // we need at least one value to determine OID
79 5
        if (!count($values)) {
80 1
            throw new \LogicException('No values.');
81
        }
82 4
        $oid = reset($values)->oid();
83 4
        return new self(new AttributeType($oid), ...$values);
84
    }
85
86
    /**
87
     * Get first value of the attribute.
88
     *
89
     * @throws \LogicException
90
     *
91
     * @return AttributeValue
92
     */
93 3
    public function first(): AttributeValue
94
    {
95 3
        if (!count($this->_values)) {
96 1
            throw new \LogicException('Attribute contains no values.');
97
        }
98 2
        return $this->_values[0];
99
    }
100
101
    /**
102
     * Get all values.
103
     *
104
     * @return AttributeValue[]
105
     */
106 1
    public function values(): array
107
    {
108 1
        return $this->_values;
109
    }
110
111
    /**
112
     * Generate ASN.1 structure.
113
     *
114
     * @return Sequence
115
     */
116 1
    public function toASN1(): Sequence
117
    {
118 1
        $values = array_map(
119
            function (AttributeValue $value) {
120 1
                return $value->toASN1();
121 1
            }, $this->_values);
122 1
        $valueset = new Set(...$values);
123 1
        return new Sequence($this->_type->toASN1(), $valueset->sortedSetOf());
124
    }
125
126
    /**
127
     * Cast attribute values to another AttributeValue class.
128
     *
129
     * This method is generally used to cast UnknownAttributeValue values
130
     * to specific objects when class is declared outside this package.
131
     *
132
     * The new class must be derived from AttributeValue and have the same OID
133
     * as current attribute values.
134
     *
135
     * @param string $cls AttributeValue class name
136
     *
137
     * @throws \LogicException
138
     *
139
     * @return self
140
     */
141 3
    public function castValues(string $cls): self
142
    {
143 3
        $refl = new \ReflectionClass($cls);
144 3
        if (!$refl->isSubclassOf(AttributeValue::class)) {
145 1
            throw new \LogicException(
146 1
                "{$cls} must be derived from " . AttributeValue::class . '.');
147
        }
148 2
        $oid = $this->oid();
149 2
        $values = array_map(
150
            function (AttributeValue $value) use ($cls, $oid) {
151 2
                $value = $cls::fromSelf($value);
152 2
                if ($value->oid() != $oid) {
153 1
                    throw new \LogicException('Attribute OID mismatch.');
154
                }
155 1
                return $value;
156 2
            }, $this->_values);
157 1
        return self::fromAttributeValues(...$values);
158
    }
159
160
    /**
161
     * @see \Countable::count()
162
     *
163
     * @return int
164
     */
165 1
    public function count(): int
166
    {
167 1
        return count($this->_values);
168
    }
169
170
    /**
171
     * @see \IteratorAggregate::getIterator()
172
     *
173
     * @return \ArrayIterator
174
     */
175 1
    public function getIterator(): \ArrayIterator
176
    {
177 1
        return new \ArrayIterator($this->_values);
178
    }
179
}
180