MDL-81337 behat: Fix behat failures
[moodle.git] / lib / adodb / adodb-memcache.lib.inc.php
bloba251c9c328bc98e2a8dbc8941ffe4a5e0bd7139e
1 <?php
2 /**
3 * Memory caching.
5 * This file is part of ADOdb, a Database Abstraction Layer library for PHP.
7 * @package ADOdb
8 * @link https://adodb.org Project's web site and documentation
9 * @link https://github.com/ADOdb/ADOdb Source code and issue tracker
11 * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause
12 * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option,
13 * any later version. This means you can use it in proprietary products.
14 * See the LICENSE.md file distributed with this source code for details.
15 * @license BSD-3-Clause
16 * @license LGPL-2.1-or-later
18 * @copyright 2000-2013 John Lim
19 * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community
21 * @noinspection PhpUnused
24 // security - hide paths
25 if (!defined('ADODB_DIR')) die();
27 global $ADODB_INCLUDED_MEMCACHE;
28 $ADODB_INCLUDED_MEMCACHE = 1;
30 global $ADODB_INCLUDED_CSV;
31 if (empty($ADODB_INCLUDED_CSV)) {
32 include_once(ADODB_DIR . '/adodb-csvlib.inc.php');
35 class ADODB_Cache_MemCache
37 /**
38 * @var bool Prevents parent class calling non-existant function
40 public $createdir = false;
42 /**
43 * @var array of hosts
45 private $hosts;
47 /**
48 * @var int Connection Port, uses default
50 private $port;
52 /**
53 * @var bool memcache compression with zlib
55 private $compress;
57 /**
58 * @var array of options for memcached only
60 private $options;
62 /**
63 * @var bool Internal flag indicating successful connection
65 private $isConnected = false;
67 /**
68 * @var Memcache|Memcached Handle for the Memcache library
70 * Populated with the proper library on connect, used later when
71 * there are differences in specific calls between memcache and memcached
73 private $memcacheLibrary = false;
75 /**
76 * @var array New server feature controller lists available servers
78 private $serverControllers = array();
80 /**
81 * @var array New server feature template uses granular server controller
83 private $serverControllerTemplate = array(
84 'host' => '',
85 'port' => 11211,
86 'weight' => 0,
89 /**
90 * An integer index into the libraries
91 * @see $libraries
93 const MCLIB = 1;
94 const MCLIBD = 2;
96 /**
97 * @var array Xrefs the library flag to the actual class name
99 private $libraries = array(
100 self::MCLIB => 'Memcache',
101 self::MCLIBD => 'Memcached'
105 * @var int An indicator of which library we are using
107 private $libraryFlag;
110 * Class Constructor.
112 * @param ADOConnection $db
114 public function __construct($db)
116 $this->hosts = $db->memCacheHost;
117 $this->port = $this->serverControllerTemplate['port'] = $db->memCachePort;
118 $this->compress = $db->memCacheCompress;
119 $this->options = $db->memCacheOptions;
123 * Return true if the current library is Memcached.
124 * @return bool
126 public function isLibMemcached(): bool
128 return $this->libraryFlag == self::MCLIBD;
132 * Lazy connection.
134 * The connection only occurs on CacheExecute call.
136 * @param string $err
138 * @return bool success of connecting to a server
140 public function connect(&$err)
142 // do we have memcache or memcached? see the note at adodb.org on memcache
143 if (class_exists('Memcache')) {
144 $this->libraryFlag = self::MCLIB;
145 } elseif (class_exists('Memcached')) {
146 $this->libraryFlag = self::MCLIBD;
147 } else {
148 $err = 'Neither the Memcache nor Memcached PECL extensions were found!';
149 return false;
152 $usedLibrary = $this->libraries[$this->libraryFlag];
154 /** @var Memcache|Memcached $memCache */
155 $memCache = new $usedLibrary;
156 if (!$memCache) {
157 $err = 'Memcache library failed to initialize';
158 return false;
161 // Convert simple compression flag for memcached
162 if ($this->isLibMemcached()) {
163 $this->options[Memcached::OPT_COMPRESSION] = $this->compress;
166 // Are there any options available for memcached
167 if ($this->isLibMemcached() && count($this->options) > 0) {
168 $optionSuccess = $memCache->setOptions($this->options);
169 if (!$optionSuccess) {
170 $err = 'Invalid option parameters passed to Memcached';
171 return false;
175 // Have we passed a controller array
176 if (!is_array($this->hosts)) {
177 $this->hosts = array($this->hosts);
180 if (!is_array($this->hosts[0])) {
181 // Old way, convert to controller
182 foreach ($this->hosts as $ipAddress) {
183 $connector = $this->serverControllerTemplate;
184 $connector['host'] = $ipAddress;
185 $connector['port'] = $this->port;
187 $this->serverControllers[] = $connector;
189 } else {
190 // New way, must validate port, etc
191 foreach ($this->hosts as $controller) {
192 $connector = array_merge($this->serverControllerTemplate, $controller);
193 if ($this->isLibMemcached()) {
194 $connector['weight'] = (int)$connector['weight'];
195 } else {
196 // Cannot use weight in memcache, simply discard
197 $connector['weight'] = 0;
200 $this->serverControllers[] = $connector;
204 // Checks for existing connections ( but only for memcached )
205 if ($this->isLibMemcached() && !empty($memCache->getServerList())) {
206 // Use the existing configuration
207 $this->isConnected = true;
208 $this->memcacheLibrary = $memCache;
209 return true;
212 $failcnt = 0;
213 foreach ($this->serverControllers as $controller) {
214 if ($this->isLibMemcached()) {
215 if (!@$memCache->addServer($controller['host'], $controller['port'], $controller['weight'])) {
216 $failcnt++;
218 } else {
219 if (!@$memCache->addServer($controller['host'], $controller['port'])) {
220 $failcnt++;
224 if ($failcnt == sizeof($this->serverControllers)) {
225 $err = 'Can\'t connect to any memcache server';
226 return false;
229 $this->memcacheLibrary = $memCache;
231 // A valid memcache connection is available
232 $this->isConnected = true;
233 return true;
237 * Writes a cached query to the server
239 * @param string $filename The MD5 of the query to cache
240 * @param string $contents The query results
241 * @param bool $debug
242 * @param int $secs2cache
244 * @return bool true or false. true if successful save
246 public function writeCache($filename, $contents, $debug, $secs2cache)
248 $err = '';
249 if (!$this->isConnected && $debug) {
250 // Call to writeCache() before connect(), try to connect
251 if (!$this->connect($err)) {
252 ADOConnection::outp($err);
254 } else {
255 if (!$this->isConnected) {
256 $this->connect($err);
260 if (!$this->memcacheLibrary) {
261 return false;
264 $failed = false;
265 switch ($this->libraryFlag) {
266 case self::MCLIB:
267 if (!$this->memcacheLibrary->set($filename, $contents, $this->compress ? MEMCACHE_COMPRESSED : 0,
268 $secs2cache)) {
269 $failed = true;
271 break;
272 case self::MCLIBD:
273 if (!$this->memcacheLibrary->set($filename, $contents, $secs2cache)) {
274 $failed = true;
276 break;
277 default:
278 $failed = true;
279 break;
282 if ($failed) {
283 if ($debug) {
284 ADOConnection::outp(" Failed to save data at the memcache server!<br>\n");
286 return false;
289 return true;
293 * Reads a cached query from the server.
295 * @param string $filename The MD5 of the query to read
296 * @param string $err The query results
297 * @param int $secs2cache
298 * @param object $rsClass **UNUSED**
300 * @return object|bool record or false.
302 * @noinspection PhpUnusedParameterInspection
304 public function readCache($filename, &$err, $secs2cache, $rsClass)
306 if (!$this->isConnected) {
307 $this->connect($err);
309 if (!$this->memcacheLibrary) {
310 return false;
313 $rs = $this->memcacheLibrary->get($filename);
314 if (!$rs) {
315 $err = 'Item with such key doesn\'t exist on the memcache server.';
316 return false;
319 // hack, should actually use _csv2rs
320 $rs = explode("\n", $rs);
321 unset($rs[0]);
322 $rs = join("\n", $rs);
323 $rs = unserialize($rs);
324 if (!is_object($rs)) {
325 $err = 'Unable to unserialize $rs';
326 return false;
328 if ($rs->timeCreated == 0) {
329 return $rs;
330 } // apparently have been reports that timeCreated was set to 0 somewhere
332 $tdiff = intval($rs->timeCreated + $secs2cache - time());
333 if ($tdiff <= 2) {
334 switch ($tdiff) {
335 case 2:
336 if ((rand() & 15) == 0) {
337 $err = "Timeout 2";
338 return false;
340 break;
341 case 1:
342 if ((rand() & 3) == 0) {
343 $err = "Timeout 1";
344 return false;
346 break;
347 default:
348 $err = "Timeout 0";
349 return false;
352 return $rs;
356 * Flushes all of the stored memcache data
358 * @param bool $debug
360 * @return bool The response from the memcache server
362 public function flushAll($debug = false)
364 if (!$this->isConnected) {
365 $err = '';
366 if (!$this->connect($err) && $debug) {
367 ADOConnection::outp($err);
370 if (!$this->memcacheLibrary) {
371 return false;
374 $del = $this->memcacheLibrary->flush();
376 if ($debug) {
377 if (!$del) {
378 ADOConnection::outp("flushall: failed!<br>\n");
379 } else {
380 ADOConnection::outp("flushall: succeeded!<br>\n");
384 return $del;
388 * Flushes the contents of a specified query
390 * @param string $filename The MD5 of the query to flush
391 * @param bool $debug
393 * @return bool The response from the memcache server
395 public function flushCache($filename, $debug = false)
397 if (!$this->isConnected) {
398 $err = '';
399 if (!$this->connect($err) && $debug) {
400 ADOConnection::outp($err);
403 if (!$this->memcacheLibrary) {
404 return false;
407 $del = $this->memcacheLibrary->delete($filename);
409 if ($debug) {
410 if (!$del) {
411 ADOConnection::outp("flushcache: $filename entry doesn't exist on memcache server!<br>\n");
412 } else {
413 ADOConnection::outp("flushcache: $filename entry flushed from memcache server!<br>\n");
417 return $del;