GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Structure::withPrepended()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Sop\ASN1\Type;
6
7
use Sop\ASN1\Component\Identifier;
8
use Sop\ASN1\Component\Length;
9
use Sop\ASN1\Element;
10
use Sop\ASN1\Exception\DecodeException;
11
use Sop\ASN1\Feature\ElementBase;
12
13
/**
14
 * Base class for the constructed types.
15
 */
16
abstract class Structure extends Element implements \Countable, \IteratorAggregate
17
{
18
    use UniversalClass;
19
20
    /**
21
     * Array of elements in the structure.
22
     *
23
     * @var Element[]
24
     */
25
    protected $_elements;
26
27
    /**
28
     * Lookup table for the tagged elements.
29
     *
30
     * @var null|TaggedType[]
31
     */
32
    private $_taggedMap;
33
34
    /**
35
     * Cache variable of elements wrapped into `UnspecifiedType` objects.
36
     *
37
     * @var null|UnspecifiedType[]
38
     */
39
    private $_unspecifiedTypes;
40
41
    /**
42
     * Constructor.
43
     *
44
     * @param ElementBase ...$elements Any number of elements
45
     */
46 78
    public function __construct(ElementBase ...$elements)
47
    {
48 78
        $this->_elements = array_map(
49
            function (ElementBase $el) {
50 69
                return $el->asElement();
51 78
            }, $elements);
52 78
    }
53
54
    /**
55
     * Clone magic method.
56
     */
57 35
    public function __clone()
58
    {
59
        // clear cache-variables
60 35
        $this->_taggedMap = null;
61 35
        $this->_unspecifiedTypes = null;
62 35
    }
63
64
    /**
65
     * {@inheritdoc}
66
     */
67 36
    public function isConstructed(): bool
68
    {
69 36
        return true;
70
    }
71
72
    /**
73
     * Explode DER structure to DER encoded components that it contains.
74
     *
75
     * @throws DecodeException
76
     *
77
     * @return string[]
78
     */
79 3
    public static function explodeDER(string $data): array
80
    {
81 3
        $offset = 0;
82 3
        $identifier = Identifier::fromDER($data, $offset);
83 3
        if (!$identifier->isConstructed()) {
84 1
            throw new DecodeException('Element is not constructed.');
85
        }
86 2
        $length = Length::expectFromDER($data, $offset);
87 2
        if ($length->isIndefinite()) {
88 1
            throw new DecodeException(
89 1
                'Explode not implemented for indefinite length encoding.');
90
        }
91 1
        $end = $offset + $length->intLength();
92 1
        $parts = [];
93 1
        while ($offset < $end) {
94
            // start of the element
95 1
            $idx = $offset;
96
            // skip identifier
97 1
            Identifier::fromDER($data, $offset);
98
            // decode element length
99 1
            $length = Length::expectFromDER($data, $offset)->intLength();
100
            // extract der encoding of the element
101 1
            $parts[] = substr($data, $idx, $offset - $idx + $length);
102
            // update offset over content
103 1
            $offset += $length;
104
        }
105 1
        return $parts;
106
    }
107
108
    /**
109
     * Get self with an element at the given index replaced by another.
110
     *
111
     * @param int     $idx Element index
112
     * @param Element $el  New element to insert into the structure
113
     *
114
     * @throws \OutOfBoundsException
115
     */
116 2
    public function withReplaced(int $idx, Element $el): self
117
    {
118 2
        if (!isset($this->_elements[$idx])) {
119 1
            throw new \OutOfBoundsException(
120 1
                "Structure doesn't have element at index {$idx}.");
121
        }
122 1
        $obj = clone $this;
123 1
        $obj->_elements[$idx] = $el;
124 1
        return $obj;
125
    }
126
127
    /**
128
     * Get self with an element inserted before the given index.
129
     *
130
     * @param int     $idx Element index
131
     * @param Element $el  New element to insert into the structure
132
     *
133
     * @throws \OutOfBoundsException
134
     */
135 4
    public function withInserted(int $idx, Element $el): self
136
    {
137 4
        if (count($this->_elements) < $idx || $idx < 0) {
138 1
            throw new \OutOfBoundsException("Index {$idx} is out of bounds.");
139
        }
140 3
        $obj = clone $this;
141 3
        array_splice($obj->_elements, $idx, 0, [$el]);
142 3
        return $obj;
143
    }
144
145
    /**
146
     * Get self with an element appended to the end.
147
     *
148
     * @param Element $el Element to insert into the structure
149
     */
150 2
    public function withAppended(Element $el): self
151
    {
152 2
        $obj = clone $this;
153 2
        array_push($obj->_elements, $el);
154 2
        return $obj;
155
    }
156
157
    /**
158
     * Get self with an element prepended in the beginning.
159
     *
160
     * @param Element $el Element to insert into the structure
161
     */
162 1
    public function withPrepended(Element $el): self
163
    {
164 1
        $obj = clone $this;
165 1
        array_unshift($obj->_elements, $el);
166 1
        return $obj;
167
    }
168
169
    /**
170
     * Get self with an element at the given index removed.
171
     *
172
     * @param int $idx Element index
173
     *
174
     * @throws \OutOfBoundsException
175
     */
176 4
    public function withoutElement(int $idx): self
177
    {
178 4
        if (!isset($this->_elements[$idx])) {
179 1
            throw new \OutOfBoundsException(
180 1
                "Structure doesn't have element at index {$idx}.");
181
        }
182 3
        $obj = clone $this;
183 3
        array_splice($obj->_elements, $idx, 1);
184 3
        return $obj;
185
    }
186
187
    /**
188
     * Get elements in the structure.
189
     *
190
     * @return UnspecifiedType[]
191
     */
192 2
    public function elements(): array
193
    {
194 2
        if (!isset($this->_unspecifiedTypes)) {
195 1
            $this->_unspecifiedTypes = array_map(
196
                function (Element $el) {
197 1
                    return new UnspecifiedType($el);
198 1
                }, $this->_elements);
199
        }
200 2
        return $this->_unspecifiedTypes;
201
    }
202
203
    /**
204
     * Check whether the structure has an element at the given index, optionally
205
     * satisfying given tag expectation.
206
     *
207
     * @param int      $idx         Index 0..n
208
     * @param null|int $expectedTag Optional type tag expectation
209
     */
210 9
    public function has(int $idx, ?int $expectedTag = null): bool
211
    {
212 9
        if (!isset($this->_elements[$idx])) {
213 2
            return false;
214
        }
215 7
        if (isset($expectedTag)) {
216 4
            if (!$this->_elements[$idx]->isType($expectedTag)) {
217 1
                return false;
218
            }
219
        }
220 6
        return true;
221
    }
222
223
    /**
224
     * Get the element at the given index, optionally checking that the element
225
     * has a given tag.
226
     *
227
     * @param int $idx Index 0..n
228
     *
229
     * @throws \OutOfBoundsException     If element doesn't exists
230
     * @throws \UnexpectedValueException If expectation fails
231
     */
232 5
    public function at(int $idx): UnspecifiedType
233
    {
234 5
        if (!isset($this->_elements[$idx])) {
235 1
            throw new \OutOfBoundsException(
236 1
                "Structure doesn't have an element at index {$idx}.");
237
        }
238 4
        return new UnspecifiedType($this->_elements[$idx]);
239
    }
240
241
    /**
242
     * Check whether the structure contains a context specific element with a
243
     * given tag.
244
     *
245
     * @param int $tag Tag number
246
     */
247 6
    public function hasTagged(int $tag): bool
248
    {
249
        // lazily build lookup map
250 6
        if (!isset($this->_taggedMap)) {
251 6
            $this->_taggedMap = [];
252 6
            foreach ($this->_elements as $element) {
253 6
                if ($element->isTagged()) {
254 6
                    $this->_taggedMap[$element->tag()] = $element;
255
                }
256
            }
257
        }
258 6
        return isset($this->_taggedMap[$tag]);
259
    }
260
261
    /**
262
     * Get a context specific element tagged with a given tag.
263
     *
264
     * @throws \LogicException If tag doesn't exists
265
     */
266 3
    public function getTagged(int $tag): TaggedType
267
    {
268 3
        if (!$this->hasTagged($tag)) {
269 1
            throw new \LogicException("No tagged element for tag {$tag}.");
270
        }
271 2
        return $this->_taggedMap[$tag];
272
    }
273
274
    /**
275
     * @see \Countable::count()
276
     */
277 4
    public function count(): int
278
    {
279 4
        return count($this->_elements);
280
    }
281
282
    /**
283
     * Get an iterator for the `UnspecifiedElement` objects.
284
     *
285
     * @see \IteratorAggregate::getIterator()
286
     */
287 1
    public function getIterator(): \ArrayIterator
288
    {
289 1
        return new \ArrayIterator($this->elements());
290
    }
291
292
    /**
293
     * {@inheritdoc}
294
     */
295 35
    protected function _encodedContentDER(): string
296
    {
297 35
        $data = '';
298 35
        foreach ($this->_elements as $element) {
299 31
            $data .= $element->toDER();
300
        }
301 35
        return $data;
302
    }
303
304
    /**
305
     * {@inheritdoc}
306
     *
307
     * @return self
308
     */
309 43
    protected static function _decodeFromDER(Identifier $identifier,
310
        string $data, int &$offset): ElementBase
311
    {
312 43
        if (!$identifier->isConstructed()) {
313 2
            throw new DecodeException(
314 2
                'Structured element must have constructed bit set.');
315
        }
316 41
        $idx = $offset;
317 41
        $length = Length::expectFromDER($data, $idx);
318 40
        if ($length->isIndefinite()) {
319 24
            $type = self::_decodeIndefiniteLength($data, $idx);
320
        } else {
321 16
            $type = self::_decodeDefiniteLength($data, $idx,
322 16
                $length->intLength());
323
        }
324 38
        $offset = $idx;
325 38
        return $type;
326
    }
327
328
    /**
329
     * Decode elements for a definite length.
330
     *
331
     * @param string $data   DER data
332
     * @param int    $offset Offset to data
333
     * @param int    $length Number of bytes to decode
334
     *
335
     * @throws DecodeException
336
     */
337 16
    private static function _decodeDefiniteLength(string $data, int &$offset,
338
        int $length): ElementBase
339
    {
340 16
        $idx = $offset;
341 16
        $end = $idx + $length;
342 16
        $elements = [];
343 16
        while ($idx < $end) {
344 12
            $elements[] = Element::fromDER($data, $idx);
345
            // check that element didn't overflow length
346 12
            if ($idx > $end) {
347 1
                throw new DecodeException("Structure's content overflows length.");
348
            }
349
        }
350 15
        $offset = $idx;
351
        // return instance by static late binding
352 15
        return new static(...$elements);
353
    }
354
355
    /**
356
     * Decode elements for an indefinite length.
357
     *
358
     * @param string $data   DER data
359
     * @param int    $offset Offset to data
360
     *
361
     * @throws DecodeException
362
     */
363 24
    private static function _decodeIndefiniteLength(string $data, int &$offset): ElementBase
364
    {
365 24
        $idx = $offset;
366 24
        $elements = [];
367 24
        $end = strlen($data);
368 24
        while (true) {
369 24
            if ($idx >= $end) {
370 1
                throw new DecodeException(
371 1
                    'Unexpected end of data while decoding indefinite length structure.');
372
            }
373 24
            $el = Element::fromDER($data, $idx);
374 24
            if ($el->isType(self::TYPE_EOC)) {
375 23
                break;
376
            }
377 23
            $elements[] = $el;
378
        }
379 23
        $offset = $idx;
380 23
        $type = new static(...$elements);
381 23
        $type->_indefiniteLength = true;
382 23
        return $type;
383
    }
384
}
385