Passed
Push — master ( 4a0863...f9bf6d )
by Magnar Ovedal
03:47 queued 10s
created

FieldListDirective::add()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 5
c 1
b 0
f 0
nc 5
nop 1
dl 0
loc 10
ccs 6
cts 6
cp 1
crap 5
rs 9.6111
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stadly\Http\Header\Value\CacheControl;
6
7
use ArrayIterator;
8
use InvalidArgumentException;
9
use IteratorAggregate;
10
use Stadly\Http\Utilities\Rfc7230;
11
use Stadly\Http\Utilities\Rfc7234;
12
13
/**
14
 * Class for handling cache control directives with field list value.
15
 *
16
 * Specification: https://tools.ietf.org/html/rfc7234#section-5.2.2
17
 *
18
 * @implements IteratorAggregate<int, string>
19
 */
20
final class FieldListDirective extends Directive implements IteratorAggregate
21
{
22
    public const RESERVED_NAMES = [
23
        'no-cache',
24
        'private',
25
    ];
26
27
    /**
28
     * @var array<string, string> Field names.
29
     */
30
    private $fields = [];
31
32
    /**
33
     * Constructor.
34
     *
35
     * @param string $name Name.
36
     * @param string ...$fields Field names to include in the field list.
37
     */
38 8
    public function __construct(string $name, string ...$fields)
39
    {
40 8
        $this->setName($name);
41 4
        $this->add(...$fields);
42 2
    }
43
44
    /**
45
     * Construct directive from string.
46
     *
47
     * @param string $directive Directive string.
48
     * @return self Directive generated based on the string.
49
     */
50 8
    public static function fromString(string $directive): self
51
    {
52 8
        $regEx = '{^' . Rfc7234::CACHE_DIRECTIVE_CAPTURE . '$}';
53 8
        if (utf8_decode($directive) !== $directive || preg_match($regEx, $directive, $matches) !== 1) {
54 3
            throw new InvalidArgumentException('Invalid directive: ' . $directive);
55
        }
56
57 5
        if (!isset($matches['VALUE'])) {
58 1
            throw new InvalidArgumentException('Directive must have value');
59
        }
60
61 4
        $value = $matches['VALUE'];
62
        // Strip slashes if value is quoted.
63 4
        if (mb_strlen($value) >= 2 && mb_substr($value, 0, 1) === '"' && mb_substr($value, -1) === '"') {
64 3
            $value = stripslashes(mb_substr($value, 1, -1));
65
        }
66
67 4
        $fieldNamesRegEx = '{^' . Rfc7230::hashRule(Rfc7230::FIELD_NAME) . '$}';
68 4
        if (utf8_decode($value) !== $value || preg_match($fieldNamesRegEx, $value) !== 1) {
69 1
            throw new InvalidArgumentException('Invalid value: ' . $value);
70
        }
71
72 3
        $fieldNameRegEx = '{' . Rfc7230::FIELD_NAME_CAPTURE . '}';
73 3
        preg_match_all($fieldNameRegEx, $value, $fieldMatches);
74
75 3
        return new self($matches['NAME'], ...$fieldMatches['FIELD_NAME']);
76
    }
77
78
    /**
79
     * @inheritDoc
80
     */
81 1
    public function __toString(): string
82
    {
83 1
        return $this->name . '="' . $this->getValue() . '"';
84
    }
85
86
    /**
87
     * @param string $name Name.
88
     */
89 5
    public function setName(string $name): void
90
    {
91 5
        if (utf8_decode($name) !== $name || preg_match('{^' . Rfc7230::TOKEN . '$}', $name) !== 1) {
92 2
            throw new InvalidArgumentException('Invalid name: ' . $name);
93
        }
94
95 5
        if (in_array(strtolower($name), IntegerDirective::RESERVED_NAMES, /*strict*/true)) {
96 1
            throw new InvalidArgumentException('Name reserved for integer directive: ' . $name);
97
        }
98 5
        if (in_array(strtolower($name), ValuelessDirective::RESERVED_NAMES, /*strict*/true)) {
99 1
            throw new InvalidArgumentException('Name reserved for valueless directive: ' . $name);
100
        }
101
102 5
        $this->name = $name;
103 5
    }
104
105
    /**
106
     * @return string Value.
107
     */
108 1
    public function getValue(): string
109
    {
110 1
        return implode(', ', $this->fields);
111
    }
112
113
    /**
114
     * Add field names to the field list.
115
     *
116
     * @param string ...$fields Field names to add.
117
     */
118 6
    public function add(string ...$fields): void
119
    {
120 6
        foreach ($fields as $field) {
121 6
            if (utf8_decode($field) !== $field || preg_match('{^' . Rfc7230::FIELD_NAME . '$}', $field) !== 1) {
122 2
                throw new InvalidArgumentException('Invalid field: ' . $field);
123
            }
124
        }
125
126 6
        foreach ($fields as $field) {
127 4
            $this->fields[strtolower($field)] = $field;
128
        }
129 6
    }
130
131
    /**
132
     * Remove field names from the field list.
133
     *
134
     * @param string ...$fields Field names to remove.
135
     */
136 4
    public function remove(string ...$fields): void
137
    {
138 4
        foreach ($fields as $field) {
139 3
            unset($this->fields[strtolower($field)]);
140
        }
141 4
    }
142
143
    /**
144
     * Remove all field names from the field list.
145
     */
146 3
    public function clear(): void
147
    {
148 3
        $this->fields = [];
149 3
    }
150
151
    /**
152
     * @return ArrayIterator<int, string> Iterator containing the field names in the field list.
153
     */
154 3
    public function getIterator(): ArrayIterator
155
    {
156 3
        return new ArrayIterator(array_values($this->fields));
157
    }
158
}
159