Autopromote::recCheckCondition()   C
last analyzed

Complexity

Conditions 16
Paths 17

Size

Total Lines 46
Code Lines 27

Duplication

Lines 18
Ratio 39.13 %

Importance

Changes 0
Metric Value
cc 16
eloc 27
nc 17
nop 2
dl 18
loc 46
rs 5.0026
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Automatic user rights promotion based on conditions specified
4
 * in $wgAutopromote.
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 2 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along
17
 * with this program; if not, write to the Free Software Foundation, Inc.,
18
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
 * http://www.gnu.org/copyleft/gpl.html
20
 *
21
 * @file
22
 */
23
24
/**
25
 * This class checks if user can get extra rights
26
 * because of conditions specified in $wgAutopromote
27
 */
28
class Autopromote {
29
	/**
30
	 * Get the groups for the given user based on $wgAutopromote.
31
	 *
32
	 * @param User $user The user to get the groups for
33
	 * @return array Array of groups to promote to.
34
	 */
35
	public static function getAutopromoteGroups( User $user ) {
36
		global $wgAutopromote;
37
38
		$promote = [];
39
40
		foreach ( $wgAutopromote as $group => $cond ) {
41
			if ( self::recCheckCondition( $cond, $user ) ) {
42
				$promote[] = $group;
43
			}
44
		}
45
46
		Hooks::run( 'GetAutoPromoteGroups', [ $user, &$promote ] );
47
48
		return $promote;
49
	}
50
51
	/**
52
	 * Get the groups for the given user based on the given criteria.
53
	 *
54
	 * Does not return groups the user already belongs to or has once belonged.
55
	 *
56
	 * @param User $user The user to get the groups for
57
	 * @param string $event Key in $wgAutopromoteOnce (each one has groups/criteria)
58
	 *
59
	 * @return array Groups the user should be promoted to.
60
	 *
61
	 * @see $wgAutopromoteOnce
62
	 */
63
	public static function getAutopromoteOnceGroups( User $user, $event ) {
64
		global $wgAutopromoteOnce;
65
66
		$promote = [];
67
68
		if ( isset( $wgAutopromoteOnce[$event] ) && count( $wgAutopromoteOnce[$event] ) ) {
69
			$currentGroups = $user->getGroups();
70
			$formerGroups = $user->getFormerGroups();
71
			foreach ( $wgAutopromoteOnce[$event] as $group => $cond ) {
72
				// Do not check if the user's already a member
73
				if ( in_array( $group, $currentGroups ) ) {
74
					continue;
75
				}
76
				// Do not autopromote if the user has belonged to the group
77
				if ( in_array( $group, $formerGroups ) ) {
78
					continue;
79
				}
80
				// Finally - check the conditions
81
				if ( self::recCheckCondition( $cond, $user ) ) {
82
					$promote[] = $group;
83
				}
84
			}
85
		}
86
87
		return $promote;
88
	}
89
90
	/**
91
	 * Recursively check a condition.  Conditions are in the form
92
	 *   array( '&' or '|' or '^' or '!', cond1, cond2, ... )
93
	 * where cond1, cond2, ... are themselves conditions; *OR*
94
	 *   APCOND_EMAILCONFIRMED, *OR*
95
	 *   array( APCOND_EMAILCONFIRMED ), *OR*
96
	 *   array( APCOND_EDITCOUNT, number of edits ), *OR*
97
	 *   array( APCOND_AGE, seconds since registration ), *OR*
98
	 *   similar constructs defined by extensions.
99
	 * This function evaluates the former type recursively, and passes off to
100
	 * self::checkCondition for evaluation of the latter type.
101
	 *
102
	 * @param mixed $cond A condition, possibly containing other conditions
103
	 * @param User $user The user to check the conditions against
104
	 * @return bool Whether the condition is true
105
	 */
106
	private static function recCheckCondition( $cond, User $user ) {
107
		$validOps = [ '&', '|', '^', '!' ];
108
109
		if ( is_array( $cond ) && count( $cond ) >= 2 && in_array( $cond[0], $validOps ) ) {
110
			# Recursive condition
111
			if ( $cond[0] == '&' ) { // AND (all conds pass)
112
				foreach ( array_slice( $cond, 1 ) as $subcond ) {
113
					if ( !self::recCheckCondition( $subcond, $user ) ) {
114
						return false;
115
					}
116
				}
117
118
				return true;
119 View Code Duplication
			} elseif ( $cond[0] == '|' ) { // OR (at least one cond passes)
120
				foreach ( array_slice( $cond, 1 ) as $subcond ) {
121
					if ( self::recCheckCondition( $subcond, $user ) ) {
122
						return true;
123
					}
124
				}
125
126
				return false;
127
			} elseif ( $cond[0] == '^' ) { // XOR (exactly one cond passes)
128
				if ( count( $cond ) > 3 ) {
129
					wfWarn( 'recCheckCondition() given XOR ("^") condition on three or more conditions.' .
130
						' Check your $wgAutopromote and $wgAutopromoteOnce settings.' );
131
				}
132
				return self::recCheckCondition( $cond[1], $user )
133
					xor self::recCheckCondition( $cond[2], $user );
134 View Code Duplication
			} elseif ( $cond[0] == '!' ) { // NOT (no conds pass)
135
				foreach ( array_slice( $cond, 1 ) as $subcond ) {
136
					if ( self::recCheckCondition( $subcond, $user ) ) {
137
						return false;
138
					}
139
				}
140
141
				return true;
142
			}
143
		}
144
		// If we got here, the array presumably does not contain other conditions;
145
		// it's not recursive.  Pass it off to self::checkCondition.
146
		if ( !is_array( $cond ) ) {
147
			$cond = [ $cond ];
148
		}
149
150
		return self::checkCondition( $cond, $user );
151
	}
152
153
	/**
154
	 * As recCheckCondition, but *not* recursive.  The only valid conditions
155
	 * are those whose first element is APCOND_EMAILCONFIRMED/APCOND_EDITCOUNT/
156
	 * APCOND_AGE.  Other types will throw an exception if no extension evaluates them.
157
	 *
158
	 * @param array $cond A condition, which must not contain other conditions
159
	 * @param User $user The user to check the condition against
160
	 * @throws MWException
161
	 * @return bool Whether the condition is true for the user
162
	 */
163
	private static function checkCondition( $cond, User $user ) {
164
		global $wgEmailAuthentication;
165
		if ( count( $cond ) < 1 ) {
166
			return false;
167
		}
168
169
		switch ( $cond[0] ) {
170
			case APCOND_EMAILCONFIRMED:
171
				if ( Sanitizer::validateEmail( $user->getEmail() ) ) {
172
					if ( $wgEmailAuthentication ) {
173
						return (bool)$user->getEmailAuthenticationTimestamp();
174
					} else {
175
						return true;
176
					}
177
				}
178
				return false;
179
			case APCOND_EDITCOUNT:
180
				return $user->getEditCount() >= $cond[1];
181
			case APCOND_AGE:
182
				$age = time() - wfTimestampOrNull( TS_UNIX, $user->getRegistration() );
0 ignored issues
show
Security Bug introduced by
It seems like $user->getRegistration() targeting User::getRegistration() can also be of type false; however, wfTimestampOrNull() does only seem to accept string|null, did you maybe forget to handle an error condition?
Loading history...
183
				return $age >= $cond[1];
184
			case APCOND_AGE_FROM_EDIT:
185
				$age = time() - wfTimestampOrNull( TS_UNIX, $user->getFirstEditTimestamp() );
0 ignored issues
show
Security Bug introduced by
It seems like $user->getFirstEditTimestamp() targeting User::getFirstEditTimestamp() can also be of type false; however, wfTimestampOrNull() does only seem to accept string|null, did you maybe forget to handle an error condition?
Loading history...
186
				return $age >= $cond[1];
187
			case APCOND_INGROUPS:
188
				$groups = array_slice( $cond, 1 );
189
				return count( array_intersect( $groups, $user->getGroups() ) ) == count( $groups );
190
			case APCOND_ISIP:
191
				return $cond[1] == $user->getRequest()->getIP();
192
			case APCOND_IPINRANGE:
193
				return IP::isInRange( $user->getRequest()->getIP(), $cond[1] );
194
			case APCOND_BLOCKED:
195
				return $user->isBlocked();
196
			case APCOND_ISBOT:
197
				return in_array( 'bot', User::getGroupPermissions( $user->getGroups() ) );
198
			default:
199
				$result = null;
200
				Hooks::run( 'AutopromoteCondition', [ $cond[0],
201
					array_slice( $cond, 1 ), $user, &$result ] );
202
				if ( $result === null ) {
203
					throw new MWException( "Unrecognized condition {$cond[0]} for autopromotion!" );
204
				}
205
206
				return (bool)$result;
207
		}
208
	}
209
}
210