RecursiveNamespaceResolver   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 92
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 16
eloc 38
dl 0
loc 92
ccs 42
cts 42
cp 1
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A registerDefaultListeners() 0 6 1
A getEventDispatcher() 0 3 1
A __construct() 0 4 1
A getDirectoryContents() 0 10 3
B resolveNamespace() 0 48 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Fabiang\ExceptionGenerator\Generator;
6
7
use DirectoryIterator;
8
use Fabiang\ExceptionGenerator\BreakListener\GitDirectoryListener;
9
use Fabiang\ExceptionGenerator\BreakListener\RootDirectoryListener;
10
use Fabiang\ExceptionGenerator\Event\FileEvent;
11
use Fabiang\ExceptionGenerator\FileLoopListener\ComposerJsonListener;
12
use Fabiang\ExceptionGenerator\FileLoopListener\PHPFileListener;
13
use Fabiang\ExceptionGenerator\Resolver\ComposerResolver;
14
use Fabiang\ExceptionGenerator\Resolver\NamespaceResolver;
15
use Symfony\Component\EventDispatcher\EventDispatcher;
16
17
use function basename;
18
use function count;
19
use function dirname;
20
21
class RecursiveNamespaceResolver
22
{
23 1
    public function __construct(protected EventDispatcher $eventDispatcher)
24
    {
25 1
        $this->eventDispatcher = $eventDispatcher;
26 1
        $this->registerDefaultListeners();
27
    }
28
29
    /**
30
     * Register default listeners
31
     */
32 1
    private function registerDefaultListeners(): void
33
    {
34 1
        $this->eventDispatcher->addSubscriber(new PHPFileListener(new NamespaceResolver()));
35 1
        $this->eventDispatcher->addSubscriber(new ComposerJsonListener(new ComposerResolver()));
36 1
        $this->eventDispatcher->addSubscriber(new GitDirectoryListener());
37 1
        $this->eventDispatcher->addSubscriber(new RootDirectoryListener());
38
    }
39
40
    /**
41
     * Run application.
42
     */
43 3
    public function resolveNamespace(string $path): ?string
44
    {
45 3
        $namespace       = null;
46 3
        $eventDispatcher = $this->eventDispatcher;
47
48
        // loop as long a break listener doesn't stop propagation or we have empty directories
49
        // we iterate through directories up
50 3
        $loopedPaths = [];
51
        do {
52 3
            $directory = $this->getDirectoryContents($path);
53
            // loop over files/directories and check if a listener can find a namespace
54 3
            foreach ($directory as $item) {
55 3
                $namespaceEvent = new FileEvent($item);
56 3
                $namespaceEvent->setLoopedDirectories($loopedPaths);
57 3
                $eventDispatcher->dispatch($namespaceEvent, 'file.loop');
58
59
                // if a listener has found a namespace and because
60
                // of its priority is want to cancel we break early
61 3
                if ($namespaceEvent->isPropagationStopped()) {
62 1
                    $namespace = $namespaceEvent->getNamespace();
63 1
                    break 2;
64
                }
65
66
                // save a possible found namespace for the next iteration
67 2
                if ($namespaceEvent->getNamespace()) {
68 1
                    $namespace = $namespaceEvent->getNamespace();
69
                }
70
            }
71
72
            // we have found a namespace, so break early
73 3
            if ($namespace) {
74 1
                break;
75
            }
76
77
            // check for listeners that check if the path iteration loop should be stopped
78 3
            foreach ($directory as $item) {
79 1
                $breakEvent = new FileEvent($item);
80 1
                $eventDispatcher->dispatch($breakEvent, 'file.break');
81 1
                if (true === $breakEvent->isPropagationStopped()) {
82 1
                    break 2;
83
                }
84
            }
85 3
            $loopedPaths[] = basename($path);
86 3
            $path          = dirname($path) !== 'vfs:' ? dirname($path) : 'vfs://';
87
            //break early cuz DirectoryIterator can't handle vfs root folder
88 3
        } while (0 === count($directory) || $path !== 'vfs://');
89
90 3
        return $namespace;
91
    }
92
93
    /**
94
     * Get directory contents without dot files.
95
     *
96
     * @psalm-return array<int, DirectoryIterator>
97
     */
98 3
    private function getDirectoryContents(string $path): iterable
99
    {
100 3
        $directory = new DirectoryIterator($path);
101 3
        $items     = [];
102 3
        foreach ($directory as $item) {
103 3
            if (! $item->isDot()) {
104 3
                $items[] = clone $item;
105
            }
106
        }
107 3
        return $items;
108
    }
109
110 1
    public function getEventDispatcher(): EventDispatcher
111
    {
112 1
        return $this->eventDispatcher;
113
    }
114
}
115