1<?php
2
3
4namespace ComboStrap;
5
6
7use DateTime;
8
9/**
10 * Class CacheManager
11 * @package ComboStrap
12 *
13 * The cache manager is public static object
14 * that can be used by plugin to report cache dependency {@link CacheManager::addDependencyForCurrentSlot()}
15 * reports and influence the cache
16 * of all slot for a requested page
17 */
18class CacheManager
19{
20
21
22    const CACHE_DELETION = "deletion";
23    const CACHE_CREATION = "creation";
24
25
26    /**
27     * @var CacheManager
28     */
29    private static $cacheManager;
30
31
32    /**
33     * The list of cache runtimes dependencies by slot {@link CacheDependencies}
34     */
35    private $slotCacheDependencies;
36    /**
37     * The list of cache results slot {@link CacheResults}
38     */
39    private $slotCacheResults;
40
41    /**
42     * @var array hold the result for slot cache expiration
43     */
44    private $slotsExpiration;
45
46
47    /**
48     * @return CacheManager
49     */
50    public static function getOrCreate(): CacheManager
51    {
52        try {
53            $page = Page::createPageFromRequestedPage();
54            $cacheKey = $page->getDokuwikiId();
55        } catch (ExceptionCombo $e) {
56            /**
57             * In test, we may generate html from snippet without
58             * request. No error in this case
59             */
60            if (!PluginUtility::isTest()) {
61                LogUtility::msg("The cache manager cannot find the requested page. Cache Errors may occurs. Error: {$e->getMessage()}");
62            }
63            $cacheKey = PluginUtility::getRequestId();
64        }
65        $cacheManager = self::$cacheManager[$cacheKey];
66        if ($cacheManager === null) {
67            // new run, delete all old cache managers
68            self::$cacheManager = [];
69            // create
70            $cacheManager = new CacheManager();
71            self::$cacheManager[$cacheKey] = $cacheManager;
72        }
73        return $cacheManager;
74    }
75
76
77    public static function resetAndGet(): CacheManager
78    {
79        self::reset();
80        return self::getOrCreate();
81    }
82
83    /**
84     * @param $id
85     * @return CacheDependencies
86     */
87    public function getCacheDependenciesForSlot($id): CacheDependencies
88    {
89
90        $cacheRuntimeDependencies = $this->slotCacheDependencies[$id];
91        if ($cacheRuntimeDependencies === null) {
92            $cacheRuntimeDependencies = new CacheDependencies($id);
93            $this->slotCacheDependencies[$id] = $cacheRuntimeDependencies;
94        }
95        return $cacheRuntimeDependencies;
96
97    }
98
99    /**
100     * In test, we may run more than once
101     * This function delete the cache manager
102     * and is called
103     * when a new request is created {@link \TestUtility::createTestRequest()}
104     */
105    public static function reset()
106    {
107
108        self::$cacheManager = null;
109
110    }
111
112
113    public function isCacheResultPresentForSlot($slotId, $mode): bool
114    {
115        $cacheReporter = $this->getCacheResultsForSlot($slotId);
116        return $cacheReporter->hasResultForMode($mode);
117    }
118
119
120    public function hasNoCacheResult(): bool
121    {
122        if($this->slotCacheResults===null){
123            return true;
124        }
125        return sizeof($this->slotCacheResults) === 0;
126    }
127
128    /**
129     * @param string $dependencyName
130     * @return CacheManager
131     */
132    public function addDependencyForCurrentSlot(string $dependencyName): CacheManager
133    {
134        $ID = PluginUtility::getCurrentSlotId();
135        $cacheDependencies = $this->getCacheDependenciesForSlot($ID);
136        $cacheDependencies->addDependency($dependencyName);
137        return $this;
138
139    }
140
141
142    public function getCacheResultsForSlot(string $id): CacheResults
143    {
144        $cacheManagerForSlot = $this->slotCacheResults[$id];
145        if ($cacheManagerForSlot === null) {
146            $cacheManagerForSlot = new CacheResults($id);
147            $this->slotCacheResults[$id] = $cacheManagerForSlot;
148        }
149        return $cacheManagerForSlot;
150    }
151
152    /**
153     * @return null|CacheResults[] - null if the page does not exists
154     */
155    public function getCacheResults(): ?array
156    {
157        return $this->slotCacheResults;
158    }
159
160    /**
161     * @throws ExceptionCombo
162     */
163    public function shouldSlotExpire($pageId): bool
164    {
165
166        /**
167         * Because of the recursive nature of rendering
168         * inside dokuwiki, we just return a result for
169         * the first call to the function
170         *
171         * We use the cache manager as scope element
172         * (ie it's {@link CacheManager::reset()} for each request
173         */
174        if (isset($this->slotsExpiration[$pageId])) {
175            return false;
176        }
177
178        $page = Page::createPageFromId($pageId);
179        $cacheExpirationFrequency = CacheExpirationFrequency::createForPage($page)
180            ->getValue();
181        if ($cacheExpirationFrequency === null) {
182            $this->slotsExpiration[$pageId] = false;
183            return false;
184        }
185
186        $cacheExpirationDateMeta = CacheExpirationDate::createForPage($page);
187        $expirationDate = $cacheExpirationDateMeta->getValue();
188
189        if ($expirationDate === null) {
190
191            $expirationDate = Cron::getDate($cacheExpirationFrequency);
192            $cacheExpirationDateMeta->setValue($expirationDate);
193
194        }
195
196
197        $actualDate = new DateTime();
198        if ($expirationDate > $actualDate) {
199            $this->slotsExpiration[$pageId] = false;
200            return false;
201        }
202
203        $this->slotsExpiration[$pageId] = true;
204        return true;
205
206    }
207
208}
209