Issues (94)

src/di/Bind.php (7 issues)

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
use Stringable;
12
13
use function assert;
14
use function class_exists;
15
use function interface_exists;
16
17
/**
18
 * @psalm-import-type BindableInterface from Types
19
 * @psalm-import-type BindingName from Types
20
 * @psalm-import-type ParameterNameMapping from Types
21
 */
22
final class Bind implements Stringable
23
{
24
    private string $name = Name::ANY;
25
    private DependencyInterface $bound;
26
    private readonly BindValidator $validate;
27
28
    /** @var ?Untarget */
29
    private $untarget;
30
31
    /**
32
     * @param Container         $container dependency container
33
     * @param BindableInterface $interface interface or concrete class name
0 ignored issues
show
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...
34
     * @phpstan-param class-string<MethodInterceptor>|string $interface
35
     */
36
    public function __construct(
37
        private readonly Container $container,
38
        private readonly string $interface
39
    ) {
40
        $this->validate = new BindValidator();
0 ignored issues
show
The property validate is declared read-only in Ray\Di\Bind.
Loading history...
41
        $bindUntarget = class_exists($this->interface) && ! (new \ReflectionClass($this->interface))->isAbstract() && ! $this->isRegistered($this->interface);
42
        $this->bound = new NullDependency();
43
        if ($bindUntarget) {
44
            /** @var class-string $interface */
45
            $interface = $this->interface;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->interface of type string is incompatible with the declared type Ray\Di\BindableInterface of property $interface.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
46
            $this->untarget = new Untarget($interface);
47
48
            return;
49
        }
50
51
        $this->validate->constructor($this->interface);
52
    }
53
54
    public function __destruct()
55
    {
56
        if ($this->untarget) {
57
            ($this->untarget)($this->container, $this);
58
            $this->untarget = null;
59
        }
60
    }
61
62
    /**
63
     * @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...
64
     */
65
    public function __toString(): string
66
    {
67
        return $this->interface . '-' . $this->name;
68
    }
69
70
    /**
71
     * Set dependency name
72
     *
73
     * @param BindingName $name
0 ignored issues
show
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...
74
     */
75
    public function annotatedWith(string $name): self
76
    {
77
        $this->name = $name;
78
79
        return $this;
80
    }
81
82
    /**
83
     * Bind to class
84
     *
85
     * @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...
86
     */
87
    public function to(string $class): self
88
    {
89
        $this->untarget = null;
90
        $refClass = $this->validate->to($this->interface, $class);
91
        $this->bound = (new DependencyFactory())->newAnnotatedDependency($refClass);
92
        $this->container->add($this);
93
94
        return $this;
95
    }
96
97
    /**
98
     * Bind to constructor
99
     *
100
     * @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...
101
     * @param ParameterNameMapping|string $name  "varName=bindName,..." or [$varName => $bindName, $varName => $bindName...]
102
     *
103
     * @throws ReflectionException
104
     *
105
     * @template T of object
106
     */
107
    public function toConstructor(string $class, string|array $name, ?InjectionPoints $injectionPoints = null, ?string $postConstruct = null): self
108
    {
109
        $this->untarget = null;
110
        $postConstructRef = $postConstruct !== null ? new ReflectionMethod($class, $postConstruct) : null;
111
        /** @var ReflectionClass<object> $reflection */
112
        $reflection = new ReflectionClass($class);
113
        $this->bound = (new DependencyFactory())->newToConstructor($reflection, $name, $injectionPoints, $postConstructRef);
114
        $this->container->add($this);
115
116
        return $this;
117
    }
118
119
    /**
120
     * Bind to provider
121
     *
122
     * @phpstan-param class-string $provider
123
     */
124
    public function toProvider(string $provider, string $context = ''): self
125
    {
126
        $this->untarget = null;
127
        $refClass = $this->validate->toProvider($provider);
128
        $this->bound = (new DependencyFactory())->newProvider($refClass, $context);
129
        $this->container->add($this);
130
131
        return $this;
132
    }
133
134
    /**
135
     * Bind to instance
136
     *
137
     * @param mixed $instance
138
     */
139
    public function toInstance($instance): self
140
    {
141
        $this->untarget = null;
142
        $this->bound = new Instance($instance);
143
        $this->container->add($this);
144
145
        return $this;
146
    }
147
148
    /**
149
     * Bind to NullObject
150
     */
151
    public function toNull(): self
152
    {
153
        $this->untarget = null;
154
        assert(interface_exists($this->interface));
155
        $this->bound = new NullObjectDependency($this->interface);
156
        $this->container->add($this);
157
158
        return $this;
159
    }
160
161
    /**
162
     * Set scope
163
     */
164
    public function in(string $scope): self
165
    {
166
        if ($this->bound instanceof Dependency || $this->bound instanceof DependencyProvider || $this->bound instanceof NullDependency) {
167
            $this->bound->setScope($scope);
168
        }
169
170
        if ($this->untarget) {
171
            $this->untarget->setScope($scope);
172
        }
173
174
        return $this;
175
    }
176
177
    public function getBound(): DependencyInterface
178
    {
179
        return $this->bound;
180
    }
181
182
    public function setBound(DependencyInterface $bound): void
183
    {
184
        $this->bound = $bound;
185
    }
186
187
    private function isRegistered(string $interface): bool
188
    {
189
        return isset($this->container->getContainer()[$interface . '-' . Name::ANY]);
190
    }
191
}
192