Passed
Pull Request — master (#22)
by Aleksei
02:09
created

DirectivesHeaderValue::__toString()   B

Complexity

Conditions 8
Paths 6

Size

Total Lines 23
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 8

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 8
eloc 15
c 1
b 0
f 1
nc 6
nop 0
dl 0
loc 23
ccs 15
cts 15
cp 1
crap 8
rs 8.4444
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Http\Header\Internal;
6
7
use InvalidArgumentException;
8
use Yiisoft\Http\Header\DirectiveHeader;
9
10
abstract class DirectivesHeaderValue extends BaseHeaderValue
11
{
12
    protected const DIRECTIVES = [];
13
14
    protected const ARG_EMPTY = 1;
15
    protected const ARG_DELTA_SECONDS = 2;
16
    protected const ARG_HEADERS_LIST = 4;
17
    protected const ARG_CUSTOM = 8;
18
19
    protected string $directive = '';
20
    protected ?string $argument = null;
21
    protected int $argumentType = self::ARG_EMPTY;
22
23 31
    public function __toString(): string
24
    {
25 31
        if ($this->directive === '') {
26 1
            return '';
27
        }
28 30
        if ($this->argument === null) {
29 9
            return $this->directive;
30
        }
31 22
        if ($this->argumentType === self::ARG_HEADERS_LIST) {
32 6
            return "{$this->directive}=\"{$this->argument}\"";
33
        }
34 16
        if ($this->argumentType === self::ARG_CUSTOM) {
35 12
            $argument = $this->encodeQuotedString($this->argument);
36
            if (
37 12
                $argument === ''
38 11
                || strlen($argument) !== strlen($this->argument)
39 12
                || preg_match('/[^a-z0-9_]/i', $argument) === 1
40
            ) {
41 7
                $argument = '"' . $argument . '"';
42
            }
43 12
            return "{$this->directive}={$argument}";
44
        }
45 4
        return "{$this->directive}={$this->argument}";
46
    }
47
48 1
    public static function createHeader(): DirectiveHeader
49
    {
50 1
        return new DirectiveHeader(static::class);
51
    }
52
53
    /**
54
     * @return string|null Returns null if the directive is not defined or cannot be parsed without error
55
     */
56 23
    public function getDirective(): string
57
    {
58 23
        return $this->directive;
59
    }
60 1
    public function getValue(): string
61
    {
62 1
        return $this->getDirective();
63
    }
64
65
    public function hasArgument(): bool
66
    {
67
        return $this->argument !== null;
68
    }
69 22
    public function getArgument(): ?string
70
    {
71 22
        return $this->argument;
72
    }
73
    public function getArgumentList(): array
74
    {
75
        return $this->argument === null ? [] : explode(',', $this->argument);
76
    }
77
78
    /**
79
     * @param string $directive
80
     * @param string|null $argument
81
     *
82
     * @throws InvalidArgumentException
83
     */
84 42
    public function withDirective(string $directive, string $argument = null): self
85
    {
86 42
        $clone = clone $this;
87 42
        $clone->setDirective($directive, $argument, true);
88 29
        return $clone;
89
    }
90
91 45
    protected function setValue(string $value): void
92
    {
93 45
        $this->setDirective($value);
94 45
    }
95 45
    private function setDirective(string $value, string $argument = null, bool $trowError = false): bool
96
    {
97 45
        $name = strtolower($value);
98
99 45
        $argumentType = static::DIRECTIVES[$name] ?? self::ARG_CUSTOM;
100
101 45
        $writeProperties = function (\Exception $err = null, string $arg = null, int $type = self::ARG_EMPTY) use (
102 45
            $name,
103 45
            $trowError
104
        ) {
105 45
            if ($trowError && $err !== null) {
106 13
                throw $err;
107
            }
108 45
            $this->directive = $name;
109 45
            $this->argument = $arg;
110 45
            $this->argumentType = $type;
111 45
            $this->error = $err;
112 45
            return $this->error === null;
113 45
        };
114
115 45
        if ($argument === null && ($argumentType & self::ARG_EMPTY) === self::ARG_EMPTY) {
116 5
            return $writeProperties();
117 45
        } elseif ($argumentType === self::ARG_EMPTY) {
118 1
            if ($argument !== null) {
119 1
                return $writeProperties(new InvalidArgumentException("{$name} directive should not have an argument"));
120
            }
121
            return $writeProperties();
122 45
        } elseif (($argumentType & self::ARG_HEADERS_LIST) === self::ARG_HEADERS_LIST) {
123
            // Validate headers list
124 12
            $argument = $argument === null ? null : trim($argument);
125 12
            if ($argument === null || preg_match('/^[\\w\\-]+(?:(?:\\s*,\\s*)[\\w\\-]+)*$/', $argument) !== 1) {
126 6
                return $writeProperties(
127 6
                    new InvalidArgumentException(
128 6
                        "{$name} directive should have an argument as a comma separated headers name list"
129
                    )
130
                );
131
            }
132 6
            return $writeProperties(null, $argument, self::ARG_HEADERS_LIST);
133 45
        } elseif (($argumentType & self::ARG_DELTA_SECONDS) === self::ARG_DELTA_SECONDS) {
134 10
            $this->argumentType = self::ARG_DELTA_SECONDS;
135
            // Validate number
136 10
            if ($argument === null || preg_match('/^\\d+$/', $argument) !== 1) {
137 6
                return $writeProperties(
138 6
                    new InvalidArgumentException("{$name} directive should have numeric argument"),
139 6
                    '0',
140 6
                    self::ARG_DELTA_SECONDS
141
                );
142
            }
143 4
            return $writeProperties(null, $argument, self::ARG_DELTA_SECONDS);
144
        }
145 45
        return $writeProperties(null, $argument, $argumentType);
146
    }
147
}
148