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
groups / lib / core / class-groups-cache.php

class-groups-cache.php in Groups – Memberships and Access Control 4.7.1, at lib/core/class-groups-cache.php

594 lines 13.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * class-groups-cache.php
4 *
5 * Copyright (c) "kento" Karim Rahimpur www.itthinx.com
6 *
7 * This code is released under the GNU General Public License.
8 * See COPYRIGHT.txt and LICENSE.txt.
9 *
10 * This code 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 * This header and all notices must be kept intact.
16 *
17 * @author Karim Rahimpur
18 * @package groups
19 * @since groups 1.9.0
20 */
21
22 if ( !defined( 'ABSPATH' ) ) {
23 exit;
24 }
25
26 /**
27 * Cache service.
28 *
29 * Uses cache objects to encapsulate cached data.
30 *
31 * This makes us completely independent from the problems related to
32 * incomplete cache implementations that ignore the $found parameter used
33 * to disambiguate cache misses with wp_cache_get() when false is retrieved.
34 */
35 class Groups_Cache {
36
37 /**
38 * Default cache group.
39 *
40 * @var string
41 */
42 const CACHE_GROUP = 'groups';
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 /**
127 * Retrieve an entry from cache.
128 *
129 * @param string $key
130 * @param string $group
131 *
132 * @return Groups_Cache_Object|null returns a cache object on hit, null on cache miss
133 */
134 public static function get( $key, $group = self::CACHE_GROUP ) {
135 self::$count++;
136 $hit = false;
137 $found = null;
138 /**
139 * @var Groups_Cache_Object|boolean $value
140 */
141 $value = wp_cache_get( $key, $group, false, $found );
142 if ( !( $value instanceof Groups_Cache_Object ) ) {
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 }
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 }
166 return $value;
167 }
168
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 /**
231 * Store an entry in cache.
232 *
233 * @param string $key
234 * @param string $value
235 * @param string $group
236 * @param int|null $expires default expiration applies if not provided
237 *
238 * @return boolean true if successful, otherwise false
239 */
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;
286 }
287
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 /**
352 * Delete a cache entry.
353 *
354 * @param string $key
355 * @param string $group
356 *
357 * @return boolean true if successful, otherwise false
358 */
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 );
384 return wp_cache_delete( $key, $group );
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 }
591 }
592
593 Groups_Cache::init();
594