Issues (61)

src/di/AbstractModule.php (2 issues)

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
    public function __construct(
29
        ?self $module = null
30
    ) {
31
        $this->lastModule = $module;
32
        $this->activate();
33
        assert($this->container instanceof Container);
34
        if ($module instanceof self) {
35
            $this->container->merge($module->getContainer());
36
        }
37
    }
38
39
    public function __toString(): string
40
    {
41
        return (new ModuleString())($this->getContainer(), $this->getContainer()->getPointcuts());
42
    }
43
44
    /**
45
     * Install module
46
     */
47
    public function install(self $module): void
48
    {
49
        $this->getContainer()->merge($module->getContainer());
50
    }
51
52
    /**
53
     * Override module
54
     */
55
    public function override(self $module): void
56
    {
57
        $module->getContainer()->merge($this->getContainer());
58
        $this->container = $module->getContainer();
59
    }
60
61
    /**
62
     * Return activated container
63
     */
64
    public function getContainer(): Container
65
    {
66
        if (! $this->container instanceof Container) {
67
            $this->activate();
68
            assert($this->container instanceof Container);
69
        }
70
71
        return $this->container;
72
    }
73
74
    /**
75
     * Bind interceptor
76
     *
77
     * @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...
78
     */
79
    public function bindInterceptor(AbstractMatcher $classMatcher, AbstractMatcher $methodMatcher, array $interceptors): void
80
    {
81
        $pointcut = new Pointcut($classMatcher, $methodMatcher, $interceptors);
82
        $this->getContainer()->addPointcut($pointcut);
83
        foreach ($interceptors as $interceptor) {
84
            if (class_exists($interceptor)) {
85
                (new Bind($this->getContainer(), $interceptor))->to($interceptor)->in(Scope::SINGLETON);
86
87
                return;
88
            }
89
90
            assert(interface_exists($interceptor));
91
            (new Bind($this->getContainer(), $interceptor))->in(Scope::SINGLETON);
92
        }
93
    }
94
95
    /**
96
     * Bind interceptor early
97
     *
98
     * @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...
99
     */
100
    public function bindPriorityInterceptor(AbstractMatcher $classMatcher, AbstractMatcher $methodMatcher, array $interceptors): void
101
    {
102
        $pointcut = new PriorityPointcut($classMatcher, $methodMatcher, $interceptors);
103
        $this->getContainer()->addPointcut($pointcut);
104
        foreach ($interceptors as $interceptor) {
105
            (new Bind($this->getContainer(), $interceptor))->to($interceptor)->in(Scope::SINGLETON);
106
        }
107
    }
108
109
    /**
110
     * Rename binding name
111
     *
112
     * @param string $interface       Interface
113
     * @param string $newName         New binding name
114
     * @param string $sourceName      Original binding name
115
     * @param string $targetInterface Original interface
116
     */
117
    public function rename(string $interface, string $newName, string $sourceName = Name::ANY, string $targetInterface = ''): void
118
    {
119
        $targetInterface = $targetInterface ?: $interface;
120
        if ($this->lastModule instanceof self) {
121
            $this->lastModule->getContainer()->move($interface, $sourceName, $targetInterface, $newName);
122
        }
123
    }
124
125
    /**
126
     * Configure binding
127
     *
128
     * @return void
129
     *
130
     * @noinspection ReturnTypeCanBeDeclaredInspection
131
     */
132
    abstract protected function configure();
133
134
    /**
135
     * Bind interface
136
     *
137
     * @phpstan-param class-string|string $interface
138
     */
139
    protected function bind(string $interface = ''): Bind
140
    {
141
        return new Bind($this->getContainer(), $interface);
142
    }
143
144
    /**
145
     * Activate bindings
146
     */
147
    private function activate(): void
148
    {
149
        $this->container = new Container();
150
        $this->matcher = new Matcher();
151
        $this->configure();
152
    }
153
}
154