1<?php 2 3use ComboStrap\CacheExpirationDate; 4use ComboStrap\CacheExpirationFrequency; 5use ComboStrap\CacheLog; 6use ComboStrap\Cron; 7use ComboStrap\Event; 8use ComboStrap\ExceptionCompile; 9use ComboStrap\ExceptionNotFound; 10use ComboStrap\ExecutionContext; 11use ComboStrap\Iso8601Date; 12use ComboStrap\LogUtility; 13use ComboStrap\MarkupCacheDependencies; 14use ComboStrap\MarkupPath; 15use ComboStrap\PagePath; 16use dokuwiki\Cache\CacheRenderer; 17 18require_once(__DIR__ . '/../vendor/autoload.php'); 19 20/** 21 * Can we use the parser cache 22 * 23 * 24 * 25 */ 26class action_plugin_combo_cacheexpiration extends DokuWiki_Action_Plugin 27{ 28 29 30 const CANONICAL = CacheExpirationFrequency::CANONICAL; 31 const SLOT_CACHE_EXPIRATION_EVENT = "slot-cache-expiration"; 32 const REQUESTED_ID = "requested-id"; 33 34 35 /** 36 * @param Doku_Event_Handler $controller 37 */ 38 function register(Doku_Event_Handler $controller) 39 { 40 41 42 /** 43 * Page expiration feature 44 */ 45 $controller->register_hook('PARSER_CACHE_USE', 'BEFORE', $this, 'slotCreateCacheExpiration', array()); 46 47 48 /** 49 * Process the Async event 50 */ 51 $controller->register_hook(self::SLOT_CACHE_EXPIRATION_EVENT, 'AFTER', $this, 'handleSlotCacheExpiration'); 52 53 } 54 55 56 /** 57 * 58 * Purge the cache if needed 59 * @param Doku_Event $event 60 * @param $params 61 */ 62 function slotCreateCacheExpiration(Doku_Event $event, $params) 63 { 64 65 /** 66 * No cache for all mode 67 * (ie xhtml, instruction) 68 */ 69 $data = &$event->data; 70 $pageId = $data->page; 71 72 /** 73 * For whatever reason, the cache file of XHTML 74 * may be empty - No error found on the web server or the log. 75 * 76 * We just delete it then. 77 * 78 * It has been seen after the creation of a new page or a `move` of the page. 79 */ 80 if ($data instanceof CacheRenderer) { 81 if ($data->mode === "xhtml") { 82 if (file_exists($data->cache)) { 83 if (filesize($data->cache) === 0) { 84 $data->depends["purge"] = true; 85 return; 86 } 87 } 88 } 89 } 90 91 $executionContext = ExecutionContext::getActualOrCreateFromEnv(); 92 $cacheManager = $executionContext->getCacheManager(); 93 $shouldSlotExpire = $cacheManager->shouldSlotExpire($pageId); 94 if ($shouldSlotExpire) { 95 try { 96 $requestedWikiId = $executionContext->getRequestedPath()->getWikiId(); 97 } catch (ExceptionNotFound $e) { 98 LogUtility::internalError("Cache expiration: The requested path could not be determined, default context path was set instead."); 99 $requestedWikiId = $executionContext->getContextPath()->getWikiId(); 100 } 101 Event::createEvent( 102 self::SLOT_CACHE_EXPIRATION_EVENT, 103 [ 104 PagePath::getPersistentName() => $pageId, 105 self::REQUESTED_ID => $requestedWikiId 106 ] 107 ); 108 } 109 110 111 } 112 113 public function handleSlotCacheExpiration($event) 114 { 115 $data = $event->data; 116 $slotPath = $data[PagePath::getPersistentName()]; 117 $requestedId = $data[self::REQUESTED_ID]; 118 119 /** 120 * The cache file may be dependent on the requested id 121 * ie (@link MarkupCacheDependencies::OUTPUT_DEPENDENCIES} 122 */ 123 global $ID; 124 $keep = $ID; 125 try { 126 $ID = $requestedId; 127 $slot = MarkupPath::createPageFromAbsoluteId($slotPath); 128 129 /** 130 * Calculate a new expiration date 131 * And set it here because setting a new metadata 132 * will make the cache unusable 133 */ 134 $cacheExpirationDateMeta = CacheExpirationDate::createForPage($slot); 135 $actualDate = $cacheExpirationDateMeta->getValue(); 136 $cacheExpirationFrequency = CacheExpirationFrequency::createForPage($slot) 137 ->getValue(); 138 try { 139 $newDate = Cron::getDate($cacheExpirationFrequency); 140 } catch (ExceptionCompile $e) { 141 LogUtility::msg("Error while calculating the new expiration date. Error: {$e->getMessage()}"); 142 return; 143 } 144 if ($newDate < $actualDate) { 145 LogUtility::msg("The new calculated date cache expiration frequency ({$newDate->format(Iso8601Date::getFormat())}) is lower than the current date ({$actualDate->format(Iso8601Date::getFormat())})"); 146 } 147 try { 148 $cacheExpirationDateMeta 149 ->setValue($newDate) 150 ->persist(); 151 } catch (ExceptionCompile $e) { 152 LogUtility::msg("Error while persisting the new expiration date. Error:{$e->getMessage()}"); 153 return; 154 } 155 156 /** 157 * Cache deletion 158 */ 159 $message = "Expiration Date has expired"; 160 $outputDocument = $slot->getInstructionsDocument(); 161 CacheLog::deleteCacheIfExistsAndLog( 162 $outputDocument, 163 self::SLOT_CACHE_EXPIRATION_EVENT, 164 $message); 165 $fetcher = $slot->createHtmlFetcherWithItselfAsContextPath(); 166 CacheLog::deleteCacheIfExistsAndLog( 167 $fetcher, 168 self::SLOT_CACHE_EXPIRATION_EVENT, 169 $message); 170 171 /** 172 * Re-render 173 */ 174 $fetcher2 = $slot->createHtmlFetcherWithItselfAsContextPath(); 175 CacheLog::renderCacheAndLog( 176 $fetcher2, 177 self::SLOT_CACHE_EXPIRATION_EVENT, 178 $message); 179 180 181 } finally { 182 $ID = $keep; 183 } 184 185 } 186 187 188} 189