Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * APCu cache store main library.
19
 *
20
 * @package    cachestore_apcu
21
 * @copyright  2012 Sam Hemelryk
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
defined('MOODLE_INTERNAL') || die();
26
 
27
/**
28
 * The APCu cache store class.
29
 *
30
 * @copyright  2012 Sam Hemelryk
31
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
32
 */
33
class cachestore_apcu extends cache_store implements cache_is_key_aware, cache_is_configurable {
34
 
35
    /**
36
     * The required version of APCu for this extension.
37
     */
38
    const REQUIRED_VERSION = '4.0.0';
39
 
40
    /**
41
     * The name of this store instance.
42
     * @var string
43
     */
44
    protected $name;
45
 
46
    /**
47
     * The definition used when this instance was initialised.
48
     * @var cache_definition
49
     */
50
    protected $definition = null;
51
 
52
    /**
53
     * The storeprefix to use on all instances of this store.  Configured as part store setup.
54
     * @var string
55
     */
56
    protected $storeprefix = null;
57
 
58
    /**
59
     * The prefix added specifically for this cache.
60
     * @var string
61
     */
62
    protected $cacheprefix = null;
63
 
64
    /**
65
     * Static method to check that the APCu stores requirements have been met.
66
     *
67
     * It checks that the APCu extension has been loaded and that it has been enabled.
68
     *
69
     * @return bool True if the stores software/hardware requirements have been met and it can be used. False otherwise.
70
     */
71
    public static function are_requirements_met() {
72
        $enabled = ini_get('apc.enabled') && (php_sapi_name() != "cli" || ini_get('apc.enable_cli'));
73
        if (!extension_loaded('apcu') || !$enabled) {
74
            return false;
75
        }
76
 
77
        $version = phpversion('apcu');
78
        return $version && version_compare($version, self::REQUIRED_VERSION, '>=');
79
    }
80
 
81
    /**
82
     * Static method to check if a store is usable with the given mode.
83
     *
84
     * @param int $mode One of cache_store::MODE_*
85
     * @return bool True if the mode is supported.
86
     */
87
    public static function is_supported_mode($mode) {
88
        return ($mode === self::MODE_APPLICATION || $mode === self::MODE_SESSION);
89
    }
90
 
91
    /**
92
     * Returns the supported features as a binary flag.
93
     *
94
     * @param array $configuration The configuration of a store to consider specifically.
95
     * @return int The supported features.
96
     */
97
    public static function get_supported_features(array $configuration = array()) {
98
        return self::SUPPORTS_NATIVE_TTL;
99
    }
100
 
101
    /**
102
     * Returns the supported modes as a binary flag.
103
     *
104
     * @param array $configuration The configuration of a store to consider specifically.
105
     * @return int The supported modes.
106
     */
107
    public static function get_supported_modes(array $configuration = array()) {
108
        return self::MODE_APPLICATION + self::MODE_SESSION;
109
    }
110
 
111
    /**
112
     * Constructs an instance of the cache store.
113
     *
114
     * This method should not create connections or perform and processing, it should be used
115
     *
116
     * @param string $name The name of the cache store
117
     * @param array $configuration The configuration for this store instance.
118
     */
119
    public function __construct($name, array $configuration = array()) {
120
        global $CFG;
121
        $this->name = $name;
122
        $this->storeprefix = $CFG->prefix;
123
        if (isset($configuration['prefix'])) {
124
            $this->storeprefix = $configuration['prefix'];
125
        }
126
    }
127
 
128
    /**
129
     * Returns the name of this store instance.
130
     * @return string
131
     */
132
    public function my_name() {
133
        return $this->name;
134
    }
135
 
136
    /**
137
     * Initialises a new instance of the cache store given the definition the instance is to be used for.
138
     *
139
     * This function should prepare any given connections etc.
140
     *
141
     * @param cache_definition $definition
142
     * @return bool
143
     */
144
    public function initialise(cache_definition $definition) {
145
        $this->definition = $definition;
146
        $this->cacheprefix = $this->storeprefix.$definition->generate_definition_hash().'__';
147
        return true;
148
    }
149
 
150
    /**
151
     * Returns true if this cache store instance has been initialised.
152
     * @return bool
153
     */
154
    public function is_initialised() {
155
        return ($this->definition !== null);
156
    }
157
 
158
    /**
159
     * Prepares the given key for use.
160
     *
161
     * Should be called before all interaction.
162
     *
163
     * @param string $key The key to prepare for storing in APCu.
164
     *
165
     * @return string
166
     */
167
    protected function prepare_key($key) {
168
        return $this->cacheprefix . $key;
169
    }
170
 
171
    /**
172
     * Retrieves an item from the cache store given its key.
173
     *
174
     * @param string $key The key to retrieve
175
     * @return mixed The data that was associated with the key, or false if the key did not exist.
176
     */
177
    public function get($key) {
178
        $key = $this->prepare_key($key);
179
        $success = false;
180
        $outcome = apcu_fetch($key, $success);
181
        if ($success) {
182
            return $outcome;
183
        }
184
        return $success;
185
    }
186
 
187
    /**
188
     * Retrieves several items from the cache store in a single transaction.
189
     *
190
     * If not all of the items are available in the cache then the data value for those that are missing will be set to false.
191
     *
192
     * @param array $keys The array of keys to retrieve
193
     * @return array An array of items from the cache. There will be an item for each key, those that were not in the store will
194
     *      be set to false.
195
     */
196
    public function get_many($keys) {
197
        $map = array();
198
        foreach ($keys as $key) {
199
            $map[$key] = $this->prepare_key($key);
200
        }
201
        $outcomes = array();
202
        $success = false;
203
        $results = apcu_fetch($map, $success);
204
        if ($success) {
205
            foreach ($map as $key => $used) {
206
                if (array_key_exists($used, $results)) {
207
                    $outcomes[$key] = $results[$used];
208
                } else {
209
                    $outcomes[$key] = false;
210
                }
211
            }
212
        } else {
213
            $outcomes = array_fill_keys($keys, false);
214
        }
215
        return $outcomes;
216
    }
217
 
218
    /**
219
     * Sets an item in the cache given its key and data value.
220
     *
221
     * @param string $key The key to use.
222
     * @param mixed $data The data to set.
223
     * @return bool True if the operation was a success false otherwise.
224
     */
225
    public function set($key, $data) {
226
        $key = $this->prepare_key($key);
227
        return apcu_store($key, $data, $this->definition->get_ttl());
228
    }
229
 
230
    /**
231
     * Sets many items in the cache in a single transaction.
232
     *
233
     * @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two
234
     *      keys, 'key' and 'value'.
235
     * @return int The number of items successfully set. It is up to the developer to check this matches the number of items
236
     *      sent ... if they care that is.
237
     */
238
    public function set_many(array $keyvaluearray) {
239
        $map = array();
240
        foreach ($keyvaluearray as $pair) {
241
            $key = $this->prepare_key($pair['key']);
242
            $map[$key] = $pair['value'];
243
        }
244
        $result = apcu_store($map, null, $this->definition->get_ttl());
245
        return count($map) - count($result);
246
    }
247
 
248
    /**
249
     * Deletes an item from the cache store.
250
     *
251
     * @param string $key The key to delete.
252
     * @return bool Returns true if the operation was a success, false otherwise.
253
     */
254
    public function delete($key) {
255
        $key = $this->prepare_key($key);
256
        return apcu_delete($key);
257
    }
258
 
259
    /**
260
     * Deletes several keys from the cache in a single action.
261
     *
262
     * @param array $keys The keys to delete
263
     * @return int The number of items successfully deleted.
264
     */
265
    public function delete_many(array $keys) {
266
        $count = 0;
267
        foreach ($keys as $key) {
268
            if ($this->delete($key)) {
269
                $count++;
270
            }
271
        }
272
        return $count;
273
    }
274
 
275
    /**
276
     * Purges the cache deleting all items within it.
277
     *
278
     * @return boolean True on success. False otherwise.
279
     */
280
    public function purge() {
281
        if (class_exists('APCUIterator', false)) {
282
            $iterator = new APCUIterator('#^' . preg_quote($this->cacheprefix, '#') . '#');
283
        } else {
284
            $iterator = new APCIterator('user', '#^' . preg_quote($this->cacheprefix, '#') . '#');
285
        }
286
        return apcu_delete($iterator);
287
    }
288
 
289
    /**
290
     * Performs any necessary clean up when the store instance is being deleted.
291
     */
292
    public function instance_deleted() {
293
        if (class_exists('APCUIterator', false)) {
294
            $iterator = new APCUIterator('#^' . preg_quote($this->storeprefix, '#') . '#');
295
        } else {
296
            $iterator = new APCIterator('user', '#^' . preg_quote($this->storeprefix, '#') . '#');
297
        }
298
        return apcu_delete($iterator);
299
    }
300
 
301
    /**
302
     * Generates an instance of the cache store that can be used for testing.
303
     *
304
     * Returns an instance of the cache store, or false if one cannot be created.
305
     *
306
     * @param cache_definition $definition
307
     * @return cache_store
308
     */
309
    public static function initialise_test_instance(cache_definition $definition) {
310
        $testperformance = get_config('cachestore_apcu', 'testperformance');
311
        if (empty($testperformance)) {
312
            return false;
313
        }
314
        if (!self::are_requirements_met()) {
315
            return false;
316
        }
317
        $name = 'APCu test';
318
        $cache = new cachestore_apcu($name);
319
        // No need to check if is_ready() as this has already being done by requirement check.
320
        $cache->initialise($definition);
321
        return $cache;
322
    }
323
 
324
    /**
325
     * Test is a cache has a key.
326
     *
327
     * @param string|int $key
328
     * @return bool True if the cache has the requested key, false otherwise.
329
     */
330
    public function has($key) {
331
        $key = $this->prepare_key($key);
332
        return apcu_exists($key);
333
    }
334
 
335
    /**
336
     * Test if a cache has at least one of the given keys.
337
     *
338
     * @param array $keys
339
     * @return bool True if the cache has at least one of the given keys
340
     */
341
    public function has_any(array $keys) {
342
        foreach ($keys as $arraykey => $key) {
343
            $keys[$arraykey] = $this->prepare_key($key);
344
        }
345
        $result = apcu_exists($keys);
346
        return count($result) > 0;
347
    }
348
 
349
    /**
350
     * Test is a cache has all of the given keys.
351
     *
352
     * @param array $keys
353
     * @return bool True if the cache has all of the given keys, false otherwise.
354
     */
355
    public function has_all(array $keys) {
356
        foreach ($keys as $arraykey => $key) {
357
            $keys[$arraykey] = $this->prepare_key($key);
358
        }
359
        $result = apcu_exists($keys);
360
        return count($result) === count($keys);
361
    }
362
 
363
    /**
364
     * Generates the appropriate configuration required for unit testing.
365
     *
366
     * @return array Array of unit test configuration data to be used by initialise().
367
     */
368
    public static function unit_test_configuration() {
369
        return array('prefix' => 'phpunit');
370
    }
371
 
372
    /**
373
     * Given the data from the add instance form this function creates a configuration array.
374
     *
375
     * @param stdClass $data
376
     * @return array
377
     */
378
    public static function config_get_configuration_array($data) {
379
        $config = array();
380
 
381
        if (isset($data->prefix)) {
382
            $config['prefix'] = $data->prefix;
383
        }
384
        return $config;
385
    }
386
    /**
387
     * Allows the cache store to set its data against the edit form before it is shown to the user.
388
     *
389
     * @param moodleform $editform
390
     * @param array $config
391
     */
392
    public static function config_set_edit_form_data(moodleform $editform, array $config) {
393
        if (isset($config['prefix'])) {
394
            $data['prefix'] = $config['prefix'];
395
        } else {
396
            $data['prefix'] = '';
397
        }
398
        $editform->set_data($data);
399
    }
400
 
401
    /**
402
     * Returns true if this cache store instance is both suitable for testing, and ready for testing.
403
     *
404
     * Cache stores that support being used as the default store for unit and acceptance testing should
405
     * override this function and return true if there requirements have been met.
406
     *
407
     * @return bool
408
     */
409
    public static function ready_to_be_used_for_testing() {
410
        return true;
411
    }
412
}