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

ValueFieldParser::parse()   F

Complexity

Conditions 45
Paths 214

Size

Total Lines 131
Code Lines 105

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 95
CRAP Score 45.058

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 45
eloc 105
c 2
b 0
f 0
nc 214
nop 3
dl 0
loc 131
ccs 95
cts 98
cp 0.9694
crap 45.058
rs 2.5266

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Http\Header\Parser;
6
7
use Generator;
8
use InvalidArgumentException;
9
use Yiisoft\Http\Header\Internal\BaseHeaderValue;
10
use Yiisoft\Http\Header\Internal\DirectivesHeaderValue;
11
12
final class ValueFieldParser
13
{
14
    // Parsing's constants
15
    private const
16
        DELIMITERS = '"(),/:;<=>?@[\\]{}',
17
        READ_NONE = 0,
18
        READ_VALUE = 1,
19
        READ_PARAM_NAME = 2,
20
        READ_PARAM_QUOTED_VALUE = 3,
21
        READ_PARAM_VALUE = 4;
22
23
    /**
24
     * @psalm-param class-string<BaseHeaderValue> $class
25
     * @psalm-return Generator<int, BaseHeaderValue, void, void>
26
     */
27 87
    public static function parse(string $body, string $class, HeaderParsingParams $params): Generator
28
    {
29 87
        if (!is_a($class, BaseHeaderValue::class, true)) {
30
            throw new InvalidArgumentException('$class should be instance of BaseHeaderValue.');
31
        }
32 87
        if (!$params->valuesList && !$params->withParams && !$params->directives) {
33 4
            yield new $class(trim($body));
34 4
            return;
35
        }
36
37 83
        $state = new ValueFieldState();
38 83
        $state->part = self::READ_VALUE;
39
        try {
40
            /** @link https://tools.ietf.org/html/rfc7230#section-3.2.6 */
41 83
            for ($pos = 0, $length = strlen($body); $pos < $length; ++$pos) {
42 82
                $sym = $body[$pos];
43 82
                if ($state->part === self::READ_VALUE) {
44 82
                    if ($sym === '=' && $params->withParams) {
45 28
                        $state->key = ltrim($state->buffer);
46 28
                        $state->buffer = '';
47 28
                        if (preg_match('/\s/', $state->key) === 0) {
48 25
                            $state->part = self::READ_PARAM_VALUE;
49 25
                            continue;
50
                        }
51 4
                        $state->key = preg_replace('/\s+/', ' ', $state->key);
52 4
                        $chunks = explode(' ', $state->key);
53 4
                        if (count($chunks) > 2 || preg_match('/\s$/', $state->key) === 1) {
54 2
                            array_pop($chunks);
55 2
                            $state->buffer = implode(' ', $chunks);
56 2
                            throw new ParsingException($body, $pos, 'Syntax error');
57
                        }
58 2
                        $state->part = self::READ_PARAM_VALUE;
59 2
                        [$state->value, $state->key] = $chunks;
60 82
                    } elseif ($sym === ';' && $params->withParams) {
61 38
                        $state->part = self::READ_PARAM_NAME;
62 38
                        $state->value = trim($state->buffer);
63 38
                        $state->buffer = '';
64 80
                    } elseif ($sym === ',' && $params->valuesList) {
65 14
                        $state->value = trim($state->buffer);
66 14
                        yield self::createHeaderValue($class, $params, $state);
67
                    } else {
68 80
                        $state->buffer .= $sym;
69
                    }
70 82
                    continue;
71
                }
72 63
                if ($state->part === self::READ_PARAM_NAME) {
73 46
                    if ($sym === '=') {
74 39
                        $state->key = $state->buffer;
75 39
                        $state->buffer = '';
76 39
                        $state->part = self::READ_PARAM_VALUE;
77 46
                    } elseif (strpos(self::DELIMITERS, $sym) !== false) {
78 4
                        throw new ParsingException($body, $pos, 'Delimiter char in a param name');
79 44
                    } elseif (ord($sym) <= 32) {
80 12
                        if ($state->buffer !== '') {
81 12
                            throw new ParsingException($body, $pos, 'Space in a param name');
82
                        }
83
                    } else {
84 43
                        $state->buffer .= $sym;
85
                    }
86 44
                    continue;
87
                }
88 57
                if ($state->part === self::READ_PARAM_VALUE) {
89 57
                    if ($state->buffer === '') {
90 57
                        if ($sym === '"') {
91 13
                            $state->part = self::READ_PARAM_QUOTED_VALUE;
92 52
                        } elseif (ord($sym) <= 32) {
93
                            continue;
94 52
                        } elseif (strpos(self::DELIMITERS, $sym) === false) {
95 51
                            $state->buffer .= $sym;
96
                        } else {
97 57
                            throw new ParsingException($body, $pos, 'Delimiter char in a unquoted param value');
98
                        }
99 49
                    } elseif (ord($sym) <= 32) {
100 4
                        $state->part = self::READ_NONE;
101 4
                        $state->addParamFromBuffer();
102 48
                    } elseif (strpos(self::DELIMITERS, $sym) === false) {
103 40
                        $state->buffer .= $sym;
104 30
                    } elseif ($sym === ';') {
105 21
                        $state->part = self::READ_PARAM_NAME;
106 21
                        $state->addParamFromBuffer();
107 12
                    } elseif ($sym === ',' && $params->valuesList) {
108 8
                        $state->part = self::READ_VALUE;
109 8
                        $state->addParamFromBuffer();
110 8
                        yield self::createHeaderValue($class, $params, $state);
111
                    } else {
112 4
                        $state->buffer = '';
113 4
                        throw new ParsingException($body, $pos, 'Delimiter char in a unquoted param value');
114
                    }
115 56
                    continue;
116
                }
117 16
                if ($state->part === self::READ_PARAM_QUOTED_VALUE) {
118 12
                    if ($sym === '\\') { // quoted pair
119 3
                        if (++$pos >= $length) {
120
                            throw new ParsingException($body, $pos, 'Incorrect quoted pair');
121
                        }
122 3
                        $state->buffer .= $body[$pos];
123 12
                    } elseif ($sym === '"') { // end
124 11
                        $state->part = self::READ_NONE;
125 11
                        $state->addParamFromBuffer();
126
                    } else {
127 11
                        $state->buffer .= $sym;
128
                    }
129 12
                    continue;
130
                }
131 8
                if ($state->part === self::READ_NONE) {
132 8
                    if (ord($sym) <= 32) {
133 1
                        continue;
134
                    }
135 8
                    if ($sym === ';' && $params->withParams) {
136 3
                        $state->part = self::READ_PARAM_NAME;
137 5
                    } elseif ($sym === ',' && $params->valuesList) {
138 3
                        $state->part = self::READ_VALUE;
139 3
                        yield self::createHeaderValue($class, $params, $state);
140
                    } else {
141 2
                        throw new ParsingException($body, $pos, 'Expected Separator');
142
                    }
143
                }
144
            }
145 17
        } catch (ParsingException $e) {
146 17
            $state->error = $e;
147
        }
148 83
        if ($state->part === self::READ_VALUE) {
149 28
            $state->value = trim($state->buffer);
150 63
        } elseif (in_array($state->part, [self::READ_PARAM_VALUE, self::READ_PARAM_QUOTED_VALUE], true)) {
151 43
            if ($state->buffer === '') {
152 7
                $state->error = $state->error ?? new ParsingException($body, $pos, 'Empty value should be quoted');
153
            } else {
154 36
                $state->addParamFromBuffer();
155
            }
156
        }
157 83
        yield self::createHeaderValue($class, $params, $state);
158 83
    }
159
160
    /**
161
     * @psalm-param class-string<BaseHeaderValue> $class
162
     */
163 83
    protected static function createHeaderValue(
164
        string $class,
165
        HeaderParsingParams $params,
166
        ValueFieldState $state
167
    ): BaseHeaderValue {
168
        /** @var BaseHeaderValue|DirectivesHeaderValue $item */
169 83
        $item = new $class($state->value);
170 83
        if ($params->directives && $item instanceof DirectivesHeaderValue) {
171 3
            if ($state->value === '' && count($state->params) > 0) {
172 3
                $item = $item->withDirective(key($state->params), current($state->params));
173
            }
174 80
        } elseif ($params->withParams) {
175 73
            $item = $item->withParams($state->params);
0 ignored issues
show
Bug introduced by
The method withParams() does not exist on Yiisoft\Http\Header\Internal\BaseHeaderValue. It seems like you code against a sub-type of Yiisoft\Http\Header\Internal\BaseHeaderValue such as Yiisoft\Http\Header\Internal\WithParamsHeaderValue. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

175
            /** @scrutinizer ignore-call */ 
176
            $item = $item->withParams($state->params);
Loading history...
Bug introduced by
The method withParams() does not exist on Yiisoft\Http\Header\Internal\DirectivesHeaderValue. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

175
            /** @scrutinizer ignore-call */ 
176
            $item = $item->withParams($state->params);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
176
        }
177 83
        if ($state->error !== null) {
178 19
            $item = $item->withError($state->error);
179
        }
180 83
        $state->clear();
181 83
        return $item;
182
    }
183
}
184