Completed
Pull Request — master (#276)
by
unknown
04:37
created

CachePathManager::__destruct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 5
Bugs 0 Features 1
Metric Value
dl 0
loc 4
ccs 0
cts 3
cp 0
rs 10
c 5
b 0
f 1
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
/*
3
 * Go! AOP framework
4
 *
5
 * @copyright Copyright 2014, Lisachenko Alexander <[email protected]>
6
 *
7
 * This source file is subject to the license that is bundled
8
 * with this source code in the file LICENSE.
9
 */
10
11
namespace Go\Instrument\ClassLoading;
12
use Go\Aop\Features;
13
use Go\Core\AspectKernel;
14
15
/**
16
 * Class that manages real-code to cached-code paths mapping.
17
 * Can be extended to get a more sophisticated real-to-cached code mapping
18
 */
19
class CachePathManager
20
{
21
    /**
22
     * Name of the file with cache paths
23
     */
24
    const CACHE_FILE_NAME = '/_transformation.cache';
25
26
    /**
27
     * @var array
28
     */
29
    protected $options = [];
30
31
    /**
32
     * @var \Go\Core\AspectKernel
33
     */
34
    protected $kernel;
35
36
    /**
37
     * @var string|null
38
     */
39
    protected $cacheDir;
40
41
    /**
42
     * @var int
43
     */
44
    protected $cacheFileMode;
45
46
    /**
47
     * @var string|null
48
     */
49
    protected $appDir;
50
51
    /**
52
     * Cached metadata for transformation state for the concrete file
53
     *
54
     * @var array
55
     */
56
    protected $cacheState = [];
57
58
    /**
59
     * New metadata items, that was not present in $cacheState
60
     *
61
     * @var array
62
     */
63
    protected $newCacheState = [];
64
65 10
    public function __construct(AspectKernel $kernel)
66
    {
67 10
        $this->kernel        = $kernel;
68 10
        $this->options       = $kernel->getOptions();
69 10
        $this->appDir        = $this->options['appDir'];
70 10
        $this->cacheDir      = $this->options['cacheDir'];
71 10
        $this->cacheFileMode = $this->options['cacheFileMode'];
72
73 10
        if ($this->cacheDir) {
74
            if (!is_dir($this->cacheDir)) {
75
                $cacheRootDir = dirname($this->cacheDir);
76
                if (!is_writable($cacheRootDir) || !is_dir($cacheRootDir)) {
77
                    throw new \InvalidArgumentException(
78
                        "Can not create a directory {$this->cacheDir} for the cache.
79
                        Parent directory {$cacheRootDir} is not writable or not exist.");
80
                }
81
                mkdir($this->cacheDir, $this->cacheFileMode);
82
            }
83
            if (!$this->kernel->hasFeature(Features::PREBUILT_CACHE) && !is_writable($this->cacheDir)) {
84
                throw new \InvalidArgumentException("Cache directory {$this->cacheDir} is not writable");
85
            }
86
87
            if (file_exists($this->cacheDir . self::CACHE_FILE_NAME)) {
88
                $this->cacheState = include $this->cacheDir . self::CACHE_FILE_NAME;
89
            }
90
        }
91 10
    }
92
93
    /**
94
     * Returns current cache directory for aspects, can be bull
95
     *
96
     * @return null|string
97
     */
98
    public function getCacheDir()
99
    {
100
        return $this->cacheDir;
101
    }
102
103
    /**
104
     * Configures a new cache directory for aspects
105
     *
106
     * @param string $cacheDir New cache directory
107
     */
108
    public function setCacheDir($cacheDir)
109
    {
110
        $this->cacheDir = $cacheDir;
111
    }
112
113
    /**
114
     * @param string $resource
115
     * @return bool|string
116
     */
117
    public function getCachePathForResource($resource)
118
    {
119
        if (!$this->cacheDir) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->cacheDir of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
120
            return false;
121
        }
122
123
        return str_replace($this->appDir, $this->cacheDir, $resource);
124
    }
125
126
    /**
127
     * Tries to return an information for queried resource
128
     *
129
     * @param string|null $resource Name of the file or null to get all information
130
     *
131
     * @return array|null Information or null if no record in the cache
132
     */
133
    public function queryCacheState($resource = null)
134
    {
135
        if (!$resource) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $resource of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
136
            return $this->cacheState;
137
        }
138
139
        if (isset($this->newCacheState[$resource])) {
140
            return $this->newCacheState[$resource];
141
        }
142
143
        if (isset($this->cacheState[$resource])) {
144
            return $this->cacheState[$resource];
145
        }
146
147
        return null;
148
    }
149
150
    /**
151
     * Put a record about some resource in the cache
152
     *
153
     * This data will be persisted during object destruction
154
     *
155
     * @param string $resource Name of the file
156
     * @param array $metadata Miscellaneous information about resource
157
     */
158
    public function setCacheState($resource, array $metadata)
159
    {
160
        $this->newCacheState[$resource] = $metadata;
161
    }
162
163
    /**
164
     * Automatic destructor saves all new changes into the cache
165
     *
166
     * This implementation is not thread-safe, so be care
167
     */
168
    public function __destruct()
169
    {
170
        $this->flushCacheState();
171
    }
172
173
    /**
174
     * Flushes the cache state into the file
175
     */
176
    public function flushCacheState()
177
    {
178
        if (!empty($this->newCacheState) && is_writable($this->cacheDir)) {
179
            $fullCacheMap = $this->newCacheState + $this->cacheState;
180
            $cachePath    = substr(var_export($this->cacheDir, true), 1, -1);
181
            $rootPath     = substr(var_export($this->appDir, true), 1, -1);
182
            $cacheData    = '<?php return ' . var_export($fullCacheMap, true) . ';';
183
            $cacheData    = strtr($cacheData, array(
184
                '\'' . $cachePath => 'AOP_CACHE_DIR . \'',
185
                '\'' . $rootPath  => 'AOP_ROOT_DIR . \''
186
            ));
187
            file_put_contents($this->cacheDir . self::CACHE_FILE_NAME, $cacheData);
188
            chmod($this->cacheDir . self::CACHE_FILE_NAME, $this->cacheFileMode);
189
            if (function_exists('opcache_invalidate')) {
190
                opcache_invalidate($this->cacheDir . self::CACHE_FILE_NAME, true);
191
            }
192
            $this->cacheState    = $this->newCacheState + $this->cacheState;
193
            $this->newCacheState = [];
194
        }
195
    }
196
}
197