Issues (61)

src/di/Container.php (2 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ray\Di;
6
7
use BadMethodCallException;
8
use Ray\Aop\Compiler;
9
use Ray\Aop\CompilerInterface;
10
use Ray\Aop\Pointcut;
11
use Ray\Di\Exception\Unbound;
12
use Ray\Di\Exception\Untargeted;
13
use Ray\Di\MultiBinding\MultiBindings;
14
use ReflectionClass;
15
16
use function array_merge;
17
use function assert;
18
use function class_exists;
19
use function explode;
20
use function is_string;
21
use function ksort;
22
23
final class Container implements InjectorInterface
24
{
25
    /** @var MultiBindings */
26
    public $multiBindings;
27
28
    /** @var DependencyInterface[] */
29
    private $container = [];
30
31
    /** @var array<int, Pointcut> */
32
    private $pointcuts = [];
33
34
    public function __construct()
35
    {
36
        $this->multiBindings = new MultiBindings();
37
    }
38
39
    /**
40
     * @return list<string>
0 ignored issues
show
The type Ray\Di\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
41
     */
42
    public function __sleep()
43
    {
44
        return ['container', 'pointcuts', 'multiBindings'];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('container'...cuts', 'multiBindings') returns the type array<integer,string> which is incompatible with the documented return type Ray\Di\list.
Loading history...
45
    }
46
47
    /**
48
     * Add binding to container
49
     */
50
    public function add(Bind $bind): void
51
    {
52
        $dependency = $bind->getBound();
53
        $dependency->register($this->container, $bind);
54
    }
55
56
    /**
57
     * Add Pointcut to container
58
     */
59
    public function addPointcut(Pointcut $pointcut): void
60
    {
61
        $this->pointcuts[] = $pointcut;
62
    }
63
64
    /**
65
     * {@inheritDoc}
66
     */
67
    public function getInstance($interface, $name = Name::ANY)
68
    {
69
        /**
70
         * @psalm-var T is object ? T : mixed
71
         * @phpstan-var mixed
72
         */
73
        return $this->getDependency($interface . '-' . $name);
74
    }
75
76
    /**
77
     * Return dependency injected instance
78
     *
79
     * @param array<int, mixed> $params
80
     *
81
     * @return mixed
82
     *
83
     * @throws Unbound
84
     */
85
    public function getInstanceWithArgs(string $interface, array $params)
86
    {
87
        $index = $interface . '-';
88
        if (! isset($this->container[$index])) {
89
            throw $this->unbound($index);
90
        }
91
92
        $dependency = $this->container[$index];
93
        if (! $dependency instanceof Dependency) {
94
            throw new BadMethodCallException($interface);
95
        }
96
97
        return $dependency->injectWithArgs($this, $params);
98
    }
99
100
    /**
101
     * Return dependency injected instance
102
     *
103
     * @return mixed
104
     *
105
     * @throws Unbound
106
     */
107
    public function getDependency(string $index)
108
    {
109
        if (! isset($this->container[$index])) {
110
            throw $this->unbound($index);
111
        }
112
113
        return $this->container[$index]->inject($this);
114
    }
115
116
    /**
117
     * Rename existing dependency interface + name
118
     */
119
    public function move(string $sourceInterface, string $sourceName, string $targetInterface, string $targetName): void
120
    {
121
        $sourceIndex = $sourceInterface . '-' . $sourceName;
122
        if (! isset($this->container[$sourceIndex])) {
123
            throw $this->unbound($sourceIndex);
124
        }
125
126
        $targetIndex = $targetInterface . '-' . $targetName;
127
        $this->container[$targetIndex] = $this->container[$sourceIndex];
128
        unset($this->container[$sourceIndex]);
129
    }
130
131
    /**
132
     * Return Unbound exception
133
     *
134
     * @param string $index {interface}-{bind name}
135
     *
136
     * @return Unbound|Untargeted
137
     */
138
    public function unbound(string $index)
139
    {
140
        [$class, $name] = explode('-', $index);
141
        if (class_exists($class) && ! (new ReflectionClass($class))->isAbstract()) {
142
            return new Untargeted($class);
143
        }
144
145
        return new Unbound("{$class}-{$name}");
146
    }
147
148
    /**
149
     * Return container
150
     *
151
     * @return DependencyInterface[]
152
     */
153
    public function getContainer(): array
154
    {
155
        return $this->container;
156
    }
157
158
    /**
159
     * Return pointcuts
160
     *
161
     * @return array<int, Pointcut>
162
     */
163
    public function getPointcuts(): array
164
    {
165
        return $this->pointcuts;
166
    }
167
168
    /**
169
     * Merge container
170
     */
171
    public function merge(self $container): void
172
    {
173
        $this->multiBindings->merge($container->multiBindings);
174
        $this->container += $container->getContainer();
175
        $this->pointcuts = array_merge($this->pointcuts, $container->getPointcuts());
176
    }
177
178
    /**
179
     * Weave aspects to all dependency in container
180
     */
181
    public function weaveAspects(CompilerInterface $compiler): void
182
    {
183
        foreach ($this->container as $dependency) {
184
            if ($dependency instanceof Dependency) {
185
                $dependency->weaveAspects($compiler, $this->pointcuts);
186
            }
187
        }
188
    }
189
190
    /**
191
     * Weave aspect to single dependency
192
     */
193
    public function weaveAspect(Compiler $compiler, Dependency $dependency): self
194
    {
195
        $dependency->weaveAspects($compiler, $this->pointcuts);
196
197
        return $this;
198
    }
199
200
    /**
201
     * @param callable(DependencyInterface, string): DependencyInterface $f
202
     */
203
    public function map(callable $f): void
204
    {
205
        foreach ($this->container as $key => &$index) {
206
            assert(is_string($key));
207
            $index = $f($index, $key);
208
        }
209
    }
210
211
    public function sort(): void
212
    {
213
        ksort($this->container);
214
    }
215
}
216