1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
cacheenginefiles.php
См. документацию.
1<?php
2
3namespace Bitrix\Main\Data;
4
5use Bitrix\Main;
6use Bitrix\Main\Application;
7use Bitrix\Main\Config;
8use Bitrix\Main\Data\Internal\CacheCleanPathTable;
9
11{
12 protected const LOCK_FILE = '/bitrix/cache/cacheCleanJob_lock.php';
13
14 protected int $written = 0;
15 protected int $read = 0;
16 protected string $path = '';
17 protected bool $useLock = false;
18 protected string $rootDirectory;
19 protected static array $lockHandles = [];
20 protected static int $clusterGroup = 0;
21
26 public function __construct($options = [])
27 {
29
30 if (isset($config['use_lock']))
31 {
32 $this->useLock = (bool)$config['use_lock'];
33 }
34 if (isset($options['actual_data']) && !$this->useLock)
35 {
36 $this->useLock = !$options['actual_data'];
37 }
38
39 static::$clusterGroup = (defined('BX_CLUSTER_GROUP') ? (int)constant('BX_CLUSTER_GROUP') : 0);
40
41 $this->rootDirectory = $config['root_directory'] ?? Main\Loader::getDocumentRoot();
42
43 $key = $this->rootDirectory . self::LOCK_FILE;
44 if (!file_exists($key))
45 {
46 if ($handle = fopen($key, "wb+"))
47 {
48 fwrite($handle, 'lock');
49 fclose($handle);
50 }
51 }
52
53 if ($this->lock($key))
54 {
55 Application::getInstance()->addBackgroundJob([$this, 'delayedDelete'], [], Application::JOB_PRIORITY_LOW);
56 }
57 }
58
62 public function getReadBytes()
63 {
64 return $this->read;
65 }
66
70 public function getWrittenBytes()
71 {
72 return $this->written;
73 }
74
78 public function getCachePath()
79 {
80 return $this->path;
81 }
82
86 public function isAvailable()
87 {
88 return true;
89 }
90
96 protected function unlink(string $fileName): void
97 {
98 $this->unlock($fileName);
99
100 if (file_exists($fileName))
101 {
102 // Handle E_WARNING
103 set_error_handler(function () {
104 // noop
105 });
106
107 chmod($fileName, BX_FILE_PERMISSIONS);
109
110 restore_error_handler();
111 }
112 }
113
120 protected function randomizeFile(string $fileName): string
121 {
122 for ($i = 0; $i < 99; $i++)
123 {
124 $suffix = rand(0, 999999);
125 if (!file_exists($this->rootDirectory . $fileName . $suffix))
126 {
127 return $fileName . $suffix;
128 }
129 }
130
131 return '';
132 }
133
137 public function clean($baseDir, $initDir = '', $filename = '')
138 {
139 if (($filename !== false) && ($filename !== ''))
140 {
141 $this->unlink($this->rootDirectory . $baseDir . $initDir . $filename);
142 }
143 else
144 {
145 $initDir = trim($initDir, '/');
146 if ($initDir == '')
147 {
148 $sourceDir = $this->rootDirectory . '/' . trim($baseDir, '/');
149 if (file_exists($sourceDir) && is_dir($sourceDir))
150 {
151 $dh = opendir($sourceDir);
152 if (is_resource($dh))
153 {
154 while ($entry = readdir($dh))
155 {
156 if (preg_match("/^(\\.|\\.\\.|.*\\.~\\d+)\$/", $entry))
157 {
158 continue;
159 }
160
161 if (is_dir($sourceDir . '/' . $entry))
162 {
163 $this->clean($baseDir, $entry);
164 }
165 elseif (is_file($sourceDir . '/' . $entry))
166 {
167 $this->unlink($sourceDir . '/' . $entry);
168 }
169 }
170 }
171 }
172 }
173 else
174 {
175 $source = '/' . trim($baseDir, '/') . '/' . $initDir;
176 $source = rtrim($source, '/');
177 $delayedDelete = false;
178
179 if (!preg_match("/^(\\.|\\.\\.|.*\\.~\\d+)\$/", $source) && file_exists($this->rootDirectory . $source))
180 {
181 if (is_file($this->rootDirectory . $source))
182 {
183 $this->unlink($this->rootDirectory . $source);
184 }
185 else
186 {
187 $target = $this->randomizeFile($source . '.~');
188 if ($target != '')
189 {
190 CacheCleanPathTable::add([
191 'PREFIX' => $target,
192 'CLUSTER_GROUP' => static::$clusterGroup
193 ]);
194
195 if (@rename($this->rootDirectory . $source, $this->rootDirectory . $target))
196 {
197 $delayedDelete = true;
198 }
199 }
200 }
201 }
202
203 if ($delayedDelete)
204 {
205 Application::getInstance()->getManagedCache()->read(3600, 'needClean');
206 Application::getInstance()->getManagedCache()->setImmediate('needClean', 'Y');
207 }
208 else
209 {
210 DeleteDirFilesEx($baseDir . $initDir, $this->rootDirectory);
211 }
212 }
213 }
214 }
215
222 protected function lock(string $fileName): bool
223 {
224 $wouldBlock = 0;
225 self::$lockHandles[$fileName] = @fopen($fileName, "r+");
226 if (self::$lockHandles[$fileName])
227 {
228 flock(self::$lockHandles[$fileName], LOCK_EX | LOCK_NB, $wouldBlock);
229 }
230 return $wouldBlock !== 1;
231 }
232
238 protected function unlock(string $fileName): void
239 {
240 if (!empty(self::$lockHandles[$fileName]))
241 {
242 fclose(self::$lockHandles[$fileName]);
243 unset(self::$lockHandles[$fileName]);
244 }
245 }
246
250 public function read(&$vars, $baseDir, $initDir, $filename, $ttl)
251 {
252 $fn = $this->rootDirectory . '/' . ltrim($baseDir . $initDir, '/') . $filename;
253
254 if (!file_exists($fn))
255 {
256 return false;
257 }
258
259 $ser_content = '';
260 $dateexpire = 0;
261 $datecreate = 0;
262 $zeroDanger = false;
263
264 $handle = null;
265 if (is_array($vars))
266 {
267 $INCLUDE_FROM_CACHE = 'Y';
268
269 if (!@include($fn))
270 {
271 return false;
272 }
273 }
274 else
275 {
276 $handle = fopen($fn, 'rb');
277 if (!$handle)
278 {
279 return false;
280 }
281
282 $datecreate = fread($handle, 2);
283 if ($datecreate == 'BX')
284 {
285 $datecreate = fread($handle, 12);
286 fread($handle, 12); // unused dateexpire
287 }
288 else
289 {
290 $datecreate .= fread($handle, 10);
291 }
292 }
293
294 $this->read = @filesize($fn);
295 $this->path = $fn;
296
297 $res = true;
298 if (intval($datecreate) < (time() - $ttl))
299 {
300 if ($this->useLock)
301 {
302 if ($this->lock($fn))
303 {
304 $res = false;
305 }
306 }
307 else
308 {
309 $res = false;
310 }
311 }
312
313 if ($res)
314 {
315 if (is_array($vars))
316 {
317 $vars = unserialize($ser_content);
318 }
319 else
320 {
321 $vars = fread($handle, $this->read);
322 }
323 }
324
325 if ($handle)
326 {
327 fclose($handle);
328 }
329
330 return $res;
331 }
332
336 public function write($vars, $baseDir, $initDir, $filename, $ttl)
337 {
338 static $search = ["\\", "'", "\0"];
339 static $replace = ["\\\\", "\\'", "'.chr(0).'"];
340
341 $ttl = (int) $ttl;
342 $folder = $this->rootDirectory . '/' . ltrim($baseDir . $initDir, '/');
343 $fn = $folder . $filename;
344 $fnTmp = $folder . md5(mt_rand()) . '.tmp';
345
346 if (!CheckDirPath($fn))
347 {
348 return;
349 }
350
351 if ($handle = fopen($fnTmp, "wb+"))
352 {
353 if (is_array($vars))
354 {
355 $contents = "<?";
356 $contents .= "\nif(\$INCLUDE_FROM_CACHE!='Y')return false;";
357 $contents .= "\n\$datecreate = '" . str_pad(time(), 12, "0", STR_PAD_LEFT) . "';";
358 $contents .= "\n\$dateexpire = '" . str_pad(time() + intval($ttl), 12, "0", STR_PAD_LEFT) . "';";
359 $contents .= "\n\$ser_content = '" . str_replace($search, $replace, serialize($vars)) . "';";
360 $contents .= "\nreturn true;";
361 $contents .= "\n?>";
362 }
363 else
364 {
365 $contents = "BX" . str_pad(time(), 12, "0", STR_PAD_LEFT) . str_pad(time() + $ttl, 12, "0", STR_PAD_LEFT);
366 $contents .= $vars;
367 }
368
369 $this->written = fwrite($handle, $contents);
370 $this->path = $fn;
371 $len = strlen($contents);
372
373 fclose($handle);
374 $this->unlink($fn);
375
376 if ($this->written === $len)
377 {
378 rename($fnTmp, $fn);
379 }
380
381 $this->unlink($fnTmp);
382
383 if ($this->useLock)
384 {
385 $this->unlock($fn);
386 }
387 }
388 }
389
393 public function isCacheExpired($path)
394 {
395 if (!file_exists($path))
396 {
397 return true;
398 }
399
400 $fileHandler = fopen($path, 'rb');
401 if ($fileHandler)
402 {
403 $header = fread($fileHandler, 150);
404 fclose($fileHandler);
405 }
406 else
407 {
408 return true;
409 }
410
411 if (
412 preg_match("/dateexpire\\s*=\\s*'(\\d+)'/im", $header, $match)
413 || preg_match("/^BX\\d{12}(\\d{12})/", $header, $match)
414 || preg_match("/^(\\d{12})/", $header, $match)
415 )
416 {
417 if ($match[1] == '' || doubleval($match[1]) < time())
418 {
419 return true;
420 }
421 }
422
423 return false;
424 }
425
432 protected function deleteOneDir(int $etime = 0, array $path = []): void
433 {
434 if (empty($path))
435 {
436 return;
437 }
438
439 $deleteFromQueue = false;
440 $root = $this->rootDirectory;
441 $dirName = $root . $path['PREFIX'];
442
443 if ($path['PREFIX'] != '' && file_exists($dirName))
444 {
445 if (is_file($dirName))
446 {
447 DeleteDirFilesEx($path['PREFIX'], $root);
448 $deleteFromQueue = true;
449 }
450 elseif (($dir = scandir($dirName)) !== false)
451 {
453 foreach ($dir as $file)
454 {
455 $counter--;
456 if ($file != '.' && $file != '..')
457 {
458 DeleteDirFilesEx($path['PREFIX'] . '/' . $file, $root);
459 }
460
461 if (time() > $etime)
462 {
463 break;
464 }
465 }
466
467 if ($counter == 0)
468 {
469 rmdir($dirName);
470 $deleteFromQueue = true;
471 }
472 }
473 }
474 else
475 {
476 $deleteFromQueue = true;
477 }
478
479 if ($deleteFromQueue)
480 {
481 CacheCleanPathTable::delete($path['ID']);
482 }
483 }
484
489 public function delayedDelete(): void
490 {
491 $delta = 10;
492 $deleted = 0;
493 $etime = time() + 5;
494
495 $managedCache = Application::getInstance()->getManagedCache();
496 $needClean = $managedCache->read(3600, 'needClean');
497
498 if ($needClean != 'Y')
499 {
500 $this->unlock($this->rootDirectory . self::LOCK_FILE);
501 return;
502 }
503
504 $count = (int)$managedCache->getImmediate(604800, 'delCount');
505 if ($count < 1)
506 {
507 $count = 1;
508 }
509
510 $paths = CacheCleanPathTable::query()
511 ->setSelect(['ID', 'PREFIX'])
512 ->where('CLEAN_FROM', '<=', new \Bitrix\Main\Type\DateTime())
513 ->where('CLUSTER_GROUP', static::$clusterGroup)
514 ->setLimit($count + $delta)
515 ->exec();
516
517 while($path = $paths->fetch())
518 {
519 $this->deleteOneDir($etime, $path);
520 $deleted++;
521
522 if (time() > $etime)
523 {
524 break;
525 }
526 }
527
528 if ($deleted > $count)
529 {
530 $count = $deleted;
531 }
532 elseif ($deleted < $count && $count > 1)
533 {
534 $count--;
535 }
536
537 $managedCache->read(604800, 'delCount');
538 if ($deleted > $count)
539 {
540 $managedCache->setImmediate('delCount', $deleted);
541 }
542 elseif ($deleted < $count && $count > 1)
543 {
544 $managedCache->setImmediate('delCount', $deleted);
545 }
546
547 if ($deleted == 0)
548 {
549 $managedCache->read(3600, 'needClean');
550 $managedCache->setImmediate('needClean', $deleted);
551 }
552
553 $this->unlock($this->rootDirectory . self::LOCK_FILE);
554 }
555}
$count
Определения admin_tab.php:4
const JOB_PRIORITY_LOW
Определения application.php:32
static getInstance()
Определения application.php:98
static getValue($name)
Определения configuration.php:24
clean($baseDir, $initDir='', $filename='')
Определения cacheenginefiles.php:137
write($vars, $baseDir, $initDir, $filename, $ttl)
Определения cacheenginefiles.php:336
lock(string $fileName)
Определения cacheenginefiles.php:222
deleteOneDir(int $etime=0, array $path=[])
Определения cacheenginefiles.php:432
unlink(string $fileName)
Определения cacheenginefiles.php:96
randomizeFile(string $fileName)
Определения cacheenginefiles.php:120
read(&$vars, $baseDir, $initDir, $filename, $ttl)
Определения cacheenginefiles.php:250
static array $lockHandles
Определения cacheenginefiles.php:19
__construct($options=[])
Определения cacheenginefiles.php:26
static int $clusterGroup
Определения cacheenginefiles.php:20
unlock(string $fileName)
Определения cacheenginefiles.php:238
static getDocumentRoot()
Определения loader.php:254
$options
Определения commerceml2.php:49
$contents
Определения commerceml2.php:57
$filename
Определения file_edit.php:47
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$res
Определения filter_act.php:7
$handle
Определения include.php:55
CheckDirPath($path)
Определения tools.php:2707
DeleteDirFilesEx($path, $root=null)
Определения tools.php:2823
Определения collection.php:2
$counter
Определения options.php:5
$delta
Определения prolog_main_admin.php:363
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$dir
Определения quickway.php:303
$config
Определения quickway.php:69
$fileName
Определения quickway.php:305
if(empty($signedUserToken)) $key
Определения quickway.php:257
$i
Определения factura.php:643
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
$paths
Определения options.php:2080
path
Определения template_copy.php:201