Completed
Branch develop (9f43d2)
by Filipe
05:14
created

Container::setHydrator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
ccs 0
cts 0
cp 0
cc 1
eloc 3
nc 1
nop 1
crap 2
1
<?php
2
3
/**
4
 * This file is part of slick/di package
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace Slick\Di;
11
12
use Interop\Container\Exception\ContainerException;
13
use Slick\Di\Definition\Alias;
14
use Slick\Di\Definition\Factory;
15
use Slick\Di\Definition\ObjectDefinition;
16
use Slick\Di\Definition\Scope;
17
use Slick\Di\Definition\Value;
18
use Slick\Di\Exception\NotFoundException;
19
20
/**
21
 * Container
22
 *
23
 * @package Slick\Di
24
 * @author  Filipe Silva <[email protected]>
25
 */
26
class Container implements ContainerInterface, ObjectHydratorAwareInterface
27
{
28
    /**
29
     * @var array
30
     */
31
    protected $definitions = [];
32
33
    /**
34
     * @var array
35
     */
36
    protected static $instances = [];
37
38
    /**
39
     * @var ObjectHydratorInterface
40
     */
41
    protected $hydrator;
42
43
    /**
44
     * @var null|ContainerInterface
45
     */
46
    protected $parent;
47
48
    /**
49
     * Creates a dependency container
50
     */
51
    public function __construct()
52
    {
53
        $this->parent = array_key_exists('container', self::$instances)
54 74
            ? self::$instances['container']
55
            : null;
56 74
57 74
        self::$instances['container'] = $this;
58 74
    }
59 72
60 72
    /**
61
     * Finds an entry of the container by its identifier and returns it.
62 74
     *
63 74
     * @param string $id Identifier of the entry to look for.
64 74
     *
65 74
     * @throws NotFoundException  No entry was found for this identifier.
66 74
     * @throws ContainerException Error while retrieving the entry.
67
     *
68
     * @return mixed Entry.
69
     */
70
    public function get($id)
71
    {
72
        if (!$this->has($id)) {
73
            throw new NotFoundException(
74
                "Dependency container has not found any definition for '{$id}'"
75
            );
76
        }
77
        return $this->resolve($id);
78 52
    }
79
80 52
    /**
81 2
     * Returns true if the container can return an entry for the given
82 2
     * identifier. Returns false otherwise.
83
     *
84 2
     * @param string $id Identifier of the entry to look for.
85
     *
86
     * @return boolean
87 50
     */
88 50
    public function has($id)
89 50
    {
90
        return array_key_exists($id, $this->definitions);
91 50
    }
92 46
93 46
    /**
94
     * Adds a definition or a value to the container
95 50
     *
96
     * @param string       $name
97
     * @param mixed        $definition
98
     * @param Scope|string $scope      Resolving scope
99
     * @param array        $parameters Used if $value is a callable
100
     *
101
     * @return Container
102
     */
103
    public function register(
104
        $name,
105
        $definition = null,
106 56
        $scope = Scope::SINGLETON,
107
        array $parameters = []
108 56
    ) {
109 28
        if (!$definition instanceof DefinitionInterface) {
110
            $definition = $this->createDefinition(
111
                $definition,
112 56
                $parameters
113 18
            );
114
            $definition->setScope($scope);
115 52
        }
116
        return $this->add($name, $definition);
117
    }
118
119
    /**
120
     * Resolves the definition that was saved under the provided name
121
     *
122
     * @param string $name
123
     *
124
     * @return mixed
125
     */
126
    protected function resolve($name)
127
    {
128
        if (! array_key_exists($name, self::$instances)) {
129
            $entry = $this->definitions[$name];
130
            return $this->registerEntry($name, $entry);
131
        }
132
        return self::$instances[$name];
133 74
    }
134
135
    /**
136
     * Checks the definition scope to register resolution result
137 74
     *
138 72
     * If scope is set to prototype the the resolution result is not
139 32
     * stores in the container instances.
140 32
     *
141 32
     * @param string              $name
142 32
     * @param DefinitionInterface $definition
143 32
     * @return mixed
144 32
     */
145 32
    protected function registerEntry($name, DefinitionInterface $definition)
146
    {
147 72
        $value = $definition->resolve();
148 72
        if ((string) $definition->getScope() !== Scope::PROTOTYPE) {
149 72
            self::$instances[$name] = $value;
150 72
        }
151
        return $value;
152 72
    }
153 72
154
    /**
155 72
     * Adds a definition to the definitions list
156
     *
157 72
     * @param string              $name
158 72
     * @param DefinitionInterface $definition
159
     *
160
     * @return Container
161
     */
162
    protected function add($name, DefinitionInterface $definition)
163
    {
164
        $this->definitions[$name] = $definition;
165
        $definition->setContainer($this);
166
        return $this;
167
    }
168
169
    /**
170
     * Creates the definition for registered data
171
     *
172
     * If value is a callable then the definition is Factory, otherwise
173
     * it will create a Value definition.
174
     *
175 10
     * @see Factory, Value
176
     *
177
     * @param callable|mixed $value
178 10
     * @param array          $parameters
179 2
     *
180
     * @return Factory|Value
181 2
     */
182
    protected function createDefinition(
183
        $value,
184 8
        array $parameters = []
185 4
    ) {
186 4
        if (is_callable($value)) {
187
            return new Factory($value, $parameters);
188 8
        }
189
        return $this->createValueDefinition($value);
190
    }
191
192
    /**
193
     * Creates a definition for provided name and value pair
194
     *
195
     * If $value is a string prefixed with '@' it will create an Alias
196
     * definition. Otherwise a Value definition will be created.
197
     *
198 46
     * @param mixed  $value
199
     *
200 46
     * @return Value|Alias
201 46
     */
202 46
    protected function createValueDefinition($value)
203 46
    {
204 46
        if (is_string($value) && strpos($value, '@') !== false) {
205 46
            return new Alias($value);
206 46
        }
207
208
        return new Value($value);
209
    }
210
211
    /**
212
     * Creates an instance of provided class injecting its dependencies
213
     *
214
     * @param string $className
215
     * @param array ...$arguments
216 70
     *
217
     * @return mixed
218 70
     */
219 70
    public function make($className, ...$arguments)
220
    {
221
        if (is_a($className, ContainerInjectionInterface::class, true)) {
222
            return call_user_func_array([$className, 'create'], [$this]);
223
        }
224
225
        $definition = (new ObjectDefinition($className))
226
            ->setContainer($this)
227
        ;
228
        call_user_func_array([$definition, 'with'], $arguments);
229
        $object = $definition->resolve();
0 ignored issues
show
Bug introduced by
It seems like resolve() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
230 72
        $this->getHydrator()->hydrate($object);
231
        return $object;
232 72
    }
233
234 72
    /**
235
     * Set the object hydrator
236 72
     *
237 72
     * @param ObjectHydratorInterface $hydrator
238
     *
239
     * @return Container|ObjectHydratorAwareInterface
240
     */
241
    public function setHydrator(ObjectHydratorInterface $hydrator)
242
    {
243
        $this->hydrator = $hydrator;
244
        return $this;
245
    }
246
247
    /**
248
     * Get the object hydrator
249
     *
250 32
     * @return ObjectHydratorInterface
251
     */
252
    public function getHydrator()
253 32
    {
254
        if (!$this->hydrator) {
255 32
            $this->setHydrator(new ObjectHydrator($this));
256 32
        }
257
        return $this->hydrator;
258
    }
259
260
    /**
261
     * Gets the parent container if it exists
262
     *
263
     * @return null|ContainerInterface
264
     */
265 50
    public function parent()
266
    {
267 50
        return $this->parent;
268 8
    }
269
}
270