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
 * The library file for the static cache store.
19
 *
20
 * This file is part of the static cache store, it contains the API for interacting with an instance of the store.
21
 * This is used as a default cache store within the Cache API. It should never be deleted.
22
 *
23
 * @package    cachestore_static
24
 * @category   cache
25
 * @copyright  2012 Sam Hemelryk
26
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27
 */
28
 
29
defined('MOODLE_INTERNAL') || die();
30
 
31
/**
32
 * The static data store class
33
 *
34
 * @copyright  2012 Sam Hemelryk
35
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36
 */
37
abstract class static_data_store extends cache_store {
38
 
39
    /**
40
     * An array for storage.
41
     * @var array
42
     */
43
    private static $staticstore = array();
44
 
45
    /**
46
     * Returns a static store by reference... REFERENCE SUPER IMPORTANT.
47
     *
48
     * @param string $id
49
     * @return array
50
     */
51
    protected static function &register_store_id($id) {
52
        if (!array_key_exists($id, self::$staticstore)) {
53
            self::$staticstore[$id] = array();
54
        }
55
        return self::$staticstore[$id];
56
    }
57
 
58
    /**
59
     * Flushes the store of all values for belonging to the store with the given id.
60
     * @param string $id
61
     */
62
    protected static function flush_store_by_id($id) {
63
        unset(self::$staticstore[$id]);
64
        self::$staticstore[$id] = array();
65
    }
66
 
67
    /**
68
     * Flushes all of the values from all stores.
69
     *
70
     * @copyright  2012 Sam Hemelryk
71
     * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
72
     */
73
    protected static function flush_store() {
74
        $ids = array_keys(self::$staticstore);
75
        unset(self::$staticstore);
76
        self::$staticstore = array();
77
        foreach ($ids as $id) {
78
            self::$staticstore[$id] = array();
79
        }
80
    }
81
}
82
 
83
/**
84
 * The static store class.
85
 *
86
 * @copyright  2012 Sam Hemelryk
87
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
88
 */
