Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
UserBucketService
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
3 / 3
8
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 bucketEditCount
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
 getUserEditCountBucket
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2namespace MediaWiki\Extension\EventLogging\Libs\UserBucketProvider;
3
4use MediaWiki\User\UserEditTracker;
5use MediaWiki\User\UserIdentity;
6
7/**
8 * A service class for getting low-granularity segmentation of users. Currently, users can be
9 * segmented by their edit count.
10 */
11class UserBucketService {
12
13    /**
14     * The user edit count for an anonymous user.
15     *
16     * To maintain backwards compatibility with existing clients, we use `null` rather than, say,
17     * `"N/A"`.
18     */
19    public const ANONYMOUS_USER_EDIT_COUNT_BUCKET = null;
20
21    /**
22     * @var UserEditTracker
23     */
24    private $userEditTracker;
25
26    public function __construct( UserEditTracker $userEditTracker ) {
27        $this->userEditTracker = $userEditTracker;
28    }
29
30    /**
31     * Find the coarse bucket corresponding to an edit count.
32     *
33     * The buckets are as follows:
34     *
35     * * 0 edits
36     * * 1-4 edits
37     * * 5-99 edits
38     * * 100-999 edits
39     * * 1000+ edits
40     *
41     * These bucket labels are the current standard, but are subject to change in the future. They are usually safe
42     * to keep in sanitized streams and should remain so even if they are changed.
43     *
44     * Sites may override this service and define their own metrics buckets.  If we rely on coarse bucketing to
45     * protect user identity, it's important to not mix different bucketing thresholds, since the intersections
46     * can reveal more detail than intended.
47     *
48     * @param int $userEditCount
49     * @return string Bucket identifier
50     */
51    public function bucketEditCount( int $userEditCount ): string {
52        if ( $userEditCount >= 1000 ) {
53            return '1000+ edits';
54        }
55        if ( $userEditCount >= 100 ) {
56            return '100-999 edits';
57        }
58        if ( $userEditCount >= 5 ) {
59            return '5-99 edits';
60        }
61        if ( $userEditCount >= 1 ) {
62            return '1-4 edits';
63        }
64        return '0 edits';
65    }
66
67    /**
68     * Find the coarse bucket corresponding to the edit count of the given user.
69     *
70     * @param UserIdentity $user provides raw edit count
71     * @return string|null Bucket identifier, or `null` for anonymous users.
72     *
73     * @see UserBucketService::bucketEditCount
74     * @see UserBucketService::ANONYMOUS_USER_EDIT_COUNT_BUCKET
75     */
76    public function getUserEditCountBucket( UserIdentity $user ): ?string {
77        $userEditCount = $this->userEditTracker->getUserEditCount( $user );
78
79        if ( $userEditCount === null ) {
80            return self::ANONYMOUS_USER_EDIT_COUNT_BUCKET;
81        }
82
83        return $this->bucketEditCount( $userEditCount );
84    }
85}