~ubuntu-branches/ubuntu/saucy/moodle/saucy

« back to all changes in this revision

Viewing changes to cache/classes/loaders.php

  • Committer: Package Import Robot
  • Author(s): Thijs Kinkhorst
  • Date: 2013-09-09 15:22:35 UTC
  • mfrom: (1.1.17) (3.1.29 sid)
  • Revision ID: package-import@ubuntu.com-20130909152235-f5g7gphaseb84qeu
Tags: 2.5.2-1
* New upstream version: 2.5.2.
  - Incorporates S3 security patch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
117
117
    /**
118
118
     * An array containing just the keys being used in the persist cache.
119
119
     * This seems redundant perhaps but is used when managing the size of the persist cache.
 
120
     * Items are added to the end of the array and the when we need to reduce the size of the cache we use the
 
121
     * key that is first on this array.
120
122
     * @var array
121
123
     */
122
124
    private $persistkeys = array();
269
271
     *      In advanced cases an array may be useful such as in situations requiring the multi-key functionality.
270
272
     * @param int $strictness One of IGNORE_MISSING | MUST_EXIST
271
273
     * @return mixed|false The data from the cache or false if the key did not exist within the cache.
272
 
     * @throws moodle_exception
 
274
     * @throws coding_exception
273
275
     */
274
276
    public function get($key, $strictness = IGNORE_MISSING) {
275
277
        // 1. Parse the key.
327
329
        }
328
330
        // 5. Validate strictness.
329
331
        if ($strictness === MUST_EXIST && $result === false) {
330
 
            throw new moodle_exception('Requested key did not exist in any cache stores and could not be loaded.');
 
332
            throw new coding_exception('Requested key did not exist in any cache stores and could not be loaded.');
331
333
        }
332
334
        // 6. Set it to the store if we got it from the loader/datasource.