89
class cachestore_static extends static_data_store implements cache_is_key_aware, cache_is_searchable {
90
 
91
    /**
92
     * The name of the store
93
     * @var store
94
     */
95
    protected $name;
96
 
97
    /**
98
     * The store id (should be unique)
99
     * @var string
100
     */
101
    protected $storeid;
102
 
103
    /**
104
     * The store we use for data.
105
     * @var array
106
     */
107
    protected $store;
108
 
109
    /**
110
     * The maximum size for the store, or false if there isn't one.
111
     * @var bool
112
     */
113
    protected $maxsize = false;
114
 
115
    /**
116
     * Where this cache uses simpledata and we don't need to serialize it.
117
     * @var bool
118
     */
119
    protected $simpledata = false;
120
 
121
    /**
122
     * The number of items currently being stored.
123
     * @var int
124
     */
125
    protected $storecount = 0;
126
 
127
    /**
128
     * igbinary extension available.
129
     * @var bool
130
     */
131
    protected $igbinaryfound = false;
132
 
133
    /**
134
     * Constructs the store instance.
135
     *
136
     * Noting that this function is not an initialisation. It is used to prepare the store for use.
137
     * The store will be initialised when required and will be provided with a cache_definition at that time.
138
     *
139
     * @param string $name
140
     * @param array $configuration
141
     */
142
    public function __construct($name, array $configuration = array()) {
143
        $this->name = $name;
144
    }
145
 
146
    /**
147
     * Returns the supported features as a combined int.
148
     *
149
     * @param array $configuration
150
     * @return int
151
     */
152
    public static function get_supported_features(array $configuration = array()) {
153
        return self::SUPPORTS_DATA_GUARANTEE +
154
               self::SUPPORTS_NATIVE_TTL +
155
               self::IS_SEARCHABLE +
156
               self::SUPPORTS_MULTIPLE_IDENTIFIERS +
157
               self::DEREFERENCES_OBJECTS;
158
    }
159
 
160
    /**
161
     * Returns true as this store does support multiple identifiers.
162
     * (This optional function is a performance optimisation; it must be
163
     * consistent with the value from get_supported_features.)
164
     *
165
     * @return bool true
166
     */
167
    public function supports_multiple_identifiers() {
168
        return true;
169
    }
170
 
171
    /**
172
     * Returns the supported modes as a combined int.
173
     *
174
     * @param array $configuration
175
     * @return int
176
     */
177
    public static function get_supported_modes(array $configuration = array()) {
178
        return self::MODE_REQUEST;
179
    }
180
 
181
    /**
182
     * Returns true if the store requirements are met.
183
     *
184
     * @return bool
185
     */
186
    public static function are_requirements_met() {
187
        return true;
188
    }
189
 
190
    /**
191
     * Returns true if the given mode is supported by this store.
192
     *
193
     * @param int $mode One of cache_store::MODE_*
194
     * @return bool
195
     */
196
    public static function is_supported_mode($mode) {
197
        return ($mode === self::MODE_REQUEST);
198
    }
199
 
200
    /**
201
     * Initialises the cache.
202
     *
203
     * Once this has been done the cache is all set to be used.
204
     *
205
     * @param cache_definition $definition
206
     */
207
    public function initialise(cache_definition $definition) {
208
        $keyarray = $definition->generate_multi_key_parts();
209
        $this->storeid = $keyarray['mode'].'/'.$keyarray['component'].'/'.$keyarray['area'].'/'.$keyarray['siteidentifier'];
210
        $this->store = &self::register_store_id($this->storeid);
211
        $maxsize = $definition->get_maxsize();
212
        $this->simpledata = $definition->uses_simple_data();
213
        $this->igbinaryfound = extension_loaded('igbinary');
214
        if ($maxsize !== null) {
215
            // Must be a positive int.
216
            $this->maxsize = abs((int)$maxsize);
217
            $this->storecount = count($this->store);
218
        }
219
    }
220
 
221
    /**
222
     * Returns true once this instance has been initialised.
223
     *
224
     * @return bool
225
     */
226
    public function is_initialised() {
227
        return (is_array($this->store));
228
    }
229
 
230
    /**
231
     * Uses igbinary serializer if igbinary extension is loaded.
232
     * Fallback to PHP serializer.
233
     *
234
     * @param mixed $data
235
     * The value to be serialized.
236
     * @return string a string containing a byte-stream representation of
237
     * value that can be stored anywhere.
238
     */
239
    protected function serialize($data) {
240
        if ($this->igbinaryfound) {
241
            return igbinary_serialize($data);
242
        } else {
243
            return serialize($data);
244
        }
245
    }
246
 
247
    /**
248
     * Uses igbinary unserializer if igbinary extension is loaded.
249
     * Fallback to PHP unserializer.
250
     *
251
     * @param string $str
252
     * The serialized string.
253
     * @return mixed The converted value is returned, and can be a boolean,
254
     * integer, float, string,
255
     * array or object.
256
     */
257
    protected function unserialize($str) {
258
        if ($this->igbinaryfound) {
259
            return igbinary_unserialize($str);
260
        } else {
261
            return unserialize($str);
262
        }
263
    }
264
 
265
    /**
266
     * Retrieves an item from the cache store given its key.
267
     *
268
     * @param string $key The key to retrieve
269
     * @return mixed The data that was associated with the key, or false if the key did not exist.
270
     */
271
    public function get($key) {
272
        if (!is_array($key)) {
273
            $key = array('key' => $key);
274
        }
275
 
276
        $key = $key['key'];
277
        if (isset($this->store[$key])) {
278
            if ($this->store[$key]['serialized']) {
279
                return $this->unserialize($this->store[$key]['data']);
280
            } else {
281
                return $this->store[$key]['data'];
282
            }
283
        }
284
        return false;
285
    }
286
 
287
    /**
288
     * Retrieves several items from the cache store in a single transaction.
289
     *
290
     * 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.
291
     *
292
     * @param array $keys The array of keys to retrieve
293
     * @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
294
     *      be set to false.
295
     */
296
    public function get_many($keys) {
297
        $return = array();
298
 
299
        foreach ($keys as $key) {
300
            if (!is_array($key)) {
301
                $key = array('key' => $key);
302
            }
303
            $key = $key['key'];
304
            $return[$key] = false;
305
            if (isset($this->store[$key])) {
306
                if ($this->store[$key]['serialized']) {
307
                    $return[$key] = $this->unserialize($this->store[$key]['data']);
308
                } else {
309
                    $return[$key] = $this->store[$key]['data'];
310
                }
311
            }
312
        }
313
        return $return;
314
    }
315
 
316
    /**
317
     * Sets an item in the cache given its key and data value.
318
     *
319
     * @param string $key The key to use.
320
     * @param mixed $data The data to set.
321
     * @param bool $testmaxsize If set to true then we test the maxsize arg and reduce if required.
322
     * @return bool True if the operation was a success false otherwise.
323
     */
324
    public function set($key, $data, $testmaxsize = true) {
325
        if (!is_array($key)) {
326
            $key = array('key' => $key);
327
        }
328
        $key = $key['key'];
329
        $testmaxsize = ($testmaxsize && $this->maxsize !== false);
330
        if ($testmaxsize) {
331
            $increment = (!isset($this->store[$key]));
332
        }
333
 
334
        if ($this->simpledata || is_scalar($data)) {
335
            $this->store[$key]['data'] = $data;
336
            $this->store[$key]['serialized'] = false;
337
        } else {
338
            $this->store[$key]['data'] = $this->serialize($data);
339
            $this->store[$key]['serialized'] = true;
340
        }
341
 
342
        if ($testmaxsize && $increment) {
343
            $this->storecount++;
344
            if ($this->storecount > $this->maxsize) {
345
                $this->reduce_for_maxsize();
346
            }
347
        }
348
        return true;
349
    }
350
 
351
    /**
352
     * Sets many items in the cache in a single transaction.
353
     *
354
     * @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two
355
     *      keys, 'key' and 'value'.
356
     * @return int The number of items successfully set. It is up to the developer to check this matches the number of items
357
     *      sent ... if they care that is.
358
     */
359
    public function set_many(array $keyvaluearray) {
360
        $count = 0;
361
        foreach ($keyvaluearray as $pair) {
362
            if (!is_array($pair['key'])) {
363
                $pair['key'] = array('key' => $pair['key']);
364
            }
365
            // Don't test the maxsize here. We'll do it once when we are done.
366
            $this->set($pair['key']['key'], $pair['value'], false);
367
            $count++;
368
        }
369
        if ($this->maxsize !== false) {
370
            $this->storecount += $count;
371
            if ($this->storecount > $this->maxsize) {
372
                $this->reduce_for_maxsize();
373
            }
374
        }
375
        return $count;
376
    }
377
 
378
    /**
379
     * Checks if the store has a record for the given key and returns true if so.
380
     *
381
     * @param string $key
382
     * @return bool
383
     */
384
    public function has($key) {
385
        if (is_array($key)) {
386
            $key = $key['key'];
387
        }
388
        return isset($this->store[$key]);
389
    }
390
 
391
    /**
392
     * Returns true if the store contains records for all of the given keys.
393
     *
394
     * @param array $keys
395
     * @return bool
396
     */
397
    public function has_all(array $keys) {
398
        foreach ($keys as $key) {
399
            if (!is_array($key)) {
400
                $key = array('key' => $key);
401
            }
402
            $key = $key['key'];
403
            if (!isset($this->store[$key])) {
404
                return false;
405
            }
406
        }
407
        return true;
408
    }
409
 
410
    /**
411
     * Returns true if the store contains records for any of the given keys.
412
     *
413
     * @param array $keys
414
     * @return bool
415
     */
416
    public function has_any(array $keys) {
417
        foreach ($keys as $key) {
418
            if (!is_array($key)) {
419
                $key = array('key' => $key);
420
            }
421
            $key = $key['key'];
422
 
423
            if (isset($this->store[$key])) {
424
                return true;
425
            }
426
        }
427
        return false;
428
    }
429
 
430
    /**
431
     * Deletes an item from the cache store.
432
     *
433
     * @param string $key The key to delete.
434
     * @return bool Returns true if the operation was a success, false otherwise.
435
     */
436
    public function delete($key) {
437
        if (!is_array($key)) {
438
            $key = array('key' => $key);
439
        }
440
        $key = $key['key'];
441
        $result = isset($this->store[$key]);
442
        unset($this->store[$key]);
443
        if ($this->maxsize !== false) {
444
            $this->storecount--;
445
        }
446
        return $result;
447
    }
448
 
449
    /**
450
     * Deletes several keys from the cache in a single action.
451
     *
452
     * @param array $keys The keys to delete
453
     * @return int The number of items successfully deleted.
454
     */
455
    public function delete_many(array $keys) {
456
        $count = 0;
457
        foreach ($keys as $key) {
458
            if (!is_array($key)) {
459
                $key = array('key' => $key);
460
            }
461
            $key = $key['key'];
462
            if (isset($this->store[$key])) {
463
                $count++;
464
            }
465
            unset($this->store[$key]);
466
        }
467
        if ($this->maxsize !== false) {
468
            $this->storecount -= $count;
469
        }
470
        return $count;
471
    }
472
 
473
    /**
474
     * Purges the cache deleting all items within it.
475
     *
476
     * @return boolean True on success. False otherwise.
477
     */
478
    public function purge() {
479
        $this->flush_store_by_id($this->storeid);
480
        $this->store = &self::register_store_id($this->storeid);
481
        // Don't worry about checking if we're using max size just set it as thats as fast as the check.
482
        $this->storecount = 0;
483
        return true;
484
    }
485
 
486
    /**
487
     * Reduces the size of the array if maxsize has been hit.
488
     *
489
     * This function reduces the size of the store reducing it by 10% of its maxsize.
490
     * It removes the oldest items in the store when doing this.
491
     * The reason it does this an doesn't use a least recently used system is purely the overhead such a system
492
     * requires. The current approach is focused on speed, MUC already adds enough overhead to static/session caches
493
     * and avoiding more is of benefit.
494
     *
495
     * @return int
496
     */
497
    protected function reduce_for_maxsize() {
498
        $diff = $this->storecount - $this->maxsize;
499
        if ($diff < 1) {
500
            return 0;
501
        }
502
        // Reduce it by an extra 10% to avoid calling this repetitively if we are in a loop.
503
        $diff += floor($this->maxsize / 10);
504
        $this->store = array_slice($this->store, $diff, null, true);
505
        $this->storecount -= $diff;
506
        return $diff;
507
    }
508
 
509
    /**
510
     * Returns true if the user can add an instance of the store plugin.
511
     *
512
     * @return bool
513
     */
514
    public static function can_add_instance() {
515
        return false;
516
    }
517
 
518
    /**
519
     * Performs any necessary clean up when the store instance is being deleted.
520
     */
521
    public function instance_deleted() {
522
        $this->purge();
523
    }
524
 
525
    /**
526
     * Generates an instance of the cache store that can be used for testing.
527
     *
528
     * @param cache_definition $definition
529
     * @return cachestore_static
530
     */
531
    public static function initialise_test_instance(cache_definition $definition) {
532
        // Do something here perhaps.
533
        $cache = new cachestore_static('Static store');
534
        $cache->initialise($definition);
535
        return $cache;
536
    }
537
 
538
    /**
539
     * Generates the appropriate configuration required for unit testing.
540
     *
541
     * @return array Array of unit test configuration data to be used by initialise().
542
     */
543
    public static function unit_test_configuration() {
544
        return array();
545
    }
546
 
547
    /**
548
     * Returns the name of this instance.
549
     * @return string
550
     */
551
    public function my_name() {
552
        return $this->name;
553
    }
554
 
555
    /**
556
     * Finds all of the keys being stored in the cache store instance.
557
     *
558
     * @return array
559
     */
560
    public function find_all() {
561
        return array_keys($this->store);
562
    }
563
 
564
    /**
565
     * Finds all of the keys whose keys start with the given prefix.
566
     *
567
     * @param string $prefix
568
     */
569
    public function find_by_prefix($prefix) {
570
        $return = array();
571
        foreach ($this->find_all() as $key) {
572
            if (strpos($key, $prefix) === 0) {
573
                $return[] = $key;
574
            }
575
        }
576
        return $return;
577
    }
578
}