Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
UserRequirementsConditionValidator
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
3 / 3
14
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
 isValid
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 isValidInternal
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
11
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\User;
8
9use Psr\Log\LoggerInterface;
10
11class UserRequirementsConditionValidator {
12
13    public function __construct(
14        private readonly LoggerInterface $logger
15    ) {
16    }
17
18    /**
19     * Checks if the passed user requirements condition is valid.
20     * If it's invalid, the details of what's wrong are logged.
21     */
22    public function isValid( mixed $condition ): bool {
23        // Empty array at top level is allowed; elsewhere it's not
24        if ( $condition === [] ) {
25            return true;
26        }
27        return $this->isValidInternal( $condition );
28    }
29
30    private function isValidInternal( mixed $condition ): bool {
31        if ( !is_array( $condition ) ) {
32            $condition = [ $condition ];
33        }
34        if ( $condition === [] ) {
35            $this->logger->warning( 'Empty array is invalid descriptor of a user requirements condition' );
36            return false;
37        }
38
39        $condType = $condition[0];
40        $args = array_slice( $condition, 1 );
41
42        if ( in_array( $condType, UserRequirementsConditionChecker::VALID_OPS ) ) {
43            // Ensure proper argument count: for XOR - exactly 2, for others - at least 1
44            if ( $condType === '^' && count( $args ) !== 2 ) {
45                $this->logger->warning( 'XOR (^) in user requirements conditions must have exactly two arguments' );
46                return false;
47            }
48            if ( count( $args ) === 0 ) {
49                $this->logger->warning( 'Compound conditions must have at least one argument' );
50                return false;
51            }
52
53            foreach ( $args as $arg ) {
54                if ( !$this->isValidInternal( $arg ) ) {
55                    // Don't log anything here, it's logged in the original place
56                    return false;
57                }
58            }
59            return true;
60        }
61
62        if ( !is_string( $condType ) && !is_int( $condType ) ) {
63            $this->logger->warning( 'User requirements conditions must be designated as string or int' );
64            return false;
65        }
66        return true;
67    }
68}