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:
Complex classes like Module 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 Module, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
13 | abstract class Module |
||
14 | { |
||
15 | use Macroable; |
||
16 | |||
17 | /** |
||
18 | * The laravel|lumen application instance. |
||
19 | * |
||
20 | * @var \Illuminate\Contracts\Foundation\Application|\Laravel\Lumen\Application |
||
21 | */ |
||
22 | protected $app; |
||
23 | |||
24 | /** |
||
25 | * The module name. |
||
26 | * |
||
27 | * @var |
||
28 | */ |
||
29 | protected $name; |
||
30 | |||
31 | /** |
||
32 | * The module path. |
||
33 | * |
||
34 | * @var string |
||
35 | */ |
||
36 | protected $path; |
||
37 | |||
38 | /** |
||
39 | * @var array of cached Json objects, keyed by filename |
||
40 | */ |
||
41 | protected $moduleJson = []; |
||
42 | /** |
||
43 | * @var CacheManager |
||
44 | */ |
||
45 | private $cache; |
||
46 | /** |
||
47 | * @var Filesystem |
||
48 | 153 | */ |
|
49 | private $files; |
||
50 | 153 | /** |
|
51 | 153 | * @var Translator |
|
52 | 153 | */ |
|
53 | 153 | private $translator; |
|
54 | |||
55 | /** |
||
56 | * The constructor. |
||
57 | * @param Container $app |
||
58 | * @param $name |
||
59 | * @param $path |
||
60 | 1 | */ |
|
61 | View Code Duplication | public function __construct(Container $app, string $name, $path) |
|
62 | 1 | { |
|
63 | $this->name = $name; |
||
64 | $this->path = $path; |
||
65 | $this->cache = $app['cache']; |
||
66 | $this->files = $app['files']; |
||
67 | $this->translator = $app['translator']; |
||
68 | $this->app = $app; |
||
|
|||
69 | } |
||
70 | 2 | ||
71 | /** |
||
72 | 2 | * Get name. |
|
73 | * |
||
74 | * @return string |
||
75 | */ |
||
76 | public function getName() |
||
77 | { |
||
78 | return $this->name; |
||
79 | } |
||
80 | 115 | ||
81 | /** |
||
82 | 115 | * Get name in lower case. |
|
83 | * |
||
84 | * @return string |
||
85 | */ |
||
86 | public function getLowerName() |
||
87 | { |
||
88 | return strtolower($this->name); |
||
89 | } |
||
90 | 101 | ||
91 | /** |
||
92 | 101 | * Get name in studly case. |
|
93 | * |
||
94 | * @return string |
||
95 | */ |
||
96 | public function getStudlyName() |
||
97 | { |
||
98 | return Str::studly($this->name); |
||
99 | } |
||
100 | 6 | ||
101 | /** |
||
102 | 6 | * Get name in snake case. |
|
103 | * |
||
104 | * @return string |
||
105 | */ |
||
106 | public function getSnakeName() |
||
107 | { |
||
108 | return Str::snake($this->name); |
||
109 | } |
||
110 | 2 | ||
111 | /** |
||
112 | 2 | * Get description. |
|
113 | * |
||
114 | * @return string |
||
115 | */ |
||
116 | public function getDescription() |
||
117 | { |
||
118 | return $this->get('description'); |
||
119 | } |
||
120 | 4 | ||
121 | /** |
||
122 | 4 | * Get alias. |
|
123 | * |
||
124 | * @return string |
||
125 | */ |
||
126 | public function getAlias() |
||
127 | { |
||
128 | return $this->get('alias'); |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | * Get priority. |
||
133 | * |
||
134 | * @return string |
||
135 | */ |
||
136 | public function getPriority() |
||
137 | { |
||
138 | return $this->get('priority'); |
||
139 | } |
||
140 | 3 | ||
141 | /** |
||
142 | 3 | * Get module requirements. |
|
143 | * |
||
144 | * @return array |
||
145 | */ |
||
146 | public function getRequires() |
||
147 | { |
||
148 | return $this->get('requires'); |
||
149 | } |
||
150 | 136 | ||
151 | /** |
||
152 | 136 | * Get path. |
|
153 | * |
||
154 | * @return string |
||
155 | */ |
||
156 | public function getPath() |
||
157 | { |
||
158 | return $this->path; |
||
159 | } |
||
160 | |||
161 | /** |
||
162 | * Set path. |
||
163 | * |
||
164 | * @param string $path |
||
165 | * |
||
166 | * @return $this |
||
167 | */ |
||
168 | public function setPath($path) |
||
169 | { |
||
170 | $this->path = $path; |
||
171 | |||
172 | 2 | return $this; |
|
173 | } |
||
174 | 2 | ||
175 | 2 | /** |
|
176 | * Bootstrap the application events. |
||
177 | */ |
||
178 | 2 | public function boot() |
|
179 | { |
||
180 | if (config('modules.register.translations', true) === true) { |
||
181 | $this->registerTranslation(); |
||
182 | 2 | } |
|
183 | 2 | ||
184 | if ($this->isLoadFilesOnBoot()) { |
||
185 | $this->registerFiles(); |
||
186 | } |
||
187 | |||
188 | $this->fireEvent('boot'); |
||
189 | } |
||
190 | 2 | ||
191 | /** |
||
192 | 2 | * Register module's translation. |
|
193 | * |
||
194 | 2 | * @return void |
|
195 | */ |
||
196 | 2 | protected function registerTranslation() |
|
197 | 2 | { |
|
198 | $lowerName = $this->getLowerName(); |
||
199 | 2 | ||
200 | $langPath = $this->getPath() . '/Resources/lang'; |
||
201 | |||
202 | if (is_dir($langPath)) { |
||
203 | $this->loadTranslationsFrom($langPath, $lowerName); |
||
204 | } |
||
205 | } |
||
206 | |||
207 | /** |
||
208 | 35 | * Get json contents from the cache, setting as needed. |
|
209 | * |
||
210 | 35 | * @param string $file |
|
211 | 33 | * |
|
212 | * @return Json |
||
213 | */ |
||
214 | public function json($file = null) : Json |
||
215 | 35 | { |
|
216 | 35 | if ($file === null) { |
|
217 | $file = 'module.json'; |
||
218 | } |
||
219 | |||
220 | return Arr::get($this->moduleJson, $file, function () use ($file) { |
||
221 | return $this->moduleJson[$file] = new Json($this->getPath() . '/' . $file, $this->files); |
||
222 | }); |
||
223 | } |
||
224 | |||
225 | /** |
||
226 | * Get a specific data from json file by given the key. |
||
227 | 24 | * |
|
228 | * @param string $key |
||
229 | 24 | * @param null $default |
|
230 | * |
||
231 | * @return mixed |
||
232 | */ |
||
233 | public function get(string $key, $default = null) |
||
234 | { |
||
235 | return $this->json()->get($key, $default); |
||
236 | } |
||
237 | |||
238 | /** |
||
239 | * Get a specific data from composer.json file by given the key. |
||
240 | 2 | * |
|
241 | * @param $key |
||
242 | 2 | * @param null $default |
|
243 | * |
||
244 | * @return mixed |
||
245 | */ |
||
246 | public function getComposerAttr($key, $default = null) |
||
247 | { |
||
248 | return $this->json('composer.json')->get($key, $default); |
||
249 | } |
||
250 | |||
251 | /** |
||
252 | * Register the module. |
||
253 | */ |
||
254 | public function register() |
||
255 | { |
||
256 | $this->registerAliases(); |
||
257 | |||
258 | $this->registerProviders(); |
||
259 | |||
260 | if ($this->isLoadFilesOnBoot() === false) { |
||
261 | $this->registerFiles(); |
||
262 | } |
||
263 | |||
264 | $this->fireEvent('register'); |
||
265 | } |
||
266 | 8 | ||
267 | /** |
||
268 | 8 | * Register the module event. |
|
269 | 8 | * |
|
270 | * @param string $event |
||
271 | */ |
||
272 | protected function fireEvent($event) |
||
273 | { |
||
274 | $this->app['events']->dispatch(sprintf('modules.%s.' . $event, $this->getLowerName()), [$this]); |
||
275 | } |
||
276 | /** |
||
277 | * Register the aliases from this module. |
||
278 | */ |
||
279 | abstract public function registerAliases(); |
||
280 | |||
281 | /** |
||
282 | * Register the service providers from this module. |
||
283 | */ |
||
284 | abstract public function registerProviders(); |
||
285 | |||
286 | /** |
||
287 | * Get the path to the cached *_module.php file. |
||
288 | * |
||
289 | * @return string |
||
290 | */ |
||
291 | abstract public function getCachedServicesPath(); |
||
292 | |||
293 | /** |
||
294 | * Register the files from this module. |
||
295 | */ |
||
296 | protected function registerFiles() |
||
297 | { |
||
298 | foreach ($this->get('files', []) as $file) { |
||
299 | include $this->path . '/' . $file; |
||
300 | } |
||
301 | } |
||
302 | 3 | ||
303 | /** |
||
304 | 3 | * Handle call __toString. |
|
305 | * |
||
306 | * @return string |
||
307 | */ |
||
308 | public function __toString() |
||
309 | { |
||
310 | return $this->getStudlyName(); |
||
311 | } |
||
312 | |||
313 | /** |
||
314 | 11 | * Determine whether the given status same with the current module status. |
|
315 | * |
||
316 | 11 | * @param $status |
|
317 | * |
||
318 | * @return bool |
||
319 | */ |
||
320 | public function isStatus($status) : bool |
||
324 | 6 | ||
325 | /** |
||
326 | 6 | * Determine whether the current module activated. |
|
327 | * |
||
328 | * @return bool |
||
329 | */ |
||
330 | public function enabled() : bool |
||
334 | 2 | ||
335 | /** |
||
336 | 2 | * Determine whether the current module not disabled. |
|
337 | * |
||
338 | * @return bool |
||
339 | */ |
||
340 | public function disabled() : bool |
||
344 | |||
345 | /** |
||
346 | 6 | * Set active state for current module. |
|
347 | * |
||
348 | 6 | * @param $active |
|
349 | * |
||
350 | * @return bool |
||
351 | */ |
||
352 | public function setActive($active) |
||
356 | 3 | ||
357 | /** |
||
358 | 3 | * Disable the current module. |
|
359 | 3 | */ |
|
360 | public function disable() |
||
369 | 3 | ||
370 | /** |
||
371 | 3 | * Enable the current module. |
|
372 | 3 | */ |
|
373 | public function enable() |
||
382 | 2 | ||
383 | /** |
||
384 | 2 | * Delete the current module. |
|
385 | * |
||
386 | * @return bool |
||
387 | */ |
||
388 | public function delete() |
||
392 | |||
393 | /** |
||
394 | 3 | * Get extra path. |
|
395 | * |
||
396 | 3 | * @param string $path |
|
397 | * |
||
398 | * @return string |
||
399 | */ |
||
400 | public function getExtraPath(string $path) : string |
||
404 | |||
405 | /** |
||
406 | * Handle call to __get method. |
||
407 | * |
||
408 | * @param $key |
||
409 | * |
||
410 | * @return mixed |
||
411 | */ |
||
412 | public function __get($key) |
||
416 | 2 | ||
417 | /** |
||
418 | 2 | * Check if can load files of module on boot method. |
|
419 | * |
||
420 | 2 | * @return bool |
|
421 | */ |
||
422 | protected function isLoadFilesOnBoot() |
||
428 | 6 | ||
429 | private function flushCache(): void |
||
435 | |||
436 | /** |
||
437 | * Register a translation file namespace. |
||
438 | * |
||
439 | * @param string $path |
||
440 | * @param string $namespace |
||
441 | * @return void |
||
442 | */ |
||
443 | private function loadTranslationsFrom(string $path, string $namespace): void |
||
447 | } |
||
448 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..