Issues (101)

src/di/Container.php (11 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
21
/**
22
 * @psalm-import-type DependencyContainer from Types
23
 * @psalm-import-type PointcutList from Types
24
 * @psalm-import-type DependencyIndex from Types
25
 * @psalm-import-type MethodArguments from Types
26
 * @psalm-import-type InjectableValue from Types
27
 */
28
final class Container implements InjectorInterface
29
{
30
    /** @var MultiBindings */
31
    public $multiBindings;
32
33
    /** @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...
34
    private $container = [];
35
36
    /** @var array<int, Pointcut> */
37
    private $pointcuts = [];
38
39
    public function __construct()
40
    {
41
        $this->multiBindings = new MultiBindings();
42
    }
43
44
    /**
45
     * @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...
46
     */
47
    public function __sleep()
48
    {
49
        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...
50
    }
51
52
    /**
53
     * Add binding to a container
54
     */
55
    public function add(Bind $bind): void
56
    {
57
        $dependency = $bind->getBound();
58
        $dependency->register($this->container, $bind);
0 ignored issues
show
$this->container of type Ray\Di\DependencyContainer is incompatible with the type array expected by parameter $container of Ray\Di\DependencyInterface::register(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

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

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

225
        ksort(/** @scrutinizer ignore-type */ $this->container);
Loading history...
226
    }
227
}
228