Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
43.04% covered (danger)
43.04%
34 / 79
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
memory
43.04% covered (danger)
43.04%
34 / 79
0.00% covered (danger)
0.00%
0 / 10
320.12
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
 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 load()
52    {
53        // grab the global cache
54        $data = $this->_read('global');
55
56        if ($data !== false)
57        {
58            $this->vars = $data;
59            return true;
60        }
61
62        return false;
63    }
64
65    /**
66    * {@inheritDoc}
67    */
68    function save()
69    {
70        if (!$this->is_modified)
71        {
72            return;
73        }
74
75        $this->_write('global', $this->vars, 2592000);
76
77        $this->is_modified = false;
78    }
79
80    /**
81    * {@inheritDoc}
82    */
83    function tidy()
84    {
85        global $config;
86
87        // cache has auto GC, no need to have any code here :)
88        $config->set('cache_last_gc', time(), false);
89    }
90
91    /**
92    * {@inheritDoc}
93    */
94    function get($var_name)
95    {
96        if ($var_name[0] == '_')
97        {
98            if (!$this->_exists($var_name))
99            {
100                return false;
101            }
102
103            return $this->_read($var_name);
104        }
105        else
106        {
107            return ($this->_exists($var_name)) ? $this->vars[$var_name] : false;
108        }
109    }
110
111    /**
112    * {@inheritDoc}
113    */
114    function put($var_name, $var, $ttl = 2592000)
115    {
116        if ($var_name[0] == '_')
117        {
118            $this->_write($var_name, $var, $ttl);
119        }
120        else
121        {
122            $this->vars[$var_name] = $var;
123            $this->is_modified = true;
124        }
125    }
126
127    /**
128    * {@inheritDoc}
129    */
130    function destroy($var_name, $table = '')
131    {
132        if ($var_name == 'sql' && !empty($table))
133        {
134            if (!is_array($table))
135            {
136                $table = array($table);
137            }
138
139            foreach ($table as $table_name)
140            {
141                // gives us the md5s that we want
142                $temp = $this->_read('sql_' . $table_name);
143
144                if ($temp === false)
145                {
146                    continue;
147                }
148
149                // delete each query ref
150                foreach ($temp as $md5_id => $void)
151                {
152                    $this->_delete('sql_' . $md5_id);
153                }
154
155                // delete the table ref
156                $this->_delete('sql_' . $table_name);
157            }
158
159            return;
160        }
161
162        if (!$this->_exists($var_name))
163        {
164            return;
165        }
166
167        if ($var_name[0] == '_')
168        {
169            $this->_delete($var_name);
170        }
171        else if (isset($this->vars[$var_name]))
172        {
173            $this->is_modified = true;
174            unset($this->vars[$var_name]);
175
176            // We save here to let the following cache hits succeed
177            $this->save();
178        }
179    }
180
181    /**
182    * {@inheritDoc}
183    */
184    function _exists($var_name)
185    {
186        if ($var_name[0] == '_')
187        {
188            return $this->_isset($var_name);
189        }
190        else
191        {
192            if (!count($this->vars))
193            {
194                $this->load();
195            }
196
197            return isset($this->vars[$var_name]);
198        }
199    }
200
201    /**
202    * {@inheritDoc}
203    */
204    function sql_save(\phpbb\db\driver\driver_interface $db, $query, $query_result, $ttl)
205    {
206        // Remove extra spaces and tabs
207        $query = preg_replace('/[\n\r\s\t]+/', ' ', $query);
208        $query_id = md5($query);
209
210        // determine which tables this query belongs to
211        // Some queries use backticks, namely the get_database_size() query
212        // don't check for conformity, the SQL would error and not reach here.
213        if (!preg_match_all('/(?:FROM \\(?(`?\\w+`?(?: \\w+)?(?:, ?`?\\w+`?(?: \\w+)?)*)\\)?)|(?:JOIN (`?\\w+`?(?: \\w+)?))/', $query, $regs, PREG_SET_ORDER))
214        {
215            // Bail out if the match fails.
216            return $query_result;
217        }
218
219        $tables = array();
220        foreach ($regs as $match)
221        {
222            if ($match[0][0] == 'F')
223            {
224                $tables = array_merge($tables, array_map('trim', explode(',', $match[1])));
225            }
226            else
227            {
228                $tables[] = $match[2];
229            }
230        }
231
232        foreach ($tables as $table_name)
233        {
234            // Remove backticks
235            $table_name = ($table_name[0] == '`') ? substr($table_name, 1, -1) : $table_name;
236
237            if (($pos = strpos($table_name, ' ')) !== false)
238            {
239                $table_name = substr($table_name, 0, $pos);
240            }
241
242            $temp = $this->_read('sql_' . $table_name);
243
244            if ($temp === false)
245            {
246                $temp = array();
247            }
248
249            $temp[$query_id] = true;
250
251            // This must never expire
252            $this->_write('sql_' . $table_name, $temp, 0);
253        }
254
255        // store them in the right place
256        $this->sql_rowset[$query_id] = array();
257        $this->sql_row_pointer[$query_id] = 0;
258
259        while ($row = $db->sql_fetchrow($query_result))
260        {
261            $this->sql_rowset[$query_id][] = $row;
262        }
263        $db->sql_freeresult($query_result);
264
265        $this->_write('sql_' . $query_id, $this->sql_rowset[$query_id], $ttl);
266
267        return $query_id;
268    }
269
270    /**
271    * Check if a cache var exists
272    *
273    * @param string $var Cache key
274    *
275    * @return bool True if it exists, otherwise false
276    */
277    protected function _isset(string $var): bool
278    {
279        // Most caches don't need to check
280        return true;
281    }
282
283    /**
284     * Remove an item from the cache
285     *
286     * @param string $var Cache key
287     *
288     * @return bool True if the operation succeeded
289     */
290    abstract protected function _delete(string $var): bool;
291
292    /**
293     * Store data in the cache
294     *
295     * @param string $var Cache key
296     * @param mixed $data Data to store
297     * @param int $ttl Time-to-live of cached data
298     *
299     * @return bool True if the operation succeeded
300     */
301    abstract protected function _write(string $var, $data, int $ttl = 2592000): bool;
302}