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