Completed
Pull Request — master (#425)
by
unknown
03:11
created

Cache::rename()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 4
rs 10
c 1
b 0
f 0
cc 2
eloc 2
nc 2
nop 2
1
<?php
2
3
namespace Gaufrette\Adapter;
4
5
use Gaufrette\File;
6
use Gaufrette\Adapter;
7
use Gaufrette\Adapter\InMemory as InMemoryAdapter;
8
9
/**
10
 * Cache adapter.
11
 *
12
 * @author  Antoine Hérault <[email protected]>
13
 */
14
class Cache implements Adapter,
0 ignored issues
show
Coding Style introduced by
The first item in a multi-line implements list must be on the line following the implements keyword
Loading history...
15
                       MetadataSupporter
0 ignored issues
show
Coding Style introduced by
Expected 4 spaces before interface name; 23 found
Loading history...
16
{
17
    /**
18
     * @var Adapter
19
     */
20
    protected $source;
21
22
    /**
23
     * @var Adapter
24
     */
25
    protected $cache;
26
27
    /**
28
     * @var int
29
     */
30
    protected $ttl;
31
32
    /**
33
     * @var Adapter
34
     */
35
    protected $serializeCache;
36
37
    /**
38
     * @param Adapter $source         The source adapter that must be cached
39
     * @param Adapter $cache          The adapter used to cache the source
40
     * @param int     $ttl            Time to live of a cached file
41
     * @param Adapter $serializeCache The adapter used to cache serializations
42
     */
43
    public function __construct(Adapter $source, Adapter $cache, $ttl = 0, Adapter $serializeCache = null)
44
    {
45
        $this->source = $source;
46
        $this->cache = $cache;
47
        $this->ttl = $ttl;
48
49
        if (!$serializeCache) {
50
            $serializeCache = new InMemoryAdapter();
51
        }
52
        $this->serializeCache = $serializeCache;
53
    }
54
55
    /**
56
     * Returns the time to live of the cache.
57
     *
58
     * @return int $ttl
59
     */
60
    public function getTtl()
61
    {
62
        return $this->ttl;
63
    }
64
65
    /**
66
     * Defines the time to live of the cache.
67
     *
68
     * @param int $ttl
69
     */
70
    public function setTtl($ttl)
71
    {
72
        $this->ttl = $ttl;
73
    }
74
75
    /**
76
     * {@inheritdoc}
77
     */
78
    public function read($key)
79
    {
80
        if ($this->needsReload($key)) {
81
            $contents = $this->source->read($key);
82
            $this->cache->write($key, $contents);
0 ignored issues
show
Bug introduced by
It seems like $contents defined by $this->source->read($key) on line 81 can also be of type boolean; however, Gaufrette\Adapter::write() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
83
        } else {
84
            $contents = $this->cache->read($key);
85
        }
86
87
        return $contents;
88
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93
    public function rename($key, $new)
94
    {
95
        return $this->source->rename($key, $new) && $this->cache->rename($key, $new);
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101
    public function write($key, $content, array $metadata = null)
102
    {
103
        $bytesSource = $this->source->write($key, $content);
104
105
        if (false === $bytesSource) {
106
            return false;
107
        }
108
109
        $bytesCache = $this->cache->write($key, $content);
110
111
        if ($bytesSource !== $bytesCache) {
112
            return false;
113
        }
114
115
        return $bytesSource;
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121
    public function exists($key)
122
    {
123
        if ($this->needsReload($key)) {
124
            return $this->source->exists($key);
125
        }
126
127
        return $this->cache->exists($key);
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function mtime($key)
134
    {
135
        return $this->source->mtime($key);
136
    }
137
138
    /**
139
     * {@inheritdoc}
140
     */
141
    public function keys()
142
    {
143
        $cacheFile = 'keys.cache';
144
        if ($this->needsRebuild($cacheFile)) {
145
            $keys = $this->source->keys();
146
            sort($keys);
147
            $this->serializeCache->write($cacheFile, serialize($keys));
148
        } else {
149
            $keys = unserialize($this->serializeCache->read($cacheFile));
150
        }
151
152
        return $keys;
153
    }
154
155
    /**
156
     * {@inheritdoc}
157
     */
158
    public function delete($key)
159
    {
160
        return $this->source->delete($key) && $this->cache->delete($key);
161
    }
162
163
    /**
164
     * {@inheritdoc}
165
     */
166
    public function isDirectory($key)
167
    {
168
        return $this->source->isDirectory($key);
169
    }
170
171
    /**
172
     * {@inheritDoc}
173
     */
174
    public function listDirectory($key)
175
    {
176
        return $this->source->listDirectory($key);
0 ignored issues
show
Bug introduced by
The method listDirectory() does not exist on Gaufrette\Adapter. Did you maybe mean isDirectory()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
177
    }
178
179
    /**
180
     * {@inheritdoc}
181
     */
182
    public function setMetadata($key, $metadata)
183
    {
184
        if ($this->source instanceof MetadataSupporter) {
185
            $this->source->setMetadata($key, $metadata);
186
        }
187
188
        if ($this->cache instanceof MetadataSupporter) {
189
            $this->cache->setMetadata($key, $metadata);
190
        }
191
    }
192
193
    /**
194
     * {@inheritdoc}
195
     */
196
    public function getMetadata($key)
197
    {
198
        if ($this->source instanceof MetadataSupporter) {
199
            return $this->source->getMetadata($key);
200
        }
201
202
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type declared by the interface Gaufrette\Adapter\MetadataSupporter::getMetadata of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
203
    }
204
205
    /**
206
     * Indicates whether the cache for the specified key needs to be reloaded.
207
     *
208
     * @param string $key
209
     */
210
    public function needsReload($key)
211
    {
212
        $needsReload = true;
213
214
        if ($this->cache->exists($key)) {
215
            try {
216
                $dateCache = $this->cache->mtime($key);
217
                $needsReload = false;
218
219
                if (time() - $this->ttl >= $dateCache) {
220
                    $dateSource = $this->source->mtime($key);
221
                    $needsReload = $dateCache < $dateSource;
222
                }
223
            } catch (\RuntimeException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
224
            }
225
        }
226
227
        return $needsReload;
228
    }
229
230
    /**
231
     * Indicates whether the serialized cache file needs to be rebuild.
232
     *
233
     * @param string $cacheFile
234
     */
235
    public function needsRebuild($cacheFile)
236
    {
237
        $needsRebuild = true;
238
239
        if ($this->serializeCache->exists($cacheFile)) {
240
            try {
241
                $needsRebuild = time() - $this->ttl >= $this->serializeCache->mtime($cacheFile);
242
            } catch (\RuntimeException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
243
            }
244
        }
245
246
        return $needsRebuild;
247
    }
248
}
249