5 * This file is part of ADOdb, a Database Abstraction Layer library for PHP.
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
38 * @var bool Prevents parent class calling non-existant function
40 public $createdir = false;
48 * @var int Connection Port, uses default
53 * @var bool memcache compression with zlib
58 * @var array of options for memcached only
63 * @var bool Internal flag indicating successful connection
65 private $isConnected = false;
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;
76 * @var array New server feature controller lists available servers
78 private $serverControllers = array();
81 * @var array New server feature template uses granular server controller
83 private $serverControllerTemplate = array(
90 * An integer index into the libraries
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;
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.
126 public function isLibMemcached(): bool
128 return $this->libraryFlag
== self
::MCLIBD
;
134 * The connection only occurs on CacheExecute call.
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
;
148 $err = 'Neither the Memcache nor Memcached PECL extensions were found!';
152 $usedLibrary = $this->libraries
[$this->libraryFlag
];
154 /** @var Memcache|Memcached $memCache */
155 $memCache = new $usedLibrary;
157 $err = 'Memcache library failed to initialize';
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';
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;
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'];
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;
213 foreach ($this->serverControllers
as $controller) {
214 if ($this->isLibMemcached()) {
215 if (!@$memCache->addServer($controller['host'], $controller['port'], $controller['weight'])) {
219 if (!@$memCache->addServer($controller['host'], $controller['port'])) {
224 if ($failcnt == sizeof($this->serverControllers
)) {
225 $err = 'Can\'t connect to any memcache server';
229 $this->memcacheLibrary
= $memCache;
231 // A valid memcache connection is available
232 $this->isConnected
= 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
242 * @param int $secs2cache
244 * @return bool true or false. true if successful save
246 public function writeCache($filename, $contents, $debug, $secs2cache)
249 if (!$this->isConnected
&& $debug) {
250 // Call to writeCache() before connect(), try to connect
251 if (!$this->connect($err)) {
252 ADOConnection
::outp($err);
255 if (!$this->isConnected
) {
256 $this->connect($err);
260 if (!$this->memcacheLibrary
) {
265 switch ($this->libraryFlag
) {
267 if (!$this->memcacheLibrary
->set($filename, $contents, $this->compress ? MEMCACHE_COMPRESSED
: 0,
273 if (!$this->memcacheLibrary
->set($filename, $contents, $secs2cache)) {
284 ADOConnection
::outp(" Failed to save data at the memcache server!<br>\n");
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
) {
313 $rs = $this->memcacheLibrary
->get($filename);
315 $err = 'Item with such key doesn\'t exist on the memcache server.';
319 // hack, should actually use _csv2rs
320 $rs = explode("\n", $rs);
322 $rs = join("\n", $rs);
323 $rs = unserialize($rs);
324 if (!is_object($rs)) {
325 $err = 'Unable to unserialize $rs';
328 if ($rs->timeCreated
== 0) {
330 } // apparently have been reports that timeCreated was set to 0 somewhere
332 $tdiff = intval($rs->timeCreated +
$secs2cache - time());
336 if ((rand() & 15) == 0) {
342 if ((rand() & 3) == 0) {
356 * Flushes all of the stored memcache data
360 * @return bool The response from the memcache server
362 public function flushAll($debug = false)
364 if (!$this->isConnected
) {
366 if (!$this->connect($err) && $debug) {
367 ADOConnection
::outp($err);
370 if (!$this->memcacheLibrary
) {
374 $del = $this->memcacheLibrary
->flush();
378 ADOConnection
::outp("flushall: failed!<br>\n");
380 ADOConnection
::outp("flushall: succeeded!<br>\n");
388 * Flushes the contents of a specified query
390 * @param string $filename The MD5 of the query to flush
393 * @return bool The response from the memcache server
395 public function flushCache($filename, $debug = false)
397 if (!$this->isConnected
) {
399 if (!$this->connect($err) && $debug) {
400 ADOConnection
::outp($err);
403 if (!$this->memcacheLibrary
) {
407 $del = $this->memcacheLibrary
->delete($filename);
411 ADOConnection
::outp("flushcache: $filename entry doesn't exist on memcache server!<br>\n");
413 ADOConnection
::outp("flushcache: $filename entry flushed from memcache server!<br>\n");