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-capability.php +408 -83 1.10.14.7.1 View file →
@@ -22,33 +22,272 @@
22 22 if ( !defined( 'ABSPATH' ) ) {
23 23 exit;
24 24 }
25 25
26 +// phpcs:disable PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
27 +
26 28 /**
27 29 * Capability OPM
28 30 */
29 31 class Groups_Capability {
30 32
31 - const CACHE_GROUP = 'groups';
33 + /**
34 + * @var string cache group
35 + */
36 + const CACHE_GROUP = 'groups';
37 +
38 + /**
39 + * @var string key
40 + *
41 + * @deprecated since 4.3.0
42 + */
32 43 const READ_BY_CAPABILITY = 'read_by_capability';
33 44
34 45 /**
35 - * @var persisted capability object
46 + * @var string key
47 + *
48 + * @deprecated since 4.3.0
36 49 */
37 - var $capability = null;
50 + const READ_CAPABILITY_BY_ID = 'read_capability_by_id';
38 51
39 52 /**
53 + * @var string key
54 + */
55 + const ID_MAP = 'map_capability_by_id';
56 +
57 + /**
58 + * @var string key
59 + */
60 + const NAME_MAP = 'map_capability_by_name';
61 +
62 + /**
63 + * Lock timeout in microseconds.
64 + *
65 + * @var int
66 + */
67 + const LOCK_TIMEOUT = 30000000;
68 +
69 + /**
70 + * Lock name.
71 + *
72 + * @var string
73 + */
74 + const LOCK = 'groups_capability_lock';
75 +
76 + /**
77 + * Mutex lock.
78 + *
79 + * @var Groups_Lock
80 + */
81 + private static $lock = null;
82 +
83 + /**
84 + * @var object persisted capability object
85 + *
86 + * @access private - do not access this property directly, the visibility will be made private in the future
87 + */
88 + public $capability = null;
89 +
90 + /**
91 + * Lock object.
92 + *
93 + * @throws Groups_Lock_Exception
94 + *
95 + * @return Groups_Lock
96 + */
97 + private static function get_lock() {
98 + $lock = null;
99 + if ( self::$lock !== null ) {
100 + $lock = self::$lock;
101 + } else {
102 + $timeout = apply_filters( 'groups_capability_lock_timeout', self::LOCK_TIMEOUT );
103 + if ( is_numeric( $timeout ) ) {
104 + $timeout = max( 0, intval( $timeout ) );
105 + } else {
106 + $timeout = null;
107 + }
108 + $lock = new Groups_Lock( self::LOCK, $timeout );
109 + self::$lock = $lock;
110 + }
111 + return $lock;
112 + }
113 +
114 + /**
115 + * Mutex locked.
116 + *
117 + * @return boolean
118 + */
119 + private static function is_locked() {
120 + return self::$lock !== null && self::$lock->is_locked();
121 + }
122 +
123 + /**
124 + * Mutex reader.
125 + *
126 + * @return boolean
127 + */
128 + private static function reader() {
129 + $locked = false;
130 + try {
131 + $lock = self::get_lock();
132 + $locked = $lock->reader();
133 + } catch ( Groups_Lock_Exception $lex ) {
134 + if ( defined( 'GROUPS_DEBUG' ) && GROUPS_DEBUG ) {
135 + Groups_Log::log(
136 + sprintf(
137 + 'Capability read lock fail [%s] [%s]',
138 + self::LOCK,
139 + $lex->getMessage()
140 + )
141 + );
142 + }
143 + }
144 + return $locked;
145 + }
146 +
147 + /**
148 + * Mutex writer.
149 + *
150 + * @return boolean
151 + */
152 + private static function writer() {
153 + $locked = false;
154 + try {
155 + $lock = self::get_lock();
156 + $locked = $lock->writer();
157 + } catch ( Groups_Lock_Exception $lex ) {
158 + if ( defined( 'GROUPS_DEBUG' ) && GROUPS_DEBUG ) {
159 + Groups_Log::log(
160 + sprintf(
161 + 'Capability write lock fail [%s] [%s]',
162 + self::LOCK,
163 + $lex->getMessage()
164 + )
165 + );
166 + }
167 + }
168 + return $locked;
169 + }
170 +
171 + /**
172 + * Mutex release.
173 + *
174 + * @return boolean
175 + */
176 + private static function release() {
177 + $released = false;
178 + if ( self::$lock !== null ) {
179 + $released = self::$lock->release();
180 + }
181 + return $released;
182 + }
183 +
184 + /**
40 185 * Create by capability id.
186 + *
41 187 * Must have been persisted.
188 + *
42 189 * @param int $capability_id
43 190 */
44 191 public function __construct( $capability_id ) {
45 192 $this->capability = self::read( $capability_id );
193 + if ( $this->capability === false ) {
194 + $this->capability = null;
195 + }
46 196 }
47 197
48 198 /**
199 + * Provides the object ID.
200 + *
201 + * @return int
202 + */
203 + public function get_id() {
204 + return $this->get_capability_id();
205 + }
206 +
207 + /**
208 + * Provides the object ID.
209 + *
210 + * @return int
211 + */
212 + public function get_capability_id() {
213 + return $this->capability_id; // @phpstan-ignore property.notFound
214 + }
215 +
216 + /**
217 + * Provides the literal capability.
218 + *
219 + * @return string
220 + */
221 + public function get_capability() {
222 + $capability = '';
223 + if (
224 + $this->capability !== null &&
225 + is_object( $this->capability ) &&
226 + !empty( $this->capability->capability )
227 + ) {
228 + $capability = $this->capability->capability;
229 + }
230 + return $capability;
231 + }
232 +
233 + /**
234 + * Provides the capability's class.
235 + *
236 + * @return string
237 + */
238 + public function get_class() {
239 + return $this->class; // @phpstan-ignore property.notFound
240 + }
241 +
242 + /**
243 + * Provides the capability's object.
244 + *
245 + * @return string
246 + */
247 + public function get_object() {
248 + return $this->object; // @phpstan-ignore property.notFound
249 + }
250 +
251 + /**
252 + * Provides the capability's name.
253 + *
254 + * @return string
255 + */
256 + public function get_name() {
257 + return $this->name; // @phpstan-ignore property.notFound
258 + }
259 +
260 + /**
261 + * Provides the capability's description.
262 + *
263 + * @return string
264 + */
265 + public function get_description() {
266 + return $this->description; // @phpstan-ignore property.notFound
267 + }
268 +
269 + /**
270 + * Provides the IDs of groups that have the capability.
271 + *
272 + * @return int[]
273 + */
274 + public function get_group_ids(){
275 + return $this->group_ids; // @phpstan-ignore property.notFound
276 + }
277 +
278 + /**
279 + * Provides the groups that have the capability.
280 + *
281 + * @return Groups_Group[]
282 + */
283 + public function get_groups() {
284 + return $this->groups; // @phpstan-ignore property.notFound
285 + }
286 +
287 + /**
49 288 * Retrieve a property by name.
50 - *
289 + *
51 290 * Possible properties:
52 291 * - capability_id
53 292 * - capability
54 293 * - class
@@ -54,13 +293,14 @@
54 293 * - class
55 294 * - object
56 295 * - name
57 296 * - description
58 - *
297 + *
59 298 * - group_ids groups that have the capability
60 - *
299 + *
61 300 * @param string $name property's name
62 - * @return property value, will return null if property does not exist
301 + *
302 + * @return mixed property value, will return null if property does not exist
63 303 */
64 304 public function __get( $name ) {
65 305
66 306 global $wpdb;
@@ -66,39 +306,39 @@
66 306 global $wpdb;
67 307
68 308 $result = null;
69 309 if ( $this->capability !== null ) {
70 - switch( $name ) {
71 - case "capability_id" :
72 - case "capability" :
73 - case "class" :
74 - case "object" :
75 - case "name" :
76 - case "description" :
310 + switch ( $name ) {
311 + case 'capability_id' :
312 + case 'capability' :
313 + case 'class' :
314 + case 'object' :
315 + case 'name' :
316 + case 'description' :
77 317 $result = $this->capability->$name;
78 318 break;
79 319 case 'group_ids' :
80 - $group_capability_table = _groups_get_tablename( "group_capability" );
320 + $group_capability_table = _groups_get_tablename( 'group_capability' );
81 321 $rows = $wpdb->get_results( $wpdb->prepare(
82 - "SELECT group_id FROM $group_capability_table WHERE capability_id = %d",
322 + "SELECT group_id FROM $group_capability_table WHERE capability_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
83 323 Groups_Utility::id( $this->capability->capability_id )
84 324 ) );
85 325 if ( $rows ) {
86 326 $result = array();
87 - foreach( $rows as $row ) {
327 + foreach ( $rows as $row ) {
88 328 $result[] = $row->group_id;
89 329 }
90 330 }
91 331 break;
92 332 case 'groups' :
93 - $group_capability_table = _groups_get_tablename( "group_capability" );
333 + $group_capability_table = _groups_get_tablename( 'group_capability' );
94 334 $rows = $wpdb->get_results( $wpdb->prepare(
95 - "SELECT group_id FROM $group_capability_table WHERE capability_id = %d",
335 + "SELECT group_id FROM $group_capability_table WHERE capability_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
96 336 Groups_Utility::id( $this->capability->capability_id )
97 337 ) );
98 338 if ( $rows ) {
99 339 $result = array();
100 - foreach( $rows as $row ) {
340 + foreach ( $rows as $row ) {
101 341 $result[] = new Groups_Group( $row->group_id );
102 342 }
103 343 }
104 344 break;
@@ -108,35 +348,42 @@
108 348 }
109 349
110 350 /**
111 351 * Persist a capability.
112 - *
352 + *
113 353 * Possible keys in $map:
114 - *
354 + *
115 355 * - "capability" (required) - unique capability label, max 20 characters
116 356 * - "class" (optional) - class the capability applies to, max 100 chars
117 357 * - "object" (optional) - identifies object of that class, max 100 chars
118 358 * - "name" (optional) - name it if you have to
119 359 * - "description" (optional) - dito
120 - *
360 + *
121 361 * @param array $map attributes, requires at least: "capability"
122 - * @return capability_id on success, otherwise false
362 + *
363 + * @return int capability_id on success, otherwise false
123 364 */
124 365 public static function create( $map ) {
125 366
126 367 global $wpdb;
127 - extract( $map );
368 +
128 369 $result = false;
129 370
371 + $capability = isset( $map['capability'] ) ? $map['capability'] : null;
372 + $class = isset( $map['class'] ) ? $map['class'] : null;
373 + $object = isset( $map['object'] ) ? $map['object'] : null;
374 + $name = isset( $map['name'] ) ? $map['name'] : null;
375 + $description = isset( $map['description'] ) ? $map['description'] : null;
376 +
130 377 if ( !empty( $capability ) ) {
131 378
379 + self::writer();
380 +
132 381 if ( self::read_by_capability( $capability ) === false ) {
133 -
134 382 $data = array(
135 383 'capability' => $capability
136 384 );
137 385 $formats = array( '%s' );
138 -
139 386 if ( !empty( $class ) ) {
140 387 $data['class'] = $class;
141 388 $formats[] = '%s';
142 389 }
@@ -154,14 +401,21 @@
154 401 }
155 402 $capability_table = _groups_get_tablename( 'capability' );
156 403 if ( $wpdb->insert( $capability_table, $data, $formats ) ) {
157 404 if ( $result = $wpdb->get_var( "SELECT LAST_INSERT_ID()" ) ) {
158 - // read_by_capability above created a cache entry which needs to be reset
159 - Groups_Cache::delete( self::READ_BY_CAPABILITY . '_' . $capability, self::CACHE_GROUP );
160 - do_action( "groups_created_capability", $result );
405 + // refresh cache
406 + Groups_Cache::delete( self::ID_MAP, self::CACHE_GROUP );
407 + Groups_Cache::delete( self::NAME_MAP, self::CACHE_GROUP );
161 408 }
162 409 }
163 410 }
411 +
412 + self::release();
413 +
414 + if ( $result !== false ) {
415 + do_action( 'groups_created_capability', $result );
416 + }
417 +
164 418 }
165 419 return $result;
166 420 }
167 421
@@ -166,92 +420,150 @@
166 420 }
167 421
168 422 /**
169 423 * Retrieve a capability.
170 - *
171 - * Use Groups_Capability::read_capability() if you are trying to retrieve a capability by its unique label.
172 - *
424 + *
425 + * Use Groups_Capability::read_by_capability() if you are trying to retrieve a capability by its name.
426 + *
173 427 * @see Groups_Capability::read_by_capability()
174 428 * @param int $capability_id capability's id
429 + *
175 430 * @return object upon success, otherwise false
176 431 */
177 432 public static function read( $capability_id ) {
178 433 global $wpdb;
434 +
435 + $capability_id = Groups_Utility::id( $capability_id );
436 + if ( $capability_id === false || $capability_id === 0 ) {
437 + return false;
438 + }
439 +
179 440 $result = false;
180 - $capability_table = _groups_get_tablename( 'capability' );
181 - $capability = $wpdb->get_row( $wpdb->prepare(
182 - "SELECT * FROM $capability_table WHERE capability_id = %d",
183 - Groups_Utility::id( $capability_id )
184 - ) );
185 - if ( isset( $capability->capability_id ) ) {
186 - $result = $capability;
441 +
442 + $is_locked = self::is_locked();
443 + if ( !$is_locked ) {
444 + self::writer();
187 445 }
446 +
447 + $cached = Groups_Cache::get( self::ID_MAP, self::CACHE_GROUP );
448 + if ( $cached !== null ) {
449 + $map = $cached->get_value();
450 + $result = $map[$capability_id] ?? false;
451 + } else {
452 + $map = array();
453 + $name_map = array();
454 + $capability_table = _groups_get_tablename( 'capability' );
455 + $capabilities = $wpdb->get_results( "SELECT * FROM $capability_table" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
456 + if ( is_array( $capabilities ) ) {
457 + foreach ( $capabilities as $capability ) {
458 + $map[$capability->capability_id] = $capability; // numerical key is automatically cast to int
459 + $name_map[$capability->capability] = $capability;
460 + }
461 + }
462 + if ( isset( $map[$capability_id] ) ) {
463 + $result = $map[$capability_id];
464 + }
465 + Groups_Cache::set( self::ID_MAP, $map, self::CACHE_GROUP );
466 + Groups_Cache::set( self::NAME_MAP, $name_map, self::CACHE_GROUP );
467 + }
468 +
469 + if ( !$is_locked ) {
470 + self::release();
471 + }
472 +
188 473 return $result;
189 474 }
190 475
191 476 /**
192 - * Retrieve a capability by its unique label.
193 - *
194 - * @param string $capability capability's unique label
477 + * Retrieve a capability by its name.
478 + *
479 + * @param string $name capability name
480 + *
195 481 * @return object upon success, otherwise false
196 482 */
197 - public static function read_by_capability( $capability ) {
483 + public static function read_by_capability( $name ) {
198 484 global $wpdb;
199 - $_capability = $capability;
200 - $cached = Groups_Cache::get( self::READ_BY_CAPABILITY . '_' . $_capability, self::CACHE_GROUP );
485 +
486 + $result = false;
487 +
488 + $is_locked = self::is_locked();
489 + if ( !$is_locked ) {
490 + self::writer();
491 + }
492 +
493 + $cached = Groups_Cache::get( self::NAME_MAP, self::CACHE_GROUP );
201 494 if ( $cached !== null ) {
202 - $result = $cached->value;
203 - unset( $cached );
495 + $name_map = $cached->get_value();
496 + $result = $name_map[$name] ?? false;
204 497 } else {
205 - $result = false;
498 + $map = array();
499 + $name_map = array();
206 500 $capability_table = _groups_get_tablename( 'capability' );
207 - $capability = $wpdb->get_row( $wpdb->prepare(
208 - "SELECT * FROM $capability_table WHERE capability = %s",
209 - $capability
210 - ) );
211 - if ( isset( $capability->capability_id ) ) {
212 - $result = $capability;
501 + $capabilities = $wpdb->get_results( "SELECT * FROM $capability_table" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
502 + if ( is_array( $capabilities ) ) {
503 + foreach ( $capabilities as $capability ) {
504 + $map[$capability->capability_id] = $capability; // numerical key is automatically cast to int
505 + $name_map[$capability->capability] = $capability;
506 + }
213 507 }
214 - Groups_Cache::set( self::READ_BY_CAPABILITY . '_' . $_capability, $result, self::CACHE_GROUP );
508 + if ( isset( $name_map[$name] ) ) {
509 + $result = $name_map[$name];
510 + }
511 + Groups_Cache::set( self::ID_MAP, $map, self::CACHE_GROUP );
512 + Groups_Cache::set( self::NAME_MAP, $name_map, self::CACHE_GROUP );
215 513 }
514 +
515 + if ( !$is_locked ) {
516 + self::release();
517 + }
518 +
216 519 return $result;
217 520 }
218 521
219 522 /**
220 523 * Update capability.
221 - *
524 + *
222 525 * @param array $map capability attribute, must contain capability_id
223 - * @return capability_id on success, otherwise false
526 + *
527 + * @return int capability_id on success, otherwise false
224 528 */
225 529 public static function update( $map ) {
226 530
227 531 global $wpdb;
228 - extract( $map );
532 +
229 533 $result = false;
230 534
231 - if ( isset( $capability_id ) && !empty( $capability ) ) {
535 + $capability_id = isset( $map['capability_id'] ) ? $map['capability_id'] : null;
536 + $capability = isset( $map['capability'] ) ? $map['capability'] : null;
537 + $class = isset( $map['class'] ) ? $map['class'] : null;
538 + $object = isset( $map['object'] ) ? $map['object'] : null;
539 + $name = isset( $map['name'] ) ? $map['name'] : null;
540 + $description = isset( $map['description'] ) ? $map['description'] : null;
541 +
542 + if ( $capability_id !== null ) {
543 +
544 + self::writer();
545 +
232 546 $capability_table = _groups_get_tablename( 'capability' );
233 547 $old_capability = Groups_Capability::read( $capability_id );
234 548 if ( $old_capability ) {
235 - if ( isset( $capability ) ) {
236 - $old_capability_capability = $old_capability->capability;
549 + if ( $capability !== null ) {
237 550 $old_capability->capability = $capability;
238 551 }
239 - if ( isset( $class ) ) {
552 + if ( $class !== null ) {
240 553 $old_capability->class = $class;
241 554 }
242 - if ( isset( $object ) ) {
555 + if ( $object !== null ) {
243 556 $old_capability->object = $object;
244 557 }
245 - if ( isset( $name ) ) {
246 - $old_name = $old_capability->name;
558 + if ( $name !== null ) {
247 559 $old_capability->name = $name;
248 560 }
249 - if ( isset( $description ) ) {
561 + if ( $description !== null ) {
250 562 $old_capability->description = $description;
251 563 }
252 564 $rows = $wpdb->query( $wpdb->prepare(
253 - "UPDATE $capability_table SET capability = %s, class = %s, object = %s, name = %s, description = %s WHERE capability_id = %d",
565 + "UPDATE $capability_table SET capability = %s, class = %s, object = %s, name = %s, description = %s WHERE capability_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
254 566 $old_capability->capability,
255 567 $old_capability->class,
256 568 $old_capability->object,
257 569 $old_capability->name,
@@ -259,17 +571,19 @@
259 571 Groups_Utility::id( $capability_id )
260 572 ) );
261 573 if ( ( $rows !== false ) ) {
262 574 $result = $capability_id;
263 - if ( !empty( $old_capability ) && !empty( $old_capability->capability ) ) {
264 - Groups_Cache::delete( self::READ_BY_CAPABILITY . '_' . $old_capability->capability, self::CACHE_GROUP );
265 - }
266 - if ( !empty( $old_capability_capability ) ) {
267 - Groups_Cache::delete( self::READ_BY_CAPABILITY . '_' . $old_capability_capability, self::CACHE_GROUP );
268 - }
269 - do_action( "groups_updated_capability", $result );
575 + Groups_Cache::delete( self::ID_MAP, self::CACHE_GROUP );
576 + Groups_Cache::delete( self::NAME_MAP, self::CACHE_GROUP );
270 577 }
271 578 }
579 +
580 + self::release();
581 +
582 + if ( $result !== false ) {
583 + do_action( 'groups_updated_capability', $result );
584 + }
585 +
272 586 }
273 587 return $result;
274 588 }
275 589
@@ -274,11 +588,12 @@
274 588 }
275 589
276 590 /**
277 591 * Remove capability and its relations.
278 - *
592 + *
279 593 * @param int $capability_id
280 - * @return capability_id if successful, false otherwise
594 + *
595 + * @return int capability_id if successful, false otherwise
281 596 */
282 597 public static function delete( $capability_id ) {
283 598
284 599 global $wpdb;
@@ -283,23 +598,33 @@
283 598
284 599 global $wpdb;
285 600 $result = false;
286 601
602 + self::writer();
603 +
287 604 // avoid nonsense requests
288 605 if ( $capability = Groups_Capability::read( $capability_id ) ) {
289 606 $capability_table = _groups_get_tablename( 'capability' );
290 607 // get rid of it
291 - if ( $rows = $wpdb->query( $wpdb->prepare(
292 - "DELETE FROM $capability_table WHERE capability_id = %d",
608 + $rows = $wpdb->query( $wpdb->prepare(
609 + "DELETE FROM $capability_table WHERE capability_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
293 610 Groups_Utility::id( $capability_id )
294 - ) ) ) {
611 + ) );
612 + if ( $rows !== false && $rows > 0 ) {
295 613 $result = $capability_id;
296 614 if ( !empty( $capability->capability ) ) {
297 - Groups_Cache::delete( self::READ_BY_CAPABILITY . '_' . $capability->capability, self::CACHE_GROUP );
298 615 do_action( 'groups_deleted_capability_capability', $capability->capability );
299 616 }
300 - do_action( "groups_deleted_capability", $result );
617 + Groups_Cache::delete( self::ID_MAP, self::CACHE_GROUP );
618 + Groups_Cache::delete( self::NAME_MAP, self::CACHE_GROUP );
301 619 }
302 620 }
621 +
622 + self::release();
623 +
624 + if ( $result !== false ) {
625 + do_action( 'groups_deleted_capability', $result );
626 + }
627 +
303 628 return $result;
304 629 }
305 -}
630 +}