Completed
Push — master ( d64c7f...d0ede0 )
by Nicolas
11s
created

src/Gaufrette/Adapter/Cache.php (5 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
@trigger_error('The '.__NAMESPACE__.'\Cache adapter is deprecated since version 0.4 and will be removed in 1.0.', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
10
11
/**
12
 * Cache adapter.
13
 *
14
 * @author  Antoine Hérault <[email protected]>
15
 *
16
 * @deprecated The Cache adapter is deprecated since version 0.4 and will be removed in 1.0.
17
 */
18
class Cache implements Adapter,
19
                       MetadataSupporter
20
{
21
    /**
22
     * @var Adapter
23
     */
24
    protected $source;
25
26
    /**
27
     * @var Adapter
28
     */
29
    protected $cache;
30
31
    /**
32
     * @var int
33
     */
34
    protected $ttl;
35
36
    /**
37
     * @var Adapter
38
     */
39
    protected $serializeCache;
40
41
    /**
42
     * @param Adapter $source         The source adapter that must be cached
43
     * @param Adapter $cache          The adapter used to cache the source
44
     * @param int     $ttl            Time to live of a cached file
45
     * @param Adapter $serializeCache The adapter used to cache serializations
46
     */
47
    public function __construct(Adapter $source, Adapter $cache, $ttl = 0, Adapter $serializeCache = null)
48
    {
49
        $this->source = $source;
50
        $this->cache = $cache;
51
        $this->ttl = $ttl;
52
53
        if (!$serializeCache) {
54
            $serializeCache = new InMemoryAdapter();
55
        }
56
        $this->serializeCache = $serializeCache;
57
    }
58
59
    /**
60
     * Returns the time to live of the cache.
61
     *
62
     * @return int $ttl
63
     */
64
    public function getTtl()
65
    {
66
        return $this->ttl;
67
    }
68
69
    /**
70
     * Defines the time to live of the cache.
71
     *
72
     * @param int $ttl
73
     */
74
    public function setTtl($ttl)
75
    {
76
        $this->ttl = $ttl;
77
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82
    public function read($key)
83
    {
84
        if ($this->needsReload($key)) {
85
            $contents = $this->source->read($key);
86
            $this->cache->write($key, $contents);
0 ignored issues
show
It seems like $contents defined by $this->source->read($key) on line 85 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...
87
        } else {
88
            $contents = $this->cache->read($key);
89
        }
90
91
        return $contents;
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97
    public function rename($key, $new)
98
    {
99
        return $this->source->rename($key, $new) && $this->cache->rename($key, $new);
100
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105
    public function write($key, $content, array $metadata = null)
106
    {
107
        $bytesSource = $this->source->write($key, $content);
108
109
        if (false === $bytesSource) {
110
            return false;
111
        }
112
113
        $bytesCache = $this->cache->write($key, $content);
114
115
        if ($bytesSource !== $bytesCache) {
116
            return false;
117
        }
118
119
        return $bytesSource;
120
    }
121
122
    /**
123
     * {@inheritdoc}
124
     */
125
    public function exists($key)
126
    {
127
        if ($this->needsReload($key)) {
128
            return $this->source->exists($key);
129
        }
130
131
        return $this->cache->exists($key);
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137
    public function mtime($key)
138
    {
139
        return $this->source->mtime($key);
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145
    public function keys()
146
    {
147
        $cacheFile = 'keys.cache';
148
        if ($this->needsRebuild($cacheFile)) {
149
            $keys = $this->source->keys();
150
            sort($keys);
151
            $this->serializeCache->write($cacheFile, serialize($keys));
152
        } else {
153
            $keys = unserialize($this->serializeCache->read($cacheFile));
154
        }
155
156
        return $keys;
157
    }
158
159
    /**
160
     * {@inheritdoc}
161
     */
162
    public function delete($key)
163
    {
164
        return $this->source->delete($key) && $this->cache->delete($key);
165
    }
166
167
    /**
168
     * {@inheritdoc}
169
     */
170
    public function isDirectory($key)
171
    {
172
        return $this->source->isDirectory($key);
173
    }
174
175
    /**
176
     * {@inheritdoc}
177
     */
178
    public function setMetadata($key, $metadata)
179
    {
180
        if ($this->source instanceof MetadataSupporter) {
181
            $this->source->setMetadata($key, $metadata);
182
        }
183
184
        if ($this->cache instanceof MetadataSupporter) {
185
            $this->cache->setMetadata($key, $metadata);
186
        }
187
    }
188
189
    /**
190
     * {@inheritdoc}
191
     */
192
    public function getMetadata($key)
193
    {
194
        if ($this->source instanceof MetadataSupporter) {
195
            return $this->source->getMetadata($key);
196
        }
197
198
        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...
199
    }
200
201
    /**
202
     * Indicates whether the cache for the specified key needs to be reloaded.
203
     *
204
     * @param string $key
205
     */
206
    public function needsReload($key)
207
    {
208
        $needsReload = true;
209
210
        if ($this->cache->exists($key)) {
211
            try {
212
                $dateCache = $this->cache->mtime($key);
213
                $needsReload = false;
214
215
                if (time() - $this->ttl >= $dateCache) {
216
                    $dateSource = $this->source->mtime($key);
217
                    $needsReload = $dateCache < $dateSource;
218
                }
219
            } catch (\RuntimeException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
220
            }
221
        }
222
223
        return $needsReload;
224
    }
225
226
    /**
227
     * Indicates whether the serialized cache file needs to be rebuild.
228
     *
229
     * @param string $cacheFile
230
     */
231
    public function needsRebuild($cacheFile)
232
    {
233
        $needsRebuild = true;
234
235
        if ($this->serializeCache->exists($cacheFile)) {
236
            try {
237
                $needsRebuild = time() - $this->ttl >= $this->serializeCache->mtime($cacheFile);
238
            } catch (\RuntimeException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
239
            }
240
        }
241
242
        return $needsRebuild;
243
    }
244
}
245