Passed
Push — php82 ( f26871 )
by Akihito
02:19
created

Bind::getStringName()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 24
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 24
c 0
b 0
f 0
rs 9.9332
cc 2
nc 1
nop 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 ReflectionException;
10
use ReflectionMethod;
11
12
use function assert;
13
use function class_exists;
14
use function interface_exists;
15
16
/**
17
 * @psalm-import-type BindableInterface from Types
18
 * @psalm-import-type BindingName from Types
19
 * @psalm-import-type ParameterNameMapping from Types
20
 */
21
final class Bind
22
{
23
    /** @var Container */
24
    private $container;
25
26
    /**
27
     * @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...
28
     * @phpstan-var class-string<MethodInterceptor>|string
29
     */
30
    private $interface;
31
32
    /** @var string */
33
    private $name = Name::ANY;
34
35
    /** @var DependencyInterface */
36
    private $bound;
37
38
    /** @var BindValidator */
39
    private $validate;
40
41
    /** @var ?Untarget */
42
    private $untarget;
43
44
    /**
45
     * @param Container         $container dependency container
46
     * @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...
47
     */
48
    public function __construct(Container $container, string $interface)
49
    {
50
        $this->container = $container;
51
        $this->interface = $interface;
52
        $this->validate = new BindValidator();
53
        $bindUntarget = class_exists($interface) && ! (new \ReflectionClass($interface))->isAbstract() && ! $this->isRegistered($interface);
54
        $this->bound = new NullDependency();
55
        if ($bindUntarget) {
56
            $this->untarget = new Untarget($interface);
57
58
            return;
59
        }
60
61
        $this->validate->constructor($interface);
62
    }
63
64
    public function __destruct()
65
    {
66
        if ($this->untarget) {
67
            ($this->untarget)($this->container, $this);
68
            $this->untarget = null;
69
        }
70
    }
71
72
    /**
73
     * @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...
74
     */
75
    public function __toString(): string
76
    {
77
        return $this->interface . '-' . $this->name;
78
    }
79
80
    /**
81
     * Set dependency name
82
     *
83
     * @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...
84
     */
85
    public function annotatedWith(string $name): self
86
    {
87
        $this->name = $name;
88
89
        return $this;
90
    }
91
92
    /**
93
     * Bind to class
94
     *
95
     * @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...
96
     */
97
    public function to(string $class): self
98
    {
99
        $this->untarget = null;
100
        $refClass = $this->validate->to($this->interface, $class);
101
        $this->bound = (new DependencyFactory())->newAnnotatedDependency($refClass);
102
        $this->container->add($this);
103
104
        return $this;
105
    }
106
107
    /**
108
     * Bind to constructor
109
     *
110
     * @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...
111
     * @param ParameterNameMapping|string $name  "varName=bindName,..." or [$varName => $bindName, $varName => $bindName...]
112
     *
113
     * @throws ReflectionException
114
     *
115
     * @template T of object
116
     */
117
    public function toConstructor(string $class, $name, ?InjectionPoints $injectionPoints = null, ?string $postConstruct = null): self
118
    {
119
        $this->untarget = null;
120
        $postConstructRef = $postConstruct !== null ? new ReflectionMethod($class, $postConstruct) : null;
121
        /** @var ReflectionClass<object> $reflection */
122
        $reflection = new ReflectionClass($class);
123
        $this->bound = (new DependencyFactory())->newToConstructor($reflection, $name, $injectionPoints, $postConstructRef);
124
        $this->container->add($this);
125
126
        return $this;
127
    }
128
129
    /**
130
     * Bind to provider
131
     *
132
     * @phpstan-param class-string $provider
133
     */
134
    public function toProvider(string $provider, string $context = ''): self
135
    {
136
        $this->untarget = null;
137
        $refClass = $this->validate->toProvider($provider);
138
        $this->bound = (new DependencyFactory())->newProvider($refClass, $context);
139
        $this->container->add($this);
140
141
        return $this;
142
    }
143
144
    /**
145
     * Bind to instance
146
     *
147
     * @param mixed $instance
148
     */
149
    public function toInstance($instance): self
150
    {
151
        $this->untarget = null;
152
        $this->bound = new Instance($instance);
153
        $this->container->add($this);
154
155
        return $this;
156
    }
157
158
    /**
159
     * Bind to NullObject
160
     */
161
    public function toNull(): self
162
    {
163
        $this->untarget = null;
164
        assert(interface_exists($this->interface));
165
        $this->bound = new NullObjectDependency($this->interface);
166
        $this->container->add($this);
167
168
        return $this;
169
    }
170
171
    /**
172
     * Set scope
173
     */
174
    public function in(string $scope): self
175
    {
176
        if ($this->bound instanceof Dependency || $this->bound instanceof DependencyProvider || $this->bound instanceof NullDependency) {
177
            $this->bound->setScope($scope);
178
        }
179
180
        if ($this->untarget) {
181
            $this->untarget->setScope($scope);
182
        }
183
184
        return $this;
185
    }
186
187
    public function getBound(): DependencyInterface
188
    {
189
        return $this->bound;
190
    }
191
192
    public function setBound(DependencyInterface $bound): void
193
    {
194
        $this->bound = $bound;
195
    }
196
197
    private function isRegistered(string $interface): bool
198
    {
199
        return isset($this->container->getContainer()[$interface . '-' . Name::ANY]);
200
    }
201
}
202