indexLocation = $directory . '/' . md5($directory) . 'index'; $this->ttlLocation = $directory . '/' . md5($directory) . 'ttl'; $this->directory = $directory; $this->serializer = $serializer; $this->directorySeparator = $directorySeparator; $this->chmod = $chmod; $this->ttl = $ttl; if (file_exists($this->indexLocation)) { $this->index = json_decode(file_get_contents($this->indexLocation), true); } else { $this->index = []; } if (file_exists($this->ttlLocation)) { $this->timeToLive = json_decode(file_get_contents($this->ttlLocation), true); } else { $this->timeToLive = []; } } /** * @param string $key * @return mixed */ public function get(string $key) { $file = $this->getFilename($key); if ($this->exists($file)) { if (isset($this->timeToLive[$key])) { if ($this->ttl($key) > 0) { return $this->serializer->deserialize(file_get_contents($file)); } } else { return $this->serializer->deserialize(file_get_contents($file)); } } return null; } /** * @param string $key * @param $value * @param int|null $ttl * @return void */ public function set(string $key, $value, int $ttl = null): void { $file = $this->getFilename($key); file_put_contents($file, $this->serializer->serialize($value)); if ($this->chmod !== null) { chmod($file, $this->chmod); } if ($ttl !== null) { $this->timeToLive[$key] = $ttl; } else if ($this->ttl !== null) { $this->timeToLive[$key] = $this->ttl; } $this->index[$key] = $file; $this->saveIndex(); } /** * @param string $key */ public function delete(string $key) { if (strpos($key, '*') !== false) { $this->deleteGlob($key); $this->saveIndex(); } else { $file = $this->getFilename($key); if ($this->exists($file)) { unlink($file); } unset($this->index[$key], $this->timeToLive[$key]); $this->saveIndex(); } } /** * @param string $key * @return mixed */ public function keys($key = '*') { $matches = []; foreach ($this->index as $name => $index) { if (fnmatch($key, $name)) { $matches[] = $name; } } return $matches; } /** * @param $key * @return int */ public function ttl($key): int { $file = $this->getFilename($key); if (isset($this->timeToLive[$key])) { return max(filemtime($file) + $this->timeToLive[$key] - time(), 0); } return self::NO_TTL; } /** * @param $pattern */ private function deleteGlob($pattern): void { foreach ($this->index as $key => $value) { if (fnmatch($pattern, $key)) { $this->delete($key); } } } /** * @param $file * @return bool */ private function exists(string $file): bool { return file_exists($file); } /** * @param $key * @return string */ private function getFilename(string $key): string { [$directory, $file] = $this->getDirectoryAndFile($key); return $directory . '/' . md5($file); } /** * @param string $key * @return array */ private function getDirectoryAndFile(string $key): array { $directory = $this->directory; if ($this->directorySeparator !== null) { while (($position = strpos($key, $this->directorySeparator)) !== false) { $dirName = md5(substr($key, 0, $position)); $directory = $directory . '/' . $dirName; $this->createSubDirectoryIfNotExists($directory); $key = substr($key, $position + 1); } } return [$directory, $key]; } /** * @param $directory */ private function createSubDirectoryIfNotExists($directory): void { if (file_exists($directory) === false) { if (!mkdir($directory) && !is_dir($directory)) { throw new \RuntimeException(sprintf('Directory "%s" was not created', $directory)); } if ($this->chmod !== null) { chmod($directory, $this->chmod); } } } private function saveIndex(): void { file_put_contents($this->indexLocation, json_encode($this->index)); file_put_contents($this->ttlLocation, json_encode($this->timeToLive)); } }