Completed
Push — master ( 02df2a...023697 )
by Vitaly
02:15
created

StructureSorter::compareStringStructure()   C

Complexity

Conditions 7
Paths 8

Size

Total Lines 40
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 40
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 15
nc 8
nop 2
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 = $this->equalizeStructures($initial, $compared);
0 ignored issues
show
Unused Code introduced by
$maxStructureSize is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
124
125
        // Iterate every structure group
126
        //for ($i = 0; $i < $maxStructureSize; $i++) {
127
        foreach ($initial as $key => $initialGroup) {
128
            $comparedGroup = $compared[$key];
129
130
            // If initial structure has NPCG than it has higher priority
131
            $return = $this->compareCSGData($initialGroup, $comparedGroup, self::G_VARIABLE, 0);
132
133
            if ($return !== 0) {
134
                return $return;
135
            }
136
137
            // Compare NOT starting NPCG length
138
            if ($key > 0 && $initialGroup[0] === 1) {
139
                // If initial structure has NPCG than it has higher priority
140
                $return = $this->compareCSGData($initialGroup, $comparedGroup, self::G_VARIABLE, 1);
141
142
                if ($return !== 0) {
143
                    return $return;
144
                }
145
            }
146
147
            // They are equal continue to next structure group comparison
148
        }
149
150
        // Compare fixed length CGS
151
        $return = $this->compareStructureLengths($initial, $compared, self::G_FIXED);
152
153
        // Fixed CGS are equal
154
        if ($return === 0) {
155
            // Compare variable length CGS
156
            $return = $this->compareStructureLengths($initial, $compared, self::G_VARIABLE);
157
        }
158
159
        return $return;
160
    }
161
162
    /**
163
     * Make CGS equals size.
164
     *
165
     * @param array $initial Initial CGS, will be changed
166
     * @param array $compared Compared CGS, will be changed
167
     *
168
     * @return int Longest CGS size(now they are both equal)
169
     */
170
    protected function equalizeStructures(array &$initial, array &$compared): int
171
    {
172
        $size = max(count($initial), count($compared));
173
174
        // Make structures same size preserving previous existing structure value
175
        for ($i = 1; $i < $size; $i++) {
176
            $this->fillMissingStructureGroup($initial, $i);
177
            $this->fillMissingStructureGroup($compared, $i);
178
        }
179
180
        return $size;
181
    }
182
183
    /**
184
     * Fill CSG with previous group value if not present.
185
     *
186
     * @param array $groups CSG for filling
187
     * @param int   $index  CSG index
188
     */
189
    private function fillMissingStructureGroup(array &$groups, int $index)
190
    {
191
        if (!array_key_exists($index, $groups)) {
192
            $groups[$index] = $groups[$index - 1];
193
        }
194
    }
195
196
    /**
197
     * Compare longer CGS considering that:
198
     * - Shortest fixed CGS should have higher priority
199
     * - Longest variable CGS should have higher priority
200
     *
201
     * @param array $initialGroup  Initial CGS
202
     * @param array $comparedGroup Compared CGS
203
     * @param int   $type          Fixed/Variable CGS
204
     * @param int   $dataIndex CSG data index
205
     *
206
     * @return int 0 if initial CGS is not longer than compared,
207
     *                  otherwise -1/1 depending on CGS type.
208
     */
209
    private function compareCSGData(array $initialGroup, array $comparedGroup, int $type, int $dataIndex = 1)
210
    {
211
        // Compare character group length
212 View Code Duplication
        if ($initialGroup[$dataIndex] > $comparedGroup[$dataIndex]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
213
            return ($type === self::G_FIXED ? 1 : -1);
214
        }
215
216 View Code Duplication
        if ($initialGroup[$dataIndex] < $comparedGroup[$dataIndex]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
217
            return ($type === self::G_FIXED ? -1 : 1);
218
        }
219
220
        // Cannot define
221
        return 0;
222
    }
223
224
    /**
225
     * Compare two character group structure(CGS) length and define
226
     * which one is longer.
227
     *
228
     * @param array $initial Initial CGS
229
     * @param array $compared Compared CGS
230
     * @param int   $type CGS type (Variable|Fixed length)
231
     *
232
     * @return int -1 if initial CGS longer
233
     *             0 if initial and compared CGS are equal
234
     *             1 if compared CGS longer
235
     */
236
    protected function compareStructureLengths(array $initial, array $compared, int $type = self::G_FIXED): int
237
    {
238
        // Iterate character group structures
239
        foreach ($initial as $index => $initialGroup) {
240
            $comparedGroup = $compared[$index];
241
            // Check if character group matches passed character group type
242
            if ($initialGroup[0] === $type) {
243
                $return = $this->compareCSGData($initialGroup, $comparedGroup, $type);
244
245
                // Compare character group length
246
                if ($return !== 0) {
247
                    return $return;
248
                }
249
250
                // Continue to next CGS
251
            }
252
        }
253
254
        // CGS have equal length
255
        return 0;
256
    }
257
}
258