Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
cacheengine.php
1<?php
2namespace Bitrix\Main\Data;
3
7
9{
10 const BX_BASE_LIST = '|bx_base_list|';
11 const BX_DIR_LIST = '|bx_dir_list|';
12
14 protected static $engine = null;
15 protected static array $locks = [];
16 protected static bool $isConnected = false;
17 protected static array $baseDirVersion = [];
18 protected static array $initDirVersion = [];
19
20 protected static array $listKeys = [];
21
22 protected string $sid = 'BX';
23 protected bool $useLock = false;
24 protected int $ttlMultiplier = 1;
25 protected int $ttlOld = 2;
26 protected bool $old = false;
27 protected bool $fullClean = false;
28
29 abstract public function getConnectionName(): string;
30 abstract public static function getConnectionClass();
31
32 abstract public function set($key, $ttl, $value);
33 abstract public function get($key);
34 abstract public function del($key);
35
36 abstract public function setNotExists($key, $ttl , $value);
37 abstract public function addToSet($key, $value);
38 abstract public function getSet($key) : array;
39 abstract public function delFromSet($key, $member);
40
41 abstract public function deleteBySet($key, $prefix = '');
42
47 public function __construct(array $options = [])
48 {
49 static $config = [];
50 if (self::$engine == null)
51 {
52 if (empty($config))
53 {
54 $config = $this->configure($options);
55 }
56 }
57
58 $this->connect($config);
59 }
60
61 protected function connect($config)
62 {
63 $connectionPool = Application::getInstance()->getConnectionPool();
64 $connectionPool->setConnectionParameters($this->getConnectionName(), $config);
65
67 $engineConnection = $connectionPool->getConnection($this->getConnectionName());
68 self::$engine = $engineConnection->getResource();
69 self::$isConnected = $engineConnection->isConnected();
70 }
71
72 protected function configure($options = []) : array
73 {
74 $config = [];
75 $cacheConfig = Config\Configuration::getValue('cache');
76
77 if (!$cacheConfig || !is_array($cacheConfig))
78 {
79 return $config;
80 }
81
82 if (isset($options['type']))
83 {
84 $type = $options['type'];
85 }
86 else
87 {
88 if (is_array($cacheConfig['type']) && is_set($cacheConfig['type']['extension']))
89 {
90 $type = $cacheConfig['type']['extension'];
91 }
92 else
93 {
94 $type = $cacheConfig['type'];
95 }
96 }
97
98 $config['type'] = $type;
99 $config['className'] = static::getConnectionClass();
100
101 if (!isset($config['servers']) || !is_array($config['servers']))
102 {
103 $config['servers'] = [];
104 }
105
106 if (isset($cacheConfig[$type]) && is_array($cacheConfig[$type]) && !empty($cacheConfig[$type]['host']))
107 {
108 $config['servers'][] = [
109 'host' => $cacheConfig[$type]['host'],
110 'port' => (int) ($cacheConfig[$type]['port'] ?? 0)
111 ];
112 }
113
114 // Settings from .settings.php
115 if (isset($cacheConfig['servers']) && is_array($cacheConfig['servers']))
116 {
117 $config['servers'] = array_merge($config['servers'], $cacheConfig['servers']);
118 }
119
120 // Setting from cluster config
121 if (isset($options['servers']) && is_array($options['servers']))
122 {
123 $config['servers'] = array_merge($config['servers'], $options['servers']);
124 }
125
126 if (isset($cacheConfig['use_lock']))
127 {
128 $this->useLock = (bool) $cacheConfig['use_lock'];
129 }
130
131 if (isset($cacheConfig['sid']) && ($cacheConfig['sid'] != ''))
132 {
133 $this->sid = $cacheConfig['sid'];
134 }
135
136 // Only redis
137 if (isset($cacheConfig['serializer']))
138 {
139 $config['serializer'] = (int) $cacheConfig['serializer'];
140 }
141
142 $config['persistent'] = true;
143 if (isset($cacheConfig['persistent']) && $cacheConfig['persistent'] == 0)
144 {
145 $config['persistent'] = false;
146 }
147
148 if (isset($cacheConfig['actual_data']))
149 {
150 $this->useLock = !$cacheConfig['actual_data'];
151 }
152
153 if (!$this->useLock)
154 {
155 $this->ttlMultiplier = 1;
156 }
157
158 if (isset($cacheConfig['ttl_multiplier']) && $this->useLock)
159 {
160 $this->ttlMultiplier = (int) $cacheConfig['ttl_multiplier'];
161 if ($this->ttlMultiplier < 1)
162 {
163 $this->ttlMultiplier = 1;
164 }
165 }
166
167 if (isset($cacheConfig['full_clean']))
168 {
169 $this->fullClean = (bool) $cacheConfig['full_clean'];
170 }
171
172 return $config;
173 }
174
184 protected function lock(string $key = '', int $ttl = 0) : bool
185 {
186 if ($key == '')
187 {
188 return false;
189 }
190
191 $key .= '~';
192 if (isset(self::$locks[$key]))
193 {
194 return true;
195 }
196 else
197 {
198 if ($this->setNotExists($key, $ttl, $this->ttlOld))
199 {
200 self::$locks[$key] = true;
201 return true;
202 }
203 }
204
205 return false;
206 }
207
216 protected function unlock(string $key = '', int $ttl = 0) : void
217 {
218 if ($key != '')
219 {
220 $key .= '~';
221
222 if ($ttl > 0)
223 {
224 $this->set($key, $ttl, 1);
225 }
226 else
227 {
228 $this->del($key);
229 }
230
231 unset(self::$locks[$key]);
232 }
233 }
234
239 function close() : void
240 {
241 if (self::$engine != null)
242 {
243 self::$engine->close();
244 self::$engine = null;
245 }
246 }
247
252 public function isAvailable()
253 {
254 return self::$isConnected;
255 }
256
263 public function isCacheExpired($path)
264 {
265 return false;
266 }
267
268 protected function getPartition($key) : string
269 {
270 return '|' . substr(sha1($key), 0, 2) . '|';
271 }
272
273 protected function getInitDirKey($baseDir, $initDir = false) : string
274 {
275 return $this->sid . '|' . $this->getBaseDirVersion($baseDir) . '|init_dir|' . sha1($initDir);
276 }
277
286 protected function getInitDirVersion($baseDir, $initDir = false, $generateNew = true) : string
287 {
288 $key = $this->getInitDirKey($baseDir, $initDir);
289
290 if (!array_key_exists($key, static::$initDirVersion))
291 {
292 static::$initDirVersion[$key] = $this->get($key);
293 }
294
295 if (
296 static::$initDirVersion[$key] === false
297 || (static::$initDirVersion[$key] == '' && $generateNew)
298 )
299 {
300 if ($generateNew)
301 {
302 static::$initDirVersion[$key] = sha1(mt_rand() . '|' . microtime());
303 $this->set($key, 0, static::$initDirVersion[$key]);
304 }
305 else
306 {
307 static::$initDirVersion[$key] = '';
308 }
309 }
310
311 return static::$initDirVersion[$key];
312 }
313
320 protected function getBaseDirVersion($baseDir) : string
321 {
322 $baseDirHash = sha1($baseDir);
323 $key = $this->sid . '|base_dir|' . $baseDirHash;
324
325 if (!isset(static::$baseDirVersion[$key]))
326 {
327 static::$baseDirVersion[$key] = $this->get($key);
328 }
329
330 if (static::$baseDirVersion[$key] === false)
331 {
332 static::$baseDirVersion[$key] = sha1($baseDirHash . '|' . mt_rand() . '|' . microtime());
333 $this->set($key, 0, static::$baseDirVersion[$key]);
334 }
335
336 return static::$baseDirVersion[$key];
337 }
338
350 public function read(&$vars, $baseDir, $initDir, $filename, $ttl)
351 {
352 $initDirVersion = $this->getInitDirVersion($baseDir, $initDir, false);
353
354 if ($initDirVersion == '')
355 {
356 return false;
357 }
358
359 $key = $this->sid . '|' . sha1($this->getBaseDirVersion($baseDir) . '|' . $initDirVersion) . '|' .$filename;
360
361 if ($this->useLock)
362 {
363 $cachedData = $this->get($key);
364
365 if (!is_array($cachedData))
366 {
367 $cachedData = $this->get($key . '|old');
368 if (is_array($cachedData))
369 {
370 $this->old = true;
371 }
372 }
373
374 if (!is_array($cachedData))
375 {
376 return false;
377 }
378
379 if ($this->lock($key, $ttl))
380 {
381 if ($this->old || $cachedData['datecreate'] < (time() - $ttl))
382 {
383 return false;
384 }
385 }
386
387 $vars = $cachedData['content'];
388 }
389 else
390 {
391 $vars = $this->get($key);
392 }
393
394 return $vars !== false;
395 }
396
408 public function write($vars, $baseDir, $initDir, $filename, $ttl)
409 {
410 $baseDirVersion = $this->getBaseDirVersion($baseDir);
411 $initDirVersion = $this->getInitDirVersion($baseDir, $initDir);
412
413 $dir = sha1($baseDirVersion . '|' . $initDirVersion);
414 $key = $this->sid. '|' . $dir . '|' . $filename;
415 $exp = $this->ttlMultiplier * (int) $ttl;
416
417 if ($this->useLock)
418 {
419 $this->set($key, $exp, ['datecreate' => time(), 'content' => $vars]);
420 $this->del($key . '|old');
421 $this->unlock($key, $ttl);
422 }
423 else
424 {
425 $this->set($key, $exp, $vars);
426 }
427
428 $initListKey = $this->sid . '|' . $dir . self::BX_DIR_LIST;
429
430 $initPartition = $this->getPartition($filename);
431 $initListKeyPartition = $initListKey . $initPartition;
432
433 $this->addToSet($initListKeyPartition, $filename);
434 $this->addToSet($initListKey, $initPartition);
435
436 if ($this->fullClean)
437 {
438 $baseListKey = $this->sid . '|' . $baseDirVersion . self::BX_BASE_LIST;
439 $baseListKeyPartition = $this->getPartition($initListKeyPartition);
440 $this->addToSet($baseListKey . $baseListKeyPartition, $initListKeyPartition);
441 $this->addToSet($baseListKey, $baseListKeyPartition);
442 }
443 }
444
454 public function clean($baseDir, $initDir = false, $filename = false)
455 {
456 if (!self::isAvailable())
457 {
458 return;
459 }
460
461 $baseDirVersion = $this->getBaseDirVersion($baseDir);
462 $initDirVersion = $this->getInitDirVersion($baseDir, $initDir);
463
464 $dir = sha1($baseDirVersion . '|' . $initDirVersion);
465 $initListKey = $this->sid . '|' .$dir . self::BX_DIR_LIST;
466
467 if ($this->fullClean)
468 {
469 $baseListKey = $this->sid . '|' .$baseDirVersion . self::BX_BASE_LIST;
470 }
471
472 if ($filename <> '')
473 {
474 $key = $this->sid . '|' .$dir . '|' . $filename;
475 $this->delFromSet($initListKey . $this->getPartition($filename), $filename);
476
477 if ($this->useLock && $cachedData = $this->get($key))
478 {
479 $this->set($key . '|old', $this->ttlOld, $cachedData);
480 }
481
482 $this->del($key);
483 if ($this->useLock)
484 {
485 $this->unlock($key);
486 }
487 }
488 elseif ($initDir != '')
489 {
490 $keyPrefix = $this->sid . '|' .$dir . '|';
491 $initDirKey = $this->getInitDirKey($baseDir, $initDir);
492 $this->del($initDirKey);
493 unset(static::$initDirVersion[$initDirKey]);
494
495 $partitionKeys = $this->getSet($initListKey);
496 foreach ($partitionKeys as $partition)
497 {
498 $delKey = $initListKey . $partition;
499 $this->deleteBySet($delKey, $keyPrefix);
500 $this->del($delKey);
501 if ($this->fullClean)
502 {
503 $this->delFromSet($baseListKey . $this->getPartition($delKey), $delKey);
504 }
505 }
506 $this->del($initListKey);
507 }
508 else
509 {
510 $baseDirKey = $this->sid . '|base_dir|' . sha1 ($baseDir);
511 $this->del($baseDirKey);
512 unset(static::$baseDirVersion[$baseDirKey]);
513
514 if ($this->fullClean)
515 {
516 $keyPrefix = $this->sid . '|' .$dir . '|';
517 $partitionKeys = $this->getSet($baseListKey);
518
519 foreach ($partitionKeys as $partition)
520 {
521 $baseListKeyPartition = $baseListKey . $partition;
522 $keys = $this->getSet($baseListKeyPartition);
523
524 foreach ($keys as $initKey)
525 {
526 $this->deleteBySet($initKey, $keyPrefix);
527 $this->del($initKey);
528 }
529
530 $this->del($baseListKeyPartition);
531 unset($keys);
532 }
533
534 $this->del($baseListKey);
535
536 }
537 }
538
539 }
540}
write($vars, $baseDir, $initDir, $filename, $ttl)
__construct(array $options=[])
deleteBySet($key, $prefix='')
setNotExists($key, $ttl, $value)
getInitDirKey($baseDir, $initDir=false)
unlock(string $key='', int $ttl=0)
lock(string $key='', int $ttl=0)
read(&$vars, $baseDir, $initDir, $filename, $ttl)
getInitDirVersion($baseDir, $initDir=false, $generateNew=true)
clean($baseDir, $initDir=false, $filename=false)