AllAppModulesFinder::getNamespace()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 7
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Gacela\Console\Domain\AllAppModules;
6
7
use Gacela\Framework\AbstractFacade;
8
use OuterIterator;
9
use RecursiveDirectoryIterator;
10
use RecursiveIteratorIterator;
11
use ReflectionClass;
12
use SplFileInfo;
13
14
use function sprintf;
15
16
final class AllAppModulesFinder
17
{
18
    /**
19
     * @param RecursiveIteratorIterator<RecursiveDirectoryIterator> $fileIterator
20
     */
21
    public function __construct(
22
        private readonly OuterIterator $fileIterator,
23
        private readonly AppModuleCreator $appModuleCreator,
24
    ) {
25
    }
26
27
    /**
28
     * @return list<AppModule>
29
     */
30
    public function findAllAppModules(string $filter): array
31
    {
32
        $result = [];
33
34
        /** @var SplFileInfo $fileInfo */
35
        foreach ($this->fileIterator as $fileInfo) {
36
            $appModule = $this->createAppModule($fileInfo, $filter);
37
            if ($appModule instanceof AppModule && $this->isFacade($appModule)) {
38
                $result[$appModule->facadeClass()] = $appModule;
39
            }
40
        }
41
42
        uksort($result, static fn ($a, $b): int => $a <=> $b);
43
44
        return array_values($result);
45
    }
46
47
    private function createAppModule(SplFileInfo $fileInfo, string $filter): ?AppModule
48
    {
49
        if (!$fileInfo->isFile()
50
            || $fileInfo->getExtension() !== 'php'
51
            || str_contains($fileInfo->getRealPath(), 'vendor' . DIRECTORY_SEPARATOR)
52
        ) {
53
            return null;
54
        }
55
56
        $namespace = $this->getNamespace($fileInfo);
57
        $className = $this->buildClassName($fileInfo);
58
59
        $fullyQualifiedClassName = sprintf(
60
            '%s\\%s',
61
            $namespace,
62
            $className,
63
        );
64
65
        if ($filter !== '') {
66
            $filterNamespace = str_replace('/', '\\', $filter);
67
            if (!str_contains($fullyQualifiedClassName, $filterNamespace)) {
68
                return null;
69
            }
70
        }
71
72
        if (!class_exists($fullyQualifiedClassName)) {
73
            return null;
74
        }
75
76
        return $this->appModuleCreator->fromClass($fullyQualifiedClassName);
77
    }
78
79
    private function getNamespace(SplFileInfo $fileInfo): string
80
    {
81
        $fileContent = (string)file_get_contents($fileInfo->getRealPath());
82
83
        preg_match('#namespace (.*);#', $fileContent, $matches);
84
85
        return $matches[1] ?? '';
86
    }
87
88
    private function buildClassName(SplFileInfo $fileInfo): string
89
    {
90
        $pieces = explode(DIRECTORY_SEPARATOR, $fileInfo->getFilename());
91
        $filename = end($pieces);
92
93
        return substr($filename, 0, strpos($filename, '.') ?: 1);
94
    }
95
96
    private function isFacade(AppModule $appModule): bool
97
    {
98
        $rc = new ReflectionClass($appModule->facadeClass());
99
        $parentClass = $rc->getParentClass();
100
101
        return $parentClass !== false
102
            && $parentClass->name === AbstractFacade::class;
103
    }
104
}
105