Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
75.00% |
30 / 40 |
|
50.00% |
3 / 6 |
CRAP | |
0.00% |
0 / 1 |
ConditionalDefaultsLookup | |
75.00% |
30 / 40 |
|
50.00% |
3 / 6 |
23.06 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
hasConditionalDefault | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
getConditionallyDefaultOptions | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getOptionDefaultForUser | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
checkConditionsForUser | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
checkConditionForUser | |
83.33% |
15 / 18 |
|
0.00% |
0 / 1 |
9.37 |
1 | <?php |
2 | |
3 | namespace MediaWiki\User\Options; |
4 | |
5 | use InvalidArgumentException; |
6 | use MediaWiki\Config\ServiceOptions; |
7 | use MediaWiki\MainConfigNames; |
8 | use MediaWiki\User\Registration\UserRegistrationLookup; |
9 | use MediaWiki\User\UserIdentity; |
10 | use MediaWiki\User\UserIdentityUtils; |
11 | use Wikimedia\Timestamp\ConvertibleTimestamp; |
12 | |
13 | class ConditionalDefaultsLookup { |
14 | |
15 | /** |
16 | * @internal Exposed for ServiceWiring only |
17 | */ |
18 | public const CONSTRUCTOR_OPTIONS = [ |
19 | MainConfigNames::ConditionalUserOptions, |
20 | ]; |
21 | |
22 | private ServiceOptions $options; |
23 | private UserRegistrationLookup $userRegistrationLookup; |
24 | private UserIdentityUtils $userIdentityUtils; |
25 | |
26 | public function __construct( |
27 | ServiceOptions $options, |
28 | UserRegistrationLookup $userRegistrationLookup, |
29 | UserIdentityUtils $userIdentityUtils |
30 | ) { |
31 | $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS ); |
32 | |
33 | $this->options = $options; |
34 | $this->userRegistrationLookup = $userRegistrationLookup; |
35 | $this->userIdentityUtils = $userIdentityUtils; |
36 | } |
37 | |
38 | /** |
39 | * Does the option support conditional defaults? |
40 | * |
41 | * @param string $option |
42 | * @return bool |
43 | */ |
44 | public function hasConditionalDefault( string $option ): bool { |
45 | return array_key_exists( |
46 | $option, |
47 | $this->options->get( MainConfigNames::ConditionalUserOptions ) |
48 | ); |
49 | } |
50 | |
51 | /** |
52 | * Get all conditionally default user options |
53 | * |
54 | * @return string[] |
55 | */ |
56 | public function getConditionallyDefaultOptions(): array { |
57 | return array_keys( |
58 | $this->options->get( MainConfigNames::ConditionalUserOptions ) |
59 | ); |
60 | } |
61 | |
62 | /** |
63 | * Get the conditional default for user and option |
64 | * |
65 | * @param string $optionName |
66 | * @param UserIdentity $userIdentity |
67 | * @return mixed|null The default value if set, or null if it cannot be determined |
68 | * conditionally (default from DefaultOptionsLookup should be used in that case). |
69 | */ |
70 | public function getOptionDefaultForUser( |
71 | string $optionName, |
72 | UserIdentity $userIdentity |
73 | ) { |
74 | $conditionalDefaults = $this->options |
75 | ->get( MainConfigNames::ConditionalUserOptions )[$optionName] ?? []; |
76 | foreach ( $conditionalDefaults as $conditionalDefault ) { |
77 | // At the zeroth index of the conditional case, the intended value is found; the rest |
78 | // of the array are conditions, which are evaluated in checkConditionsForUser(). |
79 | $value = array_shift( $conditionalDefault ); |
80 | if ( $this->checkConditionsForUser( $userIdentity, $conditionalDefault ) ) { |
81 | return $value; |
82 | } |
83 | } |
84 | |
85 | return null; |
86 | } |
87 | |
88 | /** |
89 | * Are ALL conditions satisfied for the given user? |
90 | * |
91 | * @param UserIdentity $userIdentity |
92 | * @param array $conditions |
93 | * @return bool |
94 | */ |
95 | private function checkConditionsForUser( UserIdentity $userIdentity, array $conditions ): bool { |
96 | foreach ( $conditions as $condition ) { |
97 | if ( !$this->checkConditionForUser( $userIdentity, $condition ) ) { |
98 | return false; |
99 | } |
100 | } |
101 | return true; |
102 | } |
103 | |
104 | /** |
105 | * Is ONE condition satisfied for the given user? |
106 | * |
107 | * @param UserIdentity $userIdentity |
108 | * @param array|int $cond Either [ CUDCOND_*, args ] or CUDCOND_*, depending on whether the |
109 | * condition has any arguments. |
110 | * @return bool |
111 | */ |
112 | private function checkConditionForUser( |
113 | UserIdentity $userIdentity, |
114 | $cond |
115 | ): bool { |
116 | if ( !is_array( $cond ) ) { |
117 | $cond = [ $cond ]; |
118 | } |
119 | if ( $cond === [] ) { |
120 | throw new InvalidArgumentException( 'Empty condition' ); |
121 | } |
122 | $condName = array_shift( $cond ); |
123 | switch ( $condName ) { |
124 | case CUDCOND_AFTER: |
125 | $registration = $this->userRegistrationLookup->getRegistration( $userIdentity ); |
126 | if ( $registration === null || $registration === false ) { |
127 | return false; |
128 | } |
129 | |
130 | return ( |
131 | (int)ConvertibleTimestamp::convert( TS_UNIX, $registration ) - |
132 | (int)ConvertibleTimestamp::convert( TS_UNIX, $cond[0] ) |
133 | ) > 0; |
134 | case CUDCOND_ANON: |
135 | return !$userIdentity->isRegistered(); |
136 | case CUDCOND_NAMED: |
137 | return $this->userIdentityUtils->isNamed( $userIdentity ); |
138 | default: |
139 | throw new InvalidArgumentException( 'Unsupported condition ' . $condName ); |
140 | } |
141 | } |
142 | } |