Passed
Push — master ( aff6da...a69d30 )
by Oleg
03:41
created

Container::__set()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 2
1
<?php /** MicroContainer */
2
3
namespace Micro\Base;
4
5
/**
6
 * Container class file.
7
 *
8
 * @author Oleg Lunegov <[email protected]>
9
 * @link https://github.com/lugnsk/micro
10
 * @copyright Copyright &copy; 2013 Oleg Lunegov
11
 * @license /LICENSE
12
 * @package Micro
13
 * @subpackage Base
14
 * @version 1.0
15
 * @since 1.0
16
 */
17
class Container extends \stdClass implements IContainer
18
{
19
    /** @var array $data data */
20
    protected $data = [];
21
    /** @var array $config Configs */
22
    protected $config = [];
23
    /** @var array $components Components config */
24
    protected $components = [];
25
26
27
    /**
28
     * Load more configs from file
29
     *
30
     * @access public
31
     *
32
     * @param string $filename
33
     *
34
     * @return void
35
     */
36
    public function load($filename)
37
    {
38
        if (file_exists($filename)) {
39
            /** @noinspection PhpIncludeInspection */
40
            $this->config = array_merge_recursive($this->config, require $filename);
41
            $this->components = array_merge_recursive($this->components, $this->config['components']);
42
            unset($this->config['components']);
43
        }
44
    }
45
46
    /**
47
     * Is set component or option name into Container
48
     *
49
     * @access public
50
     *
51
     * @param string $name Name attribute
52
     *
53
     * @return bool
54
     */
55
    public function __isset($name)
56
    {
57
        if (array_key_exists($name, $this->config)) {
58
            return true;
59
        }
60
        if (array_key_exists($name, $this->data)) {
61
            return true;
62
        }
63
        if (array_key_exists($name, $this->components)) {
64
            return true;
65
        }
66
67
        return false;
68
    }
69
70
    /**
71
     * Get Container value
72
     *
73
     * @access public
74
     *
75
     * @param string $name element name
76
     *
77
     * @return mixed
78
     */
79
    public function __get($name = '')
80
    {
81
        if (!empty($this->config[$name])) {
82
            return $this->config[$name];
83
        }
84
85
        if (empty($this->data[$name]) && !$this->configure($name)) {
86
            return false;
87
        }
88
89
        return $this->data[$name];
90
    }
91
92
    /**
93
     * Set attribute
94
     *
95
     * @access public
96
     *
97
     * @param string $name Name attribute
98
     * @param mixed $component Component or option
99
     *
100
     * @return void
101
     */
102
    public function __set($name, $component)
103
    {
104
        $this->data[$name] = $component;
105
    }
106
107
    /**
108
     * Get component's
109
     *
110
     * @access public
111
     *
112
     * @param string|null $name name element to initialize
113
     *
114
     * @return bool
115
     */
116
    public function configure($name = null)
117
    {
118
        if (0 === count($this->components)) {
119
            return false;
120
        }
121
122
        if ($name === null) {
123
            foreach ($this->components AS $key => $options) {
124
                if (!$this->loadComponent($key, $options)) {
125
                    return false;
126
                }
127
            }
128
129
            return true;
130
        }
131
132
        if (empty($this->components[$name])) {
133
            return false;
134
        }
135
136
        if (!$this->loadComponent($name, $this->components[$name])) {
137
            return false;
138
        }
139
140
        return true;
141
    }
142
143
    /**
144
     * Load component
145
     *
146
     * @access public
147
     *
148
     * @param string $name component name
149
     * @param array $options component configs
150
     *
151
     * @return bool
152
     */
153
    public function loadComponent($name, $options)
154
    {
155
        if (empty($options['class']) || !class_exists($options['class'])) {
156
            return false;
157
        }
158
159
        $className = $options['class'];
160
        $this->data[$name] = null;
161
162
        $options['arguments'] = !empty($options['arguments']) ? $this->buildParams($options['arguments']) : null;
163
        $options['property'] = !empty($options['property']) ? $this->buildParams($options['property']) : null;
164
        $options['calls'] = !empty($options['calls']) ? $this->buildCalls($options['calls']) : null;
165
166
        $this->data[$name] = $this->makeObject($className, $options['arguments']);
167
        if (!$this->data[$name]) {
168
            return false;
169
        }
170
171
        if (!empty($options['property'])) { // load properties
172
            foreach ($options['property'] as $property => $value) {
173
                if (property_exists($this->data[$name], $property)) {
174
                    $this->data[$name]->$property = $value;
175
                }
176
            }
177
        }
178
179
        if (!empty($options['calls'])) { // run methods
180
            foreach ($options['calls'] as $method => $arguments) {
181
                if (method_exists($this->data['name'], $method)) {
182
                    $reflectionMethod = new \ReflectionMethod($className, $method);
183
                    if ($reflectionMethod->getNumberOfParameters() === 0) {
184
                        $this->data['name']->$method();
185
                    } else {
186
                        call_user_func_array([$this->data['name'], $method], $arguments);
187
                    }
188
                }
189
            }
190
        }
191
192
        return true;
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
    private function makeObject($className, array $arguments = [])
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
221
    /**
222
     * Build calls arguments
223
     *
224
     * @access private
225
     * @param array $params
226
     * @return array
227
     */
228
    private function buildCalls(array $params)
229
    {
230
        $callers = [];
231
232
        if (!is_array($params[0])) {
233
            $params = [
234
                $params
235
            ];
236
        }
237
238
        foreach ($params as $arguments) {
239
            if (is_string($arguments[0])) {
240
                if (!empty($arguments[1]) && is_array($arguments[1])) {
241
                    $callers[$arguments[0]] = $this->buildParams($arguments[1]);
242
                } else {
243
                    $callers[$arguments[0]] = null;
244
                }
245
            }
246
        }
247
248
        return $callers;
249
    }
250
251
    /**
252
     * Build params from array
253
     *
254
     * @access private
255
     * @param array $params
256
     * @return array
257
     */
258
    private function buildParams(array $params)
259
    {
260
        /** @noinspection AlterInForeachInspection */
261
        foreach ($params AS $key => &$val) { // IoC Constructor
262
            if (is_string($params[$key]) && (0 === strpos($val, '@'))) {
263
                if ($val === '@this') {
264
                    $val = $this;
265
                } else {
266
                    if (null === $this->{substr($val, 1)}) {
267
                        return false;
268
                    }
269
                    $val = $this->{substr($val, 1)};
270
                }
271
            }
272
        }
273
274
        return $params;
275
    }
276
}
277