Passed
Push — master ( c4286f...5969a6 )
by Oleg
04:27
created

Injector::build()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
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
     * Injector constructor.
27
     *
28
     * @access public
29
     * @param string $configPath
30
     * @result void
31
     */
32
    public function __construct($configPath = '')
33
    {
34
        if ($configPath !== '' && file_exists($configPath)) {
35
            /** @noinspection PhpIncludeInspection */
36
            self::$CONFIG = array_merge_recursive(self::$CONFIG, require $configPath);
37
        }
38
    }
39
40
    /**
41
     * @return null
42
     */
43
    public function build()
44
    {
45
        return null; // Replace this in your code
46
    }
47
48
    /**
49
     * @param string $name
50
     * @return string
51
     */
52
    public function param($name)
53
    {
54
        return array_key_exists($name, self::$CONFIG) ? self::$CONFIG[$name] : null;
55
    }
56
57
    /**
58
     * Add requirement to injector
59
     *
60
     * @access public
61
     * @param string $name
62
     * @param mixed $component
63
     * @return void
64
     */
65
    public function addRequirement($name, $component)
66
    {
67
        if (is_object($component)) {
68
            self::$INJECTS[$name] = $component;
69
        } else {
70
            self::$CONFIG[$name] = $component;
71
        }
72
    }
73
74
    /**
75
     * Build object with injector
76
     *
77
     * class LogInject extends Injector {
78
     *  public function build() {
79
     *   return $this->get('logger');
80
     *  }
81
     * }
82
     * $log = (new LogInject())->build();
83
     *
84
     * @access protected
85
     * @param string $name
86
     * @return false
87
     */
88
    protected function get($name)
89
    {
90
        if (!empty(self::$CONFIG[$name])) {
91
            return self::$CONFIG[$name];
92
        }
93
94
        if (!empty(self::$CONFIG['components'][$name])) {
95
            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 95 which is incompatible with the return type documented by Micro\Base\Injector::get of type boolean.
Loading history...
96
        }
97
98
        return false;
99
    }
100
101
    /**
102
     * Load injection
103
     *
104
     * @access public
105
     *
106
     * @param string $name Name injection
107
     *
108
     * @return false
109
     */
110
    private function loadInjection($name)
111
    {
112
        $options = self::$CONFIG['components'][$name];
113
114
        if (empty($options['class']) || !class_exists($options['class'])) {
115
            return false;
116
        }
117
118
        $className = $options['class'];
119
120
        $options['arguments'] = !empty($options['arguments']) ? $this->buildParams($options['arguments']) : null;
121
        $options['property'] = !empty($options['property']) ? $this->buildParams($options['property']) : null;
122
        $options['calls'] = !empty($options['calls']) ? $this->buildCalls($options['calls']) : null;
123
124
        /** Depends via construction */
125
        self::$INJECTS[$name] = $this->makeObject($className, $options['arguments']);
126
        if (!self::$INJECTS[$name]) {
127
            return false;
128
        }
129
130
        /** Depends via property */
131
        if (!empty($options['property'])) { // load properties
132
            foreach ($options['property'] as $property => $value) {
133
                if (property_exists(self::$INJECTS[$name], $property)) {
134
                    self::$INJECTS[$name]->$property = $value;
135
                }
136
            }
137
        }
138
139
        /** Depends via calls */
140
        if (!empty($options['calls'])) { // run methods
141
            foreach ($options['calls'] as $method => $arguments) {
142
                if (method_exists(self::$INJECTS[$name], $method)) {
143
                    $reflectionMethod = new \ReflectionMethod($className, $method);
144
                    if ($reflectionMethod->getNumberOfParameters() === 0) {
145
                        self::$INJECTS[$name]->$method();
146
                    } else {
147
                        call_user_func_array([self::$INJECTS[$name], $method], $arguments);
148
                    }
149
                }
150
            }
151
        }
152
153
        return self::$INJECTS[$name];
154
    }
155
156
    /**
157
     * Build params from array
158
     *
159
     * @access private
160
     * @param array $params
161
     * @return array
162
     */
163
    private function buildParams(array $params)
164
    {
165
        /** @noinspection AlterInForeachInspection */
166
        foreach ($params AS $key => &$val) { // IoC Constructor
167
            if (is_string($params[$key]) && (0 === strpos($val, '@'))) {
168
                if ($val === '@this') {
169
                    $val = $this;
170
                } else {
171
                    $val = $this->get(substr($val, 1));
172
                }
173
            }
174
        }
175
176
        return $params;
177
    }
178
179
    /**
180
     * Build calls arguments
181
     *
182
     * @access private
183
     * @param array $params
184
     * @return array
185
     */
186
    private function buildCalls(array $params)
187
    {
188
        $callers = [];
189
190
        if (!is_array($params[0])) {
191
            $params = [
192
                $params
193
            ];
194
        }
195
196
        foreach ($params as $arguments) {
197
            if (is_string($arguments[0])) {
198
                if (!empty($arguments[1]) && is_array($arguments[1])) {
199
                    $callers[$arguments[0]] = $this->buildParams($arguments[1]);
200
                } else {
201
                    $callers[$arguments[0]] = null;
202
                }
203
            }
204
        }
205
206
        return $callers;
207
    }
208
209
    /**
210
     * Make object with arguments
211
     *
212
     * @access private
213
     *
214
     * @param string $className
215
     * @param array $arguments
216
     *
217
     * @return mixed
218
     */
219
    private function makeObject($className, array $arguments = [])
220
    {
221
        try {
222
            $reflection = new \ReflectionClass($className);
223
            $reflectionMethod = new \ReflectionMethod($className, '__construct');
224
225
            if ($reflectionMethod->getNumberOfParameters() === 0) {
226
                return new $className;
227
            } else {
228
                return $reflection->newInstanceArgs($arguments);
229
            }
230
        } catch (Exception $e) {
231
            return false;
232
        }
233
    }
234
}