Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
41.46% covered (danger)
41.46%
34 / 82
0.00% covered (danger)
0.00%
0 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
memory
41.46% covered (danger)
41.46%
34 / 82
0.00% covered (danger)
0.00%
0 / 11
360.92
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
 purge
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 load
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 save
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 tidy
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 get
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 put
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 destroy
52.63% covered (warning)
52.63%
10 / 19
0.00% covered (danger)
0.00%
0 / 1
20.63
 _exists
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 sql_save
96.00% covered (success)
96.00%
24 / 25
0.00% covered (danger)
0.00%
0 / 1
9
 _isset
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 _delete
n/a
0 / 0
n/a
0 / 0
0
 _write
n/a
0 / 0
n/a
0 / 0
0
1<?php
2/**
3*
4* This file is part of the phpBB Forum Software package.
5*
6* @copyright (c) phpBB Limited <https://www.phpbb.com>
7* @license GNU General Public License, version 2 (GPL-2.0)
8*
9* For full copyright and license information, please see
10* the docs/CREDITS.txt file.
11*
12*/
13
14namespace phpbb\cache\driver;
15
16/**
17* ACM Abstract Memory Class
18*/
19abstract class memory extends \phpbb\cache\driver\base
20{
21    var $key_prefix;
22
23    /**
24    * Set cache path
25    */
26    function __construct()
27    {
28        global $dbname, $table_prefix, $phpbb_container;
29
30        $this->cache_dir    = $phpbb_container->getParameter('core.cache_dir');
31        $this->key_prefix    = substr(md5($dbname . $table_prefix), 0, 8) . '_';
32
33        if (!isset($this->extension) || !extension_loaded($this->extension))
34        {
35            global $acm_type;
36
37            trigger_error("Could not find required extension [{$this->extension}] for the ACM module $acm_type.", E_USER_ERROR);
38        }
39
40        if (isset($this->function) && !function_exists($this->function))
41        {
42            global $acm_type;
43
44            trigger_error("The required function [{$this->function}] is not available for the ACM module $acm_type.", E_USER_ERROR);
45        }
46    }
47
48    /**
49    * {@inheritDoc}
50    */
51    function purge()
52    {
53        parent::purge();
54
55        $this->is_modified = true;
56
57        // We save here to let the following cache hits succeed
58        $this->save();
59    }
60
61    /**
62    * {@inheritDoc}
63    */
64    function load()
65    {
66        // grab the global cache
67        $data = $this->_read('global');
68
69        if ($data !== false)
70        {
71            $this->vars = $data;
72            return true;
73        }
74
75        return false;
76    }
77
78    /**
79    * {@inheritDoc}
80    */
81    function save()
82    {
83        if (!$this->is_modified)
84        {
85            return;
86        }
87
88        $this->_write('global', $this->vars, 2592000);
89
90        $this->is_modified = false;
91    }
92
93    /**
94    * {@inheritDoc}
95    */
96    function tidy()
97    {
98        global $config;
99
100        // cache has auto GC, no need to have any code here :)
101        $config->set('cache_last_gc', time(), false);
102    }
103
104    /**
105    * {@inheritDoc}
106    */
107    function get($var_name)
108    {
109        if ($var_name[0] == '_')
110        {
111            if (!$this->_exists($var_name))
112            {
113                return false;
114            }
115
116            return $this->_read($var_name);
117        }
118        else
119        {
120            return ($this->_exists($var_name)) ? $this->vars[$var_name] : false;
121        }
122    }
123
124    /**
125    * {@inheritDoc}
126    */
127    function put($var_name, $var, $ttl = 2592000)
128    {
129        if ($var_name[0] == '_')
130        {
131            $this->_write($var_name, $var, $ttl);
132        }
133        else
134        {
135            $this->vars[$var_name] = $var;
136            $this->is_modified = true;
137        }
138    }
139
140    /**
141    * {@inheritDoc}
142    */
143    function destroy($var_name, $table = '')
144    {
145        if ($var_name == 'sql' && !empty($table))
146        {
147            if (!is_array($table))
148            {
149                $table = array($table);
150            }
151
152            foreach ($table as $table_name)
153            {
154                // gives us the md5s that we want
155                $temp = $this->_read('sql_' . $table_name);
156
157                if ($temp === false)
158                {
159                    continue;
160                }
161
162                // delete each query ref
163                foreach ($temp as $md5_id => $void)
164                {
165                    $this->_delete('sql_' . $md5_id);
166                }
167
168                // delete the table ref
169                $this->_delete('sql_' . $table_name);
170            }
171
172            return;
173        }
174
175        if (!$this->_exists($var_name))
176        {
177            return;
178        }
179
180        if ($var_name[0] == '_')
181        {
182            $this->_delete($var_name);
183        }
184        else if (isset($this->vars[$var_name]))
185        {
186            $this->is_modified = true;
187            unset($this->vars[$var_name]);
188
189            // We save here to let the following cache hits succeed
190            $this->save();
191        }
192    }
193
194    /**
195    * {@inheritDoc}
196    */
197    function _exists($var_name)
198    {
199        if ($var_name[0] == '_')
200        {
201            return $this->_isset($var_name);
202        }
203        else
204        {
205            if (!count($this->vars))
206            {
207                $this->load();
208            }
209
210            return isset($this->vars[$var_name]);
211        }
212    }
213
214    /**
215    * {@inheritDoc}
216    */
217    function sql_save(\phpbb\db\driver\driver_interface $db, $query, $query_result, $ttl)
218    {
219        // Remove extra spaces and tabs
220        $query = preg_replace('/[\n\r\s\t]+/', ' ', $query);
221        $query_id = md5($query);
222
223        // determine which tables this query belongs to
224        // Some queries use backticks, namely the get_database_size() query
225        // don't check for conformity, the SQL would error and not reach here.
226        if (!preg_match_all('/(?:FROM \\(?(`?\\w+`?(?: \\w+)?(?:, ?`?\\w+`?(?: \\w+)?)*)\\)?)|(?:JOIN (`?\\w+`?(?: \\w+)?))/', $query, $regs, PREG_SET_ORDER))
227        {
228            // Bail out if the match fails.
229            return $query_result;
230        }
231
232        $tables = array();
233        foreach ($regs as $match)
234        {
235            if ($match[0][0] == 'F')
236            {
237                $tables = array_merge($tables, array_map('trim', explode(',', $match[1])));
238            }
239            else
240            {
241                $tables[] = $match[2];
242            }
243        }
244
245        foreach ($tables as $table_name)
246        {
247            // Remove backticks
248            $table_name = ($table_name[0] == '`') ? substr($table_name, 1, -1) : $table_name;
249
250            if (($pos = strpos($table_name, ' ')) !== false)
251            {
252                $table_name = substr($table_name, 0, $pos);
253            }
254
255            $temp = $this->_read('sql_' . $table_name);
256
257            if ($temp === false)
258            {
259                $temp = array();
260            }
261
262            $temp[$query_id] = true;
263
264            // This must never expire
265            $this->_write('sql_' . $table_name, $temp, 0);
266        }
267
268        // store them in the right place
269        $this->sql_rowset[$query_id] = array();
270        $this->sql_row_pointer[$query_id] = 0;
271
272        while ($row = $db->sql_fetchrow($query_result))
273        {
274            $this->sql_rowset[$query_id][] = $row;
275        }
276        $db->sql_freeresult($query_result);
277
278        $this->_write('sql_' . $query_id, $this->sql_rowset[$query_id], $ttl);
279
280        return $query_id;
281    }
282
283    /**
284    * Check if a cache var exists
285    *
286    * @param string $var Cache key
287    *
288    * @return bool True if it exists, otherwise false
289    */
290    protected function _isset(string $var): bool
291    {
292        // Most caches don't need to check
293        return true;
294    }
295
296    /**
297     * Remove an item from the cache
298     *
299     * @param string $var Cache key
300     *
301     * @return bool True if the operation succeeded
302     */
303    abstract protected function _delete(string $var): bool;
304
305    /**
306     * Store data in the cache
307     *
308     * @param string $var Cache key
309     * @param mixed $data Data to store
310     * @param int $ttl Time-to-live of cached data
311     *
312     * @return bool True if the operation succeeded
313     */
314    abstract protected function _write(string $var, $data, int $ttl = 2592000): bool;
315}