Passed
Push — master ( a3a10e...ed4630 )
by Vitaly
03:10
created

StructureSorter::compareStringStructure()   D

Complexity

Conditions 9
Paths 10

Size

Total Lines 41
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 41
rs 4.909
c 0
b 0
f 0
cc 9
eloc 16
nc 10
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);
124
125
        // Iterate every structure group
126
        for ($i = 0; $i < $maxStructureSize; $i++) {
127
            // If initial structure has NPCG than it has higher priority
128
            if ($initial[$i][0] > $compared[$i][0]) {
129
                return -1;
130
            }
131
132
            // If compared structure has NPCG than it has higher priority
133
            if ($initial[$i][0] < $compared[$i][0]) {
134
                return 1;
135
            }
136
137
            // Compare NOT starting NPCG length
138
            if ($i > 0 && $initial[$i][0] === 1) {
139
                if ($initial[$i][1] > $compared[$i][1]) {
140
                    return -1;
141
                }
142
143
                if ($initial[$i][1] < $compared[$i][1]) {
144
                    return 1;
145
                }
146
            }
147
148
            // They are equal continue to next structure group comparison
149
        }
150
151
        // Compare fixed length CGS
152
        $return = $this->compareStructureLengths($maxStructureSize, $initial, $compared, self::G_FIXED);
153
154
        // Fixed CGS are equal
155
        if ($return === 0) {
156
            // Compare variable length CGS
157
            $return = $this->compareStructureLengths($maxStructureSize, $initial, $compared, self::G_VARIABLE);
158
        }
159
160
        return $return;
161
    }
162
163
    /**
164
     * Make CGS equals size.
165
     *
166
     * @param array $initial Initial CGS, will be changed
167
     * @param array $compared Compared CGS, will be changed
168
     *
169
     * @return int Longest CGS size(now they are both equal)
170
     */
171
    protected function equalizeStructures(array &$initial, array &$compared): int
172
    {
173
        $size = max(count($initial), count($compared));
174
175
        // Make structures same size preserving previous existing structure value
176
        for ($i = 1; $i < $size; $i++) {
177
            if (!array_key_exists($i, $initial)) {
178
                $initial[$i] = $initial[$i - 1];
179
            }
180
            if (!array_key_exists($i, $compared)) {
181
                $compared[$i] = $compared[$i - 1];
182
            }
183
        }
184
185
        return $size;
186
    }
187
188
    /**
189
     * Compare two character group structure(CGS) length and define
190
     * which one is longer.
191
     *
192
     * @param int   $size CGS size
193
     * @param array $initial Initial CGS
194
     * @param array $compared Compared CGS
195
     * @param int   $type CGS type (Variable|Fixed length)
196
     *
197
     * @return int -1 if initial CGS longer
198
     *             0 if initial and compared CGS are equal
199
     *             1 if compared CGS longer
200
     */
201
    protected function compareStructureLengths(int $size, array $initial, array $compared, int $type = self::G_FIXED): int
202
    {
203
        // Iterate character group structures
204
        for ($i = 0; $i < $size; $i++) {
205
            // Check if character group matches passed character group type
206
            if ($initial[$i][0] === $type) {
207
                // Compare character group length
208 View Code Duplication
                if ($initial[$i][1] > $compared[$i][1]) {
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...
209
                    /**
210
                     * Shortest fixed CGS should have higher priority
211
                     * Longest variable CGS should have higher priority
212
                     */
213
                    return ($type === self::G_FIXED ? 1 : -1);
214
                }
215
216 View Code Duplication
                if ($initial[$i][1] < $compared[$i][1]) {
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
                // Continue to next character group structure
221
            }
222
        }
223
224
        // Character group structures have equal length
225
        return 0;
226
    }
227
}
228