Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
/**
3
 * Memory caching.
4
 *
5
 * This file is part of ADOdb, a Database Abstraction Layer library for PHP.
6
 *
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
10
 *
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
17
 *
18
 * @copyright 2000-2013 John Lim
19
 * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community
20
 *
21
 * @noinspection PhpUnused
22
 */
23
 
24
// security - hide paths
25
if (!defined('ADODB_DIR')) die();
26
 
27
global $ADODB_INCLUDED_MEMCACHE;
28
$ADODB_INCLUDED_MEMCACHE = 1;
29
 
30
global $ADODB_INCLUDED_CSV;
31
if (empty($ADODB_INCLUDED_CSV)) {
32
	include_once(ADODB_DIR . '/adodb-csvlib.inc.php');
33
}
34
 
35
class ADODB_Cache_MemCache
36
{
37
	/**
38
	 * @var bool Prevents parent class calling non-existant function
39
	 */
40
	public $createdir = false;
41
 
42
	/**
43
	 * @var array of hosts
44
	 */
45
	private $hosts;
46
 
47
	/**
48
	 * @var int Connection Port, uses default
49
	 */
50
	private $port;
51
 
52
	/**
53
	 * @var bool memcache compression with zlib
54
	 */
55
	private $compress;
56
 
57
	/**
58
	 * @var array of options for memcached only
59
	 */
60
	private $options;
61
 
62
	/**
63
	 * @var bool Internal flag indicating successful connection
64
	 */
65
	private $isConnected = false;
66
 
67
	/**
68
	 * @var Memcache|Memcached Handle for the Memcache library
69
	 *
70
	 * Populated with the proper library on connect, used later when
71
	 * there are differences in specific calls between memcache and memcached
72
	 */
73
	private $memcacheLibrary = false;
74
 
75
	/**
76
	 * @var array New server feature controller lists available servers
77
	 */
78
	private $serverControllers = array();
79
 
80
	/**
81
	 * @var array New server feature template uses granular server controller
82
	 */
83
	private $serverControllerTemplate = array(
84
		'host' => '',
85
		'port' => 11211,
86
		'weight' => 0,
87
	);
88
 
89
	/**
90
	 * An integer index into the libraries
91
	 * @see $libraries
92
	 */
93
	const MCLIB = 1;
94
	const MCLIBD = 2;
95
 
96
	/**
97
	 * @var array Xrefs the library flag to the actual class name
98
	 */
99
	private $libraries = array(
100
		self::MCLIB => 'Memcache',
101
		self::MCLIBD => 'Memcached'
102
	);
103
 
104
	/**
105
	 * @var int An indicator of which library we are using
106
	 */
107
	private $libraryFlag;
108
 
109
	/**
110
	 * Class Constructor.
111
	 *
112
	 * @param ADOConnection $db
113
	 */
114
	public function __construct($db)
115
	{
116
		$this->hosts = $db->memCacheHost;
117
		$this->port = $this->serverControllerTemplate['port'] = $db->memCachePort;
118
		$this->compress = $db->memCacheCompress;
119
		$this->options = $db->memCacheOptions;
120
	}
121
 
122
	/**
123
	 * Return true if the current library is Memcached.
124
	 * @return bool
125
	 */
126
	public function isLibMemcached(): bool
127
	{
128
		return $this->libraryFlag == self::MCLIBD;
129
	}
130
 
131
	/**
132
	 * Lazy connection.
133
	 *
134
	 * The connection only occurs on CacheExecute call.
135
	 *
136
	 * @param string $err
137
	 *
138
	 * @return bool success of connecting to a server
139
	 */
140
	public function connect(&$err)
141
	{
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;
150
		}
151
 
152
		$usedLibrary = $this->libraries[$this->libraryFlag];
153
 
154
		/** @var Memcache|Memcached $memCache */
155
		$memCache = new $usedLibrary;
156
		if (!$memCache) {
157
			$err = 'Memcache library failed to initialize';
158
			return false;
159
		}
160
 
161
		// Convert simple compression flag for memcached
162
		if ($this->isLibMemcached()) {
163
			$this->options[Memcached::OPT_COMPRESSION] = $this->compress;
164
		}
165
 
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;
172
			}
173
		}
174
 
175
		// Have we passed a controller array
176
		if (!is_array($this->hosts)) {
177
			$this->hosts = array($this->hosts);
178
		}
179
 
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;
186
 
