Passed
Push — re-bind ( bd2668...fded5a )
by Akihito
01:34
created

AbstractModule::activate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 5
c 0
b 0
f 0
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ray\Di;
6
7
use Ray\Aop\AbstractMatcher;
8
use Ray\Aop\Matcher;
9
use Ray\Aop\MethodInterceptor;
10
use Ray\Aop\Pointcut;
11
use Ray\Aop\PriorityPointcut;
12
13
use function assert;
14
use function class_exists;
15
use function interface_exists;
16
17
abstract class AbstractModule
18
{
19
    /** @var Matcher */
20
    protected $matcher;
21
22
    /** @var ?AbstractModule */
23
    protected $lastModule;
24
25
    /** @var ?Container */
26
    private $container;
27
28
    /** @var Rebind >*/
29
    private $rebind;
30
31
    public function __construct(
32
        ?self $module = null
33
    ) {
34
        $this->rebind = new Rebind();
35
        $this->lastModule = $module;
36
        $this->activate();
37
        assert($this->container instanceof Container);
38
        if ($module instanceof self) {
39
            $this->container->merge($module->getContainer());
40
        }
41
42
        $this->rebind->commit($this->container);
43
    }
44
45
    public function __toString(): string
46
    {
47
        return (new ModuleString())($this->getContainer(), $this->getContainer()->getPointcuts());
48
    }
49
50
    /**
51
     * Install module
52
     */
53
    public function install(self $module): void
54
    {
55
        $this->getContainer()->merge($module->getContainer());
56
    }
57
58
    /**
59
     * Override module
60
     */
61
    public function override(self $module): void
62
    {
63
        $module->getContainer()->merge($this->getContainer());
64
        $this->container = $module->getContainer();
65
    }
66
67
    /**
68
     * Return activated container
69
     */
70
    public function getContainer(): Container
71
    {
72
        if (! $this->container instanceof Container) {
73
            $this->activate();
74
            assert($this->container instanceof Container);
75
        }
76
77
        return $this->container;
78
    }
79
80
    /**
81
     * Bind interceptor
82
     *
83
     * @param array<class-string<MethodInterceptor>> $interceptors
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string<MethodInterceptor>> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string<MethodInterceptor>>.
Loading history...
84
     */
85
    public function bindInterceptor(AbstractMatcher $classMatcher, AbstractMatcher $methodMatcher, array $interceptors): void
86
    {
87
        $pointcut = new Pointcut($classMatcher, $methodMatcher, $interceptors);
88
        $this->getContainer()->addPointcut($pointcut);
89
        foreach ($interceptors as $interceptor) {
90
            if (class_exists($interceptor)) {
91
                (new Bind($this->getContainer(), $interceptor))->to($interceptor)->in(Scope::SINGLETON);
92
93
                return;
94
            }
95
96
            assert(interface_exists($interceptor));
97
            (new Bind($this->getContainer(), $interceptor))->in(Scope::SINGLETON);
98
        }
99
    }
100
101
    /**
102
     * Bind interceptor early
103
     *
104
     * @param array<class-string<MethodInterceptor>> $interceptors
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string<MethodInterceptor>> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string<MethodInterceptor>>.
Loading history...
105
     */
106
    public function bindPriorityInterceptor(AbstractMatcher $classMatcher, AbstractMatcher $methodMatcher, array $interceptors): void
107
    {
108
        $pointcut = new PriorityPointcut($classMatcher, $methodMatcher, $interceptors);
109
        $this->getContainer()->addPointcut($pointcut);
110
        foreach ($interceptors as $interceptor) {
111
            (new Bind($this->getContainer(), $interceptor))->to($interceptor)->in(Scope::SINGLETON);
112
        }
113
    }
114
115
    /**
116
     * Rename binding name
117
     *
118
     * @param string $interface       Interface
119
     * @param string $newName         New binding name
120
     * @param string $sourceName      Original binding name
121
     * @param string $targetInterface Original interface
122
     *
123
     * @deprecated Use rebind() instead
124
     */
125
    public function rename(string $interface, string $newName, string $sourceName = Name::ANY, string $targetInterface = ''): void
126
    {
127
        $targetInterface = $targetInterface ?: $interface;
128
        if ($this->lastModule instanceof self) {
129
            $this->lastModule->getContainer()->move($interface, $sourceName, $targetInterface, $newName);
130
        }
131
    }
132
133
    /**
134
     * Re-bind an existing binding
135
     */
136
    public function rebind(string $fromInterface, string $toName, string $fromName = '', string $toInterface = ''): void
137
    {
138
        $toInterface = $toInterface ?: $fromInterface;
139
        $this->rebind->bind($fromInterface, $toName, $fromName, $toInterface);
140
    }
141
142
    /**
143
     * Configure binding
144
     *
145
     * @return void
146
     *
147
     * @noinspection ReturnTypeCanBeDeclaredInspection
148
     */
149
    abstract protected function configure();
150
151
    /**
152
     * Bind interface
153
     *
154
     * @phpstan-param class-string|string $interface
155
     */
156
    protected function bind(string $interface = ''): Bind
157
    {
158
        return new Bind($this->getContainer(), $interface);
159
    }
160
161
    /**
162
     * Activate bindings
163
     */
164
    private function activate(): void
165
    {
166
        $this->container = new Container();
167
        $this->matcher = new Matcher();
168
        $this->configure();
169
    }
170
}
171