1
|
|
|
<?php declare(strict_types=1); |
2
|
|
|
/** |
3
|
|
|
* Created by Vitaly Iegorov <[email protected]>. |
4
|
|
|
* on 05.04.17 at 15:18 |
5
|
|
|
*/ |
6
|
|
|
namespace samsonframework\stringconditiontree; |
7
|
|
|
|
8
|
|
|
/** |
9
|
|
|
* Parametrized strings sorting. |
10
|
|
|
* |
11
|
|
|
* @author Vitaly Egorov <[email protected]> |
12
|
|
|
*/ |
13
|
|
|
class StructureSorter |
14
|
|
|
{ |
15
|
|
|
/** Variable length characters group */ |
16
|
|
|
const G_VARIABLE = 0; |
17
|
|
|
|
18
|
|
|
/** Fixed length characters group */ |
19
|
|
|
const G_FIXED = 1; |
20
|
|
|
|
21
|
|
|
/** @var string Parametrized string start marker */ |
22
|
|
|
protected $parameterStartMarker; |
23
|
|
|
|
24
|
|
|
/** @var string Parametrized string end marker */ |
25
|
|
|
protected $parameterEndMarker; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* StructureSorter constructor. |
29
|
|
|
* |
30
|
|
|
* @param string $parameterStartMarker Parametrized string start marker |
31
|
|
|
* @param string $parameterEndMarker Parametrized string end marker |
32
|
|
|
*/ |
33
|
|
|
public function __construct(string $parameterStartMarker, string $parameterEndMarker) |
34
|
|
|
{ |
35
|
|
|
$this->parameterStartMarker = $parameterStartMarker; |
36
|
|
|
$this->parameterEndMarker = $parameterEndMarker; |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* Sort strings array considering PCG and NPCG string structure. |
41
|
|
|
* |
42
|
|
|
* @param array $input Input array for sorting |
43
|
|
|
* |
44
|
|
|
* @return array Sorted keys array |
45
|
|
|
*/ |
46
|
|
|
public function sortArrayByKeys(array $input): array |
47
|
|
|
{ |
48
|
|
|
// Convert string array keys into structure arrays |
49
|
|
|
$prefixes = array_map([$this, 'getPrefixStructure'], array_keys($input)); |
50
|
|
|
|
51
|
|
|
// Sort parametrized string array according sorting rules |
52
|
|
|
usort($prefixes, [$this, 'compareStringStructure']); |
53
|
|
|
|
54
|
|
|
// Restore initial strings sub-arrays |
55
|
|
|
$result = []; |
56
|
|
|
foreach ($prefixes as $sortingData) { |
57
|
|
|
$result[$sortingData[0][2]] = $input[$sortingData[0][2]]; |
58
|
|
|
} |
59
|
|
|
return $result; |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Build string character group structure considering parametrized |
64
|
|
|
* and not parametrized character groups and their length(PCG, NPCG). |
65
|
|
|
* |
66
|
|
|
* @param string $prefix Prefix string |
67
|
|
|
* |
68
|
|
|
* @return array String character groups structure |
69
|
|
|
*/ |
70
|
|
|
protected function getPrefixStructure(string $prefix): array |
71
|
|
|
{ |
72
|
|
|
/** @var array $structureMatrix String PCG(0)/NPCG(1) structure matrix for comparison */ |
73
|
|
|
$structureMatrix = []; |
74
|
|
|
|
75
|
|
|
// Flags for showing current string character group |
76
|
|
|
/** @var bool $isPCG Flags showing PCG started */ |
77
|
|
|
$isPCG = false; |
78
|
|
|
/** @var bool $isNPCG Flags showing NPCG started */ |
79
|
|
|
$isNPCG = true; |
80
|
|
|
|
81
|
|
|
// Pointer to current CG to count string NPCG length |
82
|
|
|
$currentCG = 0; |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* TODO: Try to find PCG filter :... pattern and process it also as |
86
|
|
|
* PCG with filters should be prioritized over PSG without filter |
87
|
|
|
* even if filter is .* |
88
|
|
|
*/ |
89
|
|
|
|
90
|
|
|
// Iterate string by characters |
91
|
|
|
for ($i = 0, $length = strlen($prefix); $i < $length; $i++) { |
92
|
|
|
if (!$isPCG && $prefix{$i} === $this->parameterStartMarker) { |
93
|
|
|
$isPCG = true; |
94
|
|
|
$isNPCG = false; |
95
|
|
|
$structureMatrix[] = [0, 0, $prefix]; |
96
|
|
|
$currentCG = &$structureMatrix[count($structureMatrix) - 1][1]; |
97
|
|
|
} elseif ($isPCG && $prefix{$i} === $this->parameterEndMarker) { |
98
|
|
|
$isPCG = false; |
99
|
|
|
$isNPCG = true; |
100
|
|
|
} elseif ($isNPCG) { |
101
|
|
|
$isNPCG = false; |
102
|
|
|
$structureMatrix[] = [1, 0, $prefix]; |
103
|
|
|
$currentCG = &$structureMatrix[count($structureMatrix) - 1][1]; |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
// Store current character group length |
107
|
|
|
$currentCG++; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
return $structureMatrix; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Compare string structures. |
115
|
|
|
* |
116
|
|
|
* @param array $initial Initial string structure |
117
|
|
|
* @param array $compared Compared string structure |
118
|
|
|
* |
119
|
|
|
* @return int Result of array elements comparison |
120
|
|
|
*/ |
121
|
|
|
protected function compareStringStructure(array $initial, array $compared): int |
122
|
|
|
{ |
123
|
|
|
$maxStructureSize = max(count($initial), count($compared)); |
124
|
|
|
|
125
|
|
|
// Make structures same size preserving previous existing structure value |
126
|
|
|
for ($i = 1; $i < $maxStructureSize; $i++) { |
127
|
|
|
if (!array_key_exists($i, $initial)) { |
128
|
|
|
$initial[$i] = $initial[$i - 1]; |
129
|
|
|
} |
130
|
|
|
if (!array_key_exists($i, $compared)) { |
131
|
|
|
$compared[$i] = $compared[$i - 1]; |
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
// Iterate every structure group |
136
|
|
|
for ($i = 0; $i < $maxStructureSize; $i++) { |
137
|
|
|
// If initial structure has NPCG than it has higher priority |
138
|
|
|
if ($initial[$i][0] > $compared[$i][0]) { |
139
|
|
|
return -1; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
// If compared structure has NPCG than it has higher priority |
143
|
|
|
if ($initial[$i][0] < $compared[$i][0]) { |
144
|
|
|
return 1; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
// Compare NOT starting NPCG length |
148
|
|
|
if ($i > 0 && $initial[$i][0] === 1) { |
149
|
|
|
if ($initial[$i][1] > $compared[$i][1]) { |
150
|
|
|
return -1; |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
if ($initial[$i][1] < $compared[$i][1]) { |
154
|
|
|
return 1; |
155
|
|
|
} |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
// They are equal continue to next structure group comparison |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
// If both structures are equal compare lengths of NPCG |
162
|
|
View Code Duplication |
for ($i = 0; $i < $maxStructureSize; $i++) { |
|
|
|
|
163
|
|
|
// If current CG is NPCG |
164
|
|
|
if ($initial[$i][0] === 1) { |
165
|
|
|
if ($initial[$i][1] > $compared[$i][1]) { |
166
|
|
|
return 1; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
if ($initial[$i][1] < $compared[$i][1]) { |
170
|
|
|
return -1; |
171
|
|
|
} |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
// Current NPCG character groups have equal length - continue |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
$return = $this->compareStructureLengths($maxStructureSize, $initial, $compared, self::G_FIXED); |
178
|
|
|
if ($return === 0) { |
179
|
|
|
$return = $this->compareStructureLengths($maxStructureSize, $initial, $compared, self::G_VARIABLE); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
return $return; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Compare two character group structure(CGS) length and define |
187
|
|
|
* which one is longer. |
188
|
|
|
* |
189
|
|
|
* @param int $size CGS size |
190
|
|
|
* @param array $initial Initial CGS |
191
|
|
|
* @param array $compared Compared CGS |
192
|
|
|
* @param int $type CGS type (Variable|Fixed length) |
193
|
|
|
* |
194
|
|
|
* @return int -1 if initial CGS longer |
195
|
|
|
* 0 if initial and compared CGS are equal |
196
|
|
|
* 1 if compared CGS longer |
197
|
|
|
*/ |
198
|
|
|
protected function compareStructureLengths(int $size, array $initial, array $compared, int $type = self::G_FIXED): int |
199
|
|
|
{ |
200
|
|
|
// Iterate character group structures |
201
|
|
View Code Duplication |
for ($i = 0; $i < $size; $i++) { |
|
|
|
|
202
|
|
|
// Check if character group matches passed character group type |
203
|
|
|
if ($initial[$i][$type] === $type) { |
204
|
|
|
// Compare character group length |
205
|
|
|
if ($initial[$i][1] > $compared[$i][1]) { |
206
|
|
|
return -1; |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
if ($initial[$i][1] < $compared[$i][1]) { |
210
|
|
|
return 1; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
// Continue to next character group structure |
214
|
|
|
} |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
// Character group structures have equal length |
218
|
|
|
return 0; |
219
|
|
|
} |
220
|
|
|
} |
221
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.