Completed
Pull Request — master (#1160)
by
unknown
08:22 queued 06:43
created

Module::getKebabName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Nwidart\Modules;
4
5
use Illuminate\Cache\CacheManager;
6
use Illuminate\Container\Container;
7
use Illuminate\Filesystem\Filesystem;
8
use Illuminate\Support\Arr;
9
use Illuminate\Support\Str;
10
use Illuminate\Support\Traits\Macroable;
11
use Illuminate\Translation\Translator;
12
use Nwidart\Modules\Contracts\ActivatorInterface;
13
14
abstract class Module
15
{
16
    use Macroable;
17
18
    /**
19
     * The laravel|lumen application instance.
20
     *
21
     * @var \Illuminate\Contracts\Foundation\Application|\Laravel\Lumen\Application
22
     */
23
    protected $app;
24
25
    /**
26
     * The module name.
27
     *
28
     * @var
29
     */
30
    protected $name;
31
32
    /**
33
     * The module path.
34
     *
35
     * @var string
36
     */
37
    protected $path;
38
39
    /**
40
     * @var array of cached Json objects, keyed by filename
41
     */
42
    protected $moduleJson = [];
43
    /**
44
     * @var CacheManager
45
     */
46
    private $cache;
47
    /**
48
     * @var Filesystem
49
     */
50
    private $files;
51
    /**
52
     * @var Translator
53
     */
54
    private $translator;
55
    /**
56
     * @var ActivatorInterface
57
     */
58
    private $activator;
59
60
    /**
61
     * The constructor.
62
     * @param Container $app
63
     * @param $name
64
     * @param $path
65
     */
66 190 View Code Duplication
    public function __construct(Container $app, string $name, $path)
67
    {
68 190
        $this->name = $name;
69 190
        $this->path = $path;
70 190
        $this->cache = $app['cache'];
71 190
        $this->files = $app['files'];
72 190
        $this->translator = $app['translator'];
73 190
        $this->activator = $app[ActivatorInterface::class];
74 190
        $this->app = $app;
0 ignored issues
show
Documentation Bug introduced by
It seems like $app of type object<Illuminate\Container\Container> is incompatible with the declared type object<Illuminate\Contra...avel\Lumen\Application> of property $app.

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..

Loading history...
75 190
    }
76
77
    /**
78
     * Get name.
79
     *
80
     * @return string
81
     */
82 132
    public function getName(): string
83
    {
84 132
        return $this->name;
85
    }
86
87
    /**
88
     * Get name in lower case.
89
     *
90
     * @return string
91
     */
92 146
    public function getLowerName(): string
93
    {
94 146
        return strtolower($this->name);
95
    }
96
97
    /**
98
     * Get name in studly case.
99
     *
100
     * @return string
101
     */
102 131
    public function getStudlyName(): string
103
    {
104 131
        return Str::studly($this->name);
105
    }
106
107
    /**
108
     * Get name in snake case.
109
     *
110
     * @return string
111
     */
112 6
    public function getSnakeName(): string
113
    {
114 6
        return Str::snake($this->name);
115
    }
116
117
    /**
118
     * Get name in kebab case.
119
     *
120
     * @return string
121
     */
122 2
    public function getKebabName(): string
123
    {
124 2
        return Str::kebab($this->name);
125
    }
126
127
    /**
128
     * Get description.
129
     *
130
     * @return string
131
     */
132 4
    public function getDescription(): string
133
    {
134 4
        return $this->get('description');
135
    }
136
137
    /**
138
     * Get alias.
139
     *
140
     * @return string
141
     */
142
    public function getAlias(): string
143
    {
144
        return $this->get('alias');
145
    }
146
147
    /**
148
     * Get priority.
149
     *
150
     * @return string
151
     */
152 3
    public function getPriority(): string
153
    {
154 3
        return $this->get('priority');
155
    }
156
157
    /**
158
     * Get module requirements.
159
     *
160
     * @return array
161
     */
162 152
    public function getRequires(): array
163
    {
164 152
        return $this->get('requires');
165
    }
166
167
    /**
168
     * Get path.
169
     *
170
     * @return string
171
     */
172
    public function getPath(): string
173
    {
174
        return $this->path;
175
    }
176
177
    /**
178
     * Set path.
179
     *
180
     * @param string $path
181
     *
182
     * @return $this
183
     */
184 2
    public function setPath($path): Module
185
    {
186 2
        $this->path = $path;
187 2
188
        return $this;
189
    }
190 2
191
    /**
192
     * Bootstrap the application events.
193
     */
194 2
    public function boot(): void
195 2
    {
196
        if (config('modules.register.translations', true) === true) {
197
            $this->registerTranslation();
198
        }
199
200
        if ($this->isLoadFilesOnBoot()) {
201
            $this->registerFiles();
202 2
        }
203
204 2
        $this->fireEvent('boot');
205
    }
206 2
207
    /**
208 2
     * Register module's translation.
209 2
     *
210
     * @return void
211 2
     */
212
    protected function registerTranslation(): void
213
    {
214
        $lowerName = $this->getLowerName();
215
216
        $langPath = $this->getPath() . '/Resources/lang';
217
218
        if (is_dir($langPath)) {
219
            $this->loadTranslationsFrom($langPath, $lowerName);
220 124
        }
221
    }
222 124
223 122
    /**
224
     * Get json contents from the cache, setting as needed.
225
     *
226
     * @param string $file
227 124
     *
228 124
     * @return Json
229
     */
230
    public function json($file = null) : Json
231
    {
232
        if ($file === null) {
233
            $file = 'module.json';
234
        }
235
236
        return Arr::get($this->moduleJson, $file, function () use ($file) {
237
            return $this->moduleJson[$file] = new Json($this->getPath() . '/' . $file, $this->files);
238
        });
239 14
    }
240
241 14
    /**
242
     * Get a specific data from json file by given the key.
243
     *
244
     * @param string $key
245
     * @param null $default
246
     *
247
     * @return mixed
248
     */
249
    public function get(string $key, $default = null)
250
    {
251
        return $this->json()->get($key, $default);
252 2
    }
253
254 2
    /**
255
     * Get a specific data from composer.json file by given the key.
256
     *
257
     * @param $key
258
     * @param null $default
259
     *
260
     * @return mixed
261
     */
262
    public function getComposerAttr($key, $default = null)
263
    {
264
        return $this->json('composer.json')->get($key, $default);
265
    }
266
267
    /**
268
     * Register the module.
269
     */
270
    public function register(): void
271
    {
272
        $this->registerAliases();
273
274
        $this->registerProviders();
275
276
        if ($this->isLoadFilesOnBoot() === false) {
277
            $this->registerFiles();
278 11
        }
279
280 11
        $this->fireEvent('register');
281 11
    }
282
283
    /**
284
     * Register the module event.
285
     *
286
     * @param string $event
287
     */
288
    protected function fireEvent($event): void
289
    {
290
        $this->app['events']->dispatch(sprintf('modules.%s.' . $event, $this->getLowerName()), [$this]);
291
    }
292
293
    /**
294
     * Register the aliases from this module.
295
     */
296
    abstract public function registerAliases(): void;
297
298
    /**
299
     * Register the service providers from this module.
300
     */
301
    abstract public function registerProviders(): void;
302
303
    /**
304
     * Get the path to the cached *_module.php file.
305
     *
306
     * @return string
307
     */
308
    abstract public function getCachedServicesPath(): string;
309
310
    /**
311
     * Register the files from this module.
312
     */
313
    protected function registerFiles(): void
314 5
    {
315
        foreach ($this->get('files', []) as $file) {
316 5
            include $this->path . '/' . $file;
317
        }
318
    }
319
320
    /**
321
     * Handle call __toString.
322
     *
323
     * @return string
324
     */
325
    public function __toString()
326 5
    {
327
        return $this->getStudlyName();
328 5
    }
329
330
    /**
331
     * Determine whether the given status same with the current module status.
332
     *
333
     * @param bool $status
334
     *
335
     * @return bool
336 12
     */
337
    public function isStatus(bool $status) : bool
338 12
    {
339
        return $this->activator->hasStatus($this, $status);
340
    }
341
342
    /**
343
     * Determine whether the current module activated.
344
     *
345
     * @return bool
346 4
     */
347
    public function isEnabled() : bool
348 4
    {
349
        return $this->activator->hasStatus($this, true);
350
    }
351
352
    /**
353
     *  Determine whether the current module not disabled.
354
     *
355
     * @return bool
356
     */
357
    public function isDisabled() : bool
358 1
    {
359
        return !$this->isEnabled();
360 1
    }
361 1
362
    /**
363
     * Set active state for current module.
364
     *
365
     * @param bool $active
366 5
     *
367
     * @return void
368 5
     */
369
    public function setActive(bool $active): void
370 5
    {
371 5
        $this->activator->setActive($this, $active);
372
    }
373 5
374 5
    /**
375
     * Disable the current module.
376
     */
377
    public function disable(): void
378
    {
379 6
        $this->fireEvent('disabling');
380
381 6
        $this->activator->disable($this);
382
        $this->flushCache();
383 6
384 6
        $this->fireEvent('disabled');
385
    }
386 6
387 6
    /**
388
     * Enable the current module.
389
     */
390
    public function enable(): void
391
    {
392
        $this->fireEvent('enabling');
393
394 107
        $this->activator->enable($this);
395
        $this->flushCache();
396 107
397
        $this->fireEvent('enabled');
398 107
    }
399
400
    /**
401
     * Delete the current module.
402
     *
403
     * @return bool
404
     */
405
    public function delete(): bool
406
    {
407
        $this->activator->delete($this);
408 3
409
        return $this->json()->getFilesystem()->deleteDirectory($this->getPath());
410 3
    }
411
412
    /**
413
     * Get extra path.
414
     *
415
     * @param string $path
416
     *
417
     * @return string
418 2
     */
419
    public function getExtraPath(string $path) : string
420 2
    {
421
        return $this->getPath() . '/' . $path;
422 2
    }
423
424
    /**
425 9
     * Check if can load files of module on boot method.
426
     *
427 9
     * @return bool
428
     */
429
    protected function isLoadFilesOnBoot(): bool
430 9
    {
431
        return config('modules.register.files', 'register') === 'boot' &&
432
            // force register method if option == boot && app is AsgardCms
433
            !class_exists('\Modules\Core\Foundation\AsgardCms');
434
    }
435
436
    private function flushCache(): void
437
    {
438
        if (config('modules.cache.enabled')) {
439 2
            $this->cache->store()->flush();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Illuminate\Contracts\Cache\Repository as the method flush() does only exist in the following implementations of said interface: Illuminate\Cache\RedisTaggedCache, Illuminate\Cache\TaggedCache.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
440
        }
441 2
    }
442 2
443
    /**
444
     * Register a translation file namespace.
445
     *
446
     * @param  string  $path
447
     * @param  string  $namespace
448
     * @return void
449
     */
450
    private function loadTranslationsFrom(string $path, string $namespace): void
451
    {
452
        $this->translator->addNamespace($namespace, $path);
453
    }
454
}
455