PluginProbe
Groups – Memberships and Access Control / 4.7.1
Groups – Memberships and Access Control v4.7.1
4.7.1 4.7.0 4.6.0 4.5.0 4.4.0 4.3.0 trunk 1.0.0-beta-1 1.0.0-beta-2 1.0.0-beta-3 1.0.0-beta-3b 1.0.0-beta-3c 1.0.0-beta-3d 1.1.4 1.1.5 1.10.0 1.10.1 1.10.2 1.10.3 1.11.0 1.11.1 1.11.2 1.11.3 1.12.0 1.13.0 All 131 releases
← All changes | lib/core/class-groups-cache.php +521 -10 1.11.14.7.1 View file →
@@ -24,11 +24,11 @@
24 24 }
25 25
26 26 /**
27 27 * Cache service.
28 - *
28 + *
29 29 * Uses cache objects to encapsulate cached data.
30 - *
30 + *
31 31 * This makes us completely independent from the problems related to
32 32 * incomplete cache implementations that ignore the $found parameter used
33 33 * to disambiguate cache misses with wp_cache_get() when false is retrieved.
34 34 */
@@ -35,48 +35,559 @@
35 35 class Groups_Cache {
36 36
37 37 /**
38 38 * Default cache group.
39 + *
39 40 * @var string
40 41 */
41 42 const CACHE_GROUP = 'groups';
42 43
43 44 /**
45 + * Cache group specializer, use the user's groups.
46 + *
47 + * @since 2.20.0
48 + *
49 + * @var int
50 + */
51 + const GROUPS_CACHE_GROUP = 0x01;
52 +
53 + /**
54 + * Cache group specializer, use the user's roles.
55 + *
56 + * @since 2.20.0
57 + *
58 + * @var int
59 + */
60 + const ROLES_CACHE_GROUP = 0x02;
61 +
62 + /**
63 + * Cache group specializer, use the user's ID.
64 + *
65 + * @since 2.20.0
66 + *
67 + * @var int
68 + */
69 + const USER_CACHE_GROUP = 0x04;
70 +
71 + /**
72 + * Default expiration.
73 + *
74 + * @since 4.0.0
75 + *
76 + * @var int
77 + */
78 + const EXPIRES_DEFAULT = 86400;
79 +
80 + /**
81 + * Lock timeout in microseconds.
82 + *
83 + * @since 4.3.0
84 + *
85 + * @var int
86 + */
87 + const LOCK_TIMEOUT = 30000000;
88 +
89 + /**
90 + * @since 4.0.0
91 + *
92 + * @var boolean
93 + */
94 + private static $debug = false;
95 +
96 + /**
97 + * @since 4.0.0
98 + *
99 + * @var int
100 + */
101 + private static $hit = 0;
102 +
103 + /**
104 + * @since 4.0.0
105 + *
106 + * @var integer
107 + */
108 + private static $count = 0;
109 +
110 + /**
111 + * @since 4.3.0
112 + *
113 + * @var Groups_Lock[]
114 + */
115 + private static $locks = null;
116 +
117 + /**
118 + * Initialize.
119 + */
120 + public static function init() {
121 + if ( defined( 'GROUPS_CACHE_DEBUG' ) && GROUPS_CACHE_DEBUG ) {
122 + self::$debug = true;
123 + }
124 + }
125 +
126 + /**
44 127 * Retrieve an entry from cache.
45 - *
128 + *
46 129 * @param string $key
47 130 * @param string $group
131 + *
48 132 * @return Groups_Cache_Object|null returns a cache object on hit, null on cache miss
49 133 */
50 134 public static function get( $key, $group = self::CACHE_GROUP ) {
135 + self::$count++;
136 + $hit = false;
51 137 $found = null;
138 + /**
139 + * @var Groups_Cache_Object|boolean $value
140 + */
52 141 $value = wp_cache_get( $key, $group, false, $found );
53 142 if ( !( $value instanceof Groups_Cache_Object ) ) {
54 143 $value = null;
144 + } else {
145 + // verify validity (safeguard if cache has not expired entry appropriately)
146 + if ( $value->has_expired() ) {
147 + wp_cache_delete( $key, $group );
148 + $value = null;
149 + } else {
150 + self::$hit++;
151 + $hit = true;
152 + }
55 153 }
154 + if ( self::$debug ) {
155 + $ratio = self::$hit / self::$count;
156 + Groups_Log::log(
157 + sprintf(
158 + 'Cache get [%4s] [%s|%s] [%6.2f%%]',
159 + $hit ? 'hit' : 'miss',
160 + json_encode( $key ),
161 + json_encode( $group ),
162 + $ratio * 100.0
163 + )
164 + );
165 + }
56 166 return $value;
57 167 }
58 168
59 169 /**
170 + * Retrieve an entry from cache, extended key.
171 + *
172 + * @since 4.0.0
173 + *
174 + * @param string $key
175 + * @param string $group
176 + *
177 + * @return Groups_Cache_Object|NULL
178 + */
179 + public static function get_ext( $key, $group = self::CACHE_GROUP ) {
180 + $key = self::extend_key( $key );
181 + return self::get( $key, $group );
182 + }
183 +
184 + /**
185 + * Retrieve an entry from cache, mutex read.
186 + *
187 + * @since 4.3.0
188 + *
189 + * @param string $key
190 + * @param string $group
191 + *
192 + * @return Groups_Cache_Object|null returns a cache object on hit, null on cache miss
193 + */
194 + public static function get_lock( $key, $group = self::CACHE_GROUP ) {
195 + $name = $key . '-' . $group;
196 + $lock = null;
197 + try {
198 + if ( isset( self::$locks[$name] ) ) {
199 + $lock = self::$locks[$name];
200 + } else {
201 + $timeout = apply_filters( 'groups_cache_get_lock_timeout', self::LOCK_TIMEOUT, $key, $group );
202 + if ( is_numeric( $timeout ) ) {
203 + $timeout = max( 0, intval( $timeout ) );
204 + } else {
205 + $timeout = null;
206 + }
207 + $lock = new Groups_Lock( $name, $timeout );
208 + }
209 + $lock->reader();
210 + } catch ( Groups_Lock_Exception $lex ) {
211 + $lock = null;
212 + if ( self::$debug ) {
213 + Groups_Log::log(
214 + sprintf(
215 + 'Cache read lock fail [%s] [%s]',
216 + json_encode( $name ),
217 + $lex->getMessage()
218 + )
219 + );
220 + }
221 + }
222 + $result = self::get( $key, $group );
223 + if ( $lock !== null ) {
224 + $lock->release();
225 + self::$locks[$name] = $lock;
226 + }
227 + return $result;
228 + }
229 +
230 + /**
60 231 * Store an entry in cache.
61 - *
232 + *
62 233 * @param string $key
63 234 * @param string $value
64 235 * @param string $group
65 - * @return true if successful, otherwise false
236 + * @param int|null $expires default expiration applies if not provided
237 + *
238 + * @return boolean true if successful, otherwise false
66 239 */
67 - public static function set( $key, $value, $group = self::CACHE_GROUP ) {
68 - $object = new Groups_Cache_Object( $key, $value );
69 - return wp_cache_set( $key, $object, $group );
240 + public static function set( $key, $value, $group = self::CACHE_GROUP, $expires = null ) {
241 + /**
242 + * Filter when cache entry expires.
243 + *
244 + * @since 4.0.0
245 + *
246 + * @param int|null $expires
247 + * @param string $key
248 + * @param mixed $value
249 + * @param string $group
250 + *
251 + * @return int|null
252 + */
253 + $expires = apply_filters( 'groups_cache_set_expires', $expires, $key, $value, $group );
254 + if ( is_numeric( $expires ) ) {
255 + $expires = max( 0, intval( $expires ) );
256 + } else {
257 + $expires = self::EXPIRES_DEFAULT;
258 + }
259 + $object = new Groups_Cache_Object( $key, $value, $expires );
260 + if ( $expires === null || $expires === 0 ) {
261 + /**
262 + * Indefinite value.
263 + *
264 + * @since 4.0.0
265 + *
266 + * @param int $value
267 + *
268 + * @return int
269 + */
270 + $expires = apply_filters( 'groups_cache_set_expires_indefinite', PHP_INT_MAX );
271 + $expires = is_numeric( $expires ) ? max( 0, intval( $expires ) ) : PHP_INT_MAX;
272 + }
273 +
274 + $set = wp_cache_set( $key, $object, $group, $expires );
275 + if ( self::$debug ) {
276 + Groups_Log::log(
277 + sprintf(
278 + 'Cache set [%4s] [%s|%s]',
279 + $set ? 'ok' : 'fail',
280 + json_encode( $key ),
281 + json_encode( $group )
282 + )
283 + );
284 + }
285 + return $set;
70 286 }
71 287
72 288 /**
289 + * Store an entry in cache, extended key.
290 + *
291 + * @param string $key
292 + * @param string $value
293 + * @param string $group
294 + * @param int|null $expires default expiration applies if not provided
295 + *
296 + * @return boolean true if successful, otherwise false
297 + */
298 + public static function set_ext( $key, $value, $group = self::CACHE_GROUP, $expires = null ) {
299 + $key = self::extend_key( $key );
300 + return self::set( $key, $value, $group, $expires );
301 + }
302 +
303 + /**
304 + * Store an entry in cache, mutex write.
305 + *
306 + * @since 4.3.0
307 + *
308 + * @param string $key
309 + * @param string $value
310 + * @param string $group
311 + * @param int|null $expires default expiration applies if not provided
312 + *
313 + * @return boolean true if successful, otherwise false
314 + */
315 + public static function set_lock( $key, $value, $group = self::CACHE_GROUP, $expires = null ) {
316 + $name = $key . '-' . $group;
317 + $lock = null;
318 + try {
319 + if ( isset( self::$locks[$name] ) ) {
320 + $lock = self::$locks[$name];
321 + } else {
322 + $timeout = apply_filters( 'groups_cache_set_lock_timeout', self::LOCK_TIMEOUT, $key, $group );
323 + if ( is_numeric( $timeout ) ) {
324 + $timeout = max( 0, intval( $timeout ) );
325 + } else {
326 + $timeout = null;
327 + }
328 + $lock = new Groups_Lock( $name, $timeout );
329 + }
330 + $lock->writer();
331 + } catch ( Groups_Lock_Exception $lex ) {
332 + $lock = null;
333 + if ( self::$debug ) {
334 + Groups_Log::log(
335 + sprintf(
336 + 'Cache write lock fail [%s] [%s]',
337 + json_encode( $name ),
338 + $lex->getMessage()
339 + )
340 + );
341 + }
342 + }
343 + $result = self::set( $key, $value, $group, $expires );
344 + if ( $lock !== null ) {
345 + $lock->release();
346 + self::$locks[$name] = $lock;
347 + }
348 + return $result;
349 + }
350 +
351 + /**
73 352 * Delete a cache entry.
74 - *
353 + *
75 354 * @param string $key
76 355 * @param string $group
77 - * @return true if successful, otherwise false
356 + *
357 + * @return boolean true if successful, otherwise false
78 358 */
79 359 public static function delete( $key, $group = self::CACHE_GROUP ) {
360 + $delete = wp_cache_delete( $key, $group );
361 + if ( self::$debug ) {
362 + Groups_Log::log(
363 + sprintf(
364 + 'Cache del [%4s] [%s|%s]',
365 + $delete ? 'ok' : 'fail',
366 + json_encode( $key ),
367 + json_encode( $group )
368 + )
369 + );
370 + }
371 + return $delete;
372 + }
373 +
374 + /**
375 + * Delete a cache entry, extended key.
376 + *
377 + * @param string $key
378 + * @param string $group
379 + *
380 + * @return boolean true if successful, otherwise false
381 + */
382 + public static function delete_ext( $key, $group = self::CACHE_GROUP ) {
383 + $key = self::extend_key( $key );
80 384 return wp_cache_delete( $key, $group );
81 385 }
386 +
387 + /**
388 + * Delete a cache entry, mutex write.
389 + *
390 + * @param string $key
391 + * @param string $group
392 + *
393 + * @return boolean true if successful, otherwise false
394 + */
395 + public static function delete_lock( $key, $group = self::CACHE_GROUP ) {
396 + $name = $key . '-' . $group;
397 + $lock = null;
398 + try {
399 + if ( isset( self::$locks[$name] ) ) {
400 + $lock = self::$locks[$name];
401 + } else {
402 + $timeout = apply_filters( 'groups_cache_delete_lock_timeout', self::LOCK_TIMEOUT, $key, $group );
403 + if ( is_numeric( $timeout ) ) {
404 + $timeout = max( 0, intval( $timeout ) );
405 + } else {
406 + $timeout = null;
407 + }
408 + $lock = new Groups_Lock( $name, $timeout );
409 + }
410 + $lock->writer();
411 + } catch ( Groups_Lock_Exception $lex ) {
412 + $lock = null;
413 + if ( self::$debug ) {
414 + Groups_Log::log(
415 + sprintf(
416 + 'Cache delete lock fail [%s] [%s]',
417 + json_encode( $name ),
418 + $lex->getMessage()
419 + )
420 + );
421 + }
422 + }
423 + $result = self::delete( $key, $group );
424 + if ( $lock !== null ) {
425 + $lock->release();
426 + self::$locks[$name] = $lock;
427 + }
428 + return $result;
429 + }
430 +
431 + /**
432 + * Provide a specialized (groups-roles-user-based) version of the $group key.
433 + *
434 + * The $flags parameter is self::GROUPS_CACHE_GROUP | self::ROLES_CACHE_GROUP by default,
435 + * meaning a user's groups and roles are used to specialize the $group key.
436 + *
437 + * @since 2.20.0
438 + *
439 + * @param string $group group key
440 + * @param int $flags determines what to take into account, null uses groups and roles by default
441 + *
442 + * @return string
443 + */
444 + public static function get_group( $group, $flags = null ) {
445 +
446 + if ( $flags === null ) {
447 + $flags = self::GROUPS_CACHE_GROUP | self::ROLES_CACHE_GROUP;
448 + }
449 +
450 + $group_ids = array();
451 + $roles = array();
452 + $user_id = null;
453 +
454 + if ( function_exists( 'wp_get_current_user' ) ) {
455 + $user = wp_get_current_user();
456 + if ( $user->exists() ) {
457 + if ( $flags & self::GROUPS_CACHE_GROUP ) {
458 + if ( is_array( $user->roles ) ) {
459 + $roles = $user->roles;
460 + sort( $roles );
461 + }
462 + }
463 + if ( $flags & self::ROLES_CACHE_GROUP ) {
464 + if ( class_exists( '\Groups_User' ) ) {
465 + $groups_user = new \Groups_User( $user->ID );
466 + $group_ids = $groups_user->get_group_ids_deep();
467 + $group_ids = array_map( 'intval', $group_ids );
468 + sort( $group_ids, SORT_NUMERIC );
469 + }
470 + }
471 + if ( $flags & self::USER_CACHE_GROUP ) {
472 + $user_id = $user->ID;
473 + }
474 + }
475 + }
476 +
477 + if ( count( $roles ) > 0 ) {
478 + $group .= '_';
479 + $group .= implode( '_', $roles );
480 + }
481 + if ( count( $group_ids ) > 0 ) {
482 + $group .= '_';
483 + $group .= implode( '_', $group_ids );
484 + }
485 + if ( $user_id !== null ) {
486 + $group .= '_';
487 + $group .= $user_id;
488 + }
489 +
490 + /**
491 + * Apply suffix to extended group.
492 + *
493 + * @param string $suffix
494 + * @param string $group
495 + * @param int $flags
496 + *
497 + * @return string
498 + */
499 + $group_suffix = apply_filters( 'groups_cache_group_suffix', '', $group, $flags );
500 + if ( !is_string( $group_suffix ) ) {
501 + $group_suffix = '';
502 + } else {
503 + $group_suffix = trim( sanitize_key( $group_suffix ) );
504 + }
505 + if ( strlen( $group_suffix ) > 0 ) {
506 + $group = $group . '_' . $group_suffix;
507 + }
508 +
509 + return $group;
510 + }
511 +
512 + /**
513 + * Provide a specialized (groups-roles-user-based) version of the $key.
514 + *
515 + * The $flags parameter is self::GROUPS_CACHE_GROUP | self::ROLES_CACHE_GROUP by default,
516 + * meaning a user's groups and roles are used to specialize the $key.
517 + *
518 + * @since 4.0.0
519 + *
520 + * @param string $key group key
521 + * @param int $flags determines what to take into account, null uses groups and roles by default
522 + *
523 + * @return string
524 + */
525 + public static function extend_key( $key, $flags = null ) {
526 + if ( $flags === null ) {
527 + $flags = self::GROUPS_CACHE_GROUP | self::ROLES_CACHE_GROUP;
528 + }
529 +
530 + $group_ids = array();
531 + $roles = array();
532 + $user_id = null;
533 +
534 + if ( function_exists( 'wp_get_current_user' ) ) {
535 + $user = wp_get_current_user();
536 + if ( $user->exists() ) {
537 + if ( $flags & self::GROUPS_CACHE_GROUP ) {
538 + if ( is_array( $user->roles ) ) {
539 + $roles = $user->roles;
540 + sort( $roles );
541 + }
542 + }
543 + if ( $flags & self::ROLES_CACHE_GROUP ) {
544 + if ( class_exists( '\Groups_User' ) ) {
545 + $groups_user = new \Groups_User( $user->ID );
546 + $group_ids = $groups_user->get_group_ids_deep();
547 + $group_ids = array_map( 'intval', $group_ids );
548 + sort( $group_ids, SORT_NUMERIC );
549 + }
550 + }
551 + if ( $flags & self::USER_CACHE_GROUP ) {
552 + $user_id = $user->ID;
553 + }
554 + }
555 + }
556 +
557 + if ( count( $roles ) > 0 ) {
558 + $key .= '_';
559 + $key .= implode( '_', $roles );
560 + }
561 + if ( count( $group_ids ) > 0 ) {
562 + $key .= '_';
563 + $key .= implode( '_', $group_ids );
564 + }
565 + if ( $user_id !== null ) {
566 + $key .= '_';
567 + $key .= $user_id;
568 + }
569 +
570 + /**
571 + * Apply suffix to extended key.
572 + *
573 + * @param string $suffix
574 + * @param string $key
575 + * @param int $flags
576 + *
577 + * @return string
578 + */
579 + $suffix = apply_filters( 'groups_cache_extend_key_suffix', '', $key, $flags );
580 + if ( !is_string( $suffix ) ) {
581 + $suffix = '';
582 + } else {
583 + $suffix = trim( sanitize_key( $suffix ) );
584 + }
585 + if ( strlen( $suffix ) > 0 ) {
586 + $key = $key . '_' . $suffix;
587 + }
588 +
589 + return $key;
590 + }
82 591 }
592 +
593 +Groups_Cache::init();