Passed
Push — master ( f3fcaa...2db009 )
by Oleg
04:04
created

Injector   B

Complexity

Total Complexity 38

Size/Duplication

Total Lines 210
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 6
Bugs 1 Features 0
Metric Value
wmc 38
c 6
b 1
f 0
lcom 1
cbo 0
dl 0
loc 210
rs 8.3999

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 3
A param() 0 4 2
A addRequirement() 0 8 2
A get() 0 12 3
C loadInjection() 0 45 14
B buildParams() 0 15 5
B buildCalls() 0 22 6
A makeObject() 0 15 3
1
<?php /** MicroInjector */
2
3
namespace Micro\Base;
4
5
6
/**
7
 * Class Injector
8
 *
9
 * @author Oleg Lunegov <[email protected]>
10
 * @link https://github.com/linpax/microphp-framework
11
 * @copyright Copyright &copy; 2013 Oleg Lunegov
12
 * @license /LICENSE
13
 * @package Micro
14
 * @subpackage Base
15
 * @version 1.0
16
 * @since 1.0
17
 */
18
class Injector implements IInjector
19
{
20
    /** @var array $CONFIG Configuration */
21
    private static $CONFIG = [];
22
    /** @var array $INJECTS Configured injects */
23
    private static $INJECTS = [];
24
25
26
    /**
27
     * Injector constructor.
28
     *
29
     * @access public
30
     * @param string $configPath
31
     * @result void
32
     */
33
    public function __construct($configPath = '')
34
    {
35
        if ($configPath !== '' && file_exists($configPath)) {
36
            /** @noinspection PhpIncludeInspection */
37
            self::$CONFIG = array_merge_recursive(self::$CONFIG, require $configPath);
38
        }
39
    }
40
41
    /**
42
     * @param $name
43
     * @return mixed|null
44
     */
45
    public function param($name)
46
    {
47
        return array_key_exists($name, self::$CONFIG) ? self::$CONFIG[$name] : null;
48
    }
49
50
    /**
51
     * Add requirement to injector
52
     *
53
     * @access public
54
     * @param string $name
55
     * @param mixed $component
56
     * @return void
57
     */
58
    public function addRequirement($name, $component)
59
    {
60
        if (is_object($component)) {
61
            self::$INJECTS[$name] = $component;
62
        } else {
63
            self::$CONFIG[$name] = $component;
64
        }
65
    }
66
67
    /**
68
     * Build object with injector
69
     *
70
     * class LogInject extends Injector {
71
     *  public function build() {
72
     *   return $this->get('logger');
73
     *  }
74
     * }
75
     * $log = (new LogInject())->build();
76
     *
77
     * @access protected
78
     * @param string $name
79
     * @return bool
80
     */
81
    protected function get($name)
82
    {
83
        if (!empty(self::$CONFIG[$name])) {
84
            return self::$CONFIG[$name];
85
        }
86
87
        if (!empty(self::$CONFIG['components'][$name])) {
88
            return $this->loadInjection($name);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->loadInjection($name); of type false|object adds the type object to the return on line 88 which is incompatible with the return type documented by Micro\Base\Injector::get of type boolean.
Loading history...
89
        }
90
91
        return false;
92
    }
93
94
    /**
95
     * Load injection
96
     *
97
     * @access public
98
     *
99
     * @param string $name Name injection
100
     *
101
     * @return bool
102
     */
103
    private function loadInjection($name)
104
    {
105
        $options = self::$CONFIG['components'][$name];
106
107
        if (empty($options['class']) || !class_exists($options['class'])) {
108
            return false;
109
        }
110
111
        $className = $options['class'];
112
113
        $options['arguments'] = !empty($options['arguments']) ? $this->buildParams($options['arguments']) : null;
114
        $options['property'] = !empty($options['property']) ? $this->buildParams($options['property']) : null;
115
        $options['calls'] = !empty($options['calls']) ? $this->buildCalls($options['calls']) : null;
116
117
        /** Depends via construction */
118
        self::$INJECTS[$name] = $this->makeObject($className, $options['arguments']);
119
        if (!self::$INJECTS[$name]) {
120
            return false;
121
        }
122
123
        /** Depends via property */
124
        if (!empty($options['property'])) { // load properties
125
            foreach ($options['property'] as $property => $value) {
126
                if (property_exists(self::$INJECTS[$name], $property)) {
127
                    self::$INJECTS[$name]->$property = $value;
128
                }
129
            }
130
        }
131
132
        /** Depends via calls */
133
        if (!empty($options['calls'])) { // run methods
134
            foreach ($options['calls'] as $method => $arguments) {
135
                if (method_exists(self::$INJECTS[$name], $method)) {
136
                    $reflectionMethod = new \ReflectionMethod($className, $method);
137
                    if ($reflectionMethod->getNumberOfParameters() === 0) {
138
                        self::$INJECTS[$name]->$method();
139
                    } else {
140
                        call_user_func_array([self::$INJECTS[$name], $method], $arguments);
141
                    }
142
                }
143
            }
144
        }
145
146
        return self::$INJECTS[$name];
147
    }
148
149
    /**
150
     * Build params from array
151
     *
152
     * @access private
153
     * @param array $params
154
     * @return array
155
     */
156
    private function buildParams(array $params)
157
    {
158
        /** @noinspection AlterInForeachInspection */
159
        foreach ($params AS $key => &$val) { // IoC Constructor
160
            if (is_string($params[$key]) && (0 === strpos($val, '@'))) {
161
                if ($val === '@this') {
162
                    $val = $this;
163
                } else {
164
                    $val = $this->get(substr($val, 1));
165
                }
166
            }
167
        }
168
169
        return $params;
170
    }
171
172
    /**
173
     * Build calls arguments
174
     *
175
     * @access private
176
     * @param array $params
177
     * @return array
178
     */
179
    private function buildCalls(array $params)
180
    {
181
        $callers = [];
182
183
        if (!is_array($params[0])) {
184
            $params = [
185
                $params
186
            ];
187
        }
188
189
        foreach ($params as $arguments) {
190
            if (is_string($arguments[0])) {
191
                if (!empty($arguments[1]) && is_array($arguments[1])) {
192
                    $callers[$arguments[0]] = $this->buildParams($arguments[1]);
193
                } else {
194
                    $callers[$arguments[0]] = null;
195
                }
196
            }
197
        }
198
199
        return $callers;
200
    }
201
202
    /**
203
     * Make object with arguments
204
     *
205
     * @access private
206
     *
207
     * @param string $className
208
     * @param array $arguments
209
     *
210
     * @return mixed
211
     */
212
    private function makeObject($className, array $arguments = [])
213
    {
214
        try {
215
            $reflection = new \ReflectionClass($className);
216
            $reflectionMethod = new \ReflectionMethod($className, '__construct');
217
218
            if ($reflectionMethod->getNumberOfParameters() === 0) {
219
                return new $className;
220
            } else {
221
                return $reflection->newInstanceArgs($arguments);
222
            }
223
        } catch (Exception $e) {
224
            return false;
225
        }
226
    }
227
}