187
				$this->serverControllers[] = $connector;
188
			}
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;
198
				}
199
 
200
				$this->serverControllers[] = $connector;
201
			}
202
		}
203
 
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;
210
		}
211
 
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++;
217
				}
218
			} else {
219
				if (!@$memCache->addServer($controller['host'], $controller['port'])) {
220
					$failcnt++;
221
				}
222
			}
223
		}
224
		if ($failcnt == sizeof($this->serverControllers)) {
225
			$err = 'Can\'t connect to any memcache server';
226
			return false;
227
		}
228
 
229
		$this->memcacheLibrary = $memCache;
230
 
231
		// A valid memcache connection is available
232
		$this->isConnected = true;
233
		return true;
234
	}
235
 
236
	/**
237
	 * Writes a cached query to the server
238
	 *
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
243
	 *
244
	 * @return bool true or false. true if successful save
245
	 */
246
	public function writeCache($filename, $contents, $debug, $secs2cache)
247
	{
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);
253
			}
254
		} else {
255
			if (!$this->isConnected) {
256
				$this->connect($err);
257
			}
258
		}
259
 
260
		if (!$this->memcacheLibrary) {
261
			return false;
262
		}
263
 
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;
270
				}
271
				break;
272
			case self::MCLIBD:
273
				if (!$this->memcacheLibrary->set($filename, $contents, $secs2cache)) {
274
					$failed = true;
275
				}
276
				break;
277
			default:
278
				$failed = true;
279
				break;
280
		}
281
 
282
		if ($failed) {
283
			if ($debug) {
284
				ADOConnection::outp(" Failed to save data at the memcache server!<br>\n");
285
			}
286
			return false;
287
		}
288
 
289
		return true;
290
	}
291
 
292
	/**
293
	 * Reads a cached query from the server.
294
	 *
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**
299
	 *
300
	 * @return object|bool record or false.
301
	 *
302
	 * @noinspection PhpUnusedParameterInspection
303
	 */
304
	public function readCache($filename, &$err, $secs2cache, $rsClass)
305
	{
306
		if (!$this->isConnected) {
307
			$this->connect($err);
308
		}
309
		if (!$this->memcacheLibrary) {
310
			return false;
311
		}
312
 
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;
317
		}
318
 
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;
327
		}
328
		if ($rs->timeCreated == 0) {
329
			return $rs;
330
		} // apparently have been reports that timeCreated was set to 0 somewhere
331
 
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;
339
					}
340
					break;
341
				case 1:
342
					if ((rand() & 3) == 0) {
343
						$err = "Timeout 1";
344
						return false;
345
					}
346
					break;
347
				default:
348
					$err = "Timeout 0";
349
					return false;
350
			}
351
		}
352
		return $rs;
353
	}
354
 
355
	/**
356
	 * Flushes all of the stored memcache data
357
	 *
358
	 * @param bool $debug
359
	 *
360
	 * @return bool The response from the memcache server
361
	 */
362
	public function flushAll($debug = false)
363
	{
364
		if (!$this->isConnected) {
365
			$err = '';
366
			if (!$this->connect($err) && $debug) {
367
				ADOConnection::outp($err);
368
			}
369
		}
370
		if (!$this->memcacheLibrary) {
371
			return false;
372
		}
373
 
374
		$del = $this->memcacheLibrary->flush();
375
 
376
		if ($debug) {
377
			if (!$del) {
378
				ADOConnection::outp("flushall: failed!<br>\n");
379
			} else {
380
				ADOConnection::outp("flushall: succeeded!<br>\n");
381
			}
382
		}
383
 
384
		return $del;
385
	}
386
 
387
	/**
388
	 * Flushes the contents of a specified query
389
	 *
390
	 * @param string $filename The MD5 of the query to flush
391
	 * @param bool $debug
392
	 *
393
	 * @return bool The response from the memcache server
394
	 */
395
	public function flushCache($filename, $debug = false)
396
	{
397
		if (!$this->isConnected) {
398
			$err = '';
399
			if (!$this->connect($err) && $debug) {
400
				ADOConnection::outp($err);
401
			}
402
		}
403
		if (!$this->memcacheLibrary) {
404
			return false;
405
		}
406
 
407
		$del = $this->memcacheLibrary->delete($filename);
408
 
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");
414
			}
415
		}
416
 
417
		return $del;
418
	}
419
 
420
}