~ubuntu-branches/ubuntu/wily/php-horde-cache/wily

« back to all changes in this revision

Viewing changes to Horde_Cache-2.4.2/lib/Horde/Cache/Storage/File.php

  • Committer: Package Import Robot
  • Author(s): Mathieu Parent
  • Date: 2014-04-13 09:11:33 UTC
  • mfrom: (1.1.10)
  • Revision ID: package-import@ubuntu.com-20140413091133-um8xxmlwuawkhbi5
Tags: 2.4.2-1
New upstream version 2.4.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/**
 
3
 * Copyright 1999-2014 Horde LLC (http://www.horde.org/)
 
4
 *
 
5
 * See the enclosed file COPYING for license information (LGPL). If you
 
6
 * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 
7
 *
 
8
 * @category  Horde
 
9
 * @copyright 1999-2014 Horde LLC
 
10
 * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
 
11
 * @package   Cache
 
12
 */
 
13
 
 
14
/**
 
15
 * Cache storage in the filesystem.
 
16
 *
 
17
 * @author    Anil Madhavapeddy <anil@recoil.org>
 
18
 * @author    Chuck Hagenbuch <chuck@horde.org>
 
19
 * @author    Michael Slusarz <slusarz@horde.org>
 
20
 * @category  Horde
 
21
 * @copyright 1999-2014 Horde LLC
 
22
 * @license   http://www.horde.org/licenses/lgpl21 LGPL 2.1
 
23
 * @package   Cache
 
24
 */
 
25
class Horde_Cache_Storage_File extends Horde_Cache_Storage_Base
 
26
{
 
27
    /* Location of the garbage collection data file. */
 
28
    const GC_FILE = 'horde_cache_gc';
 
29
 
 
30
    /**
 
31
     * List of key to filename mappings.
 
32
     *
 
33
     * @var array
 
34
     */
 
35
    protected $_file = array();
 
36
 
 
37
    /**
 
38
     * Constructor.
 
39
     *
 
40
     * @param array $params  Optional parameters:
 
41
     * <pre>
 
42
     *   - dir: (string) The base directory to store the cache files in.
 
43
     *          DEFAULT: System default
 
44
     *   - no_gc: (boolean) If true, don't perform garbage collection.
 
45
     *            DEFAULT: false
 
46
     *   - prefix: (string) The filename prefix to use for the cache files.
 
47
     *             DEFAULT: 'cache_'
 
48
     *   - sub: (integer) If non-zero, the number of subdirectories to create
 
49
     *          to store the file (i.e. PHP's session.save_path).
 
50
     *          DEFAULT: 0
 
51
     * </pre>
 
52
     */
 
53
    public function __construct(array $params = array())
 
54
    {
 
55
        $params = array_merge(array(
 
56
            'prefix' => 'cache_',
 
57
            'sub' => 0
 
58
        ), $params);
 
59
 
 
60
        if (!isset($params['dir']) || !@is_dir($params['dir'])) {
 
61
            $params['dir'] = sys_get_temp_dir();
 
62
        }
 
63
 
 
64
        parent::__construct($params);
 
65
    }
 
66
 
 
67
    /**
 
68
     * Destructor.
 
69
     */
 
70
    public function __destruct()
 
71
    {
 
72
        $c_time = time();
 
73
 
 
74
        /* Only do garbage collection 0.1% of the time we create an object. */
 
75
        if (!empty($this->_params['no_gc']) ||
 
76
            (intval(substr($c_time, -3)) !== 0)) {
 
77
            return;
 
78
        }
 
79
 
 
80
        $filename = $this->_params['dir'] . '/' . self::GC_FILE;
 
81
        $excepts = array();
 
82
 
 
83
        if (is_readable($filename)) {
 
84
            $gc_file = file($filename, FILE_IGNORE_NEW_LINES);
 
85
            reset($gc_file);
 
86
            next($gc_file);
 
87
            while (list(,$data) = each($gc_file)) {
 
88
                $parts = explode("\t", $data, 2);
 
89
                $excepts[$parts[0]] = $parts[1];
 
90
            }
 
91
        }
 
92
 
 
93
        foreach ($this->_getCacheFiles() as $fname => $pname) {
 
94
            if (!empty($excepts[$fname]) &&
 
95
                (($c_time - $excepts[$fname]) > filemtime($pname))) {
 
96
                @unlink($pname);
 
97
                unset($excepts[$fname]);
 
98
            }
 
99
        }
 
100
 
 
101
        if ($fp = @fopen($filename, 'w')) {
 
102
            foreach ($excepts as $key => $val) {
 
103
                fwrite($fp, $key . "\t" . $val . "\n");
 
104
            }
 
105
            fclose($fp);
 
106
        }
 
107
    }
 
108
 
 
109
    /**
 
110
     */
 
111
    public function get($key, $lifetime = 0)
 
112
    {
 
113
        if (!$this->exists($key, $lifetime)) {
 
114
            /* Nothing cached, return failure. */
 
115
            return false;
 
116
        }
 
117
 
 
118
        $filename = $this->_keyToFile($key);
 
119
        $size = filesize($filename);
 
120
 
 
121
        return $size
 
122
            ? @file_get_contents($filename)
 
123
            : '';
 
124
    }
 
125
 
 
126
    /**
 
127
     */
 
128
    public function set($key, $data, $lifetime = 0)
 
129
    {
 
130
        $filename = $this->_keyToFile($key, true);
 
131
        $tmp_file = Horde_Util::getTempFile('HordeCache', true, $this->_params['dir']);
 
132
        if (isset($this->_params['umask'])) {
 
133
            chmod($tmp_file, 0666 & ~$this->_params['umask']);
 
134
        }
 
135
 
 
136
        if (file_put_contents($tmp_file, $data) === false) {
 
137
            throw new Horde_Cache_Exception('Cannot write to cache directory ' . $this->_params['dir']);
 
138
        }
 
139
 
 
140
        @rename($tmp_file, $filename);
 
141
 
 
142
        if ($lifetime &&
 
143
            ($fp = @fopen($this->_params['dir'] . '/' . self::GC_FILE, 'a'))) {
 
144
            // This may result in duplicate entries in GC_FILE, but we
 
145
            // will take care of these whenever we do GC and this is quicker
 
146
            // than having to check every time we access the file.
 
147
            fwrite($fp, $filename . "\t" . (time() + $lifetime) . "\n");
 
148
            fclose($fp);
 
149
        }
 
150
    }
 
151
 
 
152
    /**
 
153
     */
 
154
    public function exists($key, $lifetime = 0)
 
155
    {
 
156
        $filename = $this->_keyToFile($key);
 
157
 
 
158
        /* Key exists in the cache */
 
159
        if (file_exists($filename)) {
 
160
            /* 0 means no expire.
 
161
             * Also, If the file was been created after the supplied value,
 
162
             * the data is valid (fresh). */
 
163
            if (($lifetime == 0) ||
 
164
                (time() - $lifetime <= filemtime($filename))) {
 
165
                return true;
 
166
            }
 
167
 
 
168
            @unlink($filename);
 
169
        }
 
170
 
 
171
        return false;
 
172
    }
 
173
 
 
174
    /**
 
175
     */
 
176
    public function expire($key)
 
177
    {
 
178
        return @unlink($this->_keyToFile($key));
 
179
    }
 
180
 
 
181
    /**
 
182
     */
 
183
    public function clear()
 
184
    {
 
185
        foreach ($this->_getCacheFiles() as $val) {
 
186
            @unlink($val);
 
187
        }
 
188
        @unlink($this->_params['dir'] . '/' . self::GC_FILE);
 
189
    }
 
190
 
 
191
    /**
 
192
     * Return a list of cache files.
 
193
     *
 
194
     * @return array  Pathnames to cache files.
 
195
     */
 
196
    protected function _getCacheFiles()
 
197
    {
 
198
        $paths = array();
 
199
 
 
200
        try {
 
201
            $it = empty($this->_params['sub'])
 
202
                ? new DirectoryIterator($this->_params['dir'])
 
203
                : new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->_params['dir']), RecursiveIteratorIterator::CHILD_FIRST);
 
204
        } catch (UnexpectedValueException $e) {
 
205
            return $paths;
 
206
        }
 
