Passed
Pull Request — master (#22)
by Aleksei
07:47
created

ValueFieldParser::parse()   F

Complexity

Conditions 44
Paths 213

Size

Total Lines 129
Code Lines 104

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 94
CRAP Score 44.0174

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 44
eloc 104
c 1
b 0
f 0
nc 213
nop 3
dl 0
loc 129
ccs 94
cts 96
cp 0.9792
crap 44.0174
rs 2.5366

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

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

167
                /** @scrutinizer ignore-call */ 
168
                $item = $item->withDirective(key($state->params), current($state->params));
Loading history...
168
            }
169 80
        } elseif ($params->withParams) {
170 73
            $item = $item->withParams($state->params);
0 ignored issues
show
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

170
            /** @scrutinizer ignore-call */ 
171
            $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...
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

170
            /** @scrutinizer ignore-call */ 
171
            $item = $item->withParams($state->params);
Loading history...
171
        }
172 83
        if ($state->error !== null) {
173 19
            $item = $item->withError($state->error);
174
        }
175 83
        $state->clear();
176 83
        return $item;
177
    }
178
}
179