Completed
Push — master ( 8bc203...e5859f )
by recca
09:43
created

UseSortFixer   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 335
Duplicated Lines 8.36 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 58.87%

Importance

Changes 0
Metric Value
wmc 36
lcom 1
cbo 0
dl 28
loc 335
ccs 73
cts 124
cp 0.5887
rs 8.8
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A setGroups() 0 6 1
A setSortDirection() 0 6 1
A setSortType() 0 6 1
A setGroupType() 0 6 1
A setGroupSkipEmpty() 0 6 1
A renderGroup() 0 15 4
C createGroups() 8 35 11
C fix() 0 70 9
C sortGroup() 20 34 7

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace Recca0120\Generator\Fixers;
4
5
// from https://github.com/mmoreram/php-formatter.git
6
class UseSortFixer
7
{
8
    /**
9
     * @var int
10
     *
11
     * Sort type Alphabetic
12
     */
13
    const SORT_TYPE_ALPHABETIC = 'alph';
14
15
    /**
16
     * @var int
17
     *
18
     * Sort type length
19
     */
20
    const SORT_TYPE_LENGTH = 'length';
21
22
    /**
23
     * @var int
24
     *
25
     * Sort direction ascendent
26
     */
27
    const SORT_DIRECTION_ASC = 'asc';
28
29
    /**
30
     * @var int
31
     *
32
     * Sort direction descendent
33
     */
34
    const SORT_DIRECTION_DESC = 'desc';
35
36
    /**
37
     * @var int
38
     *
39
     * Group type one USE
40
     */
41
    const GROUP_TYPE_ONE = 'one';
42
43
    /**
44
     * @var int
45
     *
46
     * Group type each USE
47
     */
48
    const GROUP_TYPE_EACH = 'each';
49
50
    /**
51
     * @var array
52
     *
53
     * Groups
54
     */
55
    private $groups = [];
56
57
    /**
58
     * @var int
59
     *
60
     * Sort type
61
     */
62
    private $sortType = self::SORT_TYPE_ALPHABETIC;
63
64
    /**
65
     * @var int
66
     *
67
     * Sort direction
68
     */
69
    private $sortDirection = self::SORT_DIRECTION_ASC;
70
71
    /**
72
     * @var int
73
     *
74
     * Group type
75
     */
76
    private $groupType = self::GROUP_TYPE_EACH;
77
78
    /**
79
     * @var bool
80
     *
81
     * Skip empty groups
82
     */
83
    private $groupSkipEmpty = false;
84
85
    /**
86
     * Sets Groups.
87
     *
88
     * @param array $groups
89
     *
90
     * @return UseSortFixer
91
     */
92
    public function setGroups($groups)
93
    {
94
        $this->groups = $groups;
95
96
        return $this;
97
    }
98
99
    /**
100
     * Sets SortDirection.
101
     *
102
     * @param mixed $sortDirection
103
     *
104
     * @return UseSortFixer
105
     */
106
    public function setSortDirection($sortDirection)
107
    {
108
        $this->sortDirection = $sortDirection;
109
110
        return $this;
111
    }
112
113
    /**
114
     * Sets SortType.
115
     *
116
     * @param mixed $sortType
117
     *
118
     * @return UseSortFixer
119
     */
120 3
    public function setSortType($sortType)
121
    {
122 3
        $this->sortType = $sortType;
123
124 3
        return $this;
125
    }
126
127
    /**
128
     * Sets GroupType.
129
     *
130
     * @param int $groupType
131
     *
132
     * @return UseSortFixer
133
     */
134
    public function setGroupType($groupType)
135
    {
136
        $this->groupType = $groupType;
137
138
        return $this;
139
    }
140
141
    /**
142
     * Sets GroupSkipEmpty.
143
     *
144
     * @param bool $groupSkipEmpty
145
     *
146
     * @return UseSortFixer
147
     */
148
    public function setGroupSkipEmpty($groupSkipEmpty)
149
    {
150
        $this->groupSkipEmpty = $groupSkipEmpty;
151
152
        return $this;
153
    }
154
155
    /**
156
     * Do the fix. Return the fixed code or false if the code has not changed.
157
     *
158
     * @param string $data
159
     *
160
     * @return string|false
161
     */
162 2
    public function fix($data)
163
    {
164 2
        $regex = '/(\s*(?:(?:\s+use\s+?[\w\\\\\,\s]+?;)+)\s+)/s';
165 2
        preg_match($regex, $data, $results);
166 2
        if (! isset($results[0])) {
167
            return false;
168
        }
169 2
        $result = $results[0];
170 2
        $blocks = explode(';', $result);
171 2
        $namespaces = [];
172 2
        foreach ($blocks as $block) {
173
            /**
174
             * Removing use literal.
175
             */
176 2
            $block = trim(preg_replace('/^\s+use\s+/', '', $block));
177 2
            $namespaces = array_merge(
178 2
                $namespaces,
179 2
                explode(',', $block)
180 2
            );
181 2
        }
182
        /**
183
         * Trim all results of blank spaces and line breaks.
184
         */
185
        $namespaces = array_map(function ($namespace) {
186 2
            return trim($namespace);
187 2
        }, $namespaces);
188
        /**
189
         * If any position becomes empty, removes.
190
         */
191
        $namespaces = array_filter($namespaces, function ($namespace) {
192 2
            return ! empty($namespace);
193 2
        });
194
        /**
195
         * Grouping use statements by blocks defined in blocks variable.
196
         */
197 2
        $groups = $this->createGroups($namespaces);
198
        /*
199
         * Every block is sorted as desired
200
         */
201 2
        foreach ($groups as $groupKey => $group) {
202 2
            if (is_int($groupKey)) {
203
                $subGroupSorted = [];
204
                foreach ($group as $subGroupKey => $subGroup) {
205
                    $subGroupSorted = array_merge($subGroupSorted, $this->sortGroup($subGroup));
206
                }
207
                $groups[$groupKey] = $subGroupSorted;
208
            } else {
209 2
                $groups[$groupKey] = $this->sortGroup($group);
210
            }
211
            //  Remove empty groups (if configured) after the sorting has happened.
212
            //  @see https://github.com/mmoreram/php-formatter/issues/24
213 2
            if (0 === count($groups[$groupKey])) {
214
                unset($groups[$groupKey]);
215
            }
216 2
        }
217 2
        $doubleEOL = PHP_EOL.PHP_EOL;
218 2
        $spaceBetweenGroups = $this->groupSkipEmpty
219 2
            ? PHP_EOL
220 2
            : $doubleEOL;
221 2
        $processedResult = $doubleEOL.trim(implode($spaceBetweenGroups, array_map(
222
                    function ($group) {
223 2
                        return $this->renderGroup($group);
224 2
                    }, $groups)
225 2
            )).$doubleEOL;
226 2
        $fixedData = str_replace($result, $processedResult, $data);
227
228
        return $fixedData !== $data
229 2
            ? $fixedData
230 2
            : false;
231
    }
232
233
    /**
234
     * Create blocks.
235
     *
236
     * @param array $namespaces
237
     *
238
     * @return array Groups
239
     */
240 2
    private function createGroups(array $namespaces)
241
    {
242 2
        $groups = [];
243 2
        foreach ($this->groups as $group) {
244
            if (is_array($group)) {
245
                $groups[] = array_fill_keys($group, []);
246
            } else {
247
                $groups[$group] = [];
248
            }
249 2
        }
250 2
        if (! array_key_exists('_main', $groups)) {
251 2
            $groups = array_merge(
252 2
                ['_main' => []],
253
                $groups
254 2
            );
255 2
        }
256 2
        foreach ($namespaces as $namespace) {
257 2
            foreach ($groups as $groupKey => $group) {
258 2
                if (is_int($groupKey)) {
259
                    foreach ($group as $subGroupKey => $subGroup) {
260 View Code Duplication
                        if (strpos($namespace, $subGroupKey) === 0) {
261
                            array_push($groups[$groupKey][$subGroupKey], $namespace);
262
                            continue 3;
263
                        }
264
                    }
265 2 View Code Duplication
                } elseif (is_string($groupKey) && strpos($namespace, $groupKey) === 0) {
266
                    array_push($groups[$groupKey], $namespace);
267
                    continue 2;
268
                }
269 2
            }
270 2
            array_push($groups['_main'], $namespace);
271 2
        }
272
273 2
        return $groups;
274
    }
275
276
    /**
277
     * Sort a group.
278
     *
279
     * @param array $group
280
     *
281
     * @return array $group Sorted
282
     */
283 2
    private function sortGroup(array $group)
284
    {
285 2
        if (empty($group)) {
286
            return [];
287
        }
288 2
        if ($this->sortType == self::SORT_TYPE_LENGTH) {
289 View Code Duplication
            usort($group, function ($a, $b) {
290 1
                $cmp = strlen($b) - strlen($a);
291 1
                if ($cmp === 0) {
292
                    $a = strtolower($a);
293
                    $b = strtolower($b);
294
                    $cmp = strcmp($b, $a);
295
                }
296
297 1
                return $cmp;
298 2
            });
299 2
        } elseif ($this->sortType == self::SORT_TYPE_ALPHABETIC) {
300 View Code Duplication
            usort($group, function ($a, $b) {
301
                $a = strtolower($a);
302
                $b = strtolower($b);
303
                $cmp = strcmp($b, $a);
304
                if ($cmp === 0) {
305
                    $cmp = strlen($b) - strlen($a);
306
                }
307
308
                return $cmp;
309
            });
310
        }
311 2
        if ($this->sortDirection == self::SORT_DIRECTION_ASC) {
312 2
            $group = array_reverse($group);
313 2
        }
314
315 2
        return $group;
316
    }
317
318
    /**
319
     * Render a group.
320
     *
321
     * @param array $group
322
     *
323
     * @return string Group rendered
324
     */
325 2
    private function renderGroup(array $group)
326
    {
327 2
        if (empty($group)) {
328
            return '';
329
        }
330 2
        if ($this->groupType === self::GROUP_TYPE_EACH) {
331 2
            return implode(PHP_EOL, array_map(function ($namespace) {
332 2
                return 'use '.$namespace.';';
333 2
            }, $group));
334
        } elseif ($this->groupType === self::GROUP_TYPE_ONE) {
335
            $group = implode(','.PHP_EOL.'    ', $group);
336
337
            return 'use '.$group.';';
338
        }
339
    }
340
}
341