Completed
Push — master ( 54da0a...13952c )
by Taosikai
15:03
created

Resolver::resolveReflectionArguments()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 35
rs 8.1155
c 0
b 0
f 0
cc 8
nc 8
nop 2
1
<?php
2
3
/*
4
 * This file is part of the slince/di package.
5
 *
6
 * (c) Slince <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Slince\Di;
13
14
use Slince\Di\Exception\ConfigException;
15
use Slince\Di\Exception\DependencyInjectionException;
16
use Slince\Di\Exception\NotFoundException;
17
18
class Resolver
19
{
20
    /**
21
     * @var Container
22
     */
23
    protected $container;
24
25
    /**
26
     * @param Container $container
27
     */
28
    public function __construct(Container $container)
29
    {
30
        $this->container = $container;
31
    }
32
33
    /**
34
     * @param Definition $definition
35
     *
36
     * @return mixed
37
     */
38
    public function resolve(Definition $definition)
39
    {
40
        $this->parseConcrete($definition);
41
42
        if (null !== $definition->getFactory()) {
43
            $instance = $this->createFromFactory($definition);
44
        } elseif (null !== $definition->getClass()) {
45
            $instance = $this->createFromClass($definition);
46
        } elseif (null !== $definition->getResolved()) {
47
            $instance = $definition->getResolved();
48
        } else {
49
            throw new ConfigException('The definition is not invalid.');
50
        }
51
        $this->invokeMethods($definition, $instance);
52
        $this->invokeProperties($definition, $instance);
53
        $definition->setResolved($instance);
54
55
        return $instance;
56
    }
57
58
    protected function parseConcrete(Definition $definition)
59
    {
60
        $concrete = $definition->getConcrete();
61
        if (is_string($concrete)) {
62
            $definition->setClass($concrete);
63
        } elseif (is_array($concrete) || $concrete instanceof \Closure) {
64
            $definition->setFactory($concrete);
65
        } elseif (is_object($concrete)) {
66
            $definition->setResolved($concrete)
67
                ->setShared(true);
68
        } else {
69
            throw new ConfigException('The concrete of the definition is invalid');
70
        }
71
    }
72
73
    protected function createFromClass(Definition $definition)
74
    {
75
        $class = $definition->getClass();
76
        try {
77
            $reflection = new \ReflectionClass($definition->getClass());
78
        } catch (\ReflectionException $e) {
79
            throw new DependencyInjectionException(sprintf('Class "%s" is invalid', $definition->getClass()));
80
        }
81
        if (!$reflection->isInstantiable()) {
82
            throw new DependencyInjectionException(sprintf('Can not instantiate "%s"', $definition->getClass()));
83
        }
84
        $constructor = $reflection->getConstructor();
85
        if (is_null($constructor)) {
86
            $instance = $reflection->newInstanceWithoutConstructor();
87
        } else {
88
            $arguments = $this->resolveArguments($definition->getArguments());
89
            if ($definition->isAutowired()) {
90
                $arguments = $this->resolveDependencies($constructor->getParameters(), $arguments);
91
            }
92
            if (count($arguments) < $constructor->getNumberOfRequiredParameters()) {
93
                throw new ConfigException(sprintf('Too few arguments for class "%s"', $class));
94
            }
95
            $instance = $reflection->newInstanceArgs($arguments);
96
        }
97
98
        return $instance;
99
    }
100
101
    /**
102
     * @param Definition $definition
103
     *
104
     * @return object
105
     */
106
    protected function createFromFactory(Definition $definition)
107
    {
108
        $factory = $definition->getFactory();
109
        if (is_array($factory)) {
110
            $factory = $this->resolveArguments($factory);
111
        }
112
        return call_user_func_array($factory,
113
            $this->resolveArguments($definition->getArguments()) ?: [$this->container]
114
        );
115
    }
116
117
    /**
118
     * @param Definition $definition
119
     * @param object     $instance
120
     */
121
    protected function invokeMethods(Definition $definition, $instance)
122
    {
123
        foreach ($definition->getMethodCalls() as $method) {
124
            call_user_func_array([$instance, $method[0]], $this->resolveArguments($method[1]));
125
        }
126
    }
127
128
    /**
129
     * @param Definition $definition
130
     * @param object     $instance
131
     */
132
    protected function invokeProperties(Definition $definition, $instance)
133
    {
134
        $properties = $this->resolveArguments($definition->getProperties());
135
        foreach ($properties as $name => $value) {
136
            $instance->$name = $value;
137
        }
138
    }
139
140
    /**
141
     * Resolve dependencies.
142
     *
143
     * @param \ReflectionParameter[] $dependencies
144
     * @param array $arguments
145
     * @return array
146
     * @throws DependencyInjectionException
147
     */
148
    protected function resolveDependencies($dependencies, $arguments)
149
    {
150
        $solved = [];
151
        foreach ($dependencies as $dependency) {
152
            if (isset($arguments[$dependency->getPosition()])) {
153
                $solved[] = $arguments[$dependency->getPosition()];
154
                continue;
155
            }
156
157
            if (null !== $dependency->getClass()) {
158
                $solved[] = $this->container->get($dependency->getClass()->name);
159
                continue;
160
            }
161
162
            if ($dependency->isOptional()) {
163
                $solved[] = $dependency->getDefaultValue();
164
                continue;
165
            }
166
167
            throw new DependencyInjectionException(sprintf(
168
                'Unresolvable dependency resolving "%s" in class "%s"',
169
                $dependency->name,
170
                $dependency->getDeclaringClass()->getName()
0 ignored issues
show
Bug introduced by
Consider using $dependency->getDeclaringClass()->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
171
            ));
172
        }
173
        return $solved;
174
    }
175
176
    /**
177
     * Resolves array of parameters.
178
     *
179
     * @param array $arguments
180
     *
181
     * @return array
182
     */
183
    protected function resolveArguments($arguments)
184
    {
185
        foreach ($arguments as &$argument) {
186
            if ($argument instanceof Reference) {
187
                $argument = $this->container->get($argument->getId());
188
                continue;
189
            }
190
        }
191
        return $arguments;
192
    }
193
}
194