Passed
Push — master ( 5564fc...4ab603 )
by Mauro
02:05
created

DIC::tryToStoreInApcu()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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