ContainerTrait   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 146
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
eloc 32
dl 0
loc 146
rs 10
c 2
b 0
f 1
wmc 13

7 Methods

Rating   Name   Duplication   Size   Complexity  
A executeMethods() 0 8 3
A hasDefinition() 0 3 2
A getServiceDefinition() 0 4 1
A getRawId() 0 7 1
A newInstance() 0 12 1
A getInstance() 0 24 4
A reserveObject() 0 5 1
1
<?php
2
3
/**
4
 * Phoole (PHP7.2+)
5
 *
6
 * @category  Library
7
 * @package   Phoole\Di
8
 * @copyright Copyright (c) 2019 Hong Zhang
9
 */
10
declare(strict_types=1);
11
12
namespace Phoole\Di\Util;
13
14
use Phoole\Config\ConfigAwareTrait;
15
use Phoole\Di\Exception\LogicException;
16
use Phoole\Di\Exception\UnresolvedClassException;
17
18
/**
19
 * ContainerTrait
20
 *
21
 * @package Phoole\Di
22
 */
23
trait ContainerTrait
24
{
25
    use ResolveTrait;
26
    use FactoryTrait;
27
    use ConfigAwareTrait;
28
29
    /**
30
     * service objects stored by its service id
31
     *
32
     * @var object[]
33
     */
34
    protected $objects = [];
35
36
    /**
37
     * DI definition prefix in $config
38
     *
39
     * @var string
40
     */
41
    protected $prefix = 'di.';
42
43
    /**
44
     * Get the instance by SERVICE id, create it if not yet
45
     *
46
     * @param  string $id  defined service id
47
     * @return object
48
     * @throws UnresolvedClassException  if dependencies unresolved
49
     * @throws LogicException if 'di.before' or 'di.after' definitions go wrong
50
     */
51
    protected function getInstance(string $id): object
52
    {
53
        // found in cache
54
        if (isset($this->objects[$id])) {
55
            return $this->objects[$id];
56
        }
57
58
        // initiate object
59
        $object = $this->newInstance($this->getServiceDefinition($id));
60
61
        // if '@' at the end, return without store in cache
62
        if ('@' === substr($id, -1)) {
63
            return $object;
64
        }
65
66
        // store in object cache
67
        $this->objects[$id] = $object;
68
69
        // store object in classmap
70
        if (FALSE === strpos($id, '@')) {
71
            $this->storeClass($object);
72
        }
73
74
        return $object;
75
    }
76
77
    /**
78
     * initiate an object by its definition
79
     *
80
     * @param  array $definition
81
     * @return object
82
     * @throws UnresolvedClassException if dependencies unresolved
83
     * @throws LogicException if 'di.before' or 'di.after' definitions go wrong
84
     */
85
    public function newInstance(array $definition): object
86
    {
87
        // execute global beforehand 'di.before' methods
88
        $this->executeMethods($definition, 'before');
89
90
        // fabricate this object
91
        $obj = $this->fabricate($definition);
92
93
        // execute global aftermath ('di.after') methods
94
        $this->executeMethods($obj, 'after');
95
96
        return $obj;
97
    }
98
99
    /**
100
     * get the raw service id by adding prefix & stripping the scope '@XXX' off
101
     *
102
     * @param  string $id
103
     * @return string
104
     */
105
    protected function getRawId(string $id): string
106
    {
107
        // 'di.service.' prefix
108
        $prefix = $this->prefix . 'service.';
109
110
        // cutoff the scope suffix
111
        return $prefix . explode('@', $id, 2)[0];
112
    }
113
114
    /**
115
     * execute 'di.before' or 'di.after' methods for newly created object
116
     *
117
     * @param  object|array $object  newly created object or object definition
118
     * @param  string       $stage   'before' | 'after'
119
     * @return void
120
     * @throws LogicException if 'di.before' or 'di.after' definitions go wrong
121
     */
122
    protected function executeMethods($object, string $stage): void
123
    {
124
        // 'di.before' or 'di.after'
125
        $node = $this->prefix . $stage;
126
        if ($this->getConfig()->has($node)) {
127
            foreach ($this->getConfig()->get($node) as $line) {
128
                list($runner, $arguments) = $this->fixMethod((array) $line, $object);
129
                $this->executeCallable($runner, $arguments);
130
            }
131
        }
132
    }
133
134
    /**
135
     * A service defined in the definitions ?
136
     *
137
     * @param  string $id
138
     * @return bool
139
     */
140
    protected function hasDefinition(string $id): bool
141
    {
142
        return $this->getConfig()->has($this->getRawId($id)) || isset($this->objects[$id]);
143
    }
144
145
    /**
146
     * for reserving 'container' or 'config'
147
     *
148
     * @param  string $id
149
     * @param  object $object
150
     * @return $this
151
     */
152
    protected function reserveObject(string $id, object $object)
153
    {
154
        $this->objects[$id] = $object;
155
        $this->classMap[\get_class($object)] = $object;
156
        return $this;
157
    }
158
159
    /**
160
     * Find the service definition and fix any non-standard stuff
161
     *
162
     * @param  string $id
163
     * @return array
164
     */
165
    protected function getServiceDefinition(string $id): array
166
    {
167
        $def = $this->getConfig()->get($this->getRawId($id));
168
        return $this->fixDefinition($def);
169
    }
170
}