Passed
Push — master ( ae9760...35700c )
by Andreas
17:19
created

midcom_services_cache_module_nap::put_node()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3.009

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 2
dl 0
loc 14
ccs 9
cts 10
cp 0.9
crap 3.009
rs 9.9666
c 0
b 0
f 0
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
/**
10
 * This is the NAP caching module. It provides the basic management functionality
11
 * for the various backend databases which will be created based on the root topic of the
12
 * NAP trees.
13
 *
14
 * The actual handling of the various dbs is done with Doctrine Cache, this
15
 * class is responsible for the creation of backend instances and invalidation for NAP
16
 * cache objects. (Which implies that it is fully aware of the data structures
17
 * stored in the cache.)
18
 *
19
 * All entries are indexed by their Midgard Object GUID. The entries in the NAP cache
20
 * basically resemble the arrays within the NAP backend node/leaf cache
21
 *
22
 * NAP caches can be shared over multiple sites, as all site specific data (like
23
 * site prefixes) are evaluated during runtime.
24
 *
25
 * Most of the cache update work is done in midcom_helper_nav_backend,
26
 * so you should look there for details about the caching strategy.
27
 *
28
 * @see midcom_helper_nav_backend
29
 *
30
 * @package midcom.services
31
 */
32
class midcom_services_cache_module_nap extends midcom_services_cache_module
33
{
34
    /**
35
     * The cache backend instance to use.
36
     *
37
     * @var Doctrine\Common\Cache\CacheProvider
38
     */
39
    private $_cache;
40
41
    /**
42
     * Initialization event handler.
43
     *
44
     * It will load the cache backends for the current MidCOM topic.
45
     *
46
     * Initializes the backend configuration.
47
     */
48
    public function __construct(midcom_config $config)
49
    {
50
        parent::__construct();
51
        if ($driver = $config->get('cache_module_memcache_backend')) {
52
            $backend_config = $config->get('cache_module_memcache_backend_config');
53
            $backend_config['driver'] = $driver;
54
            $this->_cache = $this->_create_backend('module_nap', $backend_config);
0 ignored issues
show
Bug introduced by
It seems like $backend_config can also be of type null; however, parameter $config of midcom_services_cache_module::_create_backend() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

54
            $this->_cache = $this->_create_backend('module_nap', /** @scrutinizer ignore-type */ $backend_config);
Loading history...
55
        }
56
    }
57
58
    /**
59
     * {@inheritDoc}
60
     */
61 288
    public function invalidate($guid, $object = null)
62
    {
63 288
        $napobject = $this->get_guid($guid);
64
65 288
        if (!$napobject) {
66
            // The object itself is not in cache, but it still might have a parent that
67
            // needs invalidating (f.x. if it is newly-created or was moved from outside the tree)
68 287
            $napobject = $this->_load_from_guid($guid, $object);
69 287
            if (!$napobject) {
70
                // We couldn't load the object (because it's deleted f.x.) or it is not in NAP.
71
                // Either way, there is nothing more we can do here.
72 262
                return;
73
            }
74
        }
75
76 98
        if ($napobject[MIDCOM_NAV_TYPE] == 'leaf') {
77 2
            $cached_node_id = $napobject[MIDCOM_NAV_NODEID];
78
            // Get parent from DB and compare to catch moves
79 2
            if ($parent = $napobject[MIDCOM_NAV_OBJECT]->get_parent()) {
80 2
                $parent_entry = $this->get_guid($parent->guid);
81 2
                if (   $parent_entry
82 2
                    && $parent_entry[MIDCOM_NAV_ID] != $cached_node_id) {
83
                    $this->_cache->delete($this->_prefix . '-' . $parent_entry[MIDCOM_NAV_ID] . '-leaves');
84
                }
85
            }
86 2
            if (!empty($napobject[MIDCOM_NAV_GUID])) {
87 2
                $this->_cache->delete($this->_prefix . '-' . $napobject[MIDCOM_NAV_GUID]);
88
            }
89
        } else {
90 98
            $cached_node_id = $napobject[MIDCOM_NAV_ID];
91
92
            //Invalidate subnode cache for the (cached) parent
93 98
            $parent_id = $napobject[MIDCOM_NAV_NODEID];
94 98
            $parent_entry = $this->get_node($parent_id);
95
96 98
            if (   $parent_entry
97 98
                && array_key_exists(MIDCOM_NAV_SUBNODES, $parent_entry)) {
98 2
                unset($parent_entry[MIDCOM_NAV_SUBNODES]);
99 2
                $this->put_node($parent_id, $parent_entry);
100
            }
101
102
            //Cross-check parent value from object to detect topic moves
103 98
            if ($parent = $napobject[MIDCOM_NAV_OBJECT]->get_parent()) {
104 24
                $parent_entry_from_object = $this->get_guid($parent->guid);
105
106 24
                if (    !empty($parent_entry_from_object[MIDCOM_NAV_ID])
107 24
                     && !empty($parent_entry[MIDCOM_NAV_ID])
108 24
                     && $parent_entry_from_object[MIDCOM_NAV_ID] != $parent_entry[MIDCOM_NAV_ID]) {
109
                    unset($parent_entry_from_object[MIDCOM_NAV_SUBNODES]);
110
                    $this->put_node($parent_entry_from_object[MIDCOM_NAV_ID], $parent_entry_from_object);
111
                }
112
            }
113
        }
114
115 98
        $leaves_key = "{$cached_node_id}-leaves";
116
117 98
        $this->_cache->delete("{$this->_prefix}-{$cached_node_id}");
118 98
        $this->_cache->delete($this->_prefix . '-' . $napobject[MIDCOM_NAV_GUID]);
119 98
        $this->_cache->delete("{$this->_prefix}-{$leaves_key}");
120 98
    }
121
122 287
    private function _load_from_guid(string $guid, $object)
123
    {
124 287
        $napobject = false;
125
        try {
126 287
            if (!is_object($object)) {
127 119
                $object = midcom::get()->dbfactory->get_object_by_guid($guid);
128
            }
129 287
            $nav = new midcom_helper_nav;
130 287
            if ($object instanceof midcom_db_topic) {
131 93
                $napobject = $nav->get_node($object->id);
132 245
            } elseif (   ($node = $nav->find_closest_topic($object))
133 245
                      && $nodeobject = $nav->get_node($node->id)) {
134 287
                $napobject = $nav->get_leaf($nodeobject[MIDCOM_NAV_ID] . '-' . $object->id);
135
            }
136
        } catch (midcom_error $e) {
137
            $e->log();
138
        }
139 287
        return $napobject;
140
    }
141
142
    /**
143
     * Looks up a node in the cache and returns it. Not existent
144
     * keys are caught in this call as well, so you do not need
145
     * to call exists first.
146
     *
147
     * @param string $key The key to look up.
148
     * @return mixed The cached value on success, false on failure.
149
     */
150 143
    public function get_node($key)
151
    {
152 143
        if ($this->_cache === null) {
153
            return false;
154
        }
155 143
        $lang_id = midcom::get()->i18n->get_current_language();
156 143
        $result = $this->_cache->fetch("{$this->_prefix}-{$key}");
157 143
        if (   !is_array($result)
158 143
            || !isset($result[$lang_id])) {
159 127
            return false;
160
        }
161
162 70
        return $result[$lang_id];
163
    }
164
165
    /**
166
     * Looks up a node in the cache and returns it. Not existent
167
     * keys are caught in this call as well, so you do not need
168
     * to call exists first.
169
     *
170
     * @param string $key The key to look up.
171
     * @return mixed The cached value on success, false on failure.
172
     */
173 40
    public function get_leaves($key)
174
    {
175 40
        if ($this->_cache === null) {
176
            return false;
177
        }
178
179 40
        $lang_id = midcom::get()->i18n->get_current_language();
180 40
        $result = $this->_cache->fetch("{$this->_prefix}-{$key}");
181 40
        if (   !is_array($result)
182 40
            || !isset($result[$lang_id])) {
183 37
            return false;
184
        }
185
186 4
        return $result[$lang_id];
187
    }
188
189
    /**
190
     * Sets a given node key in the cache.
191
     *
192
     * @param string $key The key to look up.
193
     * @param mixed $data The data to store.
194
     */
195 111
    public function put_node($key, $data)
196
    {
197 111
        if ($this->_cache === null) {
198
            return;
199
        }
200
201 111
        $lang_id = midcom::get()->i18n->get_current_language();
202 111
        $result = $this->_cache->fetch("{$this->_prefix}-{$key}");
203 111
        if (!is_array($result)) {
204 109
            $result = [];
205
        }
206 111
        $result[$lang_id] = $data;
207 111
        $this->_cache->save("{$this->_prefix}-{$key}", $result);
208 111
        $this->_cache->save($this->_prefix . '-' . $data[MIDCOM_NAV_GUID], $result);
209 111
    }
210
211
    /**
212
     * Save a given array by GUID in the cache.
213
     *
214
     * @param string $guid The key to store.
215
     * @param mixed $data The data to store.
216
     */
217 1
    public function put_guid($guid, $data)
218
    {
219 1
        if ($this->_cache === null) {
220
            return;
221
        }
222
223 1
        $lang_id = midcom::get()->i18n->get_current_language();
224 1
        $result = $this->_cache->fetch("{$this->_prefix}-{$guid}");
225 1
        if (!is_array($result)) {
226 1
            $result = [];
227
        }
228 1
        $result[$lang_id] = $data;
229 1
        $this->_cache->save($this->_prefix . '-' . $guid, $result);
230 1
    }
231
232
    /**
233
     * Get a given array by GUID from the cache.
234
     *
235
     * @param string $guid The key to look up.
236
     */
237 295
    public function get_guid($guid)
238
    {
239 295
        if ($this->_cache === null) {
240
            return;
241
        }
242
243 295
        $lang_id = midcom::get()->i18n->get_current_language();
244 295
        $result = $this->_cache->fetch("{$this->_prefix}-{$guid}");
245 295
        if (   !is_array($result)
246 295
            || !isset($result[$lang_id])) {
247 294
            return false;
248
        }
249 56
        return $result[$lang_id];
250
    }
251
252
    /**
253
     * Sets a given leave key in the cache
254
     *
255
     * @param string $key The key to look up.
256
     * @param mixed $data The data to store.
257
     */
258 13
    public function put_leaves($key, $data)
259
    {
260 13
        if ($this->_cache === null) {
261
            return;
262
        }
263
264 13
        $lang_id = midcom::get()->i18n->get_current_language();
265 13
        $result = $this->_cache->fetch("{$this->_prefix}-{$key}");
266 13
        if (!is_array($result)) {
267 13
            $result = [];
268
        }
269 13
        $result[$lang_id] = $data;
270 13
        $this->_cache->save("{$this->_prefix}-{$key}", $result);
271 13
    }
272
}
273