Passed
Push — master ( acc9d0...0d9fd2 )
by Oleg
03:54
created

Injector::addRequirement()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 2
eloc 5
nc 2
nop 2
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
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
            $config = require $configPath;
38
39
            array_merge_recursive(self::$CONFIG, $config);
40
        }
41
    }
42
43
    /**
44
     * Add requirement to injector
45
     *
46
     * @access public
47
     * @param string $name
48
     * @param mixed $component
49
     * @return void
50
     */
51
    public function addRequirement($name, $component)
52
    {
53
        if (is_object($component)) {
54
            self::$INJECTS[$name] = $component;
55
        } else {
56
            self::$CONFIG[$name] = $component;
57
        }
58
    }
59
60
    /**
61
     * Build object with injector
62
     *
63
     * class LogInject extends Injector {
64
     *  public function build() {
65
     *   return $this->get('logger');
66
     *  }
67
     * }
68
     * $log = (new LogInject())->build();
69
     *
70
     * @access protected
71
     * @param string $name
72
     * @return bool
73
     */
74
    protected function get($name)
75
    {
76
        if (!empty(self::$CONFIG[$name])) {
77
            return self::$CONFIG[$name];
78
        }
79
80
        if (!empty(self::$CONFIG['components'][$name])) {
81
            return $this->loadInjection(self::$CONFIG[$name]);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->loadInjection(self::$CONFIG[$name]); of type false|object adds the type object to the return on line 81 which is incompatible with the return type documented by Micro\Base\Injector::get of type boolean.
Loading history...
82
        }
83
84
        return false;
85
    }
86
87
    /**
88
     * Load injection
89
     *
90
     * @access public
91
     *
92
     * @param string $name Name injection
93
     *
94
     * @return bool
95
     */
96
    private function loadInjection($name)
97
    {
98
        $options = self::$CONFIG['components'][$name];
99
100
        if (empty($options['class']) || !class_exists($options['class'])) {
101
            return false;
102
        }
103
104
        $className = $options['class'];
105
106
        $options['arguments'] = !empty($options['arguments']) ? $this->buildParams($options['arguments']) : null;
107
        $options['property'] = !empty($options['property']) ? $this->buildParams($options['property']) : null;
108
        $options['calls'] = !empty($options['calls']) ? $this->buildCalls($options['calls']) : null;
109
110
        /** Depends via construction */
111
        self::$INJECTS[$name] = $this->makeObject($className, $options['arguments']);
112
        if (!self::$INJECTS[$name]) {
113
            return false;
114
        }
115
116
        /** Depends via property */
117
        if (!empty($options['property'])) { // load properties
118
            foreach ($options['property'] as $property => $value) {
119
                if (property_exists(self::$INJECTS[$name], $property)) {
120
                    self::$INJECTS[$name]->$property = $value;
121
                }
122
            }
123
        }
124
125
        /** Depends via calls */
126
        if (!empty($options['calls'])) { // run methods
127
            foreach ($options['calls'] as $method => $arguments) {
128
                if (method_exists(self::$INJECTS[$name], $method)) {
129
                    $reflectionMethod = new \ReflectionMethod($className, $method);
130
                    if ($reflectionMethod->getNumberOfParameters() === 0) {
131
                        self::$INJECTS[$name]->$method();
132
                    } else {
133
                        call_user_func_array([self::$INJECTS[$name], $method], $arguments);
134
                    }
135
                }
136
            }
137
        }
138
139
        return self::$INJECTS[$name];
140
    }
141
142
    /**
143
     * Build params from array
144
     *
145
     * @access private
146
     * @param array $params
147
     * @return array
148
     */
149
    private function buildParams(array $params)
150
    {
151
        /** @noinspection AlterInForeachInspection */
152
        foreach ($params AS $key => &$val) { // IoC Constructor
153
            if (is_string($params[$key]) && (0 === strpos($val, '@'))) {
154
                if ($val === '@this') {
155
                    $val = $this;
156
                } else {
157
                    $val = $this->get(substr($val, 1));
158
                }
159
            }
160
        }
161
162
        return $params;
163
    }
164
165
    /**
166
     * Build calls arguments
167
     *
168
     * @access private
169
     * @param array $params
170
     * @return array
171
     */
172 View Code Duplication
    private function buildCalls(array $params)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
173
    {
174
        $callers = [];
175
176
        if (!is_array($params[0])) {
177
            $params = [
178
                $params
179
            ];
180
        }
181
182
        foreach ($params as $arguments) {
183
            if (is_string($arguments[0])) {
184
                if (!empty($arguments[1]) && is_array($arguments[1])) {
185
                    $callers[$arguments[0]] = $this->buildParams($arguments[1]);
186
                } else {
187
                    $callers[$arguments[0]] = null;
188
                }
189
            }
190
        }
191
192
        return $callers;
193
    }
194
195
    /**
196
     * Make object with arguments
197
     *
198
     * @access private
199
     *
200
     * @param string $className
201
     * @param array $arguments
202
     *
203
     * @return mixed
204
     */
205 View Code Duplication
    private function makeObject($className, array $arguments = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
206
    {
207
        try {
208
            $reflection = new \ReflectionClass($className);
209
            $reflectionMethod = new \ReflectionMethod($className, '__construct');
210
211
            if ($reflectionMethod->getNumberOfParameters() === 0) {
212
                return new $className;
213
            } else {
214
                return $reflection->newInstanceArgs($arguments);
215
            }
216
        } catch (Exception $e) {
217
            return false;
218
        }
219
    }
220
}