Completed
Push — cleanup ( 6c6cf2 )
by Akihito
02:43
created

Bind::toConstructor()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 6
cts 6
cp 1
rs 9.8666
c 0
b 0
f 0
cc 3
nc 4
nop 4
crap 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Ray\Di;
6
7
use Ray\Di\Exception\NotFound;
8
9
final class Bind
10
{
11
    /**
12
     * @var Container
13
     */
14
    private $container;
15
16
    /**
17
     * @var string
18
     */
19
    private $interface;
20
21
    /**
22
     * @var string
23
     */
24
    private $name = Name::ANY;
25
26
    /**
27
     * @var DependencyInterface
28
     */
29
    private $bound;
30
31
    /**
32
     * @var BindValidator
33
     */
34
    private $validate;
35
36
    /**
37
     * @var null|Untarget
38
     */
39
    private $untarget;
40
41
    /**
42
     * @param Container $container dependency container
43
     * @param string    $interface interface or concrete class name
44
     */
45
    public function __construct(Container $container, string $interface)
46
    {
47
        $this->container = $container;
48 86
        $this->interface = $interface;
49
        $this->validate = new BindValidator;
50 86
        $bindUntarget = class_exists($interface) && ! (new \ReflectionClass($interface))->isAbstract() && ! $this->isRegistered($interface);
51 86
        if ($bindUntarget) {
52 86
            $this->untarget = new Untarget($interface);
53 86
54 86
            return;
55 46
        }
56
        $this->validate->constructor($interface);
57 46
    }
58 1
59 85
    public function __destruct()
60 84
    {
61
        if ($this->untarget) {
62 80
            ($this->untarget)($this->container, $this);
63
            $this->untarget = null;
64 80
        }
65 17
    }
66 17
67 17
    public function __toString()
68 80
    {
69
        return $this->interface . '-' . $this->name;
70
    }
71
72
    /**
73
     * Set dependency name
74
     */
75 27
    public function annotatedWith(string $name) : self
76
    {
77 27
        $this->name = $name;
78
79 27
        return $this;
80
    }
81
82
    /**
83
     * Bind to class
84
     */
85
    public function to(string $class) : self
86
    {
87 56
        $this->untarget = null;
88
        $refClass = $this->validate->to($this->interface, $class);
89 56
        $this->bound = (new DependencyFactory)->newAnnotatedDependency($refClass);
90 56
        $this->container->add($this);
91 54
92 54
        return $this;
93
    }
94 54
95
    /**
96
     * Bind to constructor
97
     *
98
     * @param string          $class           class name
99
     * @param string | array  $name            "varName=bindName,..." or [[$varName => $bindName],[$varName => $bindName]...]
100
     * @param InjectionPoints $injectionPoints injection points
101
     * @param string          $postConstruct   method name of initialization after all dependencies are injected*
102
     */
103
    public function toConstructor(string $class, $name, InjectionPoints $injectionPoints = null, string $postConstruct = null) : self
104
    {
105 3
        if (\is_array($name)) {
106
            $name = $this->getStringName($name);
107 3
        }
108 3
        $this->untarget = null;
109 3
        $postConstructRef = $postConstruct ? (new NewReflectionMethod)($class, $postConstruct) : null;
110 3
        $this->bound = (new DependencyFactory)->newToConstructor((new NewReflectionClass)($class), $name, $injectionPoints, $postConstructRef);
111
        $this->container->add($this);
112 3
113
        return $this;
114
    }
115
116
    /**
117
     * Bind to provider
118
     */
119
    public function toProvider(string $provider, string $context = '') : self
120
    {
121
        $this->untarget = null;
122 14
        $refClass = $this->validate->toProvider($provider);
123
        $this->bound = (new DependencyFactory)->newProvider($refClass, $context);
124 14
        $this->container->add($this);
125 14
126 12
        return $this;
127 12
    }
128
129 12
    /**
130
     * Bind to instance
131
     */
132
    public function toInstance($instance) : self
133
    {
134
        $this->untarget = null;
135
        $this->bound = new Instance($instance);
136
        $this->container->add($this);
137 74
138
        return $this;
139 74
    }
140 74
141 74
    /**
142
     * Set scope
143 74
     */
144
    public function in(string $scope) : self
145
    {
146
        if ($this->bound instanceof Dependency || $this->bound instanceof DependencyProvider) {
147
            $this->bound->setScope($scope);
148
        }
149
        if ($this->untarget) {
150
            $this->untarget->setScope($scope);
151 42
        }
152
153 42
        return $this;
154 41
    }
155 41
156 42
    public function getBound() : DependencyInterface
157 2
    {
158 2
        return $this->bound;
159
    }
160 42
161
    public function setBound(DependencyInterface $bound)
162
    {
163
        $this->bound = $bound;
164
    }
165
166 80
    private function isRegistered(string $interface) : bool
167
    {
168 80
        return isset($this->container->getContainer()[$interface . '-' . Name::ANY]);
169
    }
170
171
    /**
172
     * Return string
173
     *
174 79
     * input: [['varA' => 'nameA'], ['varB' => 'nameB']]
175
     * output: "varA=nameA,varB=nameB"
176 79
     */
177
    private function getStringName(array $name) : string
178
    {
179
        $names = array_reduce(array_keys($name), function (array $carry, string $key) use ($name) : array {
180
            $carry[] = $key . '=' . $name[$key];
181
182 17
            return $carry;
183
        }, []);
184 17
185 17
        return implode(',', $names);
186
    }
187
}
188