FieldListDirective::setName()   A
last analyzed

Complexity

Conditions 5
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 8
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 15
ccs 9
cts 9
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
    }
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
        $plainDirective = mb_convert_encoding($directive, 'ISO-8859-1', 'UTF-8');
54 8
        if ($plainDirective !== $directive || preg_match($regEx, $directive, $matches) !== 1) {
55 3
            throw new InvalidArgumentException('Invalid directive: ' . $directive);
56
        }
57
58 5
        if (!isset($matches['VALUE'])) {
59 1
            throw new InvalidArgumentException('Directive must have value');
60
        }
61
62 4
        $value = $matches['VALUE'];
63
        // Strip slashes if value is quoted.
64 4
        if (mb_strlen($value) >= 2 && mb_substr($value, 0, 1) === '"' && mb_substr($value, -1) === '"') {
65 3
            $value = stripslashes(mb_substr($value, 1, -1));
66
        }
67
68 4
        $fieldNamesRegEx = '{^' . Rfc7230::hashRule(Rfc7230::FIELD_NAME) . '$}';
69 4
        $plainValue = mb_convert_encoding($value, 'ISO-8859-1', 'UTF-8');
70 4
        if ($plainValue !== $value || preg_match($fieldNamesRegEx, $value) !== 1) {
71 1
            throw new InvalidArgumentException('Invalid value: ' . $value);
72
        }
73
74 3
        $fieldNameRegEx = '{' . Rfc7230::FIELD_NAME_CAPTURE . '}';
75 3
        preg_match_all($fieldNameRegEx, $value, $fieldMatches);
76
77 3
        return new self($matches['NAME'], ...$fieldMatches['FIELD_NAME']);
78
    }
79
80
    /**
81
     * @inheritDoc
82
     */
83 1
    public function __toString(): string
84
    {
85 1
        return $this->name . '="' . $this->getValue() . '"';
86
    }
87
88
    /**
89
     * @param string $name Name.
90
     */
91 5
    public function setName(string $name): void
92
    {
93 5
        $plainName = mb_convert_encoding($name, 'ISO-8859-1', 'UTF-8');
94 5
        if ($plainName !== $name || preg_match('{^' . Rfc7230::TOKEN . '$}', $name) !== 1) {
95 2
            throw new InvalidArgumentException('Invalid name: ' . $name);
96
        }
97
98 5
        if (in_array(strtolower($name), IntegerDirective::RESERVED_NAMES, /*strict*/true)) {
99 1
            throw new InvalidArgumentException('Name reserved for integer directive: ' . $name);
100
        }
101 5
        if (in_array(strtolower($name), ValuelessDirective::RESERVED_NAMES, /*strict*/true)) {
102 1
            throw new InvalidArgumentException('Name reserved for valueless directive: ' . $name);
103
        }
104
105 5
        $this->name = $name;
106
    }
107
108
    /**
109
     * @return string Value.
110
     */
111 1
    public function getValue(): string
112
    {
113 1
        return implode(', ', $this->fields);
114
    }
115
116
    /**
117
     * Add field names to the field list.
118
     *
119
     * @param string ...$fields Field names to add.
120
     */
121 6
    public function add(string ...$fields): void
122
    {
123 6
        foreach ($fields as $field) {
124 6
            $plainField = mb_convert_encoding($field, 'ISO-8859-1', 'UTF-8');
125 6
            if ($plainField !== $field || preg_match('{^' . Rfc7230::FIELD_NAME . '$}', $field) !== 1) {
126 2
                throw new InvalidArgumentException('Invalid field: ' . $field);
127
            }
128
        }
129
130 6
        foreach ($fields as $field) {
131 4
            $this->fields[strtolower($field)] = $field;
132
        }
133
    }
134
135
    /**
136
     * Remove field names from the field list.
137
     *
138
     * @param string ...$fields Field names to remove.
139
     */
140 4
    public function remove(string ...$fields): void
141
    {
142 4
        foreach ($fields as $field) {
143 3
            unset($this->fields[strtolower($field)]);
144
        }
145
    }
146
147
    /**
148
     * Remove all field names from the field list.
149
     */
150 3
    public function clear(): void
151
    {
152 3
        $this->fields = [];
153
    }
154
155
    /**
156
     * @return ArrayIterator<int, string> Iterator containing the field names in the field list.
157
     */
158 3
    public function getIterator(): ArrayIterator
159
    {
160 3
        return new ArrayIterator(array_values($this->fields));
161
    }
162
}
163