WordPoints_Rank_Group::get_rank()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Rank group class.
5
 *
6
 * @package WordPoints\Ranks
7
 * @since 2.4.0
8
 */
9
10
/**
11
 * Represents a rank group.
12
 *
13
 * @since 1.7.0
14
 *
15
 * @property-read string $slug
16
 * @property-read string $description
17
 * @property-read string $name
18
 */
19
final class WordPoints_Rank_Group {
20
21
	/**
22
	 * The slug for this group.
23
	 *
24
	 * @since 1.7.0
25
	 *
26
	 * @type string $slug
27
	 */
28
	private $slug;
29
30
	/**
31
	 * The group's data.
32
	 *
33
	 * @since 1.7.0
34
	 *
35
	 * @type array $data
36
	 */
37
	private $data;
38
39
	/**
40
	 * The name of the option in the database for the list of ranks in this group.
41
	 *
42
	 * @since 1.7.0
43
	 *
44
	 * @type string $option_name
45
	 */
46
	private $option_name;
47
48
	/**
49
	 * The types of rank allowed in this group.
50
	 *
51
	 * @since 1.7.0
52
	 *
53
	 * @type string[] $types
54
	 */
55
	private $types = array( 'base' => 'base' );
56
57
	/**
58
	 * Construct the group with its slug and other data.
59
	 *
60
	 * @since 1.7.0
61
	 *
62
	 * @param string $slug The slug of this group.
63
	 * @param array  $data {
64
	 *        Other data for the group.
65
	 *
66
	 *        @type string $name        The name for this group.
67
	 *        @type string $description A description of this group.
68
	 * }
69
	 */
70
	public function __construct( $slug, $data ) {
71
72
		$this->slug        = $slug;
0 ignored issues
show
Bug introduced by
The property slug is declared read-only in WordPoints_Rank_Group.
Loading history...
73
		$this->data        = $data;
74
		$this->option_name = "wordpoints_rank_group-{$this->slug}";
75
	}
76
77
	/**
78
	 * Magic getter for the group's data.
79
	 *
80
	 * @since 1.7.0
81
	 */
82
	public function __get( $key ) {
83
84
		if ( isset( $this->$key ) ) {
85
			return $this->$key;
86
		} elseif ( isset( $this->data[ $key ] ) ) {
87
			return $this->data[ $key ];
88
		} else {
89
			return null;
90
		}
91
	}
92
93
	/**
94
	 * Get the slug of this group.
95
	 *
96
	 * @since 1.7.0
97
	 *
98
	 * @return string The group's slug.
99
	 */
100
	public function get_slug() {
101
		return $this->slug;
102
	}
103
104
	/**
105
	 * Get the types of rank registered for this group.
106
	 *
107
	 * @since 1.7.0
108
	 *
109
	 * @return string[] The slugs of the rank types supported by this group.
110
	 */
111
	public function get_types() {
112
113
		return $this->types;
114
	}
115
116
	/**
117
	 * Check if this group is allowed to contain a given rank type.
118
	 *
119
	 * @since 1.7.0
120
	 *
121
	 * @param string $type The rank type to check for.
122
	 *
123
	 * @return bool Whether the rank type was registered for this group.
124
	 */
125
	public function has_type( $type ) {
126
127
		if ( ! WordPoints_Rank_Types::is_type_registered( $type ) ) {
128
			return false;
129
		}
130
131
		return isset( $this->types[ $type ] );
132
	}
133
134
	/**
135
	 * Add a rank type to the list allowed in this group.
136
	 *
137
	 * @since 1.7.0
138
	 *
139
	 * @param string $type The rank type to add.
140
	 *
141
	 * @return bool Whether the type was added successfully.
142
	 */
143 View Code Duplication
	public function add_type( $type ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
144
145
		if ( ! WordPoints_Rank_Types::is_type_registered( $type ) ) {
146
			return false;
147
		}
148
149
		if ( isset( $this->types[ $type ] ) ) {
150
			return false;
151
		}
152
153
		$this->types[ $type ] = $type;
154
155
		return true;
156
	}
157
158
	/**
159
	 * Remove a rank type from the list allowed in this group.
160
	 *
161
	 * Note that the 'base' rank type cannot be removed.
162
	 *
163
	 * @since 1.7.0
164
	 *
165
	 * @param string $type The rank type to remove.
166
	 *
167
	 * @return bool Whether the type was removed successfully.
168
	 */
169 View Code Duplication
	public function remove_type( $type ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
170
171
		if ( 'base' === $type ) {
172
			return false;
173
		}
174
175
		if ( ! WordPoints_Rank_Types::is_type_registered( $type ) ) {
176
			return false;
177
		}
178
179
		if ( ! isset( $this->types[ $type ] ) ) {
180
			return false;
181
		}
182
183
		unset( $this->types[ $type ] );
184
185
		return true;
186
	}
187
188
	/**
189
	 * Add a rank to the group.
190
	 *
191
	 * If the position is after the last rank, it will be added as the last rank. So
192
	 * if there are 4 ranks, and you try to add a rank in position 7, it will be the
193
	 * 5th rank in the group.
194
	 *
195
	 * @since 1.7.0
196
	 *
197
	 * @param int $rank_id  The ID of the rank to add to this group.
198
	 * @param int $position The position of this rank in the rank list.
199
	 *
200
	 * @return bool Whether the rank was added successfully.
201
	 */
202
	public function add_rank( $rank_id, $position ) {
203
204
		if (
205
			! wordpoints_posint( $rank_id )
206
			|| false === wordpoints_int( $position )
207
			|| $position < 0
208
		) {
209
			return false;
210
		}
211
212
		// Don't allow a rank to be added to this group more than once.
213
		if ( false !== $this->get_rank_position( $rank_id ) ) {
214
			return false;
215
		}
216
217
		$ranks = $this->insert_rank( $this->get_ranks(), $rank_id, $position );
218
219
		if ( ! $this->save_ranks( $ranks ) ) {
220
			return false;
221
		}
222
223
		// If there is a rank before this one, check if any of the users who have it
224
		// can increase to this new one.
225
		$this->maybe_increase_users_with_previous_rank( $rank_id );
226
227
		return true;
228
	}
229
230
	/**
231
	 * Move a rank from one position to another.
232
	 *
233
	 * Calling this method saves us from having to call remove and then add causing
234
	 * two database writes. This way we only write to the database once.
235
	 *
236
	 * @since 1.7.0
237
	 *
238
	 * @param int $rank_id  The ID of the rank to add to this group.
239
	 * @param int $position The position of this rank in the rank list.
240
	 *
241
	 * @return bool Whether the rank was moved successfully.
242
	 */
243
	public function move_rank( $rank_id, $position ) {
244
245
		$current_position = $this->get_rank_position( $rank_id );
246
247
		if ( $current_position === $position ) {
248
			return false;
249
		}
250
251
		$ranks = $this->get_ranks();
252
253
		unset( $ranks[ $current_position ] );
254
255
		ksort( $ranks );
256
257
		$ranks = $this->insert_rank( array_values( $ranks ), $rank_id, $position );
258
259
		if ( ! $this->save_ranks( $ranks ) ) {
260
			return false;
261
		}
262
263
		// Users of the rank's former position should be moved to the previous
264
		// rank's position.
265
		$this->move_users_from_rank_to_rank(
266
			$rank_id
267
			, $this->get_rank( $current_position - 1 )
0 ignored issues
show
Bug introduced by
It seems like $this->get_rank($current_position - 1) can also be of type false; however, parameter $rank_to_id of WordPoints_Rank_Group::m...ers_from_rank_to_rank() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

267
			, /** @scrutinizer ignore-type */ $this->get_rank( $current_position - 1 )
Loading history...
268
		);
269
270
		// The users of the rank previous to this one's new position should maybe be
271
		// increased to this rank.
272
		$this->maybe_increase_users_with_previous_rank( $rank_id );
273
274
		return true;
275
	}
276
277
	/**
278
	 * Remove a rank from the group.
279
	 *
280
	 * @since 1.7.0
281
	 *
282
	 * @param int $rank_id The ID of the rank to remove from this group.
283
	 *
284
	 * @return bool Whether the rank was removed successfully.
285
	 */
286
	public function remove_rank( $rank_id ) {
287
288
		global $wpdb;
289
290
		$position = $this->get_rank_position( $rank_id );
291
292
		if ( false === $position ) {
293
			return false;
294
		}
295
296
		$ranks = $this->get_ranks();
297
298
		unset( $ranks[ $position ] );
299
300
		if ( ! $this->save_ranks( $ranks ) ) {
301
			return false;
302
		}
303
304
		// Assign the previous rank to users who have this rank.
305
		if ( isset( $ranks[ $position - 1 ] ) ) {
306
307
			$this->move_users_from_rank_to_rank( $rank_id, $ranks[ $position - 1 ] );
308
309
		} else {
310
311
			// If there is no previous rank, just delete the rows.
312
			$wpdb->delete(
313
				$wpdb->wordpoints_user_ranks
314
				, array( 'rank_id' => $rank_id )
315
				, '%d'
316
			);
317
318
			$group_ranks = wp_cache_get( $this->slug, 'wordpoints_user_ranks' );
0 ignored issues
show
Bug introduced by
The function wp_cache_get was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

318
			$group_ranks = /** @scrutinizer ignore-call */ wp_cache_get( $this->slug, 'wordpoints_user_ranks' );
Loading history...
319
			unset( $group_ranks[ $rank_id ] );
320
			wp_cache_set( $this->slug, $group_ranks, 'wordpoints_user_ranks' );
0 ignored issues
show
Bug introduced by
The function wp_cache_set was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

320
			/** @scrutinizer ignore-call */ 
321
   wp_cache_set( $this->slug, $group_ranks, 'wordpoints_user_ranks' );
Loading history...
321
322
			unset( $group_ranks );
323
		}
324
325
		return true;
326
	}
327
328
	/**
329
	 * Save the list of ranks.
330
	 *
331
	 * The save is aborted and false is returned if there are duplicate entries.
332
	 *
333
	 * @since 1.7.0
334
	 *
335
	 * @param int[] $rank_ids The IDs of the ranks in this group, in correct order.
336
	 *
337
	 * @return bool Whether the ranks were saved successfully.
338
	 */
339
	public function save_ranks( $rank_ids ) {
340
341
		if ( count( array_unique( $rank_ids ) ) !== count( $rank_ids ) ) {
342
			return false;
343
		}
344
345
		ksort( $rank_ids );
346
347
		$rank_ids = array_values( $rank_ids );
348
349
		return update_option( $this->option_name, $rank_ids );
0 ignored issues
show
Bug introduced by
The function update_option was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

349
		return /** @scrutinizer ignore-call */ update_option( $this->option_name, $rank_ids );
Loading history...
350
	}
351
352
	/**
353
	 * Get a list of ranks in this group.
354
	 *
355
	 * @since 1.7.0
356
	 *
357
	 * @return int[] The IDs of the ranks in this group, indexed by order.
358
	 */
359
	public function get_ranks() {
360
361
		return wordpoints_get_array_option( $this->option_name );
362
	}
363
364
	/**
365
	 * Get the ID of the default rank.
366
	 *
367
	 * The default rank is the lowest rank in the group.
368
	 *
369
	 * @since 1.7.0
370
	 *
371
	 * @return int|false The ID of the rank, or false if not found.
372
	 */
373
	public function get_base_rank() {
374
375
		return $this->get_rank( 0 );
376
	}
377
378
	/**
379
	 * Get the ID of a rank from its position.
380
	 *
381
	 * @since 1.7.0
382
	 *
383
	 * @param int $position The position of the rank to get the ID of.
384
	 *
385
	 * @return int|false The ID of the rank, or false if not found.
386
	 */
387
	public function get_rank( $position ) {
388
389
		$ranks = $this->get_ranks();
390
391
		if ( ! isset( $ranks[ $position ] ) ) {
392
			return false;
393
		}
394
395
		return $ranks[ $position ];
396
	}
397
398
	/**
399
	 * Get a rank's position by its ID.
400
	 *
401
	 * @since 1.7.0
402
	 *
403
	 * @param int $rank_id The ID of the rank to get the position of.
404
	 *
405
	 * @return int|false The rank's position, or false.
406
	 */
407
	public function get_rank_position( $rank_id ) {
408
409
		return array_search(
410
			wordpoints_posint( $rank_id )
411
			, $this->get_ranks()
412
			, true
413
		);
414
	}
415
416
	//
417
	// Private Methods.
418
	//
419
420
	/**
421
	 * Insert a rank into a list of ranks at a given position.
422
	 *
423
	 * @since 1.7.0
424
	 *
425
	 * @param int[] $ranks    The list of rank IDs to insert this rank ID into.
426
	 * @param int   $rank_id  The rank to insert into the list.
427
	 * @param int   $position The position in the list to insert it.
428
	 *
429
	 * @return int[] The list of ranks with the rank inserted.
430
	 */
431
	private function insert_rank( $ranks, $rank_id, $position ) {
432
433
		$count = count( $ranks );
434
435
		if ( $count === $position ) {
436
437
			$ranks[ $position ] = $rank_id;
438
439
		} elseif ( $count < $position ) {
440
441
			$ranks[] = $rank_id;
442
443
		} else {
444
445
			$lower_ranks  = array_slice( $ranks, 0, $position, true );
446
			$higher_ranks = array_slice( $ranks, $position, null, true );
447
448
			$ranks = array_merge(
449
				$lower_ranks
450
				, array( $position => $rank_id )
451
				, $higher_ranks
452
			);
453
		}
454
455
		return $ranks;
456
	}
457
458
	/**
459
	 * Assign all users who have a given rank a different rank instead.
460
	 *
461
	 * @since 1.7.0
462
	 *
463
	 * @param int $rank_from_id The ID of the rank the users have now.
464
	 * @param int $rank_to_id   The ID of the rank to give the users instead.
465
	 */
466
	private function move_users_from_rank_to_rank( $rank_from_id, $rank_to_id ) {
467
468
		global $wpdb;
469
470
		if ( ! wordpoints_posint( $rank_from_id ) || ! wordpoints_posint( $rank_to_id ) ) {
471
			return;
472
		}
473
474
		$group_ranks = wp_cache_get( $this->slug, 'wordpoints_user_ranks' );
0 ignored issues
show
Bug introduced by
The function wp_cache_get was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

474
		$group_ranks = /** @scrutinizer ignore-call */ wp_cache_get( $this->slug, 'wordpoints_user_ranks' );
Loading history...
475
476
		unset( $group_ranks[ $rank_from_id ], $group_ranks[ $rank_to_id ] );
477
478
		wp_cache_set( $this->slug, $group_ranks, 'wordpoints_user_ranks' );
0 ignored issues
show
Bug introduced by
The function wp_cache_set was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

478
		/** @scrutinizer ignore-call */ 
479
  wp_cache_set( $this->slug, $group_ranks, 'wordpoints_user_ranks' );
Loading history...
479
480
		unset( $group_ranks );
481
482
		$wpdb->update(
483
			$wpdb->wordpoints_user_ranks
484
			, array( 'rank_id' => $rank_to_id )
485
			, array( 'rank_id' => $rank_from_id )
486
			, '%d'
487
			, '%d'
488
		);
489
	}
490
491
	/**
492
	 * Increases users with the previous rank if needed.
493
	 *
494
	 * @since 1.7.0
495
	 *
496
	 * @param int $rank_id A rank ID.
497
	 */
498
	private function maybe_increase_users_with_previous_rank( $rank_id ) {
499
500
		$rank          = wordpoints_get_rank( $rank_id );
501
		$previous_rank = $rank->get_adjacent( -1 );
502
503
		if ( ! $previous_rank ) {
504
			return;
505
		}
506
507
		$maybe_increase = new WordPoints_User_Ranks_Maybe_Increase( $previous_rank );
508
		$maybe_increase->run();
509
	}
510
}
511
512
// EOF
513