View Raw
<?php
class IndexerCache {
    private $cacheType;
    private $indexCacheDir;
    private $pdo = null;
    public function __construct($cacheType, $indexCacheDir) {
        $this->cacheType = $cacheType;
        $this->indexCacheDir = $indexCacheDir;
        if ($this->cacheType === 'sqlite') {
            $this->initializeSQLite();
        }
    }
    private function initializeSQLite() {
        if (!is_dir($this->indexCacheDir)) {
            mkdir($this->indexCacheDir, 0755, true);
        }
        $dbPath = $this->indexCacheDir . '/cache.sqlite';
        try {
            $this->pdo = new PDO('sqlite:' . $dbPath);
            $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->pdo->exec("CREATE TABLE IF NOT EXISTS unified_cache (
                cache_key TEXT PRIMARY KEY,
                cache_type TEXT NOT NULL,
                data TEXT NOT NULL,
                last_modified INTEGER NOT NULL,
                expires_at INTEGER DEFAULT NULL
            )");
            $this->pdo->exec("CREATE INDEX IF NOT EXISTS idx_cache_type ON unified_cache(cache_type)");
            $this->pdo->exec("CREATE INDEX IF NOT EXISTS idx_expires_at ON unified_cache(expires_at)");
        } catch (Exception $e) {
            $this->cacheType = 'json';
        }
    }
    public function get($key, $type = 'directory') {
        if ($this->cacheType === 'sqlite') {
            return $this->getSQLiteCache($key, $type);
        } else {
            return $this->getJSONCache($key, $type);
        }
    }
    public function set($key, $type, $data, $ttl = null) {
        if ($this->cacheType === 'sqlite') {
            $this->setSQLiteCache($key, $type, $data, $ttl);
        } else {
            $this->setJSONCache($key, $type, $data, $ttl);
        }
    }
    public function cleanup() {
        if ($this->cacheType === 'sqlite') {
            $this->cleanupSQLite();
        } else {
            $this->cleanupJSON();
        }
    }
    private function getSQLiteCache($key, $type) {
        if (!$this->pdo) return null;
        try {
            $stmt = $this->pdo->prepare("SELECT data, last_modified, expires_at FROM unified_cache WHERE cache_key = ? AND cache_type = ?");
            $stmt->execute([$key, $type]);
            $result = $stmt->fetch(PDO::FETCH_ASSOC);
            if ($result) {
                if ($result['expires_at'] && time() > $result['expires_at']) {
                    $this->pdo->prepare("DELETE FROM unified_cache WHERE cache_key = ? AND cache_type = ?")->execute([$key, $type]);
                    return null;
                }
                if ($type === 'directory') {
                    $actualPath = preg_replace('/_sort_.*$/', '', $key);
                    $currentModified = $this->getPathLastModified($actualPath);
                    if ($result['last_modified'] < $currentModified) {
                        $this->pdo->prepare("DELETE FROM unified_cache WHERE cache_key = ? AND cache_type = ?")->execute([$key, $type]);
                        return null;
                    }
                }
                return json_decode($result['data'], true);
            }
        } catch (Exception $e) {
            error_log("Cache error: " . $e->getMessage());
        }
        return null;
    }
    private function setSQLiteCache($key, $type, $data, $ttl = null) {
        if (!$this->pdo) return;
        try {
            $expiresAt = $ttl ? time() + $ttl : null;
            if ($type === 'directory') {
                $actualPath = preg_replace('/_sort_.*$/', '', $key);
                $lastModified = $this->getPathLastModified($actualPath);
            } else {
                $lastModified = time();
            }
            $stmt = $this->pdo->prepare("INSERT OR REPLACE INTO unified_cache (cache_key, cache_type, data, last_modified, expires_at) VALUES (?, ?, ?, ?, ?)");
            $stmt->execute([$key, $type, json_encode($data), $lastModified, $expiresAt]);
        } catch (Exception $e) {
            error_log("Cache set error: " . $e->getMessage());
        }
    }
    private function getJSONCache($key, $type) {
        if (!is_dir($this->indexCacheDir)) {
            mkdir($this->indexCacheDir, 0755, true);
        }
        $cacheFile = $this->indexCacheDir . '/cache.json';
        if (!file_exists($cacheFile)) {
            return null;
        }
        $cacheData = json_decode(file_get_contents($cacheFile), true) ?: [];
        $cacheKey = $type . ':' . $key;
        if (isset($cacheData[$cacheKey])) {
            $item = $cacheData[$cacheKey];
            if (isset($item['expires_at']) && $item['expires_at'] && time() > $item['expires_at']) {
                unset($cacheData[$cacheKey]);
                file_put_contents($cacheFile, json_encode($cacheData, JSON_PRETTY_PRINT));
                return null;
            }
            if ($type === 'directory') {
                $actualPath = preg_replace('/_sort_.*$/', '', $key);
                $currentModified = $this->getPathLastModified($actualPath);
                if ($item['last_modified'] < $currentModified) {
                    unset($cacheData[$cacheKey]);
                    file_put_contents($cacheFile, json_encode($cacheData, JSON_PRETTY_PRINT));
                    return null;
                }
            }
            return $item['data'];
        }
        return null;
    }
    private function setJSONCache($key, $type, $data, $ttl = null) {
        if (!is_dir($this->indexCacheDir)) {
            mkdir($this->indexCacheDir, 0755, true);
        }
        $cacheFile = $this->indexCacheDir . '/cache.json';
        $cacheData = [];
        if (file_exists($cacheFile)) {
            $cacheData = json_decode(file_get_contents($cacheFile), true) ?: [];
        }
        $cacheKey = $type . ':' . $key;
        $expiresAt = $ttl ? time() + $ttl : null;
        if ($type === 'directory') {
            $actualPath = preg_replace('/_sort_.*$/', '', $key);
            $lastModified = $this->getPathLastModified($actualPath);
        } else {
            $lastModified = time();
        }
        $cacheData[$cacheKey] = [
            'cache_type' => $type,
            'data' => $data,
            'last_modified' => $lastModified,
            'expires_at' => $expiresAt
        ];
        file_put_contents($cacheFile, json_encode($cacheData, JSON_PRETTY_PRINT));
    }
    private function cleanupSQLite() {
        if (!$this->pdo) return;
        try {
            $this->pdo->prepare("DELETE FROM unified_cache WHERE expires_at IS NOT NULL AND expires_at < ?")->execute([time()]);
        } catch (Exception $e) {
        }
    }
    private function cleanupJSON() {
        $cacheFile = $this->indexCacheDir . '/cache.json';
        if (!file_exists($cacheFile)) {
            return;
        }
        $cacheData = json_decode(file_get_contents($cacheFile), true) ?: [];
        $modified = false;
        foreach ($cacheData as $cacheKey => $item) {
            if (isset($item['expires_at']) && $item['expires_at'] && time() > $item['expires_at']) {
                unset($cacheData[$cacheKey]);
                $modified = true;
            }
        }
        if ($modified) {
            file_put_contents($cacheFile, json_encode($cacheData, JSON_PRETTY_PRINT));
        }
    }
    private function getPathLastModified($path) {
        global $baseDir, $configFile;
        $fullPath = $baseDir . '/' . ltrim($path, '/');
        if (!is_dir($fullPath)) return 0;
        $lastModified = filemtime($fullPath);
        $items = scandir($fullPath);
        foreach ($items as $item) {
            if ($item == '.' || $item == '..') continue;
            $itemPath = $fullPath . '/' . $item;
            if (is_readable($itemPath)) {
                $itemModified = filemtime($itemPath);
                if ($itemModified > $lastModified) {
                    $lastModified = $itemModified;
                }
                if (is_dir($itemPath)) {
                    $subItems = @scandir($itemPath);
                    if ($subItems) {
                        foreach ($subItems as $subItem) {
                            if ($subItem == '.' || $subItem == '..') continue;
                            $subItemPath = $itemPath . '/' . $subItem;
                            if (is_readable($subItemPath)) {
                                $subItemModified = filemtime($subItemPath);
                                if ($subItemModified > $lastModified) {
                                    $lastModified = $subItemModified;
                                }
                            }
                        }
                    }
                }
            }
        }
        if (file_exists($configFile)) {
            $configModified = filemtime($configFile);
            if ($configModified > $lastModified) {
                $lastModified = $configModified;
            }
        }
        return $lastModified;
    }
}
?>