207
 
 
208
        foreach ($it as $val) {
 
209
            if (!$val->isDir() &&
 
210
                ($fname = $val->getFilename()) &&
 
211
                (strpos($fname, $this->_params['prefix']) === 0)) {
 
212
                $paths[$fname] = $val->getPathname();
 
213
            }
 
214
        }
 
215
 
 
216
        return $paths;
 
217
    }
 
218
 
 
219
    /**
 
220
     * Map a cache key to a unique filename.
 
221
     *
 
222
     * @param string $key     Cache key.
 
223
     * @param string $create  Create path if it doesn't exist?
 
224
     *
 
225
     * @return string  Fully qualified filename.
 
226
     */
 
227
    protected function _keyToFile($key, $create = false)
 
228
    {
 
229
        if ($create || !isset($this->_file[$key])) {
 
230
            $dir = $this->_params['dir'] . '/';
 
231
            $md5 = hash('md5', $key);
 
232
            $sub = '';
 
233
 
 
234
            if (!empty($this->_params['sub'])) {
 
235
                $max = min($this->_params['sub'], strlen($md5));
 
236
                for ($i = 0; $i < $max; $i++) {
 
237
                    $sub .= $md5[$i];
 
238
                    if ($create && !is_dir($dir . $sub)) {
 
239
                        if (!mkdir($dir . $sub)) {
 
240
                            $sub = '';
 
241
                            break;
 
242
                        }
 
243
                    }
 
244
                    $sub .= '/';
 
245
                }
 
246
            }
 
247
 
 
248
            $this->_file[$key] = $dir . $sub . $this->_params['prefix'] . $md5;
 
249
        }
 
250
 
 
251
        return $this->_file[$key];
 
252
    }
 
253
 
 
254
}