RDN   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 170
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 16
eloc 50
dl 0
loc 170
ccs 56
cts 56
cp 1
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __toString() 0 3 1
A allOf() 0 8 1
A toString() 0 7 1
A all() 0 3 1
A equals() 0 21 5
A __construct() 0 7 2
A fromAttributeValues() 0 8 1
A getIterator() 0 3 1
A count() 0 3 1
A fromASN1() 0 7 1
A toASN1() 0 8 1
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Sop\X501\ASN1;
6
7
use Sop\ASN1\Type\Constructed\Set;
8
use Sop\ASN1\Type\UnspecifiedType;
9
use Sop\X501\ASN1\AttributeValue\AttributeValue;
10
11
/**
12
 * Implements *RelativeDistinguishedName* ASN.1 type.
13
 *
14
 * @see https://www.itu.int/ITU-T/formal-language/itu-t/x/x501/2012/InformationFramework.html#InformationFramework.RelativeDistinguishedName
15
 */
16
class RDN implements \Countable, \IteratorAggregate
17
{
18
    /**
19
     * Attributes.
20
     *
21
     * @var AttributeTypeAndValue[]
22
     */
23
    protected $_attribs;
24
25
    /**
26
     * Constructor.
27
     *
28
     * @param AttributeTypeAndValue ...$attribs One or more attributes
29
     */
30 40
    public function __construct(AttributeTypeAndValue ...$attribs)
31
    {
32 40
        if (!count($attribs)) {
33 1
            throw new \UnexpectedValueException(
34 1
                'RDN must have at least one AttributeTypeAndValue.');
35
        }
36 39
        $this->_attribs = $attribs;
37 39
    }
38
39
    /**
40
     * @return string
41
     */
42 1
    public function __toString()
43
    {
44 1
        return $this->toString();
45
    }
46
47
    /**
48
     * Convenience method to initialize RDN from AttributeValue objects.
49
     *
50
     * @param AttributeValue ...$values One or more attributes
51
     *
52
     * @return self
53
     */
54 1
    public static function fromAttributeValues(AttributeValue ...$values): self
55
    {
56 1
        $attribs = array_map(
57
            function (AttributeValue $value) {
58 1
                return new AttributeTypeAndValue(
59 1
                    new AttributeType($value->oid()), $value);
60 1
            }, $values);
61 1
        return new self(...$attribs);
62
    }
63
64
    /**
65
     * Initialize from ASN.1.
66
     *
67
     * @param Set $set
68
     *
69
     * @return self
70
     */
71 4
    public static function fromASN1(Set $set): self
72
    {
73 4
        $attribs = array_map(
74
            function (UnspecifiedType $el) {
75 4
                return AttributeTypeAndValue::fromASN1($el->asSequence());
76 4
            }, $set->elements());
77 4
        return new self(...$attribs);
78
    }
79
80
    /**
81
     * Generate ASN.1 structure.
82
     *
83
     * @return Set
84
     */
85 4
    public function toASN1(): Set
86
    {
87 4
        $elements = array_map(
88
            function (AttributeTypeAndValue $tv) {
89 4
                return $tv->toASN1();
90 4
            }, $this->_attribs);
91 4
        $set = new Set(...$elements);
92 4
        return $set->sortedSetOf();
93
    }
94
95
    /**
96
     * Get name-component string conforming to RFC 2253.
97
     *
98
     * @see https://tools.ietf.org/html/rfc2253#section-2.2
99
     *
100
     * @return string
101
     */
102 15
    public function toString(): string
103
    {
104 15
        $parts = array_map(
105
            function (AttributeTypeAndValue $tv) {
106 15
                return $tv->toString();
107 15
            }, $this->_attribs);
108 15
        return implode('+', $parts);
109
    }
110
111
    /**
112
     * Check whether RDN is semantically equal to other.
113
     *
114
     * @param RDN $other Object to compare to
115
     *
116
     * @return bool
117
     */
118 22
    public function equals(RDN $other): bool
119
    {
120
        // if attribute count doesn't match
121 22
        if (count($this) !== count($other)) {
122 1
            return false;
123
        }
124 21
        $attribs1 = $this->_attribs;
125 21
        $attribs2 = $other->_attribs;
126
        // if there's multiple attributes, sort using SET OF rules
127 21
        if (count($attribs1) > 1) {
128 2
            $attribs1 = self::fromASN1($this->toASN1())->_attribs;
129 2
            $attribs2 = self::fromASN1($other->toASN1())->_attribs;
130
        }
131 21
        for ($i = count($attribs1) - 1; $i >= 0; --$i) {
132 21
            $tv1 = $attribs1[$i];
133 21
            $tv2 = $attribs2[$i];
134 21
            if (!$tv1->equals($tv2)) {
135 8
                return false;
136
            }
137
        }
138 16
        return true;
139
    }
140
141
    /**
142
     * Get all AttributeTypeAndValue objects.
143
     *
144
     * @return AttributeTypeAndValue[]
145
     */
146 1
    public function all(): array
147
    {
148 1
        return $this->_attribs;
149
    }
150
151
    /**
152
     * Get all AttributeTypeAndValue objects of the given attribute type.
153
     *
154
     * @param string $name Attribute OID or name
155
     *
156
     * @return AttributeTypeAndValue[]
157
     */
158 8
    public function allOf(string $name): array
159
    {
160 8
        $oid = AttributeType::attrNameToOID($name);
161 8
        $attribs = array_filter($this->_attribs,
162
            function (AttributeTypeAndValue $tv) use ($oid) {
163 8
                return $tv->oid() === $oid;
164 8
            });
165 8
        return array_values($attribs);
166
    }
167
168
    /**
169
     * @see \Countable::count()
170
     *
171
     * @return int
172
     */
173 23
    public function count(): int
174
    {
175 23
        return count($this->_attribs);
176
    }
177
178
    /**
179
     * @see \IteratorAggregate::getIterator()
180
     *
181
     * @return \ArrayIterator
182
     */
183 1
    public function getIterator(): \ArrayIterator
184
    {
185 1
        return new \ArrayIterator($this->_attribs);
186
    }
187
}
188