Container::share()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 1
1
<?php
2
/**
3
 * Phossa Project
4
 *
5
 * PHP version 5.4
6
 *
7
 * @category  Library
8
 * @package   Phossa2\Di
9
 * @copyright Copyright (c) 2016 phossa.com
10
 * @license   http://mit-license.org/ MIT License
11
 * @link      http://www.phossa.com/
12
 */
13
/*# declare(strict_types=1); */
14
15
namespace Phossa2\Di;
16
17
use Phossa2\Config\Config;
18
use Phossa2\Di\Message\Message;
19
use Phossa2\Di\Factory\Factory;
20
use Phossa2\Di\Resolver\Resolver;
21
use Phossa2\Di\Traits\ArrayAccessTrait;
22
use Phossa2\Shared\Base\ObjectAbstract;
23
use Phossa2\Config\Traits\WritableTrait;
24
use Interop\Container\ContainerInterface;
25
use Phossa2\Di\Interfaces\ScopeInterface;
26
use Phossa2\Di\Exception\RuntimeException;
27
use Phossa2\Di\Exception\NotFoundException;
28
use Phossa2\Di\Traits\InstanceFactoryTrait;
29
use Phossa2\Config\Interfaces\ConfigInterface;
30
use Phossa2\Config\Interfaces\WritableInterface;
31
use Phossa2\Shared\Delegator\DelegatorAwareTrait;
32
use Phossa2\Di\Interfaces\ExtendedContainerInterface;
33
use Phossa2\Shared\Delegator\DelegatorAwareInterface;
34
35
/**
36
 * Container
37
 *
38
 * A writable, array accessable, delegator-aware and extended instance container.
39
 *
40
 * - writable:
41
 *
42
 *   ```php
43
 *   $container->set('cache', $cache);
44
 *   ```
45
 *
46
 * - array accessable:
47
 *
48
 *   ```php
49
 *   // get
50
 *   $cache = $container['cache'];
51
 *
52
 *   // set/replace
53
 *   $container['cache'] = $anotherCache;
54
 *   ```
55
 *
56
 * - delegator-aware: lookup dependent instances in the delegator
57
 *
58
 *   ```php
59
 *   $delegator->addContainer($container);
60
 *   ```
61
 *
62
 * - extended container
63
 *
64
 *   ```php
65
 *   // get new instance
66
 *   $newCache = $container->one('cache');
67
 *
68
 *   // run callables
69
 *   $container->run(['${#logger}', 'warning'], ['A warning message from ${user}']);
70
 *   ```
71
 *
72
 * @package Phossa2\Di
73
 * @author  Hong Zhang <[email protected]>
74
 * @see     ObjectAbstract
75
 * @see     ScopeInterface
76
 * @see     ContainerInterface
77
 * @see     ExtendedContainerInterface
78
 * @see     DelegatorAwareInterface
79
 * @see     WritableInterface
80
 * @see     \ArrayAccess
81
 * @version 2.1.0
82
 * @since   2.0.0 added
83
 * @since   2.1.0 added `translation()`
84
 */
