Test Failed
Push — master ( 944b52...dc25eb )
by Taosikai
13:30
created

Resolver.php (3 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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