Completed
Push — master ( 15e02a...758c52 )
by Nicolas
05:13
created

Repository::boot()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
ccs 4
cts 5
cp 0.8
rs 9.4285
cc 2
eloc 3
nc 2
nop 0
crap 2.032
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 55
    public function __construct(Application $app, $path = null)
48
    {
49 55
        $this->app = $app;
50 55
        $this->path = $path;
51 55
    }
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 55
    public function getScanPaths()
95
    {
96 55
        $paths = $this->paths;
97
98 55
        $paths[] = $this->getPath().'/*';
99
100 55
        if ($this->config('scan.enabled')) {
101
            $paths = array_merge($paths, $this->config('scan.paths'));
102
        }
103
104 55
        return $paths;
105
    }
106
107
    /**
108
     * Get & scan all modules.
109
     *
110
     * @return array
111
     */
112 55
    public function scan()
113
    {
114 55
        $paths = $this->getScanPaths();
115
116 55
        $modules = [];
117
118 55
        foreach ($paths as $key => $path) {
119 55
            $manifests = $this->app['files']->glob("{$path}/module.json");
120
121 55
            is_array($manifests) || $manifests = [];
122
123 55
            foreach ($manifests as $manifest) {
124 24
                $name = Json::make($manifest)->get('name');
125
126 24
                $lowerName = strtolower($name);
127
128 24
                $modules[$name] = new Module($this->app, $lowerName, dirname($manifest));
129 55
            }
130 55
        }
131
132 55
        return $modules;
133
    }
134
135
    /**
136
     * Get all modules.
137
     *
138
     * @return array
139
     */
140 55
    public function all()
141
    {
142 55
        if (!$this->config('cache.enabled')) {
143 55
            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 55
    public function getByStatus($status)
201
    {
202 55
        $modules = [];
203
204 55
        foreach ($this->all() as $name => $module) {
205 3
            if ($module->isStatus($status)) {
206 2
                $modules[$name] = $module;
207 2
            }
208 55
        }
209
210 55
        return $modules;
211
    }
212
213
    /**
214
     * Determine whether the given module exist.
215
     *
216
     * @param $name
217
     *
218
     * @return bool
219
     */
220 16
    public function has($name)
221
    {
222 16
        return array_key_exists($name, $this->all());
223
    }
224
225
    /**
226
     * Get list of enabled modules.
227
     *
228
     * @return array
229
     */
230 55
    public function enabled()
231
    {
232 55
        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 55
    public function getOrdered($direction = 'asc')
263
    {
264 55
        $modules = $this->enabled();
265
266 55
        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 55
        });
277
278 55
        return $modules;
279
    }
280
281
    /**
282
     * Get a module path.
283
     *
284
     * @return string
285
     */
286 55
    public function getPath()
287
    {
288 55
        return $this->path ?: $this->config('paths.modules');
289
    }
290
291
    /**
292
     * Register the modules.
293
     */
294 55
    public function register()
295
    {
296 55
        foreach ($this->getOrdered() as $module) {
297
            $module->register();
298 55
        }
299 55
    }
300
301
    /**
302
     * Boot the modules.
303
     */
304 55
    public function boot()
305
    {
306 55
        foreach ($this->getOrdered() as $module) {
307
            $module->boot();
308 55
        }
309 55
    }
310
311
    /**
312
     * Find a specific module.
313
     *
314
     * @param $name
315
     */
316 21
    public function find($name)
317
    {
318 21
        foreach ($this->all() as $module) {
319 20
            if ($module->getLowerName() == strtolower($name)) {
320 20
                return $module;
321
            }
322 17
        }
323
324 17
        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 20
    public function findOrFail($name)
347
    {
348 20
        if (!is_null($module = $this->find($name))) {
349 19
            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 17
        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 16
    public function getModulePath($module)
373
    {
374
        try {
375 16
            return $this->findOrFail($module)->getPath().'/';
376 16
        } catch (ModuleNotFoundException $e) {
377 16
            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 55
    public function config($key)
401
    {
402 55
        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 1
        }
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