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	* @access protected
274	* @param string $var Cache key
275	* @return bool True if it exists, otherwise false
276	*/
277	function _isset($var)
278	{
279		// Most caches don't need to check
280		return true;
281	}
282}
283