FileRepository::collections()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Salah3id\Domains;
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 Salah3id\Domains\Contracts\RepositoryInterface;
14
use Salah3id\Domains\Exceptions\InvalidAssetPath;
15
use Salah3id\Domains\Exceptions\DomainNotFoundException;
16
use Salah3id\Domains\Process\Installer;
17
use Salah3id\Domains\Process\Updater;
18
19
abstract class FileRepository implements RepositoryInterface, Countable
20
{
21
    use Macroable;
0 ignored issues
show
Bug introduced by
The trait Illuminate\Support\Traits\Macroable requires the property $name which is not provided by Salah3id\Domains\FileRepository.
Loading history...
22
23
    /**
24
     * Application instance.
25
     *
26
     * @var \Illuminate\Contracts\Foundation\Application|\Laravel\Lumen\Application
0 ignored issues
show
Bug introduced by
The type Laravel\Lumen\Application was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
27
     */
28
    protected $app;
29
30
    /**
31
     * The domain 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
    public function __construct(Container $app, $path = null)
71
    {
72
        $this->app = $app;
0 ignored issues
show
Documentation Bug introduced by
It seems like $app of type Illuminate\Container\Container is incompatible with the declared type Illuminate\Contracts\Fou...ravel\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
        $this->path = $path;
74
        $this->url = $app['url'];
75
        $this->config = $app['config'];
76
        $this->files = $app['files'];
77
        $this->cache = $app['cache'];
78
    }
79
80
    /**
81
     * Add other domain location.
82
     *
83
     * @param string $path
84
     *
85
     * @return $this
86
     */
87
    public function addLocation($path)
88
    {
89
        $this->paths[] = $path;
90
91
        return $this;
92
    }
93
94
    /**
95
     * Get all additional paths.
96
     *
97
     * @return array
98
     */
99
    public function getPaths(): array
100
    {
101
        return $this->paths;
102
    }
103
104
    /**
105
     * Get scanned domains paths.
106
     *
107
     * @return array
108
     */
109
    public function getScanPaths(): array
110
    {
111
        $paths = $this->paths;
112
113
        $paths[] = $this->getPath();
114
115
        if ($this->config('scan.enabled')) {
116
            $paths = array_merge($paths, $this->config('scan.paths'));
117
        }
118
119
        $paths = array_map(function ($path) {
120
            return Str::endsWith($path, '/*') ? $path : Str::finish($path, '/*');
121
        }, $paths);
122
123
        return $paths;
124
    }
125
126
    /**
127
     * Creates a new Domain instance
128
     *
129
     * @param Container $app
130
     * @param string $args
131
     * @param string $path
132
     * @return \Salah3id\Domains\Domain
133
     */
134
    abstract protected function createDomain(...$args);
135
136
    /**
137
     * Get & scan all domains.
138
     *
139
     * @return array
140
     */
141
    public function scan()
142
    {
143
        $paths = $this->getScanPaths();
144
145
        $domains = [];
146
147
        foreach ($paths as $key => $path) {
148
            $manifests = $this->getFiles()->glob("{$path}/domain.json");
149
150
            is_array($manifests) || $manifests = [];
151
152
            foreach ($manifests as $manifest) {
153
                $name = Json::make($manifest)->get('name');
154
155
                $domains[$name] = $this->createDomain($this->app, $name, dirname($manifest));
0 ignored issues
show
Bug introduced by
It seems like $this->app can also be of type Illuminate\Contracts\Foundation\Application; however, parameter $args of Salah3id\Domains\FileRepository::createDomain() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

155
                $domains[$name] = $this->createDomain(/** @scrutinizer ignore-type */ $this->app, $name, dirname($manifest));
Loading history...
156
            }
157
        }
158
159
        return $domains;
160
    }
161
162
    /**
163
     * Get all domains.
164
     *
165
     * @return array
166
     */
167
    public function all(): array
168
    {
169
        if (!$this->config('cache.enabled')) {
170
            return $this->scan();
171
        }
172
173
        return $this->formatCached($this->getCached());
174
    }
175
176
    /**
177
     * Format the cached data as array of domains.
178
     *
179
     * @param array $cached
180
     *
181
     * @return array
182
     */
183
    protected function formatCached($cached)
184
    {
185
        $domains = [];
186
187
        foreach ($cached as $name => $domain) {
188
            $path = $domain['path'];
189
190
            $domains[$name] = $this->createDomain($this->app, $name, $path);
0 ignored issues
show
Bug introduced by
It seems like $this->app can also be of type Illuminate\Contracts\Foundation\Application; however, parameter $args of Salah3id\Domains\FileRepository::createDomain() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

190
            $domains[$name] = $this->createDomain(/** @scrutinizer ignore-type */ $this->app, $name, $path);
Loading history...
191
        }
192
193
        return $domains;
194
    }
195
196
    /**
197
     * Get cached domains.
198
     *
199
     * @return array
200
     */
201
    public function getCached()
202
    {
203
        return $this->cache->store($this->config->get('domains.cache.driver'))->remember($this->config('cache.key'), $this->config('cache.lifetime'), function () {
204
            return $this->toCollection()->toArray();
205
        });
206
    }
207
208
    /**
209
     * Get all domains as collection instance.
210
     *
211
     * @return Collection
212
     */
213
    public function toCollection(): Collection
214
    {
215
        return new Collection($this->scan());
0 ignored issues
show
Bug introduced by
$this->scan() of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $items of Salah3id\Domains\Collection::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

215
        return new Collection(/** @scrutinizer ignore-type */ $this->scan());
Loading history...
216
    }
217
218
    /**
219
     * Get domains by status.
220
     *
221
     * @param $status
222
     *
223
     * @return array
224
     */
225
    public function getByStatus($status): array
226
    {
227
        $domains = [];
228
229
        /** @var Domain $domain */
230
        foreach ($this->all() as $name => $domain) {
231
            if ($domain->isStatus($status)) {
232
                $domains[$name] = $domain;
233
            }
234
        }
235
236
        return $domains;
237
    }
238
239
    /**
240
     * Determine whether the given domain exist.
241
     *
242
     * @param $name
243
     *
244
     * @return bool
245
     */
246
    public function has($name): bool
247
    {
248
        return array_key_exists($name, $this->all());
249
    }
250
251
    /**
252
     * Get list of enabled domains.
253
     *
254
     * @return array
255
     */
256
    public function allEnabled(): array
257
    {
258
        return $this->getByStatus(true);
259
    }
260
261
    /**
262
     * Get list of disabled domains.
263
     *
264
     * @return array
265
     */
266
    public function allDisabled(): array
267
    {
268
        return $this->getByStatus(false);
269
    }
270
271
    /**
272
     * Get count from all domains.
273
     *
274
     * @return int
275
     */
276
    public function count(): int
277
    {
278
        return count($this->all());
279
    }
280
281
    /**
282
     * Get all ordered domains.
283
     *
284
     * @param string $direction
285
     *
286
     * @return array
287
     */
288
    public function getOrdered($direction = 'asc'): array
289
    {
290
        $domains = $this->allEnabled();
291
292
        uasort($domains, function (Domain $a, Domain $b) use ($direction) {
293
            if ($a->get('priority') === $b->get('priority')) {
294
                return 0;
295
            }
296
297
            if ($direction === 'desc') {
298
                return $a->get('priority') < $b->get('priority') ? 1 : -1;
299
            }
300
301
            return $a->get('priority') > $b->get('priority') ? 1 : -1;
302
        });
303
304
        return $domains;
305
    }
306
307
    /**
308
     * @inheritDoc
309
     */
310
    public function getPath(): string
311
    {
312
        return $this->path ?: $this->config('paths.domains', base_path('Domains'));
313
    }
314
315
    /**
316
     * @inheritDoc
317
     */
318
    public function register(): void
319
    {
320
        foreach ($this->getOrdered() as $domain) {
321
            $domain->register();
322
        }
323
    }
324
325
    /**
326
     * @inheritDoc
327
     */
328
    public function boot(): void
329
    {
330
        foreach ($this->getOrdered() as $domain) {
331
            $domain->boot();
332
        }
333
    }
334
335
    /**
336
     * @inheritDoc
337
     */
338
    public function find(string $name)
339
    {
340
        foreach ($this->all() as $domain) {
341
            if ($domain->getLowerName() === strtolower($name)) {
342
                return $domain;
343
            }
344
        }
345
346
        return;
347
    }
348
349
    /**
350
     * Find a specific domain, if there return that, otherwise throw exception.
351
     *
352
     * @param $name
353
     *
354
     * @return Domain
355
     *
356
     * @throws DomainNotFoundException
357
     */
358
    public function findOrFail(string $name)
359
    {
360
        $domain = $this->find($name);
361
362
        if ($domain !== null) {
363
            return $domain;
364
        }
365
366
        throw new DomainNotFoundException("Domain [{$name}] does not exist!");
367
    }
368
369
    /**
370
     * Get all domains as laravel collection instance.
371
     *
372
     * @param $status
373
     *
374
     * @return Collection
375
     */
376
    public function collections($status = 1): Collection
377
    {
378
        return new Collection($this->getByStatus($status));
0 ignored issues
show
Bug introduced by
$this->getByStatus($status) of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $items of Salah3id\Domains\Collection::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

378
        return new Collection(/** @scrutinizer ignore-type */ $this->getByStatus($status));
Loading history...
379
    }
380
381
    /**
382
     * Get domain path for a specific domain.
383
     *
384
     * @param $domain
385
     *
386
     * @return string
387
     */
388
    public function getDomainPath($domain)
389
    {
390
        try {
391
            return $this->findOrFail($domain)->getPath() . '/';
392
        } catch (DomainNotFoundException $e) {
393
            return $this->getPath() . '/' . Str::studly($domain) . '/';
394
        }
395
    }
396
397
    /**
398
     * @inheritDoc
399
     */
400
    public function assetPath(string $domain): string
401
    {
402
        return $this->config('paths.assets') . '/' . $domain;
403
    }
404
405
    /**
406
     * @inheritDoc
407
     */
408
    public function config(string $key, $default = null)
409
    {
410
        return $this->config->get('domains.' . $key, $default);
411
    }
412
413
    /**
414
     * Get storage path for domain used.
415
     *
416
     * @return string
417
     */
418
    public function getUsedStoragePath(): string
419
    {
420
        $directory = storage_path('app/domains');
421
        if ($this->getFiles()->exists($directory) === false) {
422
            $this->getFiles()->makeDirectory($directory, 0777, true);
423
        }
424
425
        $path = storage_path('app/domains/domains.used');
426
        if (!$this->getFiles()->exists($path)) {
427
            $this->getFiles()->put($path, '');
428
        }
429
430
        return $path;
431
    }
432
433
    /**
434
     * Set domain used for cli session.
435
     *
436
     * @param $name
437
     *
438
     * @throws DomainNotFoundException
439
     */
440
    public function setUsed($name)
441
    {
442
        $domain = $this->findOrFail($name);
443
444
        $this->getFiles()->put($this->getUsedStoragePath(), $domain);
445
    }
446
447
    /**
448
     * Forget the domain used for cli session.
449
     */
450
    public function forgetUsed()
451
    {
452
        if ($this->getFiles()->exists($this->getUsedStoragePath())) {
453
            $this->getFiles()->delete($this->getUsedStoragePath());
454
        }
455
    }
456
457
    /**
458
     * Get domain used for cli session.
459
     * @return string
460
     * @throws \Salah3id\Domains\Exceptions\DomainNotFoundException
461
     */
462
    public function getUsedNow(): string
463
    {
464
        return $this->findOrFail($this->getFiles()->get($this->getUsedStoragePath()));
465
    }
466
467
    /**
468
     * Get laravel filesystem instance.
469
     *
470
     * @return Filesystem
471
     */
472
    public function getFiles(): Filesystem
473
    {
474
        return $this->files;
475
    }
476
477
    /**
478
     * Get domain assets path.
479
     *
480
     * @return string
481
     */
482
    public function getAssetsPath(): string
483
    {
484
        return $this->config('paths.assets');
485
    }
486
487
    /**
488
     * Get asset url from a specific domain.
489
     * @param string $asset
490
     * @return string
491
     * @throws InvalidAssetPath
492
     */
493
    public function asset($asset): string
494
    {
495
        if (Str::contains($asset, ':') === false) {
496
            throw InvalidAssetPath::missingDomainName($asset);
497
        }
498
        list($name, $url) = explode(':', $asset);
499
500
        $baseUrl = str_replace(public_path() . DIRECTORY_SEPARATOR, '', $this->getAssetsPath());
501
502
        $url = $this->url->asset($baseUrl . "/{$name}/" . $url);
503
504
        return str_replace(['http://', 'https://'], '//', $url);
505
    }
506
507
    /**
508
     * @inheritDoc
509
     */
510
    public function isEnabled(string $name): bool
511
    {
512
        return $this->findOrFail($name)->isEnabled();
513
    }
514
515
    /**
516
     * @inheritDoc
517
     */
518
    public function isDisabled(string $name): bool
519
    {
520
        return !$this->isEnabled($name);
521
    }
522
523
    /**
524
     * Enabling a specific domain.
525
     * @param string $name
526
     * @return void
527
     * @throws \Salah3id\Domains\Exceptions\DomainNotFoundException
528
     */
529
    public function enable($name)
530
    {
531
        $this->findOrFail($name)->enable();
532
    }
533
534
    /**
535
     * Disabling a specific domain.
536
     * @param string $name
537
     * @return void
538
     * @throws \Salah3id\Domains\Exceptions\DomainNotFoundException
539
     */
540
    public function disable($name)
541
    {
542
        $this->findOrFail($name)->disable();
543
    }
544
545
    /**
546
     * @inheritDoc
547
     */
548
    public function delete(string $name): bool
549
    {
550
        return $this->findOrFail($name)->delete();
551
    }
552
553
    /**
554
     * Update dependencies for the specified domain.
555
     *
556
     * @param string $domain
557
     */
558
    public function update($domain)
559
    {
560
        with(new Updater($this))->update($domain);
561
    }
562
563
    /**
564
     * Install the specified domain.
565
     *
566
     * @param string $name
567
     * @param string $version
568
     * @param string $type
569
     * @param bool   $subtree
570
     *
571
     * @return \Symfony\Component\Process\Process
572
     */
573
    public function install($name, $version = 'dev-master', $type = 'composer', $subtree = false)
574
    {
575
        $installer = new Installer($name, $version, $type, $subtree);
576
577
        return $installer->run();
578
    }
579
580
    /**
581
     * Get stub path.
582
     *
583
     * @return string|null
584
     */
585
    public function getStubPath()
586
    {
587
        if ($this->stubPath !== null) {
588
            return $this->stubPath;
589
        }
590
591
        if ($this->config('stubs.enabled') === true) {
592
            return $this->config('stubs.path');
593
        }
594
595
        return $this->stubPath;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->stubPath returns the type void which is incompatible with the documented return type null|string.
Loading history...
596
    }
597
598
    /**
599
     * Set stub path.
600
     *
601
     * @param string $stubPath
602
     *
603
     * @return $this
604
     */
605
    public function setStubPath($stubPath)
606
    {
607
        $this->stubPath = $stubPath;
608
609
        return $this;
610
    }
611
}
612