Issues (94)

src/di/Container.php (8 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\NoHint;
12
use Ray\Di\Exception\Unbound;
13
use Ray\Di\Exception\Untargeted;
14
use Ray\Di\MultiBinding\MultiBindings;
15
use ReflectionClass;
16
17
use function array_merge;
18
use function class_exists;
19
use function explode;
20
use function ksort;
21
use function sprintf;
22
23
/**
24
 * @psalm-import-type DependencyContainer from Types
25
 * @psalm-import-type PointcutList from Types
26
 * @psalm-import-type DependencyIndex from Types
27
 * @psalm-import-type MethodArguments from Types
28
 * @psalm-import-type InjectableValue from Types
29
 */
30
final class Container implements InjectorInterface
31
{
32
    /** @var MultiBindings */
33
    public $multiBindings;
34
35
    /** @var DependencyContainer */
0 ignored issues
show
The type Ray\Di\DependencyContainer 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...
36
    private array $container = [];
37
38
    /** @var array<int, Pointcut> */
39
    private array $pointcuts = [];
40
41
    public function __construct()
42
    {
43
        $this->multiBindings = new MultiBindings();
44
    }
45
46
    /**
47
     * @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...
48
     */
49
    public function __sleep()
50
    {
51
        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...
52
    }
53
54
    /**
55
     * Add binding to a container
56
     */
57
    public function add(Bind $bind): void
58
    {
59
        $dependency = $bind->getBound();
60
        $dependency->register($this->container, $bind);
61
    }
62
63
    /**
64
     * Add Pointcut to container
65
     */
66
    public function addPointcut(Pointcut $pointcut): void
67
    {
68
        $this->pointcuts[] = $pointcut;
69
    }
70
71
    /**
72
     * {@inheritDoc}
73
     *
74
     * @param ''|class-string<T> $interface
0 ignored issues
show
Documentation Bug introduced by
The doc comment ''|class-string<T> at position 0 could not be parsed: Unknown type name '''' at position 0 in ''|class-string<T>.
Loading history...
75
     * @param string             $name
76
     *
77
     * @return ($interface is '' ? mixed : T)
0 ignored issues
show
Documentation Bug introduced by
The doc comment ($interface at position 1 could not be parsed: Unknown type name '$interface' at position 1 in ($interface.
Loading history...
78
     *
79
     * @template T of object
80
     */
81
    public function getInstance($interface, $name = Name::ANY)
82
    {
83
        /** @psalm-suppress MixedReturnStatement */
84
        return $this->getDependency($interface . '-' . $name);
85
    }
86
87
    /**
88
     * Return dependency injected instance
89
     *
90
     * @param MethodArguments $params
0 ignored issues
show
The type Ray\Di\MethodArguments 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...
91
     *
92
     * @return mixed
93
     *
94
     * @throws Unbound
95
     */
96
    public function getInstanceWithArgs(string $interface, array $params)
97
    {
98
        $index = $interface . '-';
99
        if (! isset($this->container[$index])) {
100
            throw $this->unbound($index);
101
        }
102
103
        $dependency = $this->container[$index];
104
        if (! $dependency instanceof Dependency) {
105
            throw new BadMethodCallException($interface);
106
        }
107
108
        return $dependency->injectWithArgs($this, $params);
109
    }
110
111
    /**
112
     * Return dependency injected instance
113
     *
114
     * @param DependencyIndex $index
0 ignored issues
show
The type Ray\Di\DependencyIndex 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...
115
     *
116
     * @return mixed
117
     *
118
     * @throws Unbound
119
     */
120
    public function getDependency(string $index)
121
    {
122
        if (! isset($this->container[$index])) {
123
            throw $this->unbound($index);
124
        }
125
126
        return $this->container[$index]->inject($this);
127
    }
128
129
    /**
130
     * Rename existing dependency interface + name
131
     */
132
    public function move(string $sourceInterface, string $sourceName, string $targetInterface, string $targetName): void
133
    {
134
        $sourceIndex = $sourceInterface . '-' . $sourceName;
135
        if (! isset($this->container[$sourceIndex])) {
136
            throw $this->unbound($sourceIndex);
137
        }
138
139
        $targetIndex = $targetInterface . '-' . $targetName;
140
        $this->container[$targetIndex] = $this->container[$sourceIndex];
141
        unset($this->container[$sourceIndex]);
142
    }
143
144
    /**
145
     * Return Unbound exception
146
     *
147
     * @param DependencyIndex $index {interface}-{bind name}
148
     */
149
    public function unbound(string $index): Untargeted|Unbound
150
    {
151
        [$class, $name] = explode('-', $index);
152
        if (class_exists($class) && ! (new ReflectionClass($class))->isAbstract()) {
153
            return new Untargeted($class);
154
        }
155
156
        if ($class === '' && $name === '') {
157
            return new NoHint();
158
        }
159
160
        return new Unbound(sprintf("'%s-%s'", $class, $name));
161
    }
162
163
    /**
164
     * Return container
165
     *
166
     * @return array<non-empty-string, DependencyInterface>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<non-empty-string, DependencyInterface> at position 2 could not be parsed: Unknown type name 'non-empty-string' at position 2 in array<non-empty-string, DependencyInterface>.
Loading history...
167
     * @psalm-return DependencyContainer
168
     */
169
    public function getContainer(): array
170
    {
171
        return $this->container;
172
    }
173
174
    /**
175
     * Return pointcuts
176
     *
177
     * @return array<int, Pointcut>
178
     * @psalm-return PointcutList
179
     */
180
    public function getPointcuts(): array
181
    {
182
        return $this->pointcuts;
183
    }
184
185
    /**
186
     * Merge container
187
     */
188
    public function merge(self $container): void
189
    {
190
        $this->multiBindings->merge($container->multiBindings);
191
        $this->container += $container->getContainer();
192
        $this->pointcuts = array_merge($this->pointcuts, $container->getPointcuts());
193
    }
194
195
    /**
196
     * Weave aspects to all dependency in container
197
     */
198
    public function weaveAspects(CompilerInterface $compiler): void
199
    {
200
        if ($this->pointcuts === []) {
201
            return;
202
        }
203
204
        foreach ($this->container as $dependency) {
205
            if ($dependency instanceof Dependency) {
206
                $dependency->weaveAspects($compiler, $this->pointcuts);
207
            }
208
        }
209
    }
210
211
    /**
212
     * Weave aspect to single dependency
213
     */
214
    public function weaveAspect(Compiler $compiler, Dependency $dependency): self
215
    {
216
        $dependency->weaveAspects($compiler, $this->pointcuts);
217
218
        return $this;
219
    }
220
221
    /**
222
     * @param callable(DependencyInterface, string): DependencyInterface $f
223
     */
224
    public function map(callable $f): void
225
    {
226
        foreach ($this->container as $key => &$index) {
227
            $index = $f($index, $key);
228
        }
229
    }
230
231
    public function sort(): void
232
    {
233
        ksort($this->container);
234
    }
235
}
236