333
335
        if ($setaftervalidation) {
361
363
     * @return array An array of key value pairs for the items that could be retrieved from the cache.
362
364
     *      If MUST_EXIST was used and not all keys existed within the cache then an exception will be thrown.
363
365
     *      Otherwise any key that did not exist will have a data value of false within the results.
364
 
     * @throws moodle_exception
 
366
     * @throws coding_exception
365
367
     */
366
368
    public function get_many(array $keys, $strictness = IGNORE_MISSING) {
367
369
 
418
420
            $missingkeys = array();
419
421
            foreach ($result as $key => $value) {
420
422
                if ($value === false) {
421
 
                    $missingkeys[] = ($usingloader) ? $key : $parsedkeys[$key];
 
423
                    $missingkeys[] = $parsedkeys[$key];
422
424
                }
423
425
            }
424
426
            if (!empty($missingkeys)) {
428
430
                    $resultmissing = $this->datasource->load_many_for_cache($missingkeys);
429
431
                }
430
432
                foreach ($resultmissing as $key => $value) {
431
 
                    $pkey = ($usingloader) ? $key : $keysparsed[$key];
432
 
                    $realkey = ($usingloader) ? $parsedkeys[$key] : $key;
433
 
                    $result[$pkey] = $value;
 
433
                    $result[$keysparsed[$key]] = $value;
434
434
                    if ($value !== false) {
435
 
                        $this->set($realkey, $value);
 
435
                        $this->set($key, $value);
436
436
                    }
437
437
                }
438
438
                unset($resultmissing);
451
451
        if ($strictness === MUST_EXIST) {
452
452
            foreach ($keys as $key) {
453
453
                if (!array_key_exists($key, $fullresult)) {
454
 
                    throw new moodle_exception('Not all the requested keys existed within the cache stores.');
 
454
                    throw new coding_exception('Not all the requested keys existed within the cache stores.');
455
455
                }
456
456
            }
457
457
        }
481
481
        if ($this->perfdebug) {
482
482
            cache_helper::record_cache_set($this->storetype, $this->definition->get_id());
483
483
        }
 
484
        if ($this->loader !== false) {
 
485
            // We have a loader available set it there as well.
 
486
            // We have to let the loader do its own parsing of data as it may be unique.
 
487
            $this->loader->set($key, $data);
 
488
        }
484
489
        if (is_object($data) && $data instanceof cacheable_object) {
485
490
            $data = new cache_cached_object($data);
486
491
        } else if (!is_scalar($data)) {
504
509
     * Removes references where required.
505
510
     *
506
511
     * @param stdClass|array $data
 
512
     * @return mixed What ever was put in but without any references.
507
513
     */
508
514
    protected function unref($data) {
509
515
        if ($this->definition->uses_simple_data()) {
591
597
     *      ... if they care that is.
592
598
     */
593
599
    public function set_many(array $keyvaluearray) {
 
600
        if ($this->loader !== false) {
 
601
            // We have a loader available set it there as well.
 
602
            // We have to let the loader do its own parsing of data as it may be unique.
 
603
            $this->loader->set_many($keyvaluearray);
 
604
        }
594
605
        $data = array();
595
606
        $simulatettl = $this->has_a_ttl() && !$this->store_supports_native_ttl();
596
607
        $usepersistcache = $this->is_using_persist_cache();
856
867
     * Returns the loader associated with this instance.
857
868
     *
858
869
     * @since 2.4.4
859
 
     * @return cache_loader|false
 
870
     * @return cache|false
860
871
     */
861
872
    protected function get_loader() {
862
873
        return $this->loader;
965
976
            if ($this->perfdebug) {
966
977
                cache_helper::record_cache_hit('** static persist **', $this->definition->get_id());
967
978
            }
 
979
            if ($this->persistmaxsize > 1 && $this->persistcount > 1) {
 
980
                // Check to see if this is the last item on the persist keys array.
 
981
                if (end($this->persistkeys) !== $key) {
 
982
                    // It isn't the last item.
 
983
                    // Move the item to the end of the array so that it is last to be removed.
 
984
                    unset($this->persistkeys[$key]);
 
985
                    $this->persistkeys[$key] = $key;
 
986
                }
 
987
            }
968
988
            return $result;
969
989
        } else {
970
990
            if ($this->perfdebug) {
989
1009
        $this->persistcache[$key] = $data;
990
1010
        if ($this->persistmaxsize !== false) {
991
1011
            $this->persistcount++;
 
1012
            $this->persistkeys[$key] = $key;
992
1013
            if ($this->persistcount > $this->persistmaxsize) {
993
1014
                $dropkey = array_shift($this->persistkeys);
994
1015
                unset($this->persistcache[$dropkey]);
1328
1349
     * @param string|int $key The key for the data being requested.
1329
1350
     * @param int $strictness One of IGNORE_MISSING | MUST_EXIST
1330
1351
     * @return mixed|false The data from the cache or false if the key did not exist within the cache.
1331
 
     * @throws moodle_exception
1332
1352
     */
1333
1353
    public function get($key, $strictness = IGNORE_MISSING) {
1334
1354
        if ($this->requirelockingread && $this->check_lock_state($key) === false) {
1352
1372
     * @return array An array of key value pairs for the items that could be retrieved from the cache.
1353
1373
     *      If MUST_EXIST was used and not all keys existed within the cache then an exception will be thrown.
1354
1374
     *      Otherwise any key that did not exist will have a data value of false within the results.
1355
 
     * @throws moodle_exception
 
1375
     * @throws coding_exception
1356
1376
     */
1357
1377
    public function get_many(array $keys, $strictness = IGNORE_MISSING) {
1358
1378
        if ($this->requirelockingread) {
1446
1466
 * @todo we should support locking in the session as well. Should be pretty simple to set up.
1447
1467
 *
1448
1468
 * @internal don't use me directly.
 
1469
 * @method cache_store|cache_is_searchable get_store() Returns the cache store which must implement both cache_is_searchable.
1449
1470
 *
1450
1471
 * @package    core
1451
1472
 * @category   cache
1483
1504
    const KEY_PREFIX = 'sess_';
1484
1505
 
1485
1506
    /**
 
1507
     * This is the key used to track last access.
 
1508
     */
 
1509
    const LASTACCESS = '__lastaccess__';
 
1510
 
 
1511
    /**
1486
1512
     * Override the cache::construct method.
1487
1513
     *
1488
1514
     * This function gets overriden so that we can process any invalidation events if need be.
1495
1521
     * @param cache_definition $definition
1496
1522
     * @param cache_store $store
1497
1523
     * @param cache_loader|cache_data_source $loader
1498
 
     * @return void
1499
1524
     */
1500
1525
    public function __construct(cache_definition $definition, cache_store $store, $loader = null) {
1501
1526
        // First up copy the loadeduserid to the current user id.
1502
1527
        $this->currentuserid = self::$loadeduserid;
1503
1528
        parent::__construct($definition, $store, $loader);
 
1529
 
 
1530
        // This will trigger check tracked user. If this gets removed a call to that will need to be added here in its place.
 
1531
        $this->set(self::LASTACCESS, cache::now());
 
1532
 
1504
1533
        if ($definition->has_invalidation_events()) {
1505
1534
            $lastinvalidation = $this->get('lastsessioninvalidation');
1506
1535
            if ($lastinvalidation === false) {
1550
1579
    }
1551
1580
 
1552
1581
    /**
 
1582
     * Sets the session id for the loader.
 
1583
     */
 
1584
    protected function set_session_id() {
 
1585
        $this->sessionid = preg_replace('#[^a-zA-Z0-9_]#', '_', session_id());
 
1586
    }
 
1587
 
 
1588
    /**
 
1589
     * Returns the prefix used for all keys.
 
1590
     * @return string
 
1591
     */
 
1592
    protected function get_key_prefix() {
 
1593
        return 'u'.$this->currentuserid.'_'.$this->sessionid;
 
1594
    }
 
1595
 
 
1596
    /**
1553
1597
     * Parses the key turning it into a string (or array is required) suitable to be passed to the cache store.
1554
1598
     *
1555
1599
     * This function is called for every operation that uses keys. For this reason we use this function to also check
1561
1605
     * @return string|array String unless the store supports multi-identifiers in which case an array if returned.
1562
1606
     */
1563
1607
    protected function parse_key($key) {
1564
 
        if ($key === 'lastaccess') {
1565
 
            $key = '__lastaccess__';
 
1608
        $prefix = $this->get_key_prefix();
 
1609
        if ($key === self::LASTACCESS) {
 
1610
            return $key.$prefix;
1566
1611
        }
1567
 
        return 'sess_'.parent::parse_key($key);
 
1612
        return $prefix.'_'.parent::parse_key($key);
1568
1613
    }
1569
1614
 
1570
1615
    /**
1571
1616
     * Check that this cache instance is tracking the current user.
1572
1617
     */
1573
1618
    protected function check_tracked_user() {
1574
 
        if (isset($_SESSION['USER']->id)) {
 
1619
        if (isset($_SESSION['USER']->id) && $_SESSION['USER']->id !== null) {
1575
1620
            // Get the id of the current user.
1576
1621
            $new = $_SESSION['USER']->id;
1577
1622
        } else {
1585
1630
                // This way we don't bloat the session.
1586
1631
                $this->purge();
1587
1632
                // Update the session id just in case!
1588
 
                $this->sessionid = session_id();
 
1633
                $this->set_session_id();
1589
1634
            }
1590
1635
            self::$loadeduserid = $new;
1591
1636
            $this->currentuserid = $new;
1592
1637
        } else if ($new !== $this->currentuserid) {
1593
1638
            // The current user matches the loaded user but not the user last used by this cache.
1594
 
            $this->purge();
 
1639
            $this->purge_current_user();
1595
1640
            $this->currentuserid = $new;
1596
1641
            // Update the session id just in case!
1597
 
            $this->sessionid = session_id();
1598
 
        }
1599
 
    }
1600
 
 
1601
 
    /**
1602
 
     * Gets the session data.
1603
 
     *
1604
 
     * @param bool $force If true the session data will be loaded from the store again.
1605
 
     * @return array An array of session data.
1606
 
     */
1607
 
    protected function get_session_data($force = false) {
1608
 
        if ($this->sessionid === null) {
1609
 
            $this->sessionid = session_id();
1610
 
        }
1611
 
        if (is_array($this->session) && !$force) {
1612
 
            return $this->session;
1613
 
        }
1614
 
        $session = parent::get($this->sessionid);
1615
 
        if ($session === false) {
1616
 
            $session = array();
1617
 
        }
1618
 
        // We have to write here to ensure that the lastaccess time is recorded.
1619
 
        // And also in order to ensure the session entry exists as when we save it on __destruct
1620
 
        // $CFG is likely to have already been destroyed.
1621
 
        $this->save_session($session);
1622
 
        return $this->session;
1623
 
    }
1624
 
 
1625
 
    /**
1626
 
     * Saves the session data.
1627
 
     *
1628
 
     * This function also updates the last access time.
1629
 
     *
1630
 
     * @param array $session
1631
 
     * @return bool
1632
 
     */
1633
 
    protected function save_session(array $session) {
1634
 
        $session['lastaccess'] = time();
1635
 
        $this->session = $session;
1636
 
        return parent::set($this->sessionid, $this->session);
 
1642
            $this->set_session_id();
 
1643
        }
 
1644
    }
 
1645
 
 
1646
    /**
 
1647
     * Purges the session cache of all data belonging to the current user.
 
1648
     */
 
1649
    public function purge_current_user() {
 
1650
        $keys = $this->get_store()->find_all($this->get_key_prefix());
 
1651
        $this->get_store()->delete_many($keys);
1637
1652
    }
1638
1653
 
1639
1654
    /**
1644
1659
     *      In advanced cases an array may be useful such as in situations requiring the multi-key functionality.
1645
1660
     * @param int $strictness One of IGNORE_MISSING | MUST_EXIST
1646
1661
     * @return mixed|false The data from the cache or false if the key did not exist within the cache.
1647
 
     * @throws moodle_exception
 
1662
     * @throws coding_exception
1648
1663
     */
1649
1664
    public function get($key, $strictness = IGNORE_MISSING) {
1650
1665
        // Check the tracked user.
1652
1667
        // 2. Parse the key.
1653
1668
        $parsedkey = $this->parse_key($key);
1654
1669
        // 3. Get it from the store.
1655
 
        $result = false;
1656
 
        $session = $this->get_session_data();
1657
 
        if (array_key_exists($parsedkey, $session)) {
1658
 
            $result = $session[$parsedkey];
 
1670
        $result = $this->get_store()->get($parsedkey);
 
1671
        if ($result !== false) {
1659
1672
            if ($result instanceof cache_ttl_wrapper) {
1660
1673
                if ($result->has_expired()) {
1661
1674
                    $this->get_store()->delete($parsedkey);
1669
1682
            }
1670
1683
        }
1671
1684
        // 4. Load if from the loader/datasource if we don't already have it.
1672
 
        $setaftervalidation = false;
1673
1685
        if ($result === false) {
1674
1686
            if ($this->perfdebug) {
1675
 
                cache_helper::record_cache_miss('**static session**', $this->get_definition()->get_id());
 
1687
                cache_helper::record_cache_miss($this->storetype, $this->get_definition()->get_id());
1676
1688
            }
1677
1689
            if ($this->get_loader() !== false) {
1678
1690
                // We must pass the original (unparsed) key to the next loader in the chain.
1682
1694
            } else if ($this->get_datasource() !== false) {
1683
1695
                $result = $this->get_datasource()->load_for_cache($key);
1684
1696
            }
1685
 
            $setaftervalidation = ($result !== false);
 
1697
            // 5. Set it to the store if we got it from the loader/datasource.
 
1698
            if ($result !== false) {
 
1699
                $this->set($key, $result);
 
1700
            }
1686
1701
        } else if ($this->perfdebug) {
1687
 
            cache_helper::record_cache_hit('**static session**', $this->get_definition()->get_id());
 
1702
            cache_helper::record_cache_hit($this->storetype, $this->get_definition()->get_id());
1688
1703
        }
1689
1704
        // 5. Validate strictness.
1690
1705
        if ($strictness === MUST_EXIST && $result === false) {
1691
 
            throw new moodle_exception('Requested key did not exist in any cache stores and could not be loaded.');
1692
 
        }
1693
 
        // 6. Set it to the store if we got it from the loader/datasource.
1694
 
        if ($setaftervalidation) {
1695
 
            $this->set($key, $result);
1696
 
        }
1697
 
        // 7. Make sure we don't pass back anything that could be a reference.
 
1706
            throw new coding_exception('Requested key did not exist in any cache stores and could not be loaded.');
 
1707
        }
 
1708
        // 6. Make sure we don't pass back anything that could be a reference.
1698
1709
        //    We don't want people modifying the data in the cache.
1699
1710
        if (!is_scalar($result)) {
1700
1711
            // If data is an object it will be a reference.
1725
1736
     */
1726
1737
    public function set($key, $data) {
1727
1738
        $this->check_tracked_user();
 
1739
        $loader = $this->get_loader();
 
1740
        if ($loader !== false) {
 
1741
            // We have a loader available set it there as well.
 
1742
            // We have to let the loader do its own parsing of data as it may be unique.
 
1743
            $loader->set($key, $data);
 
1744
        }
1728
1745
        if ($this->perfdebug) {
1729
 
            cache_helper::record_cache_set('**static session**', $this->get_definition()->get_id());
 
1746
            cache_helper::record_cache_set($this->storetype, $this->get_definition()->get_id());
1730
1747
        }
1731
1748
        if (is_object($data) && $data instanceof cacheable_object) {
1732
1749
            $data = new cache_cached_object($data);
1738
1755
            $data = $this->unref($data);
1739
1756
        }
1740
1757
        // We dont' support native TTL here as we consolidate data for sessions.
1741
 
        if ($this->has_a_ttl()) {
 
1758
        if ($this->has_a_ttl() && !$this->store_supports_native_ttl()) {
1742
1759
            $data = new cache_ttl_wrapper($data, $this->get_definition()->get_ttl());
1743
1760
        }
1744
 
        $session = $this->get_session_data();
1745
 
        $session[$this->parse_key($key)] = $data;
1746
 
        return $this->save_session($session);
 
1761
        return $this->get_store()->set($this->parse_key($key), $data);
1747
1762
    }
1748
1763
 
1749
1764
    /**
1755
1770
     * @return bool True of success, false otherwise.
1756
1771
     */
1757
1772
    public function delete($key, $recurse = true) {
1758
 
        $this->check_tracked_user();
1759
1773
        $parsedkey = $this->parse_key($key);
1760
1774
        if ($recurse && $this->get_loader() !== false) {
1761
1775
            // Delete from the bottom of the stack first.
1762
1776
            $this->get_loader()->delete($key, $recurse);
1763
1777
        }
1764
 
        $session = $this->get_session_data();
1765
 
        unset($session[$parsedkey]);
1766
 
        return $this->save_session($session);
 
1778
        return $this->get_store()->delete($parsedkey);
1767
1779
    }
1768
1780
 
1769
1781
    /**
1782
1794
     * @return array An array of key value pairs for the items that could be retrieved from the cache.
1783
1795
     *      If MUST_EXIST was used and not all keys existed within the cache then an exception will be thrown.
1784
1796
     *      Otherwise any key that did not exist will have a data value of false within the results.
1785
 
     * @throws moodle_exception
 
1797
     * @throws coding_exception
1786
1798
     */
1787
1799
    public function get_many(array $keys, $strictness = IGNORE_MISSING) {
1788
1800
        $this->check_tracked_user();
 
1801
        $parsedkeys = array();
 
1802
        $keymap = array();
 
1803
        foreach ($keys as $key) {
 
1804
            $parsedkey = $this->parse_key($key);
 
1805
            $parsedkeys[$key] = $parsedkey;
 
1806
            $keymap[$parsedkey] = $key;
 
1807
        }
 
1808
        $result = $this->get_store()->get_many($parsedkeys);
1789
1809
        $return = array();
1790
 
        foreach ($keys as $key) {
1791
 
            $return[$key] = $this->get($key, $strictness);
1792
 
        }
 
1810
        $missingkeys = array();
 
1811
        $hasmissingkeys = false;
 
1812
        foreach ($result as $parsedkey => $value) {
 
1813
            $key = $keymap[$parsedkey];
 
1814
            if ($value instanceof cache_ttl_wrapper) {
 
1815
                /* @var cache_ttl_wrapper $value */
 
1816
                if ($value->has_expired()) {
 
1817
                    $this->delete($keymap[$parsedkey]);
 
1818
                    $value = false;
 
1819
                } else {
 
1820
                    $value = $value->data;
 
1821
                }
 
1822
            }
 
1823
            if ($value instanceof cache_cached_object) {
 
1824
                /* @var cache_cached_object $value */
 
1825
                $value = $value->restore_object();
 
1826
            }
 
1827
            $return[$key] = $value;
 
1828
            if ($value === false) {
 
1829
                $hasmissingkeys = true;
 
1830
                $missingkeys[$parsedkey] = $key;
 
1831
            }
 
1832
        }
 
1833
        if ($hasmissingkeys) {
 
1834
            // We've got missing keys - we've got to check any loaders or data sources.
 
1835
            $loader = $this->get_loader();
 
1836
            $datasource = $this->get_datasource();
 
1837
            if ($loader !== false) {
 
1838
                foreach ($loader->get_many($missingkeys) as $key => $value) {
 
1839
                    if ($value !== false) {
 
1840
                        $return[$key] = $value;
 
1841
                        unset($missingkeys[$parsedkeys[$key]]);
 
1842
                    }
 
1843
                }
 
1844
            }
 
1845
            $hasmissingkeys = count($missingkeys) > 0;
 
1846
            if ($datasource !== false && $hasmissingkeys) {
 
1847
                // We're still missing keys but we've got a datasource.
 
1848
                foreach ($datasource->load_many_for_cache($missingkeys) as $key => $value) {
 
1849
                    if ($value !== false) {
 
1850
                        $return[$key] = $value;
 
1851
                        unset($missingkeys[$parsedkeys[$key]]);
 
1852
                    }
 
1853
                }
 
1854
                $hasmissingkeys = count($missingkeys) > 0;
 
1855
            }
 
1856
        }
 
1857
        if ($hasmissingkeys && $strictness === MUST_EXIST) {
 
1858
            throw new coding_exception('Requested key did not exist in any cache stores and could not be loaded.');
 
1859
        }
 
1860
 
1793
1861
        return $return;
 
1862
 
1794
1863
    }
1795
1864
 
1796
1865
    /**
1802
1871
     * @return int The number of items successfully deleted.
1803
1872
     */
1804
1873
    public function delete_many(array $keys, $recurse = true) {
1805
 
        $this->check_tracked_user();
1806
1874
        $parsedkeys = array_map(array($this, 'parse_key'), $keys);
1807
1875
        if ($recurse && $this->get_loader() !== false) {
1808
1876
            // Delete from the bottom of the stack first.
1809
1877
            $this->get_loader()->delete_many($keys, $recurse);
1810
1878
        }
1811
 
        $session = $this->get_session_data();
1812
 
        foreach ($parsedkeys as $parsedkey) {
1813
 
            unset($session[$parsedkey]);
1814
 
        }
1815
 
        $this->save_session($session);
1816
 
        return count($keys);
 
1879
        return $this->get_store()->delete_many($parsedkeys);
1817
1880
    }
1818
1881
 
1819
1882
    /**
1841
1904
     */
1842
1905
    public function set_many(array $keyvaluearray) {
1843
1906
        $this->check_tracked_user();
1844
 
        $session = $this->get_session_data();
1845
 
        $simulatettl = $this->has_a_ttl();
 
1907
        $loader = $this->get_loader();
 
1908
        if ($loader !== false) {
 
1909
            // We have a loader available set it there as well.
 
1910
            // We have to let the loader do its own parsing of data as it may be unique.
 
1911
            $loader->set_many($keyvaluearray);
 
1912
        }
 
1913
        $data = array();
 
1914
        $definitionid = $this->get_definition()->get_ttl();
 
1915
        $simulatettl = $this->has_a_ttl() && !$this->store_supports_native_ttl();
1846
1916
        foreach ($keyvaluearray as $key => $value) {
1847
1917
            if (is_object($value) && $value instanceof cacheable_object) {
1848
1918
                $value = new cache_cached_object($value);
1854
1924
                $value = $this->unref($value);
1855
1925
            }
1856
1926
            if ($simulatettl) {
1857
 
                $value = new cache_ttl_wrapper($value, $this->get_definition()->get_ttl());
 
1927
                $value = new cache_ttl_wrapper($value, $definitionid);
1858
1928
            }
1859
 
            $parsedkey = $this->parse_key($key);
1860
 
            $session[$parsedkey] = $value;
 
1929
            $data[$key] = array(
 
1930
                'key' => $this->parse_key($key),
 
1931
                'value' => $value
 
1932
            );
1861
1933
        }
1862
1934
        if ($this->perfdebug) {
1863
 
            cache_helper::record_cache_set($this->storetype, $this->get_definition()->get_id());
 
1935
            cache_helper::record_cache_set($this->storetype, $definitionid);
1864
1936
        }
1865
 
        $this->save_session($session);
1866
 
        return count($keyvaluearray);
 
1937
        return $this->get_store()->set_many($data);
1867
1938
    }
1868
1939
 
1869
1940
    /**
1872
1943
     * @return bool True on success, false otherwise
1873
1944
     */
1874
1945
    public function purge() {
1875
 
        // 1. Purge the session object.
1876
 
        $this->session = array();
1877
 
        // 2. Delete the record for this users session from the store.
1878
 
        $this->get_store()->delete($this->sessionid);
1879
 
        // 3. Optionally purge any stacked loaders in the same way.
 
1946
        $this->get_store()->purge();
1880
1947
        if ($this->get_loader()) {
1881
 
            $this->get_loader()->delete($this->sessionid);
 
1948
            $this->get_loader()->purge();
1882
1949
        }
1883
1950
        return true;
1884
1951
    }
1907
1974
    public function has($key, $tryloadifpossible = false) {
1908
1975
        $this->check_tracked_user();
1909
1976
        $parsedkey = $this->parse_key($key);
1910
 
        $session = $this->get_session_data();
1911
 
        $has = false;
1912
 
        if ($this->has_a_ttl()) {
 
1977
        $store = $this->get_store();
 
1978
        if ($this->has_a_ttl() && !$this->store_supports_native_ttl()) {
1913
1979
            // The data has a TTL and the store doesn't support it natively.
1914
1980
            // We must fetch the data and expect a ttl wrapper.
1915
 
            if (array_key_exists($parsedkey, $session)) {
1916
 
                $data = $session[$parsedkey];
1917
 
                $has = ($data instanceof cache_ttl_wrapper && !$data->has_expired());
1918
 
            }
 
1981
            $data = $store->get($parsedkey);
 
1982
            $has = ($data instanceof cache_ttl_wrapper && !$data->has_expired());
 
1983
        } else if (!$this->store_supports_key_awareness()) {
 
1984
            // The store doesn't support key awareness, get the data and check it manually... puke.
 
1985
            // Either no TTL is set of the store supports its handling natively.
 
1986
            $data = $store->get($parsedkey);
 
1987
            $has = ($data !== false);
1919
1988
        } else {
1920
 
            $has = array_key_exists($parsedkey, $session);
 
1989
            // The store supports key awareness, this is easy!
 
1990
            // Either no TTL is set of the store supports its handling natively.
 
1991
            /* @var cache_store|cache_is_key_aware $store */
 
1992
            $has = $store->has($parsedkey);
1921
1993
        }
1922
1994
        if (!$has && $tryloadifpossible) {
 
1995
            $result = null;
1923
1996
            if ($this->get_loader() !== false) {
1924
 
                $result = $this->get_loader()->get($key);
 
1997
                $result = $this->get_loader()->get($parsedkey);
1925
1998
            } else if ($this->get_datasource() !== null) {
1926
1999
                $result = $this->get_datasource()->load_for_cache($key);
1927
2000
            }
1948
2021
     */
1949
2022
    public function has_all(array $keys) {
1950
2023
        $this->check_tracked_user();
1951
 
        $session = $this->get_session_data();
1952
 
        foreach ($keys as $key) {
1953
 
            $has = false;
1954
 
            $parsedkey = $this->parse_key($key);
1955
 
            if ($this->has_a_ttl()) {
1956
 
                // The data has a TTL and the store doesn't support it natively.
1957
 
                // We must fetch the data and expect a ttl wrapper.
1958
 
                if (array_key_exists($parsedkey, $session)) {
1959
 
                    $data = $session[$parsedkey];
1960
 
                    $has = ($data instanceof cache_ttl_wrapper && !$data->has_expired());
 
2024
        if (($this->has_a_ttl() && !$this->store_supports_native_ttl()) || !$this->store_supports_key_awareness()) {
 
2025
            foreach ($keys as $key) {
 
2026
                if (!$this->has($key)) {
 
2027
                    return false;
1961
2028
                }
1962
 
            } else {
1963
 
                $has = array_key_exists($parsedkey, $session);
1964
 
            }
1965
 
            if (!$has) {
1966
 
                return false;
1967
 
            }
 
2029
            }
 
2030
            return true;
1968
2031
        }
1969
 
        return true;
 
2032
        // The cache must be key aware and if support native ttl if it a ttl is set.
 
2033
        /* @var cache_store|cache_is_key_aware $store */
 
2034
        $store = $this->get_store();
 
2035
        return $store->has_all(array_map(array($this, 'parse_key'), $keys));
1970
2036
    }
1971
2037
 
1972
2038
    /**
1983
2049
     * @return bool True if the cache has at least one of the given keys
1984
2050
     */
1985
2051
    public function has_any(array $keys) {
1986
 
        $this->check_tracked_user();
1987
 
        $session = $this->get_session_data();
1988
 
        foreach ($keys as $key) {
1989
 
            $has = false;
1990
 
            $parsedkey = $this->parse_key($key);
1991
 
            if ($this->has_a_ttl()) {
1992
 
                // The data has a TTL and the store doesn't support it natively.
1993
 
                // We must fetch the data and expect a ttl wrapper.
1994
 
                if (array_key_exists($parsedkey, $session)) {
1995
 
                    $data = $session[$parsedkey];
1996
 
                    $has = ($data instanceof cache_ttl_wrapper && !$data->has_expired());
 
2052
        if (($this->has_a_ttl() && !$this->store_supports_native_ttl()) || !$this->store_supports_key_awareness()) {
 
2053
            foreach ($keys as $key) {
 
2054
                if ($this->has($key)) {
 
2055
                    return true;
1997
2056
                }
1998
 
            } else {
1999
 
                $has = array_key_exists($parsedkey, $session);
2000
 
            }
2001
 
            if ($has) {
2002
 
                return true;
2003
 
            }
 
2057
            }
 
2058
            return false;
2004
2059
        }
2005
 
        return false;
 
2060
        /* @var cache_store|cache_is_key_aware $store */
 
2061
        $store = $this->get_store();
 
2062
        return $store->has_any(array_map(array($this, 'parse_key'), $keys));
2006
2063
    }
2007
2064
 
2008
2065
    /**