php-cache / filesystem-adapter
1
<?php
2

3
/*
4
 * This file is part of php-cache organization.
5
 *
6
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11

12
namespace Cache\Adapter\Filesystem;
13

14
use Cache\Adapter\Common\AbstractCachePool;
15
use Cache\Adapter\Common\Exception\InvalidArgumentException;
16
use Cache\Adapter\Common\PhpCacheItem;
17
use League\Flysystem\FileExistsException;
18
use League\Flysystem\FileNotFoundException;
19
use League\Flysystem\FilesystemInterface;
20

21
/**
22
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
23
 */
24
class FilesystemCachePool extends AbstractCachePool
25
{
26
    /**
27
     * @type FilesystemInterface
28
     */
29
    private $filesystem;
30

31
    /**
32
     * The folder should not begin nor end with a slash. Example: path/to/cache.
33
     *
34
     * @type string
35
     */
36
    private $folder;
37

38
    /**
39
     * @param FilesystemInterface $filesystem
40
     * @param string              $folder
41
     */
42 19
    public function __construct(FilesystemInterface $filesystem, $folder = 'cache')
43
    {
44 19
        $this->folder = $folder;
45

46 19
        $this->filesystem = $filesystem;
47 19
        $this->filesystem->createDir($this->folder);
48 19
    }
49

50
    /**
51
     * @param string $folder
52
     */
53 19
    public function setFolder($folder)
54
    {
55 19
        $this->folder = $folder;
56 19
    }
57

58
    /**
59
     * {@inheritdoc}
60
     */
61 19
    protected function fetchObjectFromCache($key)
62
    {
63 19
        $empty = [false, null, [], null];
64 19
        $file  = $this->getFilePath($key);
65

66
        try {
67 19
            $data = @unserialize($this->filesystem->read($file));
68 19
            if ($data === false) {
69 19
                return $empty;
70
            }
71 19
        } catch (FileNotFoundException $e) {
72 19
            return $empty;
73
        }
74

75
        // Determine expirationTimestamp from data, remove items if expired
76 19
        $expirationTimestamp = $data[2] ?: null;
77 19
        if ($expirationTimestamp !== null && time() > $expirationTimestamp) {
78 19
            foreach ($data[1] as $tag) {
79 0
                $this->removeListItem($this->getTagKey($tag), $key);
80
            }
81 19
            $this->forceClear($key);
82

83 19
            return $empty;
84
        }
85

86 19
        return [true, $data[0], $data[1], $expirationTimestamp];
87
    }
88

89
    /**
90
     * {@inheritdoc}
91
     */
92 19
    protected function clearAllObjectsFromCache()
93
    {
94 19
        $this->filesystem->deleteDir($this->folder);
95 19
        $this->filesystem->createDir($this->folder);
96

97 19
        return true;
98
    }
99

100
    /**
101
     * {@inheritdoc}
102
     */
103 19
    protected function clearOneObjectFromCache($key)
104
    {
105 19
        return $this->forceClear($key);
106
    }
107

108
    /**
109
     * {@inheritdoc}
110
     */
111 19
    protected function storeItemInCache(PhpCacheItem $item, $ttl)
112
    {
113 19
        $data = serialize(
114
            [
115 19
                $item->get(),
116 19
                $item->getTags(),
117 19
                $item->getExpirationTimestamp(),
118
            ]
119
        );
120

121 19
        $file = $this->getFilePath($item->getKey());
122 19
        if ($this->filesystem->has($file)) {
123
            // Update file if it exists
124 19
            return $this->filesystem->update($file, $data);
125
        }
126

127
        try {
128 19
            return $this->filesystem->write($file, $data);
129 0
        } catch (FileExistsException $e) {
130
            // To handle issues when/if race conditions occurs, we try to update here.
131 0
            return $this->filesystem->update($file, $data);
132
        }
133
    }
134

135
    /**
136
     * @param string $key
137
     *
138
     * @throws InvalidArgumentException
139
     *
140
     * @return string
141
     */
142 19
    private function getFilePath($key)
143
    {
144 19
        if (!preg_match('|^[a-zA-Z0-9_\.! ]+$|', $key)) {
145 19
            throw new InvalidArgumentException(sprintf('Invalid key "%s". Valid filenames must match [a-zA-Z0-9_\.! ].', $key));
146
        }
147

148 19
        return sprintf('%s/%s', $this->folder, $key);
149
    }
150

151
    /**
152
     * {@inheritdoc}
153
     */
154 19
    protected function getList($name)
155
    {
156 19
        $file = $this->getFilePath($name);
157

158 19
        if (!$this->filesystem->has($file)) {
159 19
            $this->filesystem->write($file, serialize([]));
160
        }
161

162 19
        return unserialize($this->filesystem->read($file));
163
    }
164

165
    /**
166
     * {@inheritdoc}
167
     */
168 19
    protected function removeList($name)
169
    {
170 19
        $file = $this->getFilePath($name);
171 19
        $this->filesystem->delete($file);
172 19
    }
173

174
    /**
175
     * {@inheritdoc}
176
     */
177 19
    protected function appendListItem($name, $key)
178
    {
179 19
        $list   = $this->getList($name);
180 19
        $list[] = $key;
181

182 19
        return $this->filesystem->update($this->getFilePath($name), serialize($list));
183
    }
184

185
    /**
186
     * {@inheritdoc}
187
     */
188 19
    protected function removeListItem($name, $key)
189
    {
190 19
        $list = $this->getList($name);
191 19
        foreach ($list as $i => $item) {
192 19
            if ($item === $key) {
193 19
                unset($list[$i]);
194
            }
195
        }
196

197 19
        return $this->filesystem->update($this->getFilePath($name), serialize($list));
198
    }
199

200
    /**
201
     * @param $key
202
     *
203
     * @return bool
204
     */
205 19
    private function forceClear($key)
206
    {
207
        try {
208 19
            return $this->filesystem->delete($this->getFilePath($key));
209 19
        } catch (FileNotFoundException $e) {
210 19
            return true;
211
        }
212
    }
213
}

Read our documentation on viewing source code .

Loading