85
class Container extends ObjectAbstract implements ContainerInterface, ScopeInterface, WritableInterface, \ArrayAccess, DelegatorAwareInterface, ExtendedContainerInterface
86
{
87
    use WritableTrait,
88
        ArrayAccessTrait,
89
        DelegatorAwareTrait,
90
        InstanceFactoryTrait;
91
92
    /**
93
     * Inject a Phossa2\Config\Config
94
     *
95
     * ```php
96
     * $configData = [
97
     *     // container class
98
     *     'di.class' => 'Phossa2\\Di\\Container',
99
     *
100
     *     // container service definitions
101
     *     'di.service' => [
102
     *         // ...
103
     *     ],
104
     *
105
     *     // init methods to run after container created
106
     *     'di.init' => [
107
     *         'default' => [],
108
     *         'mystuff' => [ ... ],
109
     *     ],
110
     * ];
111
     *
112
     * // instantiate $config
113
     * $config = new Config(null, null, $configData);
114
     *
115
     * // instantiate container
116
     * $container = new $config['di.class']($config);
117
     * ```
118
     *
119
     * @param  ConfigInterface $config inject the config instance
120
     * @param  string $baseNode container's starting node in $config
121
     * @access public
122
     */
123
    public function __construct(
124
        ConfigInterface $config = null,
125
        /*# string */ $baseNode = 'di'
126
    ) {
127
        // config
128
        $conf = $config ?: new Config();
129
130
        // resolver & factory
131
        $this
132
            ->setResolver(new Resolver($this, $conf, $baseNode))
133
            ->setFactory(new Factory($this->getResolver()));
134
135
        // alias couple objects
136
        $this->alias('container', $this);
137
        $this->alias('config', $conf);
138
139
        // run methods in 'di.init'
140
        $this->initContainer();
141
    }
142
143
    // ContainerInterface related
144
145
    /**
146
     * Extensions to the Interop\Container\ContainerInterface
147
     *
148
     * - Accepting second param as object constructor arguments
149
     * - Accpeting $id with scope appended, e.g. 'cache@myScope'
150
     *
151
     * {@inheritDoc}
152
     */
153
    public function get($id)
154
    {
155
        if ($this->has($id)) {
156
            $args = func_num_args() > 1 ? func_get_arg(1) : [];
157
            $this->resolve($args);
158
            return $this->getInstance($id, $args);
159
        } else {
160
            throw new NotFoundException(
161
                Message::get(Message::DI_SERVICE_NOTFOUND, $id),
162
                Message::DI_SERVICE_NOTFOUND
163
            );
164
        }
165
    }
166
167
    /**
168
     * Extensions to the Interop\Container\ContainerInterface
169
     *
170
     * - Accpeting $id with scope appended, e.g. 'cache@myScope'
171
     *
172
     * {@inheritDoc}
173
     */
174
    public function has($id)
175
    {
176
        if (is_string($id)) {
177
            return $this->getResolver()->hasService(
178
                $this->idWithoutScope($id)
179
            );
180
        }
181
        return false;
182
    }
183
184
    // ExtendedContainerInterface
185
186
    /**
187
     * {@inheritDoc}
188
     */
189
    public function one(/*# string */ $id, array $arguments = [])
190
    {
191
        return $this->get(
192
            $this->scopedId($id, ScopeInterface::SCOPE_SINGLE),
193
            $arguments
194
        );
195
    }
196
197
    /**
198
     * {@inheritDoc}
199
     */
200
    public function run($callable, array $arguments = [])
201
    {
202
        $this->resolve($callable);
203
        $this->resolve($arguments);
204
205
        return $this->getFactory()->executeCallable($callable, $arguments);
206
    }
207
208
    /**
209
     * {@inheritDoc}
210
     */
211
    public function param(/*# string */ $name, $value)/*# : bool */
212
    {
213
        if (!$this->getResolver()->has($name)) {
214
            $this->getResolver()->set($name, $value);
215
            return $this->getResolver()->has($name);
216
        }
217
        return false;
218
    }
219
220
    /**
221
     * {@inheritDoc}
222
     */
223
    public function alias(/*# string */ $id, $object)/*# : bool */
224
    {
225
        if ($this->isWritable() && !$this->has($id)) {
226
            return $this->set($id, ['class' => $object, 'skip' => true]);
227
        }
228
        return false;
229
    }
230
231
    // AutoWiringInterface related
232
233
    /**
234
     * {@inheritDoc}
235
     */
236
    public function auto(/*# bool */ $flag = true)
237
    {
238
        $this->getResolver()->auto($flag);
239
        return $this;
240
    }
241
242
    /**
243
     * {@inheritDoc}
244
     */
245
    public function isAuto()/*# : bool */
246
    {
247
        return $this->getResolver()->isAuto();
248
    }
249
250
    // AutoTranslationInterface related
251
252
    /**
253
     * {@inheritDoc}
254
     *
255
     * @since  2.1.0 added
256
     */
257
    public function translation(/*# bool */ $flag = true)
258
    {
259
        $this->getResolver()->translation($flag);
260
        return $this;
261
    }
262
263
    // ReferenceResolveInterface related
264
265
    /**
266
     * {@inheritDoc}
267
     */
268
    public function resolve(&$toResolve)
269
    {
270
        $this->getResolver()->resolve($toResolve);
271
        return $this;
272
    }
273
274
    // ScopeInterface related
275
276
    /**
277
     * @inheritDoc
278
     */
279
    public function share(/*# bool */ $flag = true)
280
    {
281
        $this->default_scope = (bool) $flag ?
282
            self::SCOPE_SHARED : self::SCOPE_SINGLE;
283
        return $this;
284
    }
285
286
    // WritableInterface related
287
288
    /**
289
     * {@inheritDoc}
290
     */
291
    public function set(/*# string */ $id, $value)/*# : bool */
292
    {
293
        if ($this->isWritable()) {
294
            list($rawId, $scope) = $this->splitId($id);
295
296
            return $this->getResolver()->setService(
297
                $rawId,
298
                '' === $scope ? $value : $this->scopedData($value, $scope)
299
            );
300
        } else {
301
            throw new RuntimeException(
302
                Message::get(Message::DI_CONTAINER_READONLY, $id),
303
                Message::DI_CONTAINER_READONLY
304
            );
305
        }
306
    }
307
308
    /**
309
     * Override 'isWritable()' in 'Phossa2\Config\Traits\WritableTrait'
310
     *
311
     * Container's writability is depend on its resolver
312
     *
313
     * {@inheritDoc}
314
     */
315
    public function isWritable()/*# : bool */
316
    {
317
        return $this->getResolver()->isWritable();
318
    }
319
320
    /**
321
     * Override 'setWritable()' in 'Phossa2\Config\Traits\WritableTrait'
322
     *
323
     * Container's writability is depend on its resolver
324
     *
325
     * {@inheritDoc}
326
     */
327
    public function setWritable($writable)/*# : bool */
328
    {
329
        return $this->getResolver()->setWritable((bool) $writable);
330
    }
331
}
332