Bind   A
last analyzed

Complexity

Total Complexity 25

Size/Duplication

Total Lines 217
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 25
eloc 66
c 0
b 0
f 0
dl 0
loc 217
rs 10

14 Methods

Rating   Name   Duplication   Size   Complexity  
A in() 0 11 5
A toInstance() 0 7 1
A __construct() 0 14 4
A annotatedWith() 0 5 1
A to() 0 8 1
A __toString() 0 3 1
A isRegistered() 0 3 1
A __destruct() 0 5 2
A getStringName() 0 24 2
A toProvider() 0 8 1
A getBound() 0 3 1
A toConstructor() 0 14 3
A toNull() 0 8 1
A setBound() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ray\Di;
6
7
use Ray\Aop\MethodInterceptor;
8
use Ray\Aop\ReflectionClass;
9
use Ray\Di\Exception\InvalidToConstructorNameParameter;
10
use ReflectionException;
11
use ReflectionMethod;
12
13
use function array_keys;
14
use function array_reduce;
15
use function assert;
16
use function class_exists;
17
use function implode;
18
use function interface_exists;
19
use function is_array;
20
use function is_string;
21
22
/**
23
 * @psalm-import-type BindableInterface from Types
24
 * @psalm-import-type BindingName from Types
25
 * @psalm-import-type ParameterNameMapping from Types
26
 */
