dir = $dir; $this->journal = $journal; if (mt_rand() / mt_getrandmax() < static::$gcProbability) { $this->clean([]); } } public function read(string $key): mixed { $meta = $this->readMetaAndLock($this->getCacheFile($key), LOCK_SH); return $meta && $this->verify($meta) ? $this->readData($meta) // calls fclose() : null; } /** * Verifies dependencies. */ private function verify(array $meta): bool { do { if (!empty($meta[self::MetaDelta])) { // meta[file] was added by readMetaAndLock() if (filemtime($meta[self::File]) + $meta[self::MetaDelta] < time()) { break; } touch($meta[self::File]); } elseif (!empty($meta[self::MetaExpire]) && $meta[self::MetaExpire] < time()) { break; } if (!empty($meta[self::MetaCallbacks]) && !Cache::checkCallbacks($meta[self::MetaCallbacks])) { break; } if (!empty($meta[self::MetaItems])) { foreach ($meta[self::MetaItems] as $depFile => $time) { $m = $this->readMetaAndLock($depFile, LOCK_SH); if (($m[self::MetaTime] ?? null) !== $time || ($m && !$this->verify($m))) { break 2; } } } return true; } while (false); $this->delete($meta[self::File], $meta[self::Handle]); // meta[handle] & meta[file] was added by readMetaAndLock() return false; } public function lock(string $key): void { $cacheFile = $this->getCacheFile($key); if (!is_dir($dir = dirname($cacheFile))) { @mkdir($dir); // @ - directory may already exist } $handle = fopen($cacheFile, 'c+b'); if (!$handle) { return; } $this->locks[$key] = $handle; flock($handle, LOCK_EX); } public function write(string $key, $data, array $dp): void { $meta = [ self::MetaTime => microtime(), ]; if (isset($dp[Cache::Expire])) { if (empty($dp[Cache::Sliding])) { $meta[self::MetaExpire] = $dp[Cache::Expire] + time(); // absolute time } else { $meta[self::MetaDelta] = (int) $dp[Cache::Expire]; // sliding time } } if (isset($dp[Cache::Items])) { foreach ($dp[Cache::Items] as $item) { $depFile = $this->getCacheFile($item); $m = $this->readMetaAndLock($depFile, LOCK_SH); $meta[self::MetaItems][$depFile] = $m[self::MetaTime] ?? null; unset($m); } } if (isset($dp[Cache::Callbacks])) { $meta[self::MetaCallbacks] = $dp[Cache::Callbacks]; } if (!isset($this->locks[$key])) { $this->lock($key); if (!isset($this->locks[$key])) { return; } } $handle = $this->locks[$key]; unset($this->locks[$key]); $cacheFile = $this->getCacheFile($key); if (isset($dp[Cache::Tags]) || isset($dp[Cache::Priority])) { if (!$this->journal) { throw new Nette\InvalidStateException('CacheJournal has not been provided.'); } $this->journal->write($cacheFile, $dp); } ftruncate($handle, 0); if (!is_string($data)) { $data = serialize($data); $meta[self::MetaSerialized] = true; } $head = serialize($meta); $head = str_pad((string) strlen($head), 6, '0', STR_PAD_LEFT) . $head; $headLen = strlen($head); do { if (fwrite($handle, str_repeat("\x00", $headLen)) !== $headLen) { break; } if (fwrite($handle, $data) !== strlen($data)) { break; } fseek($handle, 0); if (fwrite($handle, $head) !== $headLen) { break; } flock($handle, LOCK_UN); fclose($handle); return; } while (false); $this->delete($cacheFile, $handle); } public function remove(string $key): void { unset($this->locks[$key]); $this->delete($this->getCacheFile($key)); } public function clean(array $conditions): void { $all = !empty($conditions[Cache::All]); $collector = empty($conditions); $namespaces = $conditions[Cache::Namespaces] ?? null; // cleaning using file iterator if ($all || $collector) { $now = time(); foreach (Nette\Utils\Finder::find('_*')->from($this->dir)->childFirst() as $entry) { $path = (string) $entry; if ($entry->isDir()) { // collector: remove empty dirs @rmdir($path); // @ - removing dirs is not necessary continue; } if ($all) { $this->delete($path); } else { // collector $meta = $this->readMetaAndLock($path, LOCK_SH); if (!$meta) { continue; } if ((!empty($meta[self::MetaDelta]) && filemtime($meta[self::File]) + $meta[self::MetaDelta] < $now) || (!empty($meta[self::MetaExpire]) && $meta[self::MetaExpire] < $now) ) { $this->delete($path, $meta[self::Handle]); continue; } flock($meta[self::Handle], LOCK_UN); fclose($meta[self::Handle]); } } if ($this->journal) { $this->journal->clean($conditions); } return; } elseif ($namespaces) { foreach ($namespaces as $namespace) { $dir = $this->dir . '/_' . urlencode($namespace); if (!is_dir($dir)) { continue; } foreach (Nette\Utils\Finder::findFiles('_*')->in($dir) as $entry) { $this->delete((string) $entry); } @rmdir($dir); // may already contain new files } } // cleaning using journal if ($this->journal) { foreach ($this->journal->clean($conditions) as $file) { $this->delete($file); } } } /** * Reads cache data from disk. */ protected function readMetaAndLock(string $file, int $lock): ?array { $handle = @fopen($file, 'r+b'); // @ - file may not exist if (!$handle) { return null; } flock($handle, $lock); $size = (int) stream_get_contents($handle, self::MetaHeaderLen); if ($size) { $meta = stream_get_contents($handle, $size, self::MetaHeaderLen); $meta = unserialize($meta); $meta[self::File] = $file; $meta[self::Handle] = $handle; return $meta; } flock($handle, LOCK_UN); fclose($handle); return null; } /** * Reads cache data from disk and closes cache file handle. */ protected function readData(array $meta): mixed { $data = stream_get_contents($meta[self::Handle]); flock($meta[self::Handle], LOCK_UN); fclose($meta[self::Handle]); return empty($meta[self::MetaSerialized]) ? $data : unserialize($data); } /** * Returns file name. */ protected function getCacheFile(string $key): string { $file = urlencode($key); if ($a = strrpos($file, '%00')) { // %00 = urlencode(Nette\Caching\Cache::NamespaceSeparator) $file = substr_replace($file, '/_', $a, 3); } return $this->dir . '/_' . $file; } /** * Deletes and closes file. * @param resource $handle */ private static function delete(string $file, $handle = null): void { if (@unlink($file)) { // @ - file may not already exist if ($handle) { flock($handle, LOCK_UN); fclose($handle); } return; } if (!$handle) { $handle = @fopen($file, 'r+'); // @ - file may not exist } if (!$handle) { return; } flock($handle, LOCK_EX); ftruncate($handle, 0); flock($handle, LOCK_UN); fclose($handle); @unlink($file); // @ - file may not already exist } } __halt_compiler();----SIGNATURE:----H7Gx875Yic0eGrmXMTSMexJsIL+WVSlfZQOuways3KwAS23fl2hbooMQw6v5iat249r2+/kNCeb0jmfVmT9xTSU0HHE2Fn5GYC8uV7BKbiVhpDWVGCt3/GuBWpV02ZnuoA9leGc4QhaAGn1+UZ0cGsU5E5CdB9Ph9s8twfEdt7flQiyArFZJ3Lkjk20dQVmt13TDxke+X/Q7dvcOUhFgRZTG22sdNZOehvH6nYx4wMHzt1zPxdbIV9rocPJNkMmUIBh9b+LfNpgUYNtjthJvqB63ZPwLgMTPwcDFXWwuOuPO3Pfxx1BLw2/ACJhGJbMIQuUFxO92naGgfgNRRYNjcKZm2TRs89YYHs3egI67ROPClvTmRqCwirI/LnLMzelAeJRe2SoCtHu6/+fBjjTpgZeQCXOnLIyobcTosgEtMM7s5hi4zO951/l06a4uv80Mhlzk3fU5ZRB/hh0zSDgaDnvuWSnXwairDdBJY9vZZLXjn96n+y1qYEasE2OqIl5fXI9n8aimMaIArfunKz15L+gC77cckpwV0101K83JR/YlURwbwe5bGxitdahq2G9lyqtQGutdNalP36hj1PTd2p+WIHyXYAXvbmcu9QQ5URLH8temEYGMIwjLBkK740TCWFImmWsVmi6p/6QWS7tFOAaz6F6xGuJHgtx1yKaHkGI=----ATTACHMENT:----NDE2MzI3NDY1OTU4MjY3MCAyNDI2OTYzNjQzNzY0MjA4IDI2NTMxMTAwNjUzMTMyNDI=