1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace YamlStandards\Model\YamlAlphabetical; |
6
|
|
|
|
7
|
|
|
use YamlStandards\Model\Component\Parser\YamlParser; |
8
|
|
|
use YamlStandards\Model\Component\Parser\YamlParserLineData; |
9
|
|
|
use YamlStandards\Model\Component\YamlService; |
10
|
|
|
|
11
|
|
|
class YamlAlphabeticalDataFactory |
12
|
|
|
{ |
13
|
|
|
public const REGEX_KEY_COMMON_LINE_WITH_NUMBER_AT_END = '/' . YamlParserLineData::KEY_COMMON_LINE . '\d+$/'; |
14
|
|
|
public const REGEX_KEY_COMMENT_OR_EMPTY_LINE_WITH_NUMBER_AT_END = '/' . YamlParserLineData::KEY_COMMENT_OR_EMPTY_LINE . '\d+$/'; |
15
|
|
|
public const REGEX_KEY_DASH_WITH_NUMBER_AT_END = '/' . YamlParserLineData::KEY_DASH . '\d+$/'; |
16
|
|
|
public const REGEX_KEY_EMPTY_ARRAY_WITH_NUMBER_AT_END = '/' . YamlParserLineData::KEY_EMPTY_ARRAY . '\d+$/'; |
17
|
|
|
public const REGEX_KEY_ARRAY_WITHOUT_KEY_WITH_NUMBER_AT_END = '/^' . YamlParserLineData::KEY_ARRAY_WITHOUT_KEY . '\d+/'; |
18
|
|
|
public const REGEX_KEY_CURLY_BRACKETS_WITH_NUMBER_AT_END = '/' . YamlParserLineData::KEY_CURLY_BRACKETS . '\d+$/'; |
19
|
|
|
|
20
|
|
|
public const REGEX_VALUE_EMPTY_LINE = '/' . YamlParserLineData::EMPTY_LINE_DEFAULT_VALUE . '$/'; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* @var int |
24
|
|
|
*/ |
25
|
|
|
private static $index; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* @param string $pathToYamlFile |
29
|
|
|
* @param int $depth |
30
|
|
|
* @return string[] |
31
|
|
|
*/ |
32
|
6 |
|
public static function getCorrectYamlLines(string $pathToYamlFile, int $depth): array |
33
|
|
|
{ |
34
|
6 |
|
self::$index = 0; // start from 0 in every file |
35
|
|
|
|
36
|
6 |
|
$yamlArrayData = YamlParser::getYamlParsedDataFromFile($pathToYamlFile); |
37
|
6 |
|
$yamlArrayDataSorted = self::sortArray($yamlArrayData, $depth); |
38
|
|
|
|
39
|
6 |
|
return self::createRightSortedYamlLines($yamlArrayDataSorted); |
|
|
|
|
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @param string[] $yamlArrayData |
44
|
|
|
* @param int $depth |
45
|
|
|
* @return string[] |
46
|
|
|
*/ |
47
|
6 |
|
private static function sortArray(array $yamlArrayData, int $depth): array |
48
|
|
|
{ |
49
|
6 |
|
if ($depth > 0) { |
50
|
6 |
|
$yamlArrayData = self::sortArrayKeyWithUnderscoresAsFirst($yamlArrayData); |
51
|
|
|
|
52
|
6 |
|
if ($depth > 1) { |
53
|
6 |
|
foreach ($yamlArrayData as $key => $value) { |
54
|
6 |
|
if (is_array($value)) { |
55
|
6 |
|
$yamlArrayData[$key] = self::recursiveKsort($value, $depth); |
56
|
|
|
} |
57
|
|
|
} |
58
|
|
|
} |
59
|
|
|
} |
60
|
|
|
|
61
|
6 |
|
return $yamlArrayData; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* @param string[] $yamlArrayData |
66
|
|
|
* @param int $depth |
67
|
|
|
* @param int $currentDepth |
68
|
|
|
* @return string[] |
69
|
|
|
*/ |
70
|
6 |
|
private static function recursiveKsort(array $yamlArrayData, int $depth, int $currentDepth = 1): array |
71
|
|
|
{ |
72
|
6 |
|
$yamlArrayData = self::sortArrayKeyWithUnderscoresAsFirst($yamlArrayData); |
73
|
6 |
|
foreach ($yamlArrayData as $key => $value) { |
74
|
6 |
|
if (is_array($value)) { |
75
|
6 |
|
if ($currentDepth <= $depth) { |
76
|
6 |
|
$yamlArrayData[$key] = self::recursiveKsort($value, $depth, $currentDepth + 1); |
77
|
|
|
} |
78
|
|
|
} |
79
|
|
|
} |
80
|
|
|
|
81
|
6 |
|
return $yamlArrayData; |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* @param string[] $yamlArrayData |
86
|
|
|
* @return string[]|string[][] |
87
|
|
|
*/ |
88
|
6 |
|
private static function sortArrayKeyWithUnderscoresAsFirst(array $yamlArrayData): array |
89
|
|
|
{ |
90
|
6 |
|
$arrayWithUnderscoreKeys = array_filter($yamlArrayData, [YamlService::class, 'hasArrayKeyUnderscoreAsFirstCharacter'], ARRAY_FILTER_USE_KEY); |
91
|
6 |
|
$arrayWithOtherKeys = array_filter($yamlArrayData, [YamlService::class, 'hasNotArrayKeyUnderscoreAsFirstCharacter'], ARRAY_FILTER_USE_KEY); |
92
|
|
|
|
93
|
6 |
|
uksort($arrayWithUnderscoreKeys, ['self', 'sortArrayAlphabetical']); |
94
|
6 |
|
uksort($arrayWithOtherKeys, ['self', 'sortArrayAlphabetical']); |
95
|
|
|
|
96
|
6 |
|
return array_merge($arrayWithUnderscoreKeys, $arrayWithOtherKeys); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* @param string $key1 |
101
|
|
|
* @param string $key2 |
102
|
|
|
* @return int |
103
|
|
|
*/ |
104
|
6 |
|
private static function sortArrayAlphabetical(string $key1, string $key2): int |
105
|
|
|
{ |
106
|
|
|
// remove added text for empty line and comment line |
107
|
6 |
|
$key1WithoutNumberAtEnd = preg_replace(self::REGEX_KEY_COMMENT_OR_EMPTY_LINE_WITH_NUMBER_AT_END, '', $key1); |
108
|
6 |
|
$key2WithoutNumberAtEnd = preg_replace(self::REGEX_KEY_COMMENT_OR_EMPTY_LINE_WITH_NUMBER_AT_END, '', $key2); |
109
|
|
|
|
110
|
|
|
// add key number to end for fix situation when keys are same |
111
|
6 |
|
preg_match('/\d+$/', $key1WithoutNumberAtEnd, $key1NumberAtEnd); |
112
|
6 |
|
preg_match('/\d+$/', $key2WithoutNumberAtEnd, $key2NumberAtEnd); |
113
|
6 |
|
$key1NumberAtEnd = count($key1NumberAtEnd) === 0 ? 0 : reset($key1NumberAtEnd); |
114
|
6 |
|
$key2NumberAtEnd = count($key2NumberAtEnd) === 0 ? 0 : reset($key2NumberAtEnd); |
115
|
|
|
|
116
|
6 |
|
$key1WithoutNumberAtEnd = preg_match(self::REGEX_KEY_COMMON_LINE_WITH_NUMBER_AT_END, $key1WithoutNumberAtEnd) === 0 ? $key1WithoutNumberAtEnd : preg_replace(self::REGEX_KEY_COMMON_LINE_WITH_NUMBER_AT_END, '', $key1WithoutNumberAtEnd); |
117
|
6 |
|
$key1WithoutNumberAtEnd = preg_match(self::REGEX_KEY_ARRAY_WITHOUT_KEY_WITH_NUMBER_AT_END, $key1WithoutNumberAtEnd) === 0 ? $key1WithoutNumberAtEnd : preg_replace(self::REGEX_KEY_ARRAY_WITHOUT_KEY_WITH_NUMBER_AT_END, '', $key1WithoutNumberAtEnd); |
118
|
|
|
|
119
|
6 |
|
$key2WithoutNumberAtEnd = preg_match(self::REGEX_KEY_COMMON_LINE_WITH_NUMBER_AT_END, $key2WithoutNumberAtEnd) === 0 ? $key2WithoutNumberAtEnd : preg_replace(self::REGEX_KEY_COMMON_LINE_WITH_NUMBER_AT_END, '', $key2WithoutNumberAtEnd); |
120
|
6 |
|
$key2WithoutNumberAtEnd = preg_match(self::REGEX_KEY_ARRAY_WITHOUT_KEY_WITH_NUMBER_AT_END, $key2WithoutNumberAtEnd) === 0 ? $key2WithoutNumberAtEnd : preg_replace(self::REGEX_KEY_ARRAY_WITHOUT_KEY_WITH_NUMBER_AT_END, '', $key2WithoutNumberAtEnd); |
121
|
|
|
|
122
|
|
|
/* |
123
|
|
|
* add exclamation mark (!) to penultimate position in string for fix order for "dot" key scenario, e.g: |
124
|
|
|
* foo.bar: |
125
|
|
|
* foo.bar.baz: |
126
|
|
|
* ":" is in alphabetical higher as ".", https://s2799303.files.wordpress.com/2013/08/ascii-codes-table1.jpg |
127
|
|
|
*/ |
128
|
6 |
|
$key1WithoutNumberAtEnd = substr_replace($key1WithoutNumberAtEnd, '!', -1, 0); |
129
|
6 |
|
$key2WithoutNumberAtEnd = substr_replace($key2WithoutNumberAtEnd, '!', -1, 0); |
130
|
|
|
|
131
|
6 |
|
$key1WithoutNumberAtEnd .= $key1NumberAtEnd; |
132
|
6 |
|
$key2WithoutNumberAtEnd .= $key2NumberAtEnd; |
133
|
|
|
|
134
|
6 |
|
if ($key1WithoutNumberAtEnd === $key2WithoutNumberAtEnd) { |
135
|
5 |
|
return strnatcmp(trim($key2), trim($key1)); |
136
|
|
|
} |
137
|
|
|
|
138
|
6 |
|
return strnatcmp(trim($key1WithoutNumberAtEnd), trim($key2WithoutNumberAtEnd)); |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* @param string[]|string[][] $sortedYamlData |
143
|
|
|
* @return string[] |
144
|
|
|
*/ |
145
|
6 |
|
private static function createRightSortedYamlLines(array $sortedYamlData): array |
146
|
|
|
{ |
147
|
6 |
|
$rightSortedYamlLines = []; |
148
|
|
|
|
149
|
6 |
|
foreach ($sortedYamlData as $yamlKey => $yamlValue) { |
150
|
6 |
|
if (is_array($yamlValue) === false && ($yamlValue === YamlParserLineData::EMPTY_LINE_DEFAULT_VALUE || YamlService::isLineComment($yamlValue))) { |
|
|
|
|
151
|
6 |
|
$rightSortedYamlLines[] = preg_match(self::REGEX_VALUE_EMPTY_LINE, $yamlValue) === 0 ? $yamlValue : preg_replace(self::REGEX_VALUE_EMPTY_LINE, '', $yamlValue); |
|
|
|
|
152
|
|
|
|
153
|
6 |
|
continue; |
154
|
|
|
} |
155
|
|
|
|
156
|
6 |
|
$yamlKey = is_int($yamlKey) ? (string)$yamlKey : $yamlKey; |
157
|
6 |
|
if (preg_match(self::REGEX_KEY_COMMENT_OR_EMPTY_LINE_WITH_NUMBER_AT_END, $yamlKey) === 0 && |
158
|
6 |
|
preg_match(self::REGEX_KEY_EMPTY_ARRAY_WITH_NUMBER_AT_END, $yamlKey) === 0 |
159
|
|
|
) { |
160
|
6 |
|
$key = preg_match(self::REGEX_KEY_COMMON_LINE_WITH_NUMBER_AT_END, $yamlKey) === 0 ? $yamlKey : preg_replace(self::REGEX_KEY_COMMON_LINE_WITH_NUMBER_AT_END, '', $yamlKey); |
161
|
6 |
|
$key = preg_match(self::REGEX_KEY_DASH_WITH_NUMBER_AT_END, $key) === 0 ? $key : preg_replace(self::REGEX_KEY_DASH_WITH_NUMBER_AT_END, '-', $key); |
162
|
6 |
|
$key = preg_match(self::REGEX_KEY_CURLY_BRACKETS_WITH_NUMBER_AT_END, $key) === 0 ? $key : preg_replace(self::REGEX_KEY_CURLY_BRACKETS_WITH_NUMBER_AT_END, '', $key); |
163
|
6 |
|
$key = preg_match(self::REGEX_KEY_ARRAY_WITHOUT_KEY_WITH_NUMBER_AT_END, $key) === 0 ? $key : preg_replace(self::REGEX_KEY_ARRAY_WITHOUT_KEY_WITH_NUMBER_AT_END, '', $key); |
164
|
6 |
|
$value = is_array($yamlValue) ? '' : $yamlValue; |
165
|
6 |
|
$rightSortedYamlLines[] = $key . $value; |
166
|
|
|
|
167
|
6 |
|
self::$index++; |
168
|
|
|
} |
169
|
|
|
|
170
|
6 |
|
if (is_array($yamlValue)) { |
171
|
6 |
|
$result = self::createRightSortedYamlLines($yamlValue); |
172
|
6 |
|
$rightSortedYamlLines = array_merge($rightSortedYamlLines, $result); |
173
|
|
|
} |
174
|
|
|
} |
175
|
|
|
|
176
|
6 |
|
return $rightSortedYamlLines; |
|
|
|
|
177
|
|
|
} |
178
|
|
|
} |
179
|
|
|
|