Passed
Push — master ( b778d8...37fed7 )
by Peter
08:36
created

YamlSortService::sortArray()   A

Complexity

Conditions 6
Paths 2

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 7
c 1
b 0
f 0
nc 2
nop 3
dl 0
loc 16
ccs 8
cts 8
cp 1
crap 6
rs 9.2222
1
<?php
2
3
declare(strict_types=1);
4
5
namespace YamlStandards\Model\YamlAlphabetical;
6
7
use YamlStandards\Model\Component\YamlService;
8
9
class YamlSortService
10
{
11
    /**
12
     * @param string[] $yamlArrayData
13
     * @param int $depth
14
     * @param string[] $prioritizedKeys
15
     * @return string[]
16
     */
17 10
    public static function sortArray(array $yamlArrayData, int $depth, array $prioritizedKeys): array
18
    {
19 10
        if ($depth > 0) {
20 10
            $yamlArrayData = self::sortArrayKeyWithUnderscoresAsFirst($yamlArrayData, $prioritizedKeys);
21
22 10
            foreach ($yamlArrayData as $key => $value) {
23 10
                if (is_array($value)) {
24
                    // ignore "empty_array" key because they not included in file, they are only auxiliary variables
25 10
                    if ($depth > 1 || preg_match(YamlAlphabeticalDataFactory::REGEX_KEY_EMPTY_ARRAY_WITH_NUMBER_AT_END, $key) === 1) {
26 10
                        $yamlArrayData[$key] = self::recursiveKsort($value, $depth, $prioritizedKeys);
27
                    }
28
                }
29
            }
30
        }
31
32 10
        return $yamlArrayData;
33
    }
34
35
    /**
36
     * @param string[] $yamlArrayData
37
     * @param int $depth
38
     * @param string[] $prioritizedKeys
39
     * @param int $currentDepth
40
     * @return string[]
41
     */
42 9
    private static function recursiveKsort(array $yamlArrayData, int $depth, array $prioritizedKeys, int $currentDepth = 2): array
43
    {
44 9
        $yamlArrayData = self::sortArrayKeyWithUnderscoresAsFirst($yamlArrayData, $prioritizedKeys);
45 9
        foreach ($yamlArrayData as $key => $value) {
46 9
            if (is_array($value)) {
47
                // ignore "empty_array" key because they not included in file, they are only auxiliary variables
48 9
                if ($currentDepth < $depth || preg_match(YamlAlphabeticalDataFactory::REGEX_KEY_EMPTY_ARRAY_WITH_NUMBER_AT_END, $key) === 1) {
49 9
                    $yamlArrayData[$key] = self::recursiveKsort($value, $depth, $prioritizedKeys, $currentDepth + 1);
50
                }
51
            }
52
        }
53
54 9
        return $yamlArrayData;
55
    }
56
57
    /**
58
     * @param string[] $yamlArrayData
59
     * @param string[] $prioritizedKeys
60
     * @return string[]|string[][]
61
     */
62 10
    private static function sortArrayKeyWithUnderscoresAsFirst(array $yamlArrayData, array $prioritizedKeys): array
63
    {
64 10
        $arrayWithUnderscoreKeys = array_filter($yamlArrayData, [YamlService::class, 'hasArrayKeyUnderscoreAsFirstCharacter'], ARRAY_FILTER_USE_KEY);
65 10
        $arrayWithOtherKeys = array_filter($yamlArrayData, [YamlService::class, 'hasNotArrayKeyUnderscoreAsFirstCharacter'], ARRAY_FILTER_USE_KEY);
66
67 10
        uksort($arrayWithUnderscoreKeys, ['self', 'sortArrayAlphabetical']);
68 10
        uksort($arrayWithOtherKeys, ['self', 'sortArrayAlphabetical']);
69
70 10
        $arrayData = array_merge($arrayWithUnderscoreKeys, $arrayWithOtherKeys);
71
72 10
        return self::sortArrayElementsByPrioritizedKeys($prioritizedKeys, $arrayData);
73
    }
74
75
    /**
76
     * @param string $key1
77
     * @param string $key2
78
     * @return int
79
     */
80 10
    private static function sortArrayAlphabetical(string $key1, string $key2): int
81
    {
82
        // remove added text for empty line and comment line
83 10
        $key1WithoutNumberAtEnd = preg_replace(YamlAlphabeticalDataFactory::REGEX_KEY_COMMENT_OR_EMPTY_LINE_WITH_NUMBER_AT_END, '', $key1);
84 10
        $key2WithoutNumberAtEnd = preg_replace(YamlAlphabeticalDataFactory::REGEX_KEY_COMMENT_OR_EMPTY_LINE_WITH_NUMBER_AT_END, '', $key2);
85
86
        // add key number to end for fix situation when keys are same
87 10
        preg_match('/\d+$/', $key1WithoutNumberAtEnd, $key1NumberAtEnd);
88 10
        preg_match('/\d+$/', $key2WithoutNumberAtEnd, $key2NumberAtEnd);
89 10
        $key1NumberAtEnd = count($key1NumberAtEnd) === 0 ? 0 : reset($key1NumberAtEnd);
90 10
        $key2NumberAtEnd = count($key2NumberAtEnd) === 0 ? 0 : reset($key2NumberAtEnd);
91
92 10
        $key1WithoutNumberAtEnd = preg_match(YamlAlphabeticalDataFactory::REGEX_KEY_COMMON_LINE_WITH_NUMBER_AT_END, $key1WithoutNumberAtEnd) === 0 ? $key1WithoutNumberAtEnd : preg_replace(YamlAlphabeticalDataFactory::REGEX_KEY_COMMON_LINE_WITH_NUMBER_AT_END, '', $key1WithoutNumberAtEnd);
93 10
        $key1WithoutNumberAtEnd = preg_match(YamlAlphabeticalDataFactory::REGEX_KEY_ARRAY_WITHOUT_KEY_WITH_NUMBER_AT_END, $key1WithoutNumberAtEnd) === 0 ? $key1WithoutNumberAtEnd : preg_replace(YamlAlphabeticalDataFactory::REGEX_KEY_ARRAY_WITHOUT_KEY_WITH_NUMBER_AT_END, '', $key1WithoutNumberAtEnd);
94
95 10
        $key2WithoutNumberAtEnd = preg_match(YamlAlphabeticalDataFactory::REGEX_KEY_COMMON_LINE_WITH_NUMBER_AT_END, $key2WithoutNumberAtEnd) === 0 ? $key2WithoutNumberAtEnd : preg_replace(YamlAlphabeticalDataFactory::REGEX_KEY_COMMON_LINE_WITH_NUMBER_AT_END, '', $key2WithoutNumberAtEnd);
96 10
        $key2WithoutNumberAtEnd = preg_match(YamlAlphabeticalDataFactory::REGEX_KEY_ARRAY_WITHOUT_KEY_WITH_NUMBER_AT_END, $key2WithoutNumberAtEnd) === 0 ? $key2WithoutNumberAtEnd : preg_replace(YamlAlphabeticalDataFactory::REGEX_KEY_ARRAY_WITHOUT_KEY_WITH_NUMBER_AT_END, '', $key2WithoutNumberAtEnd);
97
98
        /*
99
         * add exclamation mark (!) to penultimate position in string for fix order for "dot" key scenario, e.g:
100
         * foo.bar:
101
         * foo.bar.baz:
102
         * ":" is in alphabetical higher as ".", https://s2799303.files.wordpress.com/2013/08/ascii-codes-table1.jpg
103
         */
104 10
        $key1WithoutNumberAtEnd = substr_replace($key1WithoutNumberAtEnd, '!', -1, 0);
105 10
        $key2WithoutNumberAtEnd = substr_replace($key2WithoutNumberAtEnd, '!', -1, 0);
106
107 10
        $key1WithoutNumberAtEnd .= $key1NumberAtEnd;
108 10
        $key2WithoutNumberAtEnd .= $key2NumberAtEnd;
109
110 10
        if ($key1WithoutNumberAtEnd === $key2WithoutNumberAtEnd) {
111 5
            return strnatcmp(trim($key2), trim($key1));
112
        }
113
114 10
        return strnatcmp(trim($key1WithoutNumberAtEnd), trim($key2WithoutNumberAtEnd));
115
    }
116
117
    /**
118
     * @param string[] $prioritizedKeys
119
     * @param string[] $arrayData
120
     * @return string[]
121
     */
122 13
    private static function sortArrayElementsByPrioritizedKeys(array $prioritizedKeys, array $arrayData): array
123
    {
124 13
        $positionTo = 0;
125 13
        foreach ($prioritizedKeys as $prioritizedKey) {
126 4
            $foundKeys = preg_grep('/' . $prioritizedKey . '/', array_keys($arrayData));
127 4
            foreach ($foundKeys as $foundKey) {
128 4
                $positionFrom = (int)array_search($foundKey, array_keys($arrayData), true);
129 4
                self::changeElementPositionInArray($arrayData, $positionFrom, $positionTo);
130 4
                $positionTo++;
131
            }
132
        }
133
134 13
        return $arrayData;
135
    }
136
137
    /**
138
     * @param string[] $array
139
     * @param int $positionFrom
140
     * @param int $positionTo
141
     *
142
     * @link https://stackoverflow.com/a/28831998
143
     */
144 4
    private static function changeElementPositionInArray(array &$array, int $positionFrom, int $positionTo): void
145
    {
146 4
        $p1 = array_splice($array, $positionFrom, 1);
147 4
        $p2 = array_splice($array, 0, $positionTo);
148 4
        $array = array_merge($p2, $p1, $array);
149 4
    }
150
}
151