Passed
Push — master ( 9a9b0b...b778d8 )
by Peter
08:40
created

YamlParserLineData   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 283
Duplicated Lines 0 %

Test Coverage

Coverage 99.08%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 47
eloc 116
c 1
b 0
f 0
dl 0
loc 283
ccs 108
cts 109
cp 0.9908
rs 8.64

7 Methods

Rating   Name   Duplication   Size   Complexity  
A getPathToLineGradually() 0 20 3
B getLineValue() 0 36 8
A __construct() 0 16 3
C addArrayKeyWithValueToSeparatedArray() 0 32 14
A addLineKey() 0 8 2
A addCommentOrBlankLineAsKey() 0 36 6
B getLineKey() 0 41 11

How to fix   Complexity   

Complex Class

Complex classes like YamlParserLineData often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use YamlParserLineData, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace YamlStandards\Model\Component\Parser;
6
7
use YamlStandards\Model\Component\YamlService;
8
9
class YamlParserLineData
10
{
11
    private const KEY = 'key:';
12
    public const KEY_COMMON_LINE = self::KEY . ':common_line:';
13
    public const KEY_COMMENT_OR_EMPTY_LINE = self::KEY . 'comment_empty_line:';
14
    public const KEY_DASH = self::KEY . 'dashes:';
15
    public const KEY_EMPTY_ARRAY = self::KEY . 'empty_array:';
16
    public const KEY_ARRAY_WITHOUT_KEY = self::KEY . 'array_without_key:';
17
    public const KEY_CURLY_BRACKETS = self::KEY . 'curly_brackets:';
18
    public const KEY_LAST_ELEMENT = 'zzzLastElementInFile:';
19
20
    public const EMPTY_LINE_DEFAULT_VALUE = 'empty:line:default:value';
21
22
    /**
23
     * @var string[]
24
     */
25
    private $parentKeys;
26
27
    /**
28
     * @var string|string[]
29
     */
30
    private $value;
31
32
    /**
33
     * @var int
34
     */
35
    private $currentDashRowIndents;
36
37
    /**
38
     * @param string[] $fileLines
39
     * @param int $key
40
     */
41 6
    public function __construct(array $fileLines, int $key)
42
    {
43 6
        $this->parentKeys = [];
44
45 6
        $isCommentOrBlankLine = false;
46 6
        $line = $fileLines[$key];
47 6
        $this->currentDashRowIndents = YamlService::rowIndentsOf($line);
48 6
        if (YamlService::isLineBlank($line) || YamlService::isLineComment($line)) {
49 6
            $this->addCommentOrBlankLineAsKey($fileLines, $key);
50 6
            $isCommentOrBlankLine = true;
51
        } else {
52 6
            $this->addLineKey($fileLines, $key);
53
        }
54 6
        $this->addArrayKeyWithValueToSeparatedArray($fileLines, $key);
55
56 6
        $this->value = $this->getLineValue($fileLines, $key, $isCommentOrBlankLine);
57 6
    }
58
59
    /**
60
     * @param string[] $fileLines
61
     * @param int $key
62
     */
63 6
    public function addLineKey(array $fileLines, int $key): void
64
    {
65 6
        if (YamlService::hasLineOnlyOneDash($fileLines[$key])) {
66 5
            $countOfRowIndents = YamlService::rowIndentsOf($fileLines[$key]);
67 5
            $correctIndents = YamlService::createCorrectIndentsByCountOfIndents($countOfRowIndents);
68 5
            $this->parentKeys[] = $correctIndents . self::KEY_DASH . $key;
69
        } else {
70 6
            $this->parentKeys[] = $this->getLineKey($fileLines, $key);
71
        }
72 6
    }
73
74
    /**
75
     * add array elements to separated array, e.g.: - foo: bar
76
     *
77
     * @param string[] $fileLines
78
     * @param int $key
79
     */
80 6
    public function addArrayKeyWithValueToSeparatedArray(array $fileLines, int $key): void
81
    {
82 6
        $currentLine = $fileLines[$key];
83 6
        $prevLine = $fileLines[$key - 1] ?? null;
84 6
        $currentLineWithoutDash = str_replace('-', ' ', $currentLine);
85 6
        $countOfCurrentRowIndents = YamlService::rowIndentsOf($currentLineWithoutDash);
86 6
        $countOfCurrentRowWithDashIndents = YamlService::rowIndentsOf($currentLine);
87
88 6
        if (YamlService::isLineStartOfArrayWithKeyAndValue(trim($currentLine)) && YamlService::hasLineThreeDashesOnStartOfLine(trim($currentLine)) === false) {
89 6
            if ($prevLine !== null) {
90 6
                $prevLineWithoutDash = str_replace('-', ' ', $prevLine);
91 6
                $countOfPrevRowIndents = YamlService::rowIndentsOf($prevLineWithoutDash);
92
93
                $isThereDashInParentKeys = array_filter($this->parentKeys, function ($key) {
94 6
                    return strpos($key, self::KEY_EMPTY_ARRAY) !== false;
95 6
                });
96
97 6
                if (count($isThereDashInParentKeys) === 0 ||
98 3
                    $countOfCurrentRowWithDashIndents < $this->currentDashRowIndents ||
99 6
                    ($countOfCurrentRowWithDashIndents === 0 && (YamlService::isLineComment($prevLine) === false || YamlService::isLineNotBlank($prevLine)))
100
                ) {
101 6
                    if ((YamlService::isLineStartOfArrayWithKeyAndValue(trim($currentLine)) && YamlService::isLineStartOfArrayWithKeyAndValue(trim($prevLine)) === false) ||
102 6
                        (YamlService::isLineStartOfArrayWithKeyAndValue(trim($currentLine)) && YamlService::isLineStartOfArrayWithKeyAndValue(trim($prevLine)) && $countOfCurrentRowIndents !== $countOfPrevRowIndents)
103
                        ) {
104 6
                        $lineKey = self::KEY_EMPTY_ARRAY . $key;
105 6
                        $this->parentKeys[] = $lineKey;
106 6
                        $this->currentDashRowIndents = $countOfCurrentRowWithDashIndents;
107
                    }
108
                }
109
            } else {
110 2
                $lineKey = self::KEY_EMPTY_ARRAY . $key;
111 2
                $this->parentKeys[] = $lineKey;
112
            }
113
        }
114 6
    }
115
116
    /**
117
     * find next non-blank and non-comment line and associate with him
118
     *
119
     * @param string[] $fileLines
120
     * @param int $key
121
     */
122 6
    public function addCommentOrBlankLineAsKey(array $fileLines, int $key): void
123
    {
124 6
        $currentKey = $key;
125 6
        $arrayKeys = array_keys($fileLines);
126 6
        $lastKey = end($arrayKeys);
127 6
        while ($key < $lastKey) {
128 6
            $key++;
129 6
            $line = $fileLines[$key];
130
131
            /*
132
             * if next line is single dash then add key as dashes
133
             * imports:
134
             *     -
135
             *         resource: parameters.yml
136
             * ⬅ you are here
137
             *     -    ⬅ this is next line
138
             *         resource: security.yml
139
            */
140 6
            if (YamlService::hasLineOnlyOneDash($fileLines[$key])) {
141 3
                $this->parentKeys[] = self::KEY_DASH . $currentKey;
142 3
                break;
143
            }
144
145 6
            if (YamlService::isLineNotBlank($line) && YamlService::isLineComment($line) === false) {
146 5
                $lineKey = $this->getLineKey($fileLines, $key);
147
148 5
                $this->parentKeys[] = $lineKey . self::KEY_COMMENT_OR_EMPTY_LINE . $key;
149 5
                $this->addArrayKeyWithValueToSeparatedArray($fileLines, $key);
150
151 5
                break;
152
            }
153
        }
154
155
        // comment/blank line hasn't element to associate
156 6
        if ($key === $lastKey) {
157 6
            $this->parentKeys[] = self::KEY_LAST_ELEMENT . $currentKey;
158
        }
159 6
    }
160
161
    /**
162
     * Transform path to line value to hierarchy multidimensional path
163
     *
164
     * @return string[]
165
     *
166
     * @example
167
     * $parentLineKeys = ['foo', 'bar', 'baz']
168
     * $line = 'fooBar'
169
     *
170
     * to
171
     *
172
     * [
173
     *  'baz' => [
174
     *      'bar' => [
175
     *          'foo' => 'fooBar'
176
     *      ]
177
     *  ]
178
     * ]
179
     */
180 6
    public function getPathToLineGradually(): array
181
    {
182 6
        $pathToComment = [];
183 6
        $previousKey = null;
184
185 6
        foreach ($this->parentKeys as $lineKey) {
186 6
            if ($previousKey === null) {
187
                $pathToComment = [
188 6
                    $lineKey => $this->value,
189
                ];
190
            } else {
191 6
                $pathToComment[$lineKey] = [
192 6
                    $previousKey => $pathToComment[$previousKey],
193
                ];
194 6
                unset($pathToComment[$previousKey]);
195
            }
196 6
            $previousKey = $lineKey;
197
        }
198
199 6
        return $pathToComment;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $pathToComment returns an array which contains values of type string[] which are incompatible with the documented value type string.
Loading history...
200
    }
201
202
    /**
203
     * @param string[] $fileLines
204
     * @param int $key
205
     * @return string
206
     */
207 6
    private function getLineKey(array $fileLines, int $key): string
208
    {
209 6
        $currentLine = $fileLines[$key];
210 6
        $currentLineWithoutDash = str_replace('-', ' ', $currentLine);
211 6
        $countOfCurrentRowIndents = YamlService::rowIndentsOf($currentLineWithoutDash);
212 6
        if (YamlService::hasLineDashOnStartOfLine(trim($currentLine)) &&
213 6
            YamlService::hasLineThreeDashesOnStartOfLine(trim($currentLine)) === false &&
214 6
            YamlService::hasLineOnlyOneDash($currentLine)
215
        ) {
216
            [$indentsBeforeDash, $currentLine] = explode('-', $currentLine);
217
        }
218
219
        /**
220
         * if line start and end with curly bracket return whole line, e.g.: { someKey: someValue }
221
         */
222 6
        if (YamlService::isCurlyBracketInStartOfString(trim($currentLineWithoutDash)) && YamlService::isCurlyBracketInEndOfString(trim($currentLineWithoutDash))) {
223 5
            return self::KEY_CURLY_BRACKETS . $key;
224
        }
225
226 6
        [$lineKey] = explode(':', $currentLine);
227
228
        /*
229
         * foo: ⬅
230
         *    bar: baz
231
         */
232 6
        if (array_key_exists($key + 1, $fileLines)) {
233 6
            $nextLine = $fileLines[$key + 1];
234 6
            $nextLineWithoutDash = str_replace('-', ' ', $nextLine);
235 6
            $countOfNextRowIndents = YamlService::rowIndentsOf($nextLineWithoutDash);
236
237 6
            if ($countOfCurrentRowIndents < $countOfNextRowIndents) {
238 6
                return $currentLine . self::KEY_COMMON_LINE . $key;
239
            }
240
        }
241
242
        // don't add colon for line without colon, e.g.: "- [seed, ['%faker.seed%']]"
243 6
        $colon = YamlService::hasLineColon($currentLine) ? ':' : '';
244
        // if line is array without key (e.g. "- 'something'") then line can be duplicated, so we need to distinguish them
245 6
        $lineNumber = YamlService::hasLineDashOnStartOfLine(trim($currentLine)) && YamlService::hasLineColon($currentLine) === false ? self::KEY_ARRAY_WITHOUT_KEY . $key : '';
246
247 6
        return $lineNumber . $lineKey . $colon . self::KEY_COMMON_LINE . $key;
248
    }
249
250
    /**
251
     * @param string[] $fileLines
252
     * @param int $key
253
     * @param bool $isCommentOrBlankLine
254
     * @return string|array
255
     */
256 6
    private function getLineValue(array $fileLines, int $key, bool $isCommentOrBlankLine)
257
    {
258 6
        if ($isCommentOrBlankLine) {
259 6
            if (YamlService::isLineBlank($fileLines[$key])) {
260 6
                return self::EMPTY_LINE_DEFAULT_VALUE;
261
            }
262
263 6
            return $fileLines[$key];
264
        }
265
266 6
        $currentLine = $fileLines[$key];
267 6
        $currentLineWithoutDash = str_replace('-', ' ', $currentLine);
268 6
        $countOfCurrentRowIndents = YamlService::rowIndentsOf($currentLineWithoutDash);
269 6
        $explodedCurrentLine = explode(':', $currentLine, 2);
270
271
        /**
272
         * if line start and end with curly bracket return empty string, e.g.: { someKey: someValue }
273
         */
274 6
        if (YamlService::isCurlyBracketInStartOfString(trim($currentLineWithoutDash)) && YamlService::isCurlyBracketInEndOfString(trim($currentLineWithoutDash))) {
275 5
            return $currentLine;
276
        }
277
278
        /*
279
         * foo: ⬅
280
         *    bar: baz
281
         */
282 6
        if (array_key_exists($key + 1, $fileLines)) {
283 6
            $nextLine = $fileLines[$key + 1];
284 6
            $nextLineWithoutDash = str_replace('-', ' ', $nextLine);
285 6
            $countOfNextRowIndents = YamlService::rowIndentsOf($nextLineWithoutDash);
286 6
            if ($countOfCurrentRowIndents < $countOfNextRowIndents) {
287 6
                return [];
288
            }
289
        }
290
291 6
        return YamlService::hasLineValue(trim($currentLine)) ? $explodedCurrentLine[1] : [];
292
    }
293
}
294