This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Functions to get cache objects. |
||
4 | * |
||
5 | * This program is free software; you can redistribute it and/or modify |
||
6 | * it under the terms of the GNU General Public License as published by |
||
7 | * the Free Software Foundation; either version 2 of the License, or |
||
8 | * (at your option) any later version. |
||
9 | * |
||
10 | * This program is distributed in the hope that it will be useful, |
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
13 | * GNU General Public License for more details. |
||
14 | * |
||
15 | * You should have received a copy of the GNU General Public License along |
||
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
18 | * http://www.gnu.org/copyleft/gpl.html |
||
19 | * |
||
20 | * @file |
||
21 | * @ingroup Cache |
||
22 | */ |
||
23 | |||
24 | use MediaWiki\Logger\LoggerFactory; |
||
25 | use MediaWiki\MediaWikiServices; |
||
26 | |||
27 | /** |
||
28 | * Functions to get cache objects |
||
29 | * |
||
30 | * The word "cache" has two main dictionary meanings, and both |
||
31 | * are used in this factory class. They are: |
||
32 | * |
||
33 | * - a) Cache (the computer science definition). |
||
34 | * A place to store copies or computations on existing data for |
||
35 | * higher access speeds. |
||
36 | * - b) Storage. |
||
37 | * A place to store lightweight data that is not canonically |
||
38 | * stored anywhere else (e.g. a "hoard" of objects). |
||
39 | * |
||
40 | * The former should always use strongly consistent stores, so callers don't |
||
41 | * have to deal with stale reads. The latter may be eventually consistent, but |
||
42 | * callers can use BagOStuff:READ_LATEST to see the latest available data. |
||
43 | * |
||
44 | * Primary entry points: |
||
45 | * |
||
46 | * - ObjectCache::getMainWANInstance() |
||
47 | * Purpose: Memory cache. |
||
48 | * Stored in the local data-center's main cache (keyspace different from local-cluster cache). |
||
49 | * Delete events are broadcasted to other DCs main cache. See WANObjectCache for details. |
||
50 | * |
||
51 | * - ObjectCache::getLocalServerInstance( $fallbackType ) |
||
52 | * Purpose: Memory cache for very hot keys. |
||
53 | * Stored only on the individual web server (typically APC or APCu for web requests, |
||
54 | * and EmptyBagOStuff in CLI mode). |
||
55 | * Not replicated to the other servers. |
||
56 | * |
||
57 | * - ObjectCache::getLocalClusterInstance() |
||
58 | * Purpose: Memory storage for per-cluster coordination and tracking. |
||
59 | * A typical use case would be a rate limit counter or cache regeneration mutex. |
||
60 | * Stored centrally within the local data-center. Not replicated to other DCs. |
||
61 | * Configured by $wgMainCacheType. |
||
62 | * |
||
63 | * - ObjectCache::getMainStashInstance() |
||
64 | * Purpose: Ephemeral global storage. |
||
65 | * Stored centrally within the primary data-center. |
||
66 | * Changes are applied there first and replicated to other DCs (best-effort). |
||
67 | * To retrieve the latest value (e.g. not from a replica DB), use BagOStuff::READ_LATEST. |
||
68 | * This store may be subject to LRU style evictions. |
||
69 | * |
||
70 | * - ObjectCache::getInstance( $cacheType ) |
||
71 | * Purpose: Special cases (like tiered memory/disk caches). |
||
72 | * Get a specific cache type by key in $wgObjectCaches. |
||
73 | * |
||
74 | * All the above cache instances (BagOStuff and WANObjectCache) have their makeKey() |
||
75 | * method scoped to the *current* wiki ID. Use makeGlobalKey() to avoid this scoping |
||
76 | * when using keys that need to be shared amongst wikis. |
||
77 | * |
||
78 | * @ingroup Cache |
||
79 | */ |
||
80 | class ObjectCache { |
||
81 | /** @var BagOStuff[] Map of (id => BagOStuff) */ |
||
82 | public static $instances = []; |
||
83 | /** @var WANObjectCache[] Map of (id => WANObjectCache) */ |
||
84 | public static $wanInstances = []; |
||
85 | |||
86 | /** |
||
87 | * Get a cached instance of the specified type of cache object. |
||
88 | * |
||
89 | * @param string $id A key in $wgObjectCaches. |
||
90 | * @return BagOStuff |
||
91 | */ |
||
92 | public static function getInstance( $id ) { |
||
93 | if ( !isset( self::$instances[$id] ) ) { |
||
94 | self::$instances[$id] = self::newFromId( $id ); |
||
95 | } |
||
96 | |||
97 | return self::$instances[$id]; |
||
98 | } |
||
99 | |||
100 | /** |
||
101 | * Get a cached instance of the specified type of WAN cache object. |
||
102 | * |
||
103 | * @since 1.26 |
||
104 | * @param string $id A key in $wgWANObjectCaches. |
||
105 | * @return WANObjectCache |
||
106 | */ |
||
107 | public static function getWANInstance( $id ) { |
||
108 | if ( !isset( self::$wanInstances[$id] ) ) { |
||
109 | self::$wanInstances[$id] = self::newWANCacheFromId( $id ); |
||
110 | } |
||
111 | |||
112 | return self::$wanInstances[$id]; |
||
113 | } |
||
114 | |||
115 | /** |
||
116 | * Create a new cache object of the specified type. |
||
117 | * |
||
118 | * @param string $id A key in $wgObjectCaches. |
||
119 | * @return BagOStuff |
||
120 | * @throws InvalidArgumentException |
||
121 | */ |
||
122 | public static function newFromId( $id ) { |
||
123 | global $wgObjectCaches; |
||
124 | |||
125 | if ( !isset( $wgObjectCaches[$id] ) ) { |
||
126 | // Always recognize these ones |
||
127 | if ( $id === CACHE_NONE ) { |
||
0 ignored issues
–
show
Unused Code
Bug
introduced
by
![]() |
|||
128 | return new EmptyBagOStuff(); |
||
129 | } elseif ( $id === 'hash' ) { |
||
130 | return new HashBagOStuff(); |
||
131 | } |
||
132 | |||
133 | throw new InvalidArgumentException( "Invalid object cache type \"$id\" requested. " . |
||
134 | "It is not present in \$wgObjectCaches." ); |
||
135 | } |
||
136 | |||
137 | return self::newFromParams( $wgObjectCaches[$id] ); |
||
138 | } |
||
139 | |||
140 | /** |
||
141 | * Get the default keyspace for this wiki. |
||
142 | * |
||
143 | * This is either the value of the `CachePrefix` configuration variable, |
||
144 | * or (if the former is unset) the `DBname` configuration variable, with |
||
145 | * `DBprefix` (if defined). |
||
146 | * |
||
147 | * @return string |
||
148 | */ |
||
149 | public static function getDefaultKeyspace() { |
||
150 | global $wgCachePrefix; |
||
151 | |||
152 | $keyspace = $wgCachePrefix; |
||
153 | if ( is_string( $keyspace ) && $keyspace !== '' ) { |
||
154 | return $keyspace; |
||
155 | } |
||
156 | |||
157 | return wfWikiID(); |
||
158 | } |
||
159 | |||
160 | /** |
||
161 | * Create a new cache object from parameters. |
||
162 | * |
||
163 | * @param array $params Must have 'factory' or 'class' property. |
||
164 | * - factory: Callback passed $params that returns BagOStuff. |
||
165 | * - class: BagOStuff subclass constructed with $params. |
||
166 | * - loggroup: Alias to set 'logger' key with LoggerFactory group. |
||
167 | * - .. Other parameters passed to factory or class. |
||
168 | * @return BagOStuff |
||
169 | * @throws InvalidArgumentException |
||
170 | */ |
||
171 | public static function newFromParams( $params ) { |
||
0 ignored issues
–
show
newFromParams uses the super-global variable $GLOBALS which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
![]() |
|||
172 | View Code Duplication | if ( isset( $params['loggroup'] ) ) { |
|
173 | $params['logger'] = LoggerFactory::getInstance( $params['loggroup'] ); |
||
174 | } else { |
||
175 | $params['logger'] = LoggerFactory::getInstance( 'objectcache' ); |
||
176 | } |
||
177 | if ( !isset( $params['keyspace'] ) ) { |
||
178 | $params['keyspace'] = self::getDefaultKeyspace(); |
||
179 | } |
||
180 | if ( isset( $params['factory'] ) ) { |
||
181 | return call_user_func( $params['factory'], $params ); |
||
182 | } elseif ( isset( $params['class'] ) ) { |
||
183 | $class = $params['class']; |
||
184 | // Automatically set the 'async' update handler |
||
185 | $params['asyncHandler'] = isset( $params['asyncHandler'] ) |
||
186 | ? $params['asyncHandler'] |
||
187 | : 'DeferredUpdates::addCallableUpdate'; |
||
188 | // Enable reportDupes by default |
||
189 | $params['reportDupes'] = isset( $params['reportDupes'] ) |
||
190 | ? $params['reportDupes'] |
||
191 | : true; |
||
192 | // Do b/c logic for SqlBagOStuff |
||
193 | if ( is_a( $class, SqlBagOStuff::class, true ) ) { |
||
194 | if ( isset( $params['server'] ) && !isset( $params['servers'] ) ) { |
||
195 | $params['servers'] = [ $params['server'] ]; |
||
196 | unset( $params['server'] ); |
||
197 | } |
||
198 | // In the past it was not required to set 'dbDirectory' in $wgObjectCaches |
||
199 | if ( isset( $params['servers'] ) ) { |
||
200 | foreach ( $params['servers'] as &$server ) { |
||
201 | if ( $server['type'] === 'sqlite' && !isset( $server['dbDirectory'] ) ) { |
||
202 | $server['dbDirectory'] = MediaWikiServices::getInstance() |
||
203 | ->getMainConfig()->get( 'SQLiteDataDir' ); |
||
204 | } |
||
205 | } |
||
206 | } |
||
207 | } |
||
208 | |||
209 | // Do b/c logic for MemcachedBagOStuff |
||
210 | if ( is_subclass_of( $class, MemcachedBagOStuff::class ) ) { |
||
211 | if ( !isset( $params['servers'] ) ) { |
||
212 | $params['servers'] = $GLOBALS['wgMemCachedServers']; |
||
213 | } |
||
214 | if ( !isset( $params['debug'] ) ) { |
||
215 | $params['debug'] = $GLOBALS['wgMemCachedDebug']; |
||
216 | } |
||
217 | if ( !isset( $params['persistent'] ) ) { |
||
218 | $params['persistent'] = $GLOBALS['wgMemCachedPersistent']; |
||
219 | } |
||
220 | if ( !isset( $params['timeout'] ) ) { |
||
221 | $params['timeout'] = $GLOBALS['wgMemCachedTimeout']; |
||
222 | } |
||
223 | } |
||
224 | return new $class( $params ); |
||
225 | } else { |
||
226 | throw new InvalidArgumentException( "The definition of cache type \"" |
||
227 | . print_r( $params, true ) . "\" lacks both " |
||
228 | . "factory and class parameters." ); |
||
229 | } |
||
230 | } |
||
231 | |||
232 | /** |
||
233 | * Factory function for CACHE_ANYTHING (referenced from DefaultSettings.php) |
||
234 | * |
||
235 | * CACHE_ANYTHING means that stuff has to be cached, not caching is not an option. |
||
236 | * If a caching method is configured for any of the main caches ($wgMainCacheType, |
||
237 | * $wgMessageCacheType, $wgParserCacheType), then CACHE_ANYTHING will effectively |
||
238 | * be an alias to the configured cache choice for that. |
||
239 | * If no cache choice is configured (by default $wgMainCacheType is CACHE_NONE), |
||
240 | * then CACHE_ANYTHING will forward to CACHE_DB. |
||
241 | * |
||
242 | * @param array $params |
||
243 | * @return BagOStuff |
||
244 | */ |
||
245 | public static function newAnything( $params ) { |
||
246 | global $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType; |
||
247 | $candidates = [ $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType ]; |
||
248 | foreach ( $candidates as $candidate ) { |
||
249 | if ( $candidate !== CACHE_NONE && $candidate !== CACHE_ANYTHING ) { |
||
250 | return self::getInstance( $candidate ); |
||
251 | } |
||
252 | } |
||
253 | |||
254 | if ( MediaWikiServices::getInstance()->isServiceDisabled( 'DBLoadBalancer' ) ) { |
||
255 | // The LoadBalancer is disabled, probably because |
||
256 | // MediaWikiServices::disableStorageBackend was called. |
||
257 | $candidate = CACHE_NONE; |
||
258 | } else { |
||
259 | $candidate = CACHE_DB; |
||
260 | } |
||
261 | |||
262 | return self::getInstance( $candidate ); |
||
263 | } |
||
264 | |||
265 | /** |
||
266 | * Factory function for CACHE_ACCEL (referenced from DefaultSettings.php) |
||
267 | * |
||
268 | * This will look for any APC or APCu style server-local cache. |
||
269 | * A fallback cache can be specified if none is found. |
||
270 | * |
||
271 | * // Direct calls |
||
272 | * ObjectCache::getLocalServerInstance( $fallbackType ); |
||
273 | * |
||
274 | * // From $wgObjectCaches via newFromParams() |
||
275 | * ObjectCache::getLocalServerInstance( [ 'fallback' => $fallbackType ] ); |
||
276 | * |
||
277 | * @param int|string|array $fallback Fallback cache or parameter map with 'fallback' |
||
278 | * @return BagOStuff |
||
279 | * @throws InvalidArgumentException |
||
280 | * @since 1.27 |
||
281 | */ |
||
282 | public static function getLocalServerInstance( $fallback = CACHE_NONE ) { |
||
283 | $cache = MediaWikiServices::getInstance()->getLocalServerObjectCache(); |
||
284 | if ( $cache instanceof EmptyBagOStuff ) { |
||
285 | if ( is_array( $fallback ) ) { |
||
286 | $fallback = isset( $fallback['fallback'] ) ? $fallback['fallback'] : CACHE_NONE; |
||
287 | } |
||
288 | $cache = self::getInstance( $fallback ); |
||
289 | } |
||
290 | |||
291 | return $cache; |
||
292 | } |
||
293 | |||
294 | /** |
||
295 | * Create a new cache object of the specified type. |
||
296 | * |
||
297 | * @since 1.26 |
||
298 | * @param string $id A key in $wgWANObjectCaches. |
||
299 | * @return WANObjectCache |
||
300 | * @throws UnexpectedValueException |
||
301 | */ |
||
302 | public static function newWANCacheFromId( $id ) { |
||
303 | global $wgWANObjectCaches, $wgObjectCaches; |
||
304 | |||
305 | if ( !isset( $wgWANObjectCaches[$id] ) ) { |
||
306 | throw new UnexpectedValueException( |
||
307 | "Cache type \"$id\" requested is not present in \$wgWANObjectCaches." ); |
||
308 | } |
||
309 | |||
310 | $params = $wgWANObjectCaches[$id]; |
||
311 | if ( !isset( $wgObjectCaches[$params['cacheId']] ) ) { |
||
312 | throw new UnexpectedValueException( |
||
313 | "Cache type \"{$params['cacheId']}\" is not present in \$wgObjectCaches." ); |
||
314 | } |
||
315 | $params['store'] = $wgObjectCaches[$params['cacheId']]; |
||
316 | |||
317 | return self::newWANCacheFromParams( $params ); |
||
318 | } |
||
319 | |||
320 | /** |
||
321 | * Create a new cache object of the specified type. |
||
322 | * |
||
323 | * @since 1.28 |
||
324 | * @param array $params |
||
325 | * @return WANObjectCache |
||
326 | * @throws UnexpectedValueException |
||
327 | */ |
||
328 | public static function newWANCacheFromParams( array $params ) { |
||
329 | $erGroup = MediaWikiServices::getInstance()->getEventRelayerGroup(); |
||
330 | foreach ( $params['channels'] as $action => $channel ) { |
||
331 | $params['relayers'][$action] = $erGroup->getRelayer( $channel ); |
||
332 | $params['channels'][$action] = $channel; |
||
333 | } |
||
334 | $params['cache'] = self::newFromParams( $params['store'] ); |
||
335 | View Code Duplication | if ( isset( $params['loggroup'] ) ) { |
|
336 | $params['logger'] = LoggerFactory::getInstance( $params['loggroup'] ); |
||
337 | } else { |
||
338 | $params['logger'] = LoggerFactory::getInstance( 'objectcache' ); |
||
339 | } |
||
340 | $class = $params['class']; |
||
341 | |||
342 | return new $class( $params ); |
||
343 | } |
||
344 | |||
345 | /** |
||
346 | * Get the main cluster-local cache object. |
||
347 | * |
||
348 | * @since 1.27 |
||
349 | * @return BagOStuff |
||
350 | */ |
||
351 | public static function getLocalClusterInstance() { |
||
352 | global $wgMainCacheType; |
||
353 | |||
354 | return self::getInstance( $wgMainCacheType ); |
||
355 | } |
||
356 | |||
357 | /** |
||
358 | * Get the main WAN cache object. |
||
359 | * |
||
360 | * @since 1.26 |
||
361 | * @return WANObjectCache |
||
362 | * @deprecated Since 1.28 Use MediaWikiServices::getMainWANCache() |
||
363 | */ |
||
364 | public static function getMainWANInstance() { |
||
365 | return MediaWikiServices::getInstance()->getMainWANObjectCache(); |
||
366 | } |
||
367 | |||
368 | /** |
||
369 | * Get the cache object for the main stash. |
||
370 | * |
||
371 | * Stash objects are BagOStuff instances suitable for storing light |
||
372 | * weight data that is not canonically stored elsewhere (such as RDBMS). |
||
373 | * Stashes should be configured to propagate changes to all data-centers. |
||
374 | * |
||
375 | * Callers should be prepared for: |
||
376 | * - a) Writes to be slower in non-"primary" (e.g. HTTP GET/HEAD only) DCs |
||
377 | * - b) Reads to be eventually consistent, e.g. for get()/getMulti() |
||
378 | * In general, this means avoiding updates on idempotent HTTP requests and |
||
379 | * avoiding an assumption of perfect serializability (or accepting anomalies). |
||
380 | * Reads may be eventually consistent or data might rollback as nodes flap. |
||
381 | * Callers can use BagOStuff:READ_LATEST to see the latest available data. |
||
382 | * |
||
383 | * @return BagOStuff |
||
384 | * @since 1.26 |
||
385 | * @deprecated Since 1.28 Use MediaWikiServices::getMainObjectStash |
||
386 | */ |
||
387 | public static function getMainStashInstance() { |
||
388 | return MediaWikiServices::getInstance()->getMainObjectStash(); |
||
389 | } |
||
390 | |||
391 | /** |
||
392 | * Clear all the cached instances. |
||
393 | */ |
||
394 | public static function clear() { |
||
395 | self::$instances = []; |
||
396 | self::$wanInstances = []; |
||
397 | } |
||
398 | } |
||
399 |