ByteRangeSet   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 103
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 25
c 2
b 0
f 0
dl 0
loc 103
ccs 31
cts 31
cp 1
rs 10
wmc 15

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 2
A __toString() 0 3 1
A getIterator() 0 3 1
A getValue() 0 3 1
A getUnit() 0 3 1
A fromString() 0 17 4
A isSatisfiable() 0 9 3
A add() 0 4 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stadly\Http\Header\Value\Range;
6
7
use ArrayIterator;
8
use InvalidArgumentException;
9
use IteratorAggregate;
10
use Stadly\Http\Utilities\Rfc7233;
11
12
/**
13
 * Class for handling sets of byte ranges.
14
 *
15
 * Specification: https://tools.ietf.org/html/rfc7233#section-2.1
16
 *
17
 * @implements IteratorAggregate<int, ByteRange>
18
 */
19
final class ByteRangeSet implements RangeSet, IteratorAggregate
20
{
21
    /**
22
     * @var array<int, ByteRange> Ranges.
23
     */
24
    private $ranges = [];
25
26
    /**
27
     * Constructor.
28
     *
29
     * @param ByteRange ...$ranges Ranges.
30
     */
31 4
    public function __construct(ByteRange ...$ranges)
32
    {
33 4
        if (count($ranges) === 0) {
34 1
            throw new InvalidArgumentException('The set of ranges cannot be empty.');
35
        }
36
37 3
        $this->add(...$ranges);
38
    }
39
40
    /**
41
     * Construct set of byte ranges from string.
42
     *
43
     * @param string $rangeSet Set of ranges string.
44
     * @return self Set of byte ranges generated based on the string.
45
     */
46 12
    public static function fromString(string $rangeSet): self
47
    {
48 12
        $regEx = '{^' . Rfc7233::BYTE_RANGES_SPECIFIER_CAPTURE . '$}';
49 12
        $plainRangeSet = mb_convert_encoding($rangeSet, 'ISO-8859-1', 'UTF-8');
50 12
        if ($plainRangeSet !== $rangeSet || preg_match($regEx, $rangeSet, $matches) !== 1) {
51 7
            throw new InvalidArgumentException('Invalid set of ranges: ' . $rangeSet);
52
        }
53
54 5
        $rangeRegEx = '{(?<BYTE_RANGE_SPEC>' . Rfc7233::BYTE_RANGE_SPEC . '|' . Rfc7233::SUFFIX_BYTE_RANGE_SPEC . ')}';
55 5
        preg_match_all($rangeRegEx, $matches['BYTE_RANGE_SET'], $rangeMatches);
56
57 5
        $ranges = [];
58 5
        foreach ($rangeMatches['BYTE_RANGE_SPEC'] as $range) {
59 5
            $ranges[] = ByteRange::fromString($range);
60
        }
61
62 5
        return new self(...$ranges);
63
    }
64
65
    /**
66
     * @return string String representation of the range set.
67
     */
68 2
    public function __toString(): string
69
    {
70 2
        return $this->getUnit() . '=' . $this->getValue();
71
    }
72
73
    /**
74
     * @inheritDoc
75
     */
76 1
    public function getUnit(): string
77
    {
78 1
        return 'bytes';
79
    }
80
81
    /**
82
     * @inheritDoc
83
     */
84 2
    public function getValue(): string
85
    {
86 2
        return implode(', ', $this->ranges);
87
    }
88
89
    /**
90
     * Add ranges to the range set.
91
     *
92
     * @param ByteRange ...$ranges Ranges to add.
93
     */
94 4
    public function add(ByteRange ...$ranges): void
95
    {
96 4
        foreach ($ranges as $range) {
97 4
            $this->ranges[] = $range;
98
        }
99
    }
100
101
    /**
102
     * @param int|null $fileSize Size of the file.
103
     * @return bool Whether the set of ranges is satisfiable.
104
     */
105 3
    public function isSatisfiable(?int $fileSize): bool
106
    {
107 3
        foreach ($this->ranges as $range) {
108 3
            if ($range->isSatisfiable($fileSize)) {
109 2
                return true;
110
            }
111
        }
112
113 1
        return false;
114
    }
115
116
    /**
117
     * @return ArrayIterator<int, ByteRange> Iterator containing the ranges in the set.
118
     */
119 3
    public function getIterator(): ArrayIterator
120
    {
121 3
        return new ArrayIterator($this->ranges);
122
    }
123
}
124