Passed
Push — master ( 93e91b...83695f )
by Fabien
02:05
created

HookLoader::hookToSubscribers()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 13
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Churn\Event;
6
7
use Churn\Event\Hook\AfterAnalysisHook;
8
use Churn\Event\Hook\AfterFileAnalysisHook;
9
use Churn\Event\Hook\BeforeAnalysisHook;
10
use Churn\Event\Subscriber\AfterAnalysisHookDecorator;
11
use Churn\Event\Subscriber\AfterFileAnalysisHookDecorator;
12
use Churn\Event\Subscriber\BeforeAnalysisHookDecorator;
13
use Churn\File\FileHelper;
14
15
/**
16
 * @internal
17
 */
18
final class HookLoader
19
{
20
21
    /**
22
     * @var array<string, string>
23
     */
24
    private $decorators;
25
26
    /**
27
     * @var string
28
     */
29
    private $basePath;
30
31
    /**
32
     * @param string $basePath The base path for the hooks files.
33
     */
34
    public function __construct(string $basePath)
35
    {
36
        $this->decorators = [
37
            AfterAnalysisHookDecorator::class => AfterAnalysisHook::class,
38
            AfterFileAnalysisHookDecorator::class => AfterFileAnalysisHook::class,
39
            BeforeAnalysisHookDecorator::class => BeforeAnalysisHook::class,
40
        ];
41
        $this->basePath = $basePath;
42
    }
43
44
    /**
45
     * @param string $hookPath The class name or the file path of the hook.
46
     * @param Broker $broker The event broker.
47
     */
48
    public function attach(string $hookPath, Broker $broker): bool
49
    {
50
        $foundSubscriber = false;
51
        $subscribers = [];
52
53
        foreach ($this->loadHooks($hookPath) as $hookName) {
54
            $subscribers = \array_merge($subscribers, $this->hookToSubscribers($hookName));
55
        }
56
57
        foreach ($subscribers as $subscriber) {
58
            $broker->subscribe($subscriber);
59
            $foundSubscriber = true;
60
        }
61
62
        return $foundSubscriber;
63
    }
64
65
    /**
66
     * @param string $hookPath The class name or the file path of the hook.
67
     * @return array<mixed>
68
     */
69
    private function loadHooks(string $hookPath): array
70
    {
71
        if (\class_exists($hookPath)) {
72
            return [$hookPath];
73
        }
74
75
        $hookPath = FileHelper::toAbsolutePath($hookPath, $this->basePath);
76
77
        if (!\is_file($hookPath)) {
78
            return [];
79
        }
80
81
        $numberOfClasses = \count(\get_declared_classes());
82
        \Churn\Event\includeOnce($hookPath);
83
84
        return \array_slice(\get_declared_classes(), $numberOfClasses);
85
    }
86
87
    /**
88
     * @param string $hookName The class name of the hook.
89
     * @return array<mixed>
90
     */
91
    private function hookToSubscribers(string $hookName): array
92
    {
93
        $subscribers = [];
94
95
        foreach ($this->decorators as $decorator => $hook) {
96
            if (!\is_subclass_of($hookName, $hook)) {
97
                continue;
98
            }
99
100
            $subscribers[] = new $decorator($hookName);
101
        }
102
103
        return $subscribers;
104
    }
105
}
106
107
/**
108
 * Scope isolated include.
109
 *
110
 * Prevents access to $this/self from included files.
111
 *
112
 * @param string $file The PHP file to include.
113
 */
114
function includeOnce(string $file): void
115
{
116
    include_once $file;
117
}
118