Completed
Pull Request — master (#781)
by Nicolas
01:59
created

FileRepository::scan()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
nc 5
nop 0
dl 0
loc 20
ccs 10
cts 10
cp 1
crap 4
rs 9.6
c 0
b 0
f 0
1
<?php
2
3
namespace Nwidart\Modules;
4
5
use Countable;
6
use Illuminate\Cache\CacheManager;
7
use Illuminate\Container\Container;
8
use Illuminate\Contracts\Config\Repository as ConfigRepository;
9
use Illuminate\Contracts\Routing\UrlGenerator;
10
use Illuminate\Filesystem\Filesystem;
11
use Illuminate\Support\Str;
12
use Illuminate\Support\Traits\Macroable;
13
use Nwidart\Modules\Contracts\RepositoryInterface;
14
use Nwidart\Modules\Exceptions\InvalidAssetPath;
15
use Nwidart\Modules\Exceptions\ModuleNotFoundException;
16
use Nwidart\Modules\Process\Installer;
17
use Nwidart\Modules\Process\Updater;
18
19
abstract class FileRepository implements RepositoryInterface, Countable
20
{
21
    use Macroable;
22
23
    /**
24
     * Application instance.
25
     *
26
     * @var \Illuminate\Contracts\Foundation\Application|\Laravel\Lumen\Application
27
     */
28
    protected $app;
29
30
    /**
31
     * The module path.
32
     *
33
     * @var string|null
34
     */
35
    protected $path;
36
37
    /**
38
     * The scanned paths.
39
     *
40
     * @var array
41
     */
42
    protected $paths = [];
43
44
    /**
45
     * @var string
46
     */
47
    protected $stubPath;
48
    /**
49
     * @var UrlGenerator
50
     */
51
    private $url;
52
    /**
53
     * @var ConfigRepository
54
     */
55
    private $config;
56
    /**
57
     * @var Filesystem
58
     */
59
    private $files;
60
    /**
61
     * @var CacheManager
62
     */
63
    private $cache;
64
65
    /**
66
     * The constructor.
67
     * @param Container $app
68
     * @param string|null $path
69
     */
70 190 View Code Duplication
    public function __construct(Container $app, $path = null)
71
    {
72 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...
73 190
        $this->path = $path;
74 190
        $this->url = $app['url'];
75 190
        $this->config = $app['config'];
76 190
        $this->files = $app['files'];
77 190
        $this->cache = $app['cache'];
78 190
    }
79
80
    /**
81
     * Add other module location.
82
     *
83
     * @param string $path
84
     *
85
     * @return $this
86
     */
87 15
    public function addLocation($path)
88
    {
89 15
        $this->paths[] = $path;
90
91 15
        return $this;
92
    }
93
94
    /**
95
     * Get all additional paths.
96
     *
97
     * @return array
98
     */
99 1
    public function getPaths() : array
100
    {
101 1
        return $this->paths;
102
    }
103
104
    /**
105
     * Get scanned modules paths.
106
     *
107
     * @return array
108
     */
109 190
    public function getScanPaths() : array
110
    {
111 190
        $paths = $this->paths;
112
113 190
        $paths[] = $this->getPath();
114
115 190
        if ($this->config('scan.enabled')) {
116
            $paths = array_merge($paths, $this->config('scan.paths'));
117
        }
118
119
        $paths = array_map(function ($path) {
120 190
            return Str::endsWith($path, '/*') ? $path : Str::finish($path, '/*');
121 190
        }, $paths);
122
123 190
        return $paths;
124
    }
125
126
    /**
127
     * Creates a new Module instance
128
     *
129
     * @param Container $app
0 ignored issues
show
Bug introduced by
There is no parameter named $app. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
130
     * @param string $args
131
     * @param string $path
0 ignored issues
show
Bug introduced by
There is no parameter named $path. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
132
     * @return \Nwidart\Modules\Module
133
     */
134
    abstract protected function createModule(...$args);
135
136
    /**
137
     * Get & scan all modules.
138
     *
139
     * @return array
140
     */
141 190
    public function scan()
142
    {
143 190
        $paths = $this->getScanPaths();
144
145 190
        $modules = [];
146
147 190
        foreach ($paths as $key => $path) {
148 190
            $manifests = $this->getFiles()->glob("{$path}/module.json");
149
150 190
            is_array($manifests) || $manifests = [];
151
152 190
            foreach ($manifests as $manifest) {
153 112
                $name = Json::make($manifest)->get('name');
154
155 112
                $modules[$name] = $this->createModule($this->app, $name, dirname($manifest));
156
            }
157
        }
158
159 190
        return $modules;
160
    }
161
162
    /**
163
     * Get all modules.
164
     *
165
     * @return array
166
     */
167 190
    public function all() : array
168
    {
169 190
        if (!$this->config('cache.enabled')) {
170 190
            return $this->scan();
171
        }
172
173
        return $this->formatCached($this->getCached());
174
    }
175
176
    /**
177
     * Format the cached data as array of modules.
178
     *
179
     * @param array $cached
180
     *
181
     * @return array
182
     */
183
    protected function formatCached($cached)
184
    {
185
        $modules = [];
186
187
        foreach ($cached as $name => $module) {
188
            $path = $module['path'];
189
190
            $modules[$name] = $this->createModule($this->app, $name, $path);
191
        }
192
193
        return $modules;
194
    }
195
196
    /**
197
     * Get cached modules.
198
     *
199
     * @return array
200
     */
201
    public function getCached()
202
    {
203
        return $this->cache->remember($this->config('cache.key'), $this->config('cache.lifetime'), function () {
204
            return $this->toCollection()->toArray();
205
        });
206
    }
207
208
    /**
209
     * Get all modules as collection instance.
210
     *
211
     * @return Collection
212
     */
213 1
    public function toCollection() : Collection
214
    {
215 1
        return new Collection($this->scan());
216
    }
217
218
    /**
219
     * Get modules by status.
220
     *
221
     * @param $status
222
     *
223
     * @return array
224
     */
225 190
    public function getByStatus($status) : array
226
    {
227 190
        $modules = [];
228
229 190
        foreach ($this->all() as $name => $module) {
230 3
            if ($module->isStatus($status)) {
231 3
                $modules[$name] = $module;
232
            }
233
        }
234
235 190
        return $modules;
236
    }
237
238
    /**
239
     * Determine whether the given module exist.
240
     *
241
     * @param $name
242
     *
243
     * @return bool
244
     */
245 99
    public function has($name) : bool
246
    {
247 99
        return array_key_exists($name, $this->all());
248
    }
249
250
    /**
251
     * Get list of enabled modules.
252
     *
253
     * @return array
254
     */
255 190
    public function allEnabled() : array
256
    {
257 190
        return $this->getByStatus(1);
258
    }
259
260
    /**
261
     * Get list of disabled modules.
262
     *
263
     * @return array
264
     */
265 1
    public function allDisabled() : array
266
    {
267 1
        return $this->getByStatus(0);
268
    }
269
270
    /**
271
     * Get count from all modules.
272
     *
273
     * @return int
274
     */
275 1
    public function count() : int
276
    {
277 1
        return count($this->all());
278
    }
279
280
    /**
281
     * Get all ordered modules.
282
     *
283
     * @param string $direction
284
     *
285
     * @return array
286
     */
287 190
    public function getOrdered($direction = 'asc') : array
288
    {
289 190
        $modules = $this->allEnabled();
290
291
        uasort($modules, function (Module $a, Module $b) use ($direction) {
292
            if ($a->order == $b->order) {
293
                return 0;
294
            }
295
296
            if ($direction == 'desc') {
297
                return $a->order < $b->order ? 1 : -1;
298
            }
299
300
            return $a->order > $b->order ? 1 : -1;
301 190
        });
302
303 190
        return $modules;
304
    }
305
306
    /**
307
     * Get a module path.
308
     *
309
     * @return string
310
     */
311 190
    public function getPath() : string
312
    {
313 190
        return $this->path ?: $this->config('paths.modules', base_path('Modules'));
314
    }
315
316
    /**
317
     * Register the modules.
318
     */
319 190
    public function register()
320
    {
321 190
        foreach ($this->getOrdered() as $module) {
322
            $module->register();
323
        }
324 190
    }
325
326
    /**
327
     * Boot the modules.
328
     */
329 190
    public function boot()
330
    {
331 190
        foreach ($this->getOrdered() as $module) {
332
            $module->boot();
333
        }
334 190
    }
335
336
    /**
337
     * Find a specific module.
338
     * @param $name
339
     * @return mixed|void
340
     */
341 108
    public function find($name)
342
    {
343 108
        foreach ($this->all() as $module) {
344 107
            if ($module->getLowerName() === strtolower($name)) {
345 107
                return $module;
346
            }
347
        }
348
349 100
        return;
350
    }
351
352
    /**
353
     * Find a specific module by its alias.
354
     * @param $alias
355
     * @return mixed|void
356
     */
357 2
    public function findByAlias($alias)
358
    {
359 2
        foreach ($this->all() as $module) {
360 2
            if ($module->getAlias() === $alias) {
361 2
                return $module;
362
            }
363
        }
364
365
        return;
366
    }
367
368
    /**
369
     * Find all modules that are required by a module. If the module cannot be found, throw an exception.
370
     *
371
     * @param $name
372
     * @return array
373
     * @throws ModuleNotFoundException
374
     */
375 1
    public function findRequirements($name)
376
    {
377 1
        $requirements = [];
378
379 1
        $module = $this->findOrFail($name);
380
381 1
        foreach ($module->getRequires() as $requirementName) {
382 1
            $requirements[] = $this->findByAlias($requirementName);
383
        }
384
385 1
        return $requirements;
386
    }
387
388
    /**
389
     * Find a specific module, if there return that, otherwise throw exception.
390
     *
391
     * @param $name
392
     *
393
     * @return Module
394
     *
395
     * @throws ModuleNotFoundException
396
     */
397 106
    public function findOrFail($name)
398
    {
399 106
        $module = $this->find($name);
400
401 106
        if ($module !== null) {
402 105
            return $module;
403
        }
404
405 100
        throw new ModuleNotFoundException("Module [{$name}] does not exist!");
406
    }
407
408
    /**
409
     * Get all modules as laravel collection instance.
410
     *
411
     * @param $status
412
     *
413
     * @return Collection
414
     */
415 1
    public function collections($status = 1) : Collection
416
    {
417 1
        return new Collection($this->getByStatus($status));
418
    }
419
420
    /**
421
     * Get module path for a specific module.
422
     *
423
     * @param $module
424
     *
425
     * @return string
426
     */
427 99
    public function getModulePath($module)
428
    {
429
        try {
430 99
            return $this->findOrFail($module)->getPath() . '/';
431 99
        } catch (ModuleNotFoundException $e) {
432 99
            return $this->getPath() . '/' . Str::studly($module) . '/';
433
        }
434
    }
435
436
    /**
437
     * Get asset path for a specific module.
438
     *
439
     * @param $module
440
     *
441
     * @return string
442
     */
443 2
    public function assetPath($module) : string
444
    {
445 2
        return $this->config('paths.assets') . '/' . $module;
446
    }
447
448
    /**
449
     * Get a specific config data from a configuration file.
450
     *
451
     * @param $key
452
     *
453
     * @param null $default
454
     * @return mixed
455
     */
456 190
    public function config($key, $default = null)
457
    {
458 190
        return $this->config->get('modules.' . $key, $default);
459
    }
460
461
    /**
462
     * Get storage path for module used.
463
     *
464
     * @return string
465
     */
466 2
    public function getUsedStoragePath() : string
467
    {
468 2
        $directory = storage_path('app/modules');
469 2
        if ($this->getFiles()->exists($directory) === false) {
470 1
            $this->getFiles()->makeDirectory($directory, 0777, true);
471
        }
472
473 2
        $path = storage_path('app/modules/modules.used');
474 2
        if (!$this->getFiles()->exists($path)) {
475 1
            $this->getFiles()->put($path, '');
476
        }
477
478 2
        return $path;
479
    }
480
481
    /**
482
     * Set module used for cli session.
483
     *
484
     * @param $name
485
     *
486
     * @throws ModuleNotFoundException
487
     */
488 1
    public function setUsed($name)
489
    {
490 1
        $module = $this->findOrFail($name);
491
492 1
        $this->getFiles()->put($this->getUsedStoragePath(), $module);
493 1
    }
494
495
    /**
496
     * Forget the module used for cli session.
497
     */
498
    public function forgetUsed()
499
    {
500
        if ($this->getFiles()->exists($this->getUsedStoragePath())) {
501
            $this->getFiles()->delete($this->getUsedStoragePath());
502
        }
503
    }
504
505
    /**
506
     * Get module used for cli session.
507
     * @return string
508
     * @throws \Nwidart\Modules\Exceptions\ModuleNotFoundException
509
     */
510 1
    public function getUsedNow() : string
511
    {
512 1
        return $this->findOrFail($this->getFiles()->get($this->getUsedStoragePath()));
513
    }
514
515
    /**
516
     * Get laravel filesystem instance.
517
     *
518
     * @return Filesystem
519
     */
520 190
    public function getFiles(): Filesystem
521
    {
522 190
        return $this->files;
523
    }
524
525
    /**
526
     * Get module assets path.
527
     *
528
     * @return string
529
     */
530 2
    public function getAssetsPath() : string
531
    {
532 2
        return $this->config('paths.assets');
533
    }
534
535
    /**
536
     * Get asset url from a specific module.
537
     * @param string $asset
538
     * @return string
539
     * @throws InvalidAssetPath
540
     */
541 2
    public function asset($asset) : string
542
    {
543 2
        if (Str::contains($asset, ':') === false) {
544 1
            throw InvalidAssetPath::missingModuleName($asset);
545
        }
546 1
        list($name, $url) = explode(':', $asset);
547
548 1
        $baseUrl = str_replace(public_path() . DIRECTORY_SEPARATOR, '', $this->getAssetsPath());
549
550 1
        $url = $this->url->asset($baseUrl . "/{$name}/" . $url);
551
552 1
        return str_replace(['http://', 'https://'], '//', $url);
553
    }
554
555
    /**
556
     * Determine whether the given module is activated.
557
     * @param string $name
558
     * @return bool
559
     * @throws ModuleNotFoundException
560
     */
561 4
    public function enabled($name) : bool
562
    {
563 4
        return $this->findOrFail($name)->enabled();
564
    }
565
566
    /**
567
     * Determine whether the given module is not activated.
568
     * @param string $name
569
     * @return bool
570
     * @throws ModuleNotFoundException
571
     */
572 2
    public function disabled($name) : bool
573
    {
574 2
        return !$this->enabled($name);
575
    }
576
577
    /**
578
     * Enabling a specific module.
579
     * @param string $name
580
     * @return void
581
     * @throws \Nwidart\Modules\Exceptions\ModuleNotFoundException
582
     */
583 1
    public function enable($name)
584
    {
585 1
        $this->findOrFail($name)->enable();
586 1
    }
587
588
    /**
589
     * Disabling a specific module.
590
     * @param string $name
591
     * @return void
592
     * @throws \Nwidart\Modules\Exceptions\ModuleNotFoundException
593
     */
594 1
    public function disable($name)
595
    {
596 1
        $this->findOrFail($name)->disable();
597 1
    }
598
599
    /**
600
     * Delete a specific module.
601
     * @param string $name
602
     * @return bool
603
     * @throws \Nwidart\Modules\Exceptions\ModuleNotFoundException
604
     */
605 2
    public function delete($name) : bool
606
    {
607 2
        return $this->findOrFail($name)->delete();
608
    }
609
610
    /**
611
     * Update dependencies for the specified module.
612
     *
613
     * @param string $module
614
     */
615
    public function update($module)
616
    {
617
        with(new Updater($this))->update($module);
618
    }
619
620
    /**
621
     * Install the specified module.
622
     *
623
     * @param string $name
624
     * @param string $version
625
     * @param string $type
626
     * @param bool   $subtree
627
     *
628
     * @return \Symfony\Component\Process\Process
629
     */
630
    public function install($name, $version = 'dev-master', $type = 'composer', $subtree = false)
631
    {
632
        $installer = new Installer($name, $version, $type, $subtree);
633
634
        return $installer->run();
635
    }
636
637
    /**
638
     * Get stub path.
639
     *
640
     * @return string|null
641
     */
642 3
    public function getStubPath()
643
    {
644 3
        if ($this->stubPath !== null) {
645 1
            return $this->stubPath;
646
        }
647
648 2
        if ($this->config('stubs.enabled') === true) {
649 1
            return $this->config('stubs.path');
650
        }
651
652 1
        return $this->stubPath;
653
    }
654
655
    /**
656
     * Set stub path.
657
     *
658
     * @param string $stubPath
659
     *
660
     * @return $this
661
     */
662 1
    public function setStubPath($stubPath)
663
    {
664 1
        $this->stubPath = $stubPath;
665
666 1
        return $this;
667
    }
668
}
669