MediaWiki master
HTMLCacheUpdater.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Cache;
22
31use Traversable;
32
41 private $reboundDelay;
43 private $useFileCache;
45 private $cdnMaxAge;
46
48 private $hookRunner;
49
51 public const PURGE_NAIVE = 0;
57 public const PURGE_PRESEND = 1;
62 public const PURGE_REBOUND = 2;
63
72 public const PURGE_INTENT_TXROUND_REFLECTED = self::PURGE_PRESEND | self::PURGE_REBOUND;
73
82 public const PURGE_URLS_LINKSUPDATE_ONLY = 4;
83
90 public const UNLESS_CACHE_MTIME_AFTER = 'unless-timestamp-exceeds';
91
93 private $titleFactory;
94
104 public function __construct(
105 HookContainer $hookContainer,
106 TitleFactory $titleFactory,
107 $reboundDelay,
108 $useFileCache,
109 $cdnMaxAge
110 ) {
111 $this->hookRunner = new HookRunner( $hookContainer );
112 $this->titleFactory = $titleFactory;
113 $this->reboundDelay = $reboundDelay;
114 $this->useFileCache = $useFileCache;
115 $this->cdnMaxAge = $cdnMaxAge;
116 }
117
123 private function fieldHasFlag( $flags, $flag ) {
124 return ( ( $flags & $flag ) === $flag );
125 }
126
135 public function purgeUrls( $urls, $flags = self::PURGE_PRESEND, array $unless = [] ) {
136 $minFreshCacheMtime = $unless[self::UNLESS_CACHE_MTIME_AFTER] ?? null;
137 if ( $minFreshCacheMtime && time() > ( $minFreshCacheMtime + $this->cdnMaxAge ) ) {
138 return;
139 }
140
141 $urls = is_string( $urls ) ? [ $urls ] : $urls;
142
143 $reboundDelay = $this->fieldHasFlag( $flags, self::PURGE_REBOUND )
144 ? $this->reboundDelay
145 : 0; // no second purge
146
147 $update = new CdnCacheUpdate( $urls, [ 'reboundDelay' => $reboundDelay ] );
148 if ( $this->fieldHasFlag( $flags, self::PURGE_PRESEND ) ) {
149 DeferredUpdates::addUpdate( $update, DeferredUpdates::PRESEND );
150 } else {
151 $update->doUpdate();
152 }
153 }
154
167 public function purgeTitleUrls( $pages, $flags = self::PURGE_PRESEND, array $unless = [] ) {
168 $pages = is_iterable( $pages ) ? $pages : [ $pages ];
169 $pageIdentities = [];
170
171 foreach ( $pages as $page ) {
172 // TODO: We really only need to cast to PageIdentity. We could use a LinkBatch for that.
173 $title = $this->titleFactory->newFromPageReference( $page );
174
175 if ( $title->canExist() ) {
176 $pageIdentities[] = $title;
177 }
178 }
179
180 if ( !$pageIdentities ) {
181 return;
182 }
183
184 if ( $this->useFileCache ) {
185 $update = HtmlFileCacheUpdate::newFromPages( $pageIdentities );
186 if ( $this->fieldHasFlag( $flags, self::PURGE_PRESEND ) ) {
187 DeferredUpdates::addUpdate( $update, DeferredUpdates::PRESEND );
188 } else {
189 $update->doUpdate();
190 }
191 }
192
193 $minFreshCacheMtime = $unless[self::UNLESS_CACHE_MTIME_AFTER] ?? null;
194 if ( !$minFreshCacheMtime || time() <= ( $minFreshCacheMtime + $this->cdnMaxAge ) ) {
195 $urls = [];
196 foreach ( $pageIdentities as $pi ) {
198 $urls = array_merge( $urls, $this->getUrls( $pi, $flags ) );
199 }
200 $this->purgeUrls( $urls, $flags );
201 }
202 }
203
211 public function getUrls( PageReference $page, int $flags = 0 ): array {
212 $title = $this->titleFactory->newFromPageReference( $page );
213
214 if ( !$title->canExist() ) {
215 return [];
216 }
217
218 // These urls are affected both by direct revisions as well,
219 // as re-rendering of the same content during a LinksUpdate.
220 $urls = [
221 $title->getInternalURL()
222 ];
223 // Language variant page views are currently not cached
224 // and thus not purged (T250511).
225
226 // These urls are only affected by direct revisions, and do not require
227 // purging when a LinksUpdate merely rerenders the same content.
228 // This exists to avoid large amounts of redundant PURGE traffic (T250261).
229 if ( !$this->fieldHasFlag( $flags, self::PURGE_URLS_LINKSUPDATE_ONLY ) ) {
230 $urls[] = $title->getInternalURL( 'action=history' );
231
232 // Canonical action=raw URLs for user and site config pages (T58874, T261371).
233 if ( $title->isUserJsConfigPage() || $title->isSiteJsConfigPage() ) {
234 $urls[] = $title->getInternalURL( 'action=raw&ctype=text/javascript' );
235 } elseif ( $title->isUserJsonConfigPage() || $title->isSiteJsonConfigPage() ) {
236 $urls[] = $title->getInternalURL( 'action=raw&ctype=application/json' );
237 } elseif ( $title->isUserCssConfigPage() || $title->isSiteCssConfigPage() ) {
238 $urls[] = $title->getInternalURL( 'action=raw&ctype=text/css' );
239 }
240 }
241
242 // Extensions may add novel ways to access this content
243 $append = [];
244 $mode = $flags & self::PURGE_URLS_LINKSUPDATE_ONLY;
245 $this->hookRunner->onHtmlCacheUpdaterAppendUrls( $title, $mode, $append );
246 $urls = array_merge( $urls, $append );
247
248 // Extensions may add novel ways to access the site overall
249 $append = [];
250 $this->hookRunner->onHtmlCacheUpdaterVaryUrls( $urls, $append );
251 $urls = array_merge( $urls, $append );
252
253 // Legacy. TODO: Deprecate this
254 $this->hookRunner->onTitleSquidURLs( $title, $urls );
255
256 return $urls;
257 }
258}
259
261class_alias( HTMLCacheUpdater::class, 'HtmlCacheUpdater' );
Class to invalidate the CDN and HTMLFileCache entries associated with URLs/titles.
purgeTitleUrls( $pages, $flags=self::PURGE_PRESEND, array $unless=[])
Purge the CDN/HTMLFileCache for a title or the titles yielded by an iterator.
getUrls(PageReference $page, int $flags=0)
Get a list of URLs to purge from the CDN cache when this page changes.
__construct(HookContainer $hookContainer, TitleFactory $titleFactory, $reboundDelay, $useFileCache, $cdnMaxAge)
purgeUrls( $urls, $flags=self::PURGE_PRESEND, array $unless=[])
Purge the CDN for a URL or list of URLs.
Handles purging the appropriate CDN objects given a list of URLs or Title instances.
Defer callable updates to run later in the PHP process.
HTMLFileCache purge update for a set of titles.
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Creates Title objects.
Interface for objects (potentially) representing an editable wiki page.
Interface for objects (potentially) representing a page that can be viewable and linked to on a wiki.