Completed
Push — master ( d725fc...d5b5b8 )
by Damien
02:36
created

Container::hasParameter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php
2
/**
3
 * Veto.
4
 * PHP Microframework.
5
 *
6
 * @author Damien Walsh <[email protected]>
7
 * @copyright Damien Walsh 2013-2014
8
 * @version 0.1
9
 * @package veto
10
 */
11
namespace Veto\DI;
12
13
use Veto\Collection\Bag;
14
15
/**
16
 * Container
17
 *
18
 * @since 0.1
19
 */
20
class Container
21
{
22
    /**
23
     * Services defined for this container.
24
     *
25
     * @var Definition[]
26
     */
27
    private $definitions;
28
29
    /**
30
     * Cached instances of services in this container.
31
     *
32
     * @var object[]
33
     */
34
    private $instances;
35
36
    /**
37
     * @var Bag
38
     */
39
    private $parameterBag;
40
41
    /**
42
     * Create a new Container instance.
43
     */
44
    public function __construct()
45
    {
46
        $this->parameterBag = new Bag;
47
    }
48
49
    /**
50
     * Define a service within the container, optionally providing a pre-existing instance of the service.
51
     *
52
     * @param Definition $definition
53
     * @param mixed|null $instance
54
     */
55
    public function define(Definition $definition, $instance = null)
56
    {
57
        $this->definitions[$definition->getName()] = $definition;
58
59
        if (!is_null($instance)) {
60
            $this->instances[$definition->getName()] = $instance;
61
        }
62
    }
63
64
    /**
65
     * Helper method to define a service by name and instance.
66
     *
67
     * @param string $serviceName
68
     * @param mixed $instance
69
     */
70
    public function defineInstance($serviceName, $instance)
71
    {
72
        $definition = new Definition();
73
        $definition->setName($serviceName);
74
        $definition->setClassName(get_class($instance));
75
76
        $this->define($definition, $instance);
77
    }
78
79
    /**
80
     * Undefine a service by name.
81
     *
82
     * @param $serviceName
83
     */
84
    public function undefine($serviceName)
85
    {
86
        if (isset($this->definitions[$serviceName])) {
87
            unset($this->definitions[$serviceName]);
88
        }
89
    }
90
91
    /**
92
     * Retrieve a service from the container.
93
     *
94
     * @param $serviceName
95
     * @return mixed
96
     * @throws \RuntimeException
97
     */
98
    public function get($serviceName)
99
    {
100
        if (!$this->isDefined($serviceName)) {
101
            throw new \RuntimeException('The service name "' . $serviceName . '" is not defined.');
102
        }
103
104
        // Retrieve the definition
105
        $definition = $this->definitions[$serviceName];
106
107
        // If the service isn't One Shot and has already been built, return the instance
108
        if (!$definition->isOneShot() && $this->isInstantiated($serviceName)) {
109
            return $this->instances[$serviceName];
110
        }
111
112
        // Obtain the instance
113
        $instance = $this->buildInstance($definition);
114
115
        // Cache the instance if it isn't One Shot
116
        if (!$definition->isOneShot()) {
117
            $this->instances[$serviceName] = $instance;
118
        }
119
120
        return $instance;
121
    }
122
123
    /**
124
     * Get a parameter stored in the container.
125
     *
126
     * @param string $name The parameter name
127
     * @return mixed|null
128
     */
129
    public function getParameter($name)
130
    {
131
        return $this->parameterBag->get($name);
132
    }
133
134
    /**
135
     * Save a parameter into the container.
136
     *
137
     * @param string $name The parameter name
138
     * @param mixed $value
139
     * @return $this
140
     */
141
    public function setParameter($name, $value)
142
    {
143
        return $this->parameterBag->add($name, $value);
144
    }
145
146
    /**
147
     * Checks if a parameter exists.
148
     *
149
     * @param string $name The parameter name
150
     * @return bool The presence of the named parameter in the container
151
     */
152
    public function hasParameter($name)
153
    {
154
        return $this->parameterBag->has($name);
155
    }
156
157
    /**
158
     * Return the bag used for storing parameters in the container.
159
     *
160
     * @return Bag
161
     */
162
    public function getParameterBag()
163
    {
164
        return $this->parameterBag;
165
    }
166
167
    /**
168
     * Check if $serviceName is defined inside this container.
169
     *
170
     * @param $serviceName
171
     * @return bool
172
     */
173
    public function isDefined($serviceName)
174
    {
175
        return array_key_exists($serviceName, $this->definitions);
176
    }
177
178
    /**
179
     * Check if $serviceName is defined and instantiated inside this container.
180
     *
181
     * @param $serviceName
182
     * @return bool
183
     */
184
    public function isInstantiated($serviceName)
185
    {
186
        return $this->isDefined($serviceName) && array_key_exists($serviceName, $this->instances);
187
    }
188
189
    /**
190
     * @return Definition[]
191
     */
192
    public function getDefinitions()
193
    {
194
        return $this->definitions;
195
    }
196
197
    /**
198
     * Build and return a new instance of a service from a Definition instance.
199
     *
200
     * @param Definition $definition
201
     * @return object
202
     */
203
    private function buildInstance(Definition $definition)
204
    {
205
        // If any parameters are defined, resolve them
206
        $definedParameters = $definition->getParameters();
207
        $parameters = array();
208
209
        if (is_array($definedParameters) && count($definedParameters) > 0) {
210
            $parameters = $this->resolveArguments($definedParameters);
211
        }
212
213
        $reflectionClass = new \ReflectionClass($definition->getClassName());
214
        $instance = $reflectionClass->newInstanceArgs($parameters);
215
216
        // Process any calls that have been defined
217
        if (is_array($definition->getCalls())) {
218
            foreach ($definition->getCalls() as $method => $arguments) {
219
                if ($reflectionClass->hasMethod($method)) {
220
                    // Resolve the arguments as instances or parameters
221
                    $resolvedArguments = $this->resolveArguments($arguments);
222
                    call_user_func_array(array($instance, $method), $resolvedArguments);
223
                }
224
            }
225
        }
226
227
        // If the service is a container accessor, provide this container to it
228
        if ($instance instanceof AbstractContainerAccessor) {
229
            $instance->setContainer($this);
230
        }
231
232
        return $instance;
233
234
    }
235
236
    /**
237
     * Resolve an array of arguments into their actual instances or parameter values.
238
     *
239
     * @param array $arguments
240
     * @return array
241
     */
242
    private function resolveArguments(array $arguments)
243
    {
244
        $resolvedParameters = array();
245
246
        foreach ($arguments as $argument) {
247
248
            // Does $argument look like a reference to a service (@servicename) or a parameter (%paramname%)?
249
            if (is_string($argument) && strlen($argument) > 0) {
250
                if ($argument[0] == '@') {
251
                    $bareParameterName = substr($argument, 1);
252
                    $argument = $this->get($bareParameterName);
253
                } elseif ($argument[0] == '%' && $argument[strlen($argument) - 1] == '%') {
254
                    $bareParameterName = substr($argument, 1, -1);
255
                    $argument = $this->getParameter($bareParameterName);
256
                }
257
            }
258
259
            $resolvedParameters[] = $argument;
260
        }
261
262
        return $resolvedParameters;
263
    }
264
265
}
266