CountryLoader   B
last analyzed

Complexity

Total Complexity 47

Size/Duplication

Total Lines 240
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
dl 0
loc 240
rs 8.64
c 0
b 0
f 0
wmc 47
lcom 1
cbo 2

9 Methods

Rating   Name   Duplication   Size   Complexity  
A country() 0 10 3
A countries() 0 12 4
A where() 0 13 3
B operatorForWhere() 0 20 11
A filter() 0 8 2
C get() 0 30 13
B pluck() 0 25 6
A collapse() 0 14 3
A getFile() 0 8 2

How to fix   Complexity   

Complex Class

Complex classes like CountryLoader often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CountryLoader, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Rinvex\Country;
6
7
use Closure;
8
9
class CountryLoader
10
{
11
    /**
12
     * The countries array.
13
     *
14
     * @var array
15
     */
16
    protected static $countries;
17
18
    /**
19
     * Get the country by it's ISO 3166-1 alpha-2.
20
     *
21
     * @param string $code
22
     * @param bool   $hydrate
23
     *
24
     * @throws \Rinvex\Country\CountryLoaderException
25
     *
26
     * @return \Rinvex\Country\Country|array
27
     */
28
    public static function country($code, $hydrate = true)
29
    {
30
        $code = mb_strtolower($code);
31
32
        if (! isset(static::$countries[$code])) {
33
            static::$countries[$code] = json_decode(static::getFile(__DIR__.'/../resources/data/'.$code.'.json'), true);
34
        }
35
36
        return $hydrate ? new Country(static::$countries[$code]) : static::$countries[$code];
37
    }
38
39
    /**
40
     * Get all countries short-listed.
41
     *
42
     * @param bool $longlist
43
     * @param bool $hydrate
44
     *
45
     * @throws \Rinvex\Country\CountryLoaderException
46
     *
47
     * @return array
48
     */
49
    public static function countries($longlist = false, $hydrate = false)
50
    {
51
        $list = $longlist ? 'longlist' : 'shortlist';
52
53
        if (! isset(static::$countries[$list])) {
54
            static::$countries[$list] = json_decode(static::getFile(__DIR__.'/../resources/data/'.$list.'.json'), true);
55
        }
56
57
        return $hydrate ? array_map(function ($country) {
58
            return new Country($country);
59
        }, static::$countries[$list]) : static::$countries[$list];
60
    }
61
62
    /**
63
     * Filter items by the given key value pair.
64
     *
65
     * @param string $key
66
     * @param mixed  $operator
67
     * @param mixed  $value
68
     *
69
     * @throws \Rinvex\Country\CountryLoaderException
70
     *
71
     * @return array
72
     */
73
    public static function where($key, $operator, $value = null)
74
    {
75
        if (func_num_args() === 2) {
76
            $value = $operator;
77
            $operator = '=';
78
        }
79
80
        if (! isset(static::$countries['longlist'])) {
81
            static::$countries['longlist'] = json_decode(static::getFile(__DIR__.'/../resources/data/longlist.json'), true);
82
        }
83
84
        return static::filter(static::$countries['longlist'], static::operatorForWhere($key, $operator, $value));
85
    }
86
87
    /**
88
     * Get an operator checker callback.
89
     *
90
     * @param string $key
91
     * @param string $operator
92
     * @param mixed  $value
93
     *
94
     * @return \Closure
95
     */
96
    protected static function operatorForWhere($key, $operator, $value)
97
    {
98
        return function ($item) use ($key, $operator, $value) {
99
            $retrieved = static::get($item, $key);
100
101
            switch ($operator) {
102
                default:
103
                case '=':
104
                case '==':  return $retrieved == $value;
105
                case '!=':
106
                case '<>':  return $retrieved != $value;
107
                case '<':   return $retrieved < $value;
108
                case '>':   return $retrieved > $value;
109
                case '<=':  return $retrieved <= $value;
110
                case '>=':  return $retrieved >= $value;
111
                case '===': return $retrieved === $value;
112
                case '!==': return $retrieved !== $value;
113
            }
114
        };
115
    }
116
117
    /**
118
     * Run a filter over each of the items.
119
     *
120
     * @param array         $items
121
     * @param callable|null $callback
122
     *
123
     * @return array
124
     */
125
    protected static function filter($items, callable $callback = null)
126
    {
127
        if ($callback) {
128
            return array_filter($items, $callback, ARRAY_FILTER_USE_BOTH);
129
        }
130
131
        return array_filter($items);
132
    }
133
134
    /**
135
     * Get an item from an array or object using "dot" notation.
136
     *
137
     * @param mixed        $target
138
     * @param string|array $key
139
     * @param mixed        $default
140
     *
141
     * @return mixed
142
     */
143
    protected static function get($target, $key, $default = null)
144
    {
145
        if (is_null($key)) {
146
            return $target;
147
        }
148
149
        $key = is_array($key) ? $key : explode('.', $key);
150
151
        while (($segment = array_shift($key)) !== null) {
152
            if ($segment === '*') {
153
                if (! is_array($target)) {
154
                    return $default instanceof Closure ? $default() : $default;
155
                }
156
157
                $result = static::pluck($target, $key);
158
159
                return in_array('*', $key) ? static::collapse($result) : $result;
160
            }
161
162
            if (is_array($target) && array_key_exists($segment, $target)) {
163
                $target = $target[$segment];
164
            } elseif (is_object($target) && isset($target->{$segment})) {
165
                $target = $target->{$segment};
166
            } else {
167
                return $default instanceof Closure ? $default() : $default;
168
            }
169
        }
170
171
        return $target;
172
    }
173
174
    /**
175
     * Pluck an array of values from an array.
176
     *
177
     * @param array             $array
178
     * @param string|array      $value
179
     * @param string|array|null $key
180
     *
181
     * @return array
182
     */
183
    protected static function pluck($array, $value, $key = null)
184
    {
185
        $results = [];
186
187
        $value = is_string($value) ? explode('.', $value) : $value;
188
189
        $key = is_null($key) || is_array($key) ? $key : explode('.', $key);
190
191
        foreach ($array as $item) {
192
            $itemValue = static::get($item, $value);
193
194
            // If the key is "null", we will just append the value to the array and keep
195
            // looping. Otherwise we will key the array using the value of the key we
196
            // received from the developer. Then we'll return the final array form.
197
            if (is_null($key)) {
198
                $results[] = $itemValue;
199
            } else {
200
                $itemKey = static::get($item, $key);
201
202
                $results[$itemKey] = $itemValue;
203
            }
204
        }
205
206
        return $results;
207
    }
208
209
    /**
210
     * Collapse an array of arrays into a single array.
211
     *
212
     * @param array $array
213
     *
214
     * @return array
215
     */
216
    protected static function collapse($array)
217
    {
218
        $results = [];
219
220
        foreach ($array as $values) {
221
            if (! is_array($values)) {
222
                continue;
223
            }
224
225
            $results = array_merge($results, $values);
226
        }
227
228
        return $results;
229
    }
230
231
    /**
232
     * Get contents of the given file path.
233
     *
234
     * @param string $filePath
235
     *
236
     * @throws \Rinvex\Country\CountryLoaderException
237
     *
238
     * @return string
239
     */
240
    protected static function getFile($filePath)
241
    {
242
        if (! file_exists($filePath)) {
243
            throw CountryLoaderException::invalidCountry();
244
        }
245
246
        return file_get_contents($filePath);
247
    }
248
}
249