Completed
Push — master ( d0bf09...15e02a )
by Nicolas
04:05
created

Repository::getScanPaths()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
ccs 5
cts 6
cp 0.8333
rs 9.4285
cc 2
eloc 6
nc 2
nop 0
crap 2.0185
1
<?php
2
3
namespace Nwidart\Modules;
4
5
use Countable;
6
use Illuminate\Foundation\Application;
7
use Illuminate\Support\Str;
8
use Nwidart\Modules\Contracts\RepositoryInterface;
9
use Nwidart\Modules\Exceptions\ModuleNotFoundException;
10
use Nwidart\Modules\Process\Installer;
11
use Nwidart\Modules\Process\Updater;
12
13
class Repository implements RepositoryInterface, Countable
14
{
15
    /**
16
     * Application instance.
17
     *
18
     * @var Application
19
     */
20
    protected $app;
21
22
    /**
23
     * The module path.
24
     *
25
     * @var string|null
26
     */
27
    protected $path;
28
29
    /**
30
     * The scanned paths.
31
     *
32
     * @var array
33
     */
34
    protected $paths = [];
35
36
    /**
37
     * @var string
38
     */
39
    protected $stubPath;
40
41
    /**
42
     * The constructor.
43
     *
44
     * @param Application $app
45
     * @param string|null $path
46
     */
47 42
    public function __construct(Application $app, $path = null)
48
    {
49 42
        $this->app = $app;
50 42
        $this->path = $path;
51 42
    }
52
53
    /**
54
     * Add other module location.
55
     *
56
     * @param string $path
57
     *
58
     * @return $this
59
     */
60 10
    public function addLocation($path)
61
    {
62 10
        $this->paths[] = $path;
63
64 10
        return $this;
65
    }
66
67
    /**
68
     * Alternative method for "addPath".
69
     *
70
     * @param string $path
71
     *
72
     * @return $this
73
     */
74 1
    public function addPath($path)
75
    {
76 1
        return $this->addLocation($path);
77
    }
78
79
    /**
80
     * Get all additional paths.
81
     *
82
     * @return array
83
     */
84 1
    public function getPaths()
85
    {
86 1
        return $this->paths;
87
    }
88
89
    /**
90
     * Get scanned modules paths.
91
     *
92
     * @return array
93
     */
94 42
    public function getScanPaths()
95
    {
96 42
        $paths = $this->paths;
97
98 42
        $paths[] = $this->getPath().'/*';
99
100 42
        if ($this->config('scan.enabled')) {
101
            $paths = array_merge($paths, $this->config('scan.paths'));
102
        }
103
104 42
        return $paths;
105
    }
106
107
    /**
108
     * Get & scan all modules.
109
     *
110
     * @return array
111
     */
112 42
    public function scan()
113
    {
114 42
        $paths = $this->getScanPaths();
115
116 42
        $modules = [];
117
118 42
        foreach ($paths as $key => $path) {
119 42
            $manifests = $this->app['files']->glob("{$path}/module.json");
120
121 42
            is_array($manifests) || $manifests = [];
122
123 42
            foreach ($manifests as $manifest) {
124 11
                $name = Json::make($manifest)->get('name');
125
126 11
                $lowerName = strtolower($name);
127
128 42
                $modules[$name] = new Module($this->app, $lowerName, dirname($manifest));
129
            }
130
        }
131
132 42
        return $modules;
133
    }
134
135
    /**
136
     * Get all modules.
137
     *
138
     * @return array
139
     */
140 42
    public function all()
141
    {
142 42
        if (!$this->config('cache.enabled')) {
143 42
            return $this->scan();
144
        }
145
146
        return $this->formatCached($this->getCached());
147
    }
148
149
    /**
150
     * Format the cached data as array of modules.
151
     *
152
     * @param array $cached
153
     *
154
     * @return array
155
     */
156
    protected function formatCached($cached)
157
    {
158
        $modules = [];
159
160
        foreach ($cached as $name => $module) {
161
            $path = $this->config('paths.modules').'/'.$name;
162
163
            $lowerName = strtolower($name);
164
165
            $modules[$name] = new Module($this->app, $lowerName, $path);
166
        }
167
168
        return $modules;
169
    }
170
171
    /**
172
     * Get cached modules.
173
     *
174
     * @return array
175
     */
176
    public function getCached()
177
    {
178
        return $this->app['cache']->remember($this->config('cache.key'), $this->config('cache.lifetime'), function () {
179
            return $this->toCollection()->toArray();
180
        });
181
    }
182
183
    /**
184
     * Get all modules as collection instance.
185
     *
186
     * @return Collection
187
     */
188 1
    public function toCollection()
189
    {
190 1
        return new Collection($this->scan());
191
    }
192
193
    /**
194
     * Get modules by status.
195
     *
196
     * @param $status
197
     *
198
     * @return array
199
     */
200 42
    public function getByStatus($status)
201
    {
202 42
        $modules = [];
203
204 42
        foreach ($this->all() as $name => $module) {
205 3
            if ($module->isStatus($status)) {
206 3
                $modules[$name] = $module;
207
            }
208
        }
209
210 42
        return $modules;
211
    }
212
213
    /**
214
     * Determine whether the given module exist.
215
     *
216
     * @param $name
217
     *
218
     * @return bool
219
     */
220 3
    public function has($name)
221
    {
222 3
        return array_key_exists($name, $this->all());
223
    }
224
225
    /**
226
     * Get list of enabled modules.
227
     *
228
     * @return array
229
     */
230 42
    public function enabled()
231
    {
232 42
        return $this->getByStatus(1);
233
    }
234
235
    /**
236
     * Get list of disabled modules.
237
     *
238
     * @return array
239
     */
240 1
    public function disabled()
241
    {
242 1
        return $this->getByStatus(0);
243
    }
244
245
    /**
246
     * Get count from all modules.
247
     *
248
     * @return int
249
     */
250 1
    public function count()
251
    {
252 1
        return count($this->all());
253
    }
254
255
    /**
256
     * Get all ordered modules.
257
     *
258
     * @param string $direction
259
     *
260
     * @return array
261
     */
262 42
    public function getOrdered($direction = 'asc')
263
    {
264 42
        $modules = $this->enabled();
265
266 42
        uasort($modules, function (Module $a, Module $b) use ($direction) {
267
            if ($a->order == $b->order) {
0 ignored issues
show
Documentation introduced by
The property order does not exist on object<Nwidart\Modules\Module>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
268
                return 0;
269
            }
270
271
            if ($direction == 'desc') {
272
                return $a->order < $b->order ? 1 : -1;
0 ignored issues
show
Documentation introduced by
The property order does not exist on object<Nwidart\Modules\Module>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
273
            }
274
275
            return $a->order > $b->order ? 1 : -1;
0 ignored issues
show
Documentation introduced by
The property order does not exist on object<Nwidart\Modules\Module>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
276 42
        });
277
278 42
        return $modules;
279
    }
280
281
    /**
282
     * Get a module path.
283
     *
284
     * @return string
285
     */
286 42
    public function getPath()
287
    {
288 42
        return $this->path ?: $this->config('paths.modules');
289
    }
290
291
    /**
292
     * Register the modules.
293
     */
294 42
    public function register()
295
    {
296 42
        foreach ($this->getOrdered() as $module) {
297
            $module->register();
298
        }
299 42
    }
300
301
    /**
302
     * Boot the modules.
303
     */
304 42
    public function boot()
305
    {
306 42
        foreach ($this->getOrdered() as $module) {
307
            $module->boot();
308
        }
309 42
    }
310
311
    /**
312
     * Find a specific module.
313
     *
314
     * @param $name
315
     */
316 8
    public function find($name)
317
    {
318 8
        foreach ($this->all() as $module) {
319 7
            if ($module->getLowerName() == strtolower($name)) {
320 7
                return $module;
321
            }
322
        }
323
324 4
        return;
325
    }
326
327
    /**
328
     * Alternative for "find" method.
329
     *
330
     * @param $name
331
     */
332 1
    public function get($name)
333
    {
334 1
        return $this->find($name);
335
    }
336
337
    /**
338
     * Find a specific module, if there return that, otherwise throw exception.
339
     *
340
     * @param $name
341
     *
342
     * @return Module
343
     *
344
     * @throws ModuleNotFoundException
345
     */
346 7
    public function findOrFail($name)
347
    {
348 7
        if (!is_null($module = $this->find($name))) {
349 6
            return $module;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $module; (object|integer|double|string|array|boolean) is incompatible with the return type documented by Nwidart\Modules\Repository::findOrFail of type Nwidart\Modules\Module.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
350
        }
351
352 4
        throw new ModuleNotFoundException("Module [{$name}] does not exist!");
353
    }
354
355
    /**
356
     * Get all modules as laravel collection instance.
357
     *
358
     * @return Collection
359
     */
360 1
    public function collections()
361
    {
362 1
        return new Collection($this->enabled());
363
    }
364
365
    /**
366
     * Get module path for a specific module.
367
     *
368
     * @param $module
369
     *
370
     * @return string
371
     */
372 3
    public function getModulePath($module)
373
    {
374
        try {
375 3
            return $this->findOrFail($module)->getPath().'/';
376 3
        } catch (ModuleNotFoundException $e) {
377 3
            return $this->getPath().'/'.Str::studly($module).'/';
378
        }
379
    }
380
381
    /**
382
     * Get asset path for a specific module.
383
     *
384
     * @param $module
385
     *
386
     * @return string
387
     */
388 1
    public function assetPath($module)
389
    {
390 1
        return $this->config('paths.assets').'/'.$module;
391
    }
392
393
    /**
394
     * Get a specific config data from a configuration file.
395
     *
396
     * @param $key
397
     *
398
     * @return mixed
399
     */
400 42
    public function config($key)
401
    {
402 42
        return $this->app['config']->get('modules.'.$key);
403
    }
404
405
    /**
406
     * Get storage path for module used.
407
     *
408
     * @return string
409
     */
410 2
    public function getUsedStoragePath()
411
    {
412 2
        if (!$this->app['files']->exists($path = storage_path('app/modules'))) {
413 1
            $this->app['files']->makeDirectory($path, 0777, true);
414
        }
415
416 2
        return $path.'/modules.used';
417
    }
418
419
    /**
420
     * Set module used for cli session.
421
     *
422
     * @param $name
423
     *
424
     * @throws ModuleNotFoundException
425
     */
426 1
    public function setUsed($name)
427
    {
428 1
        $module = $this->findOrFail($name);
429
430 1
        $this->app['files']->put($this->getUsedStoragePath(), $module);
431 1
    }
432
433
    /**
434
     * Get module used for cli session.
435
     *
436
     * @return string
437
     */
438 1
    public function getUsedNow()
439
    {
440 1
        return $this->findOrFail($this->app['files']->get($this->getUsedStoragePath()));
441
    }
442
443
    /**
444
     * Get used now.
445
     *
446
     * @return string
447
     */
448 1
    public function getUsed()
449
    {
450 1
        return $this->getUsedNow();
451
    }
452
453
    /**
454
     * Get laravel filesystem instance.
455
     *
456
     * @return \Illuminate\Filesystem\Filesystem
457
     */
458 1
    public function getFiles()
459
    {
460 1
        return $this->app['files'];
461
    }
462
463
    /**
464
     * Get module assets path.
465
     *
466
     * @return string
467
     */
468 2
    public function getAssetsPath()
469
    {
470 2
        return $this->config('paths.assets');
471
    }
472
473
    /**
474
     * Get asset url from a specific module.
475
     *
476
     * @param string $asset
477
     * @param bool   $secure
0 ignored issues
show
Bug introduced by
There is no parameter named $secure. 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...
478
     *
479
     * @return string
480
     */
481 1
    public function asset($asset)
482
    {
483 1
        list($name, $url) = explode(':', $asset);
484
485 1
        $baseUrl = str_replace(public_path(), '', $this->getAssetsPath());
486
487 1
        $url = $this->app['url']->asset($baseUrl."/{$name}/".$url);
488
489 1
        return str_replace(['http://', 'https://'], '//', $url);
490
    }
491
492
    /**
493
     * Determine whether the given module is activated.
494
     *
495
     * @param string $name
496
     *
497
     * @return bool
498
     */
499 2
    public function active($name)
500
    {
501 2
        return $this->findOrFail($name)->active();
502
    }
503
504
    /**
505
     * Determine whether the given module is not activated.
506
     *
507
     * @param string $name
508
     *
509
     * @return bool
510
     */
511 1
    public function notActive($name)
512
    {
513 1
        return !$this->active($name);
514
    }
515
516
    /**
517
     * Enabling a specific module.
518
     *
519
     * @param string $name
520
     *
521
     * @return bool
522
     */
523
    public function enable($name)
524
    {
525
        return $this->findOrFail($name)->enable();
526
    }
527
528
    /**
529
     * Disabling a specific module.
530
     *
531
     * @param string $name
532
     *
533
     * @return bool
534
     */
535
    public function disable($name)
536
    {
537
        return $this->findOrFail($name)->disable();
538
    }
539
540
    /**
541
     * Delete a specific module.
542
     *
543
     * @param string $name
544
     *
545
     * @return bool
546
     */
547
    public function delete($name)
548
    {
549
        return $this->findOrFail($name)->delete();
550
    }
551
552
    /**
553
     * Update dependencies for the specified module.
554
     *
555
     * @param string $module
556
     */
557
    public function update($module)
558
    {
559
        with(new Updater($this))->update($module);
560
    }
561
562
    /**
563
     * Install the specified module.
564
     *
565
     * @param string $name
566
     * @param string $version
567
     * @param string $type
568
     * @param bool   $subtree
569
     *
570
     * @return \Symfony\Component\Process\Process
571
     */
572
    public function install($name, $version = 'dev-master', $type = 'composer', $subtree = false)
573
    {
574
        $installer = new Installer($name, $version, $type, $subtree);
575
576
        return $installer->run();
577
    }
578
579
    /**
580
     * Get stub path.
581
     *
582
     * @return string
583
     */
584 3
    public function getStubPath()
585
    {
586 3
        if (!is_null($this->stubPath)) {
587 1
            return $this->stubPath;
588
        }
589
590 2
        if ($this->config('stubs.enabled')) {
591 1
            return $this->config('stubs.path');
592
        }
593
594 1
        return $this->stubPath;
595
    }
596
597
    /**
598
     * Set stub path.
599
     *
600
     * @param string $stubPath
601
     *
602
     * @return $this
603
     */
604 1
    public function setStubPath($stubPath)
605
    {
606 1
        $this->stubPath = $stubPath;
607
608 1
        return $this;
609
    }
610
}
611