Passed
Push — master ( 3c1e69...f9ea00 )
by Mauro
02:26
created

DIC   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 329
Duplicated Lines 0 %

Importance

Changes 6
Bugs 1 Features 0
Metric Value
eloc 84
c 6
b 1
f 0
dl 0
loc 329
rs 6.96
wmc 53

18 Methods

Rating   Name   Duplication   Size   Complexity  
A getArgumentToInject() 0 5 2
A count() 0 5 1
A setCacheDir() 0 3 1
B instantiateTheClass() 0 29 7
A getParamKey() 0 3 1
B setValue() 0 33 9
A getArgumentsToInject() 0 11 3
A has() 0 9 3
A tryToStoreInApcu() 0 5 2
A getFromEnvOrDICParams() 0 11 5
A keys() 0 5 1
A initFromFile() 0 22 5
A getEnvKey() 0 5 2
A get() 0 16 4
A checkForCache() 0 4 2
A isApcuEnabled() 0 3 2
A getCacheDir() 0 3 2
A getApcuKey() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like DIC often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DIC, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace SimpleDIC;
4
5
use SimpleDIC\Exceptions\ConfigException;
6
use SimpleDIC\Parser\Parser;
7
8
class DIC
9
{
10
    /**
11
     * @var array
12
     */
13
    private static $values;
14
15
    /**
16
     * @var array
17
     */
18
    private static $cache;
19
20
    /**
21
     * @var string
22
     */
23
    private static $cacheDir;
24
25
    /**
26
     * @var string
27
     */
28
    private static $sha;
29
30
    /**
31
     * @param string $filename
32
     *
33
     * @return DIC
34
     * @throws Exceptions\ParserException
35
     */
36
    public static function initFromFile($filename)
37
    {
38
        self::$values = [];
39
        self::$sha = sha1_file($filename);
40
41
        $mapFile = self::getCacheDir(). DIRECTORY_SEPARATOR .self::$sha.'.php';
42
43
        if (false === file_exists($mapFile)) {
44
            if (false === is_dir(self::getCacheDir())) {
45
                if (false === mkdir(self::getCacheDir(), 0755, true)) {
46
                    throw new \Exception(self::getCacheDir() . ' is not a writable directory.');
47
                }
48
            }
49
50
            if (false === file_put_contents($mapFile, '<?php return unserialize(\'' . serialize(Parser::parse($filename)) . '\');' . PHP_EOL)) {
51
                throw new \Exception(' Can\'t write cache file.');
52
            }
53
        }
54
55
        self::$cache = include($mapFile);
56
57
        return new self;
58
    }
59
60
    /**
61
     * @param $dir
62
     *
63
     * @return mixed
64
     */
65
    public static function setCacheDir($dir)
66
    {
67
        return self::$cacheDir = $dir;
68
    }
69
70
    /**
71
     * @return string
72
     */
73
    private static function getCacheDir()
74
    {
75
        return (!empty(self::$cacheDir)) ? self::$cacheDir : __DIR__.'/../_cache';
76
    }
77
78
    /**
79
     * @return int
80
     * @throws ConfigException
81
     */
82
    public static function count()
83
    {
84
        self::checkForCache();
85
86
        return count(self::$values);
87
    }
88
89
    private static function checkForCache()
90
    {
91
        if(empty(self::$cache)){
92
            throw new ConfigException('No config file was provided. You MUST use before initFromFile() method.');
93
        }
94
    }
95
96
    /**
97
     * @param string $id
98
     *
99
     * @return mixed
100
     * @throws ConfigException
101
     */
102
    public static function get($id)
103
    {
104
        self::checkForCache();
105
106
        // if apcu is enabled return from cache
107
        if (self::isApcuEnabled() and apcu_exists(self::getApcuKey($id))) {
108
            return apcu_fetch(self::getApcuKey($id));
109
        }
110
111
        // otherwise set the value in memory if it is not present
112
        if (false === isset(self::$values[$id])) {
113
            self::setValue($id);
114
        }
115
116
        // return from memory
117
        return self::$values[$id];
118
    }
119
120
    /**
121
     * Set an entry in the container.
122
     *
123
     * @param array $cachedMap
124
     * @param mixed $content
125
     *
126
     * @return bool
127
     */
128
    private static function setValue($id)
129
    {
130
        $content = self::$cache[$id];
131
132
        // if is not a class set the entry value in DIC
133
        if (false === isset($content['class'])) {
134
            self::$values[$id] = self::getFromEnvOrDICParams($content);
135
136
            if (self::isApcuEnabled()) {
137
                self::tryToStoreInApcu($id);
138
            }
139
140
            return true;
141
        }
142
143
        // otherwise it's a class, so extract variables
144
        extract($content);
145
146
        $methodArgsToInject = self::getArgumentsToInject(isset($method_arguments) ? $method_arguments : null);
147
        $classArgsToInject = self::getArgumentsToInject(isset($arguments) ? $arguments : null);
148
149
        try {
150
            self::$values[$id] = self::instantiateTheClass($class, $classArgsToInject, isset($method) ? $method : null, $methodArgsToInject);
151
152
            if (self::isApcuEnabled()) {
153
                self::tryToStoreInApcu($id);
154
            }
155
156
            return true;
157
        } catch (\Error $error) {
158
            return false;
159
        } catch (\Exception $exception) {
160
            return false;
161
        }
162
    }
163
164
    /**
165
     * @return bool
166
     */
167
    private static function isApcuEnabled()
168
    {
169
        return (extension_loaded('apc') && ini_get('apc.enabled'));
170
    }
171
172
    /**
173
     * @param string $id
174
     *
175
     * @return string
176
     */
177
    private static function getApcuKey($id)
178
    {
179
        return md5(self::$sha . DIRECTORY_SEPARATOR . $id);
180
    }
181
182
    /**
183
     * @param string $id
184
     */
185
    private static function tryToStoreInApcu($id)
186
    {
187
        try {
188
            apcu_add(self::getApcuKey($id), self::$values[$id]);
189
        } catch (\Exception $e) {
190
            // nothing to do, continue
191
        }
192
    }
193
194
    /**
195
     * @param string $id
196
     *
197
     * @return bool
198
     * @throws ConfigException
199
     */
200
    public static function has($id)
201
    {
202
        self::checkForCache();
203
204
        if (self::isApcuEnabled() and apcu_exists(self::getApcuKey($id))) {
205
            return true;
206
        }
207
208
        return isset(self::$values[$id]);
209
    }
210
211
    /**
212
     * @return array
213
     * @throws ConfigException
214
     */
215
    public static function keys()
216
    {
217
        self::checkForCache();
218
219
        return array_keys(self::$cache);
220
    }
221
222
    /**
223
     * @param string $class
224
     * @param array $classArguments
225
     * @param null $method
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $method is correct as it would always require null to be passed?
Loading history...
226
     * @param array $methodArguments
227
     *
228
     * @return mixed|bool
229
     *
230
     * @throws \ReflectionException
231
     */
232
    private static function instantiateTheClass($class, array $classArguments = [], $method = null, array $methodArguments = [])
233
    {
234
        if (false === class_exists($class)) {
235
            return false;
236
        }
237
238
        $reflected = new \ReflectionClass($class);
239
240
        // 1. the class has no method to call
241
        if (null == $method) {
0 ignored issues
show
introduced by
The condition null == $method is always true.
Loading history...
242
            return new $class(...$classArguments);
243
        }
244
245
        if (false === $reflected->hasMethod($method)) {
246
            return false;
247
        }
248
249
        // 2. the method to call is static
250
        if ($reflected->getMethod($method)->isStatic()) {
251
            return $class::$method(...$methodArguments);
252
        }
253
254
        // 3. the class has a private constructor
255
        if ($reflected->hasMethod('__construct') and $reflected->getConstructor()->isPrivate()) {
256
            return call_user_func_array([$class, $method], $methodArguments);
257
        }
258
259
        // 4. the class has a public constructor
260
        return (new $class(...$classArguments))->$method(...$methodArguments);
261
    }
262
263
    /**
264
     * Get the arguments to inject into the class to instantiate within DIC.
265
     *
266
     * @param array $cachedMap
267
     * @param null $providedArguments
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $providedArguments is correct as it would always require null to be passed?
Loading history...
268
     *
269
     * @return array
270
     */
271
    private static function getArgumentsToInject($providedArguments = null)
272
    {
273
        $returnArguments = [];
274
275
        if (null != $providedArguments) {
0 ignored issues
show
introduced by
The condition null != $providedArguments is always false.
Loading history...
276
            foreach ($providedArguments as $argument) {
277
                $returnArguments[] = self::getArgumentToInject($argument);
278
            }
279
        }
280
281
        return $returnArguments;
282
    }
283
284
    /**
285
     * @param array $cachedMap
286
     * @param string $argument
287
     *
288
     * @return mixed|string|null
289
     */
290
    private static function getArgumentToInject($argument)
291
    {
292
        $id = ltrim($argument, '@');
293
294
        return (isset(self::$cache[$id])) ? self::get($id) : self::getFromEnvOrDICParams($argument);
295
    }
296
297
    /**
298
     * @param string $parameter
299
     *
300
     * @return mixed|string|null
301
     */
302
    private static function getFromEnvOrDICParams($parameter)
303
    {
304
        if (is_string($parameter)) {
0 ignored issues
show
introduced by
The condition is_string($parameter) is always true.
Loading history...
305
            if (null !== self::getEnvKey($parameter)) {
306
                return (getenv(self::getEnvKey($parameter))) ? getenv(self::getEnvKey($parameter)) : $parameter;
307
            }
308
309
            return (DICParams::has(self::getParamKey($parameter))) ? DICParams::get(self::getParamKey($parameter)) : $parameter;
310
        }
311
312
        return $parameter;
313
    }
314
315
    /**
316
     * Extract from a string like %env(FOO)%
317
     *
318
     * @param string $string
319
     *
320
     * @return mixed|null
321
     */
322
    private static function getEnvKey($string)
323
    {
324
        preg_match('~%env\((.*?)\)%~', $string, $matches);
325
326
        return (count($matches) > 0) ? $matches[1] : null;
327
    }
328
329
    /**
330
     * @param string $string
331
     *
332
     * @return string
333
     */
334
    private static function getParamKey($string)
335
    {
336
        return trim($string, '%');
337
    }
338
}
339