Issues (5)

Resolver.php (2 issues)

Labels
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()) {
0 ignored issues
show
The condition null !== $definition->getFactory() is always true.
Loading history...
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()])) {
159
                $solved[] = $arguments[$dependency->getName()];
160
                continue;
161
            }
162
163
            if (null !== ($type = $dependency->getType()) && !$type->isBuiltin()) {
164
                $solved[] = $this->container->get($type->getName());
0 ignored issues
show
The method getName() does not exist on ReflectionType. It seems like you code against a sub-type of ReflectionType such as ReflectionNamedType. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

164
                $solved[] = $this->container->get($type->/** @scrutinizer ignore-call */ getName());
Loading history...
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()
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