27
final class Bind
28
{
29
    /** @var Container */
30
    private $container;
31
32
    /**
33
     * @var string|class-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment string|class-string at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in string|class-string.
Loading history...
34
     * @phpstan-var class-string<MethodInterceptor>|string
35
     */
36
    private $interface;
37
38
    /** @var string */
39
    private $name = Name::ANY;
40
41
    /** @var DependencyInterface */
42
    private $bound;
43
44
    /** @var BindValidator */
45
    private $validate;
46
47
    /** @var ?Untarget */
48
    private $untarget;
49
50
    /**
51
     * @param Container         $container dependency container
52
     * @param BindableInterface $interface interface or concrete class name
0 ignored issues
show
Bug introduced by
The type Ray\Di\BindableInterface 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...
53
     */
54
    public function __construct(Container $container, string $interface)
55
    {
56
        $this->container = $container;
57
        $this->interface = $interface;
58
        $this->validate = new BindValidator();
59
        $bindUntarget = class_exists($interface) && ! (new \ReflectionClass($interface))->isAbstract() && ! $this->isRegistered($interface);
60
        $this->bound = new NullDependency();
61
        if ($bindUntarget) {
62
            $this->untarget = new Untarget($interface);
63
64
            return;
65
        }
66
67
        $this->validate->constructor($interface);
68
    }
69
70
    public function __destruct()
71
    {
72
        if ($this->untarget) {
73
            ($this->untarget)($this->container, $this);
74
            $this->untarget = null;
75
        }
76
    }
77
78
    /**
79
     * @return non-empty-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
80
     */
81
    public function __toString(): string
82
    {
83
        return $this->interface . '-' . $this->name;
84
    }
85
86
    /**
87
     * Set dependency name
88
     *
89
     * @param BindingName $name
0 ignored issues
show
Bug introduced by
The type Ray\Di\BindingName 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
    public function annotatedWith(string $name): self
92
    {
93
        $this->name = $name;
94
95
        return $this;
96
    }
97
98
    /**
99
     * Bind to class
100
     *
101
     * @param class-string $class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
102
     */
103
    public function to(string $class): self
104
    {
105
        $this->untarget = null;
106
        $refClass = $this->validate->to($this->interface, $class);
107
        $this->bound = (new DependencyFactory())->newAnnotatedDependency($refClass);
108
        $this->container->add($this);
109
110
        return $this;
111
    }
112
113
    /**
114
     * Bind to constructor
115
     *
116
     * @param class-string<T>             $class class name
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
117
     * @param ParameterNameMapping|string $name  "varName=bindName,..." or [$varName => $bindName, $varName => $bindName...]
118
     *
119
     * @throws ReflectionException
120
     *
121
     * @template T of object
122
     */
123
    public function toConstructor(string $class, $name, ?InjectionPoints $injectionPoints = null, ?string $postConstruct = null): self
124
    {
125
        if (is_array($name)) {
126
            $name = $this->getStringName($name);
127
        }
128
129
        $this->untarget = null;
130
        $postConstructRef = $postConstruct !== null ? new ReflectionMethod($class, $postConstruct) : null;
131
        /** @var ReflectionClass<object> $reflection */
132
        $reflection = new ReflectionClass($class);
133
        $this->bound = (new DependencyFactory())->newToConstructor($reflection, $name, $injectionPoints, $postConstructRef);
134
        $this->container->add($this);
135
136
        return $this;
137
    }
138
139
    /**
140
     * Bind to provider
141
     *
142
     * @phpstan-param class-string $provider
143
     */
144
    public function toProvider(string $provider, string $context = ''): self
145
    {
146
        $this->untarget = null;
147
        $refClass = $this->validate->toProvider($provider);
148
        $this->bound = (new DependencyFactory())->newProvider($refClass, $context);
149
        $this->container->add($this);
150
151
        return $this;
152
    }
153
154
    /**
155
     * Bind to instance
156
     *
157
     * @param mixed $instance
158
     */
159
    public function toInstance($instance): self
160
    {
161
        $this->untarget = null;
162
        $this->bound = new Instance($instance);
163
        $this->container->add($this);
164
165
        return $this;
166
    }
167
168
    /**
169
     * Bind to NullObject
170
     */
171
    public function toNull(): self
172
    {
173
        $this->untarget = null;
174
        assert(interface_exists($this->interface));
175
        $this->bound = new NullObjectDependency($this->interface);
176
        $this->container->add($this);
177
178
        return $this;
179
    }
180
181
    /**
182
     * Set scope
183
     */
184
    public function in(string $scope): self
185
    {
186
        if ($this->bound instanceof Dependency || $this->bound instanceof DependencyProvider || $this->bound instanceof NullDependency) {
187
            $this->bound->setScope($scope);
188
        }
189
190
        if ($this->untarget) {
191
            $this->untarget->setScope($scope);
192
        }
193
194
        return $this;
195
    }
196
197
    public function getBound(): DependencyInterface
198
    {
199
        return $this->bound;
200
    }
201
202
    public function setBound(DependencyInterface $bound): void
203
    {
204
        $this->bound = $bound;
205
    }
206
207
    private function isRegistered(string $interface): bool
208
    {
209
        return isset($this->container->getContainer()[$interface . '-' . Name::ANY]);
210
    }
211
212
    /**
213
     * Return string
214
     *
215
     * input: ['varA' => 'nameA', 'varB' => 'nameB']
216
     * output: "varA=nameA,varB=nameB"
217
     *
218
     * @param ParameterNameMapping $name
0 ignored issues
show
Bug introduced by
The type Ray\Di\ParameterNameMapping 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...
219
     */
220
    private function getStringName(array $name): string
221
    {
222
        $keys = array_keys($name);
223
224
        $names = array_reduce(
225
            $keys,
226
            /**
227
             * @param list<string> $carry
228
             * @param array-key $key
0 ignored issues
show
Documentation Bug introduced by
The doc comment array-key at position 0 could not be parsed: Unknown type name 'array-key' at position 0 in array-key.
Loading history...
229
             */
230
            static function (array $carry, $key) use ($name): array {
231
                if (! is_string($key)) { // @phpstan-ignore-line
232
                    throw new InvalidToConstructorNameParameter((string) $key);
233
                }
234
235
                $varName = $name[$key] ?? '';
236
                $carry[] = $key . '=' . $varName;
237
238
                return $carry;
239
            },
240
            []
241
        );
242
243
        return implode(',', $names);
244
    }
245
}
246