Passed
Push — master ( 8a140a...6eed6d )
by Andreas
26:05
created

midcom_services_cache_module_memcache   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 97
Duplicated Lines 0 %

Test Coverage

Coverage 74.36%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 32
dl 0
loc 97
ccs 29
cts 39
cp 0.7436
rs 10
c 2
b 0
f 0
wmc 12

7 Methods

Rating   Name   Duplication   Size   Complexity  
A prepare_memcached() 0 10 2
A lookup_parent_data() 0 3 1
A __construct() 0 4 1
A invalidate() 0 8 3
A update_parent_data() 0 3 1
A put() 0 14 2
A get() 0 8 2
1
<?php
2
/**
3
 * @package midcom.services
4
 * @author The Midgard Project, http://www.midgard-project.org
5
 * @copyright The Midgard Project, http://www.midgard-project.org
6
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
7
 */
8
9
use Symfony\Component\Cache\Adapter\AdapterInterface;
10
use Symfony\Component\Cache\CacheItem;
11
12
/**
13
 * The Memory caching system is geared to hold needed information available quickly.
14
 * There are a number of limitations you have to deal with, when working with the
15
 * Memory Cache.
16
 *
17
 * Number One, you cannot put arbitrary keys into the cache. Since the memcached
18
 * php extension does not support key listings, you are bound to use MidCOM object
19
 * GUIDs as cache keys, whatever you do. To allow for different subsystems of the
20
 * Framework to share the cache, I have introduce "Data groups", which are suffixes
21
 * for the actual cache information. Thus, all keys in the cache follow a
22
 * "{$datagroup}-{$guid}" naming scheme. These groups need to be registered in the
23
 * MidCOM configuration key <i>cache_module_memcache_data_groups</i>.
24
 *
25
 * Number Two, it is entirely possible (as it is the default), that the memcache
26
 * is actually not available, as no memcache daemon has been found.  This is
27
 * controlled by the <i>cache_module_memcache_backend</i> configuration option,
28
 * which tries to auto-detect a sensible default. If it is set to the name of a caching module
29
 * it will actually start caching. Otherwise it will silently ignore
30
 * put requests, and reports all keys as not existent.
31
 *
32
 * Number Three, as at least memcache's contains() check isn't working on some machines, key
33
 * values of false are forbidden, as they are used to check a keys existence
34
 * during the get cycle. You should also avoid null and 0 members, if possible,
35
 * they could naturally be error prone if you start forgetting about the typed
36
 * comparisons.
37
 *
38
 * <b>Special functionality</b>
39
 *
40
 * - Interface to the PARENT caching group, has a few simple shortcuts to the
41
 *   access the available information.
42
 *
43
 * @package midcom.services
44
 */
45
class midcom_services_cache_module_memcache extends midcom_services_cache_module
46
{
47
    /**
48
     * List of known data groups. See the class introduction for details.
49
     *
50
     * @var array
51
     */
52
    private $_data_groups = [];
53
54 2
    public static function prepare_memcached(array $config) : ?Memcached
55
    {
56 2
        $host = $config['host'] ?? 'localhost';
57 2
        $port = $config['port'] ?? 11211;
58 2
        $memcached = new Memcached;
59 2
        if (!$memcached->addServer($host, $port)) {
60
            return null;
61
        }
62
63 2
        return $memcached;
64
    }
65
66
    public function __construct(midcom_config $config, AdapterInterface $backend)
67
    {
68
        parent::__construct($backend);
69
        $this->_data_groups = $config->get_array('cache_module_memcache_data_groups');
70
    }
71
72
    /**
73
     * {@inheritDoc}
74
     */
75 303
    public function invalidate(string $guid, $object = null)
76
    {
77 303
        foreach ($this->_data_groups as $group) {
78 303
            if ($group == 'ACL') {
79 303
                $this->backend->delete("{$group}-SELF-{$guid}");
0 ignored issues
show
Bug introduced by
The method delete() does not exist on Symfony\Component\Cache\Adapter\AdapterInterface. Did you maybe mean deleteItem()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

79
                $this->backend->/** @scrutinizer ignore-call */ 
80
                                delete("{$group}-SELF-{$guid}");

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
80 303
                $this->backend->delete("{$group}-CONTENT-{$guid}");
81
            } else {
82 303
                $this->backend->delete("{$group}-{$guid}");
83
            }
84
        }
85 303
    }
86
87
    /**
88
     * Looks up a value in the cache and returns it. Not existent
89
     * keys are caught in this call as well
90
     *
91
     * @return mixed The cached value on success, false on failure.
92
     */
93 316
    public function get(string $data_group, string $key)
94
    {
95 316
        $item = $this->backend->getItem("{$data_group}-{$key}");
96 316
        if ($item->isHit()) {
97
            return $item->get();
98
        }
99
100 316
        return false;
101
    }
102
103
    /**
104
     * Sets a given key in the cache. If the data group is unknown, a Warning-Level error
105
     * is logged and putting is denied.
106
     */
107 315
    public function put(string $data_group, string $key, $data, int $timeout = null)
108
    {
109 315
        if (!in_array($data_group, $this->_data_groups)) {
110
            debug_add("Tried to add data to the unknown data group {$data_group}, cannot do that.", MIDCOM_LOG_WARN);
111
            debug_print_r('Known data groups:', $this->_data_groups);
112
            debug_print_function_stack('We were called from here:');
113
            return;
114
        }
115 315
        $item = $this->backend
116 315
            ->getItem("{$data_group}-{$key}")
117 315
            ->set($data)
118 315
            ->expiresAfter($timeout);
119
120 315
        $this->backend->save($item);
121 315
    }
122
123
    /**
124
     * This is a little helper that tries to look up a GUID in the memory
125
     * cache's PARENT data group. If it is not found, false is returned.
126
     * If the object has no parent, the array value is null
127
     *
128
     * @return array|false The classname => GUID pair or false when nothing is in cache
129
     */
130 300
    public function lookup_parent_data(string $guid)
131
    {
132 300
        return $this->get('PARENT', $guid);
133
    }
134
135
    /**
136
     * This is a little helper that saves a parent GUID and class in the memory
137
     * cache's PARENT data group.
138
     */
139 300
    public function update_parent_data(string $object_guid, array $parent_data)
140
    {
141 300
        $this->put('PARENT', $object_guid, $parent_data);
142 300
    }
143
}
144