Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
ConsequencesRegistry
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
6 / 6
11
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getDangerousActionNames
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 getAllActionNames
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 getCustomActions
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 validateCustomActions
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
 getAllEnabledActionNames
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace MediaWiki\Extension\AbuseFilter\Consequences;
4
5// phpcs:ignore MediaWiki.Classes.UnusedUseStatement.UnusedUse
6use MediaWiki\Extension\AbuseFilter\Consequences\Consequence\Consequence;
7use MediaWiki\Extension\AbuseFilter\Hooks\AbuseFilterHookRunner;
8use RuntimeException;
9
10class ConsequencesRegistry {
11    public const SERVICE_NAME = 'AbuseFilterConsequencesRegistry';
12
13    private const DANGEROUS_ACTIONS = [
14        'block',
15        'blockautopromote',
16        'degroup',
17        'rangeblock'
18    ];
19
20    /** @var AbuseFilterHookRunner */
21    private $hookRunner;
22    /** @var bool[] */
23    private $configActions;
24
25    /** @var string[]|null */
26    private $dangerousActionsCache;
27    /** @var callable[]|null */
28    private $customActionsCache;
29
30    /**
31     * @param AbuseFilterHookRunner $hookRunner
32     * @param bool[] $configActions
33     */
34    public function __construct(
35        AbuseFilterHookRunner $hookRunner,
36        array $configActions
37    ) {
38        $this->hookRunner = $hookRunner;
39        $this->configActions = $configActions;
40    }
41
42    /**
43     * Get an array of actions which harm the user.
44     *
45     * @return string[]
46     */
47    public function getDangerousActionNames(): array {
48        if ( $this->dangerousActionsCache === null ) {
49            $extActions = [];
50            $this->hookRunner->onAbuseFilterGetDangerousActions( $extActions );
51            $this->dangerousActionsCache = array_unique(
52                array_merge( $extActions, self::DANGEROUS_ACTIONS )
53            );
54        }
55        return $this->dangerousActionsCache;
56    }
57
58    /**
59     * @return string[]
60     */
61    public function getAllActionNames(): array {
62        return array_unique(
63            array_merge(
64                array_keys( $this->configActions ),
65                array_keys( $this->getCustomActions() )
66            )
67        );
68    }
69
70    /**
71     * @return callable[]
72     * @phan-return array<string,callable(Parameters,array):Consequence>
73     */
74    public function getCustomActions(): array {
75        if ( $this->customActionsCache === null ) {
76            $this->customActionsCache = [];
77            $this->hookRunner->onAbuseFilterCustomActions( $this->customActionsCache );
78            $this->validateCustomActions();
79        }
80        return $this->customActionsCache;
81    }
82
83    /**
84     * Ensure that extensions aren't putting crap in this array, since we can't enforce types on closures otherwise
85     */
86    private function validateCustomActions(): void {
87        foreach ( $this->customActionsCache as $name => $cb ) {
88            if ( !is_string( $name ) ) {
89                throw new RuntimeException( 'Custom actions keys should be strings!' );
90            }
91            // Validating parameters and return value will happen later at runtime.
92            if ( !is_callable( $cb ) ) {
93                throw new RuntimeException( 'Custom actions values should be callables!' );
94            }
95        }
96    }
97
98    /**
99     * @return string[]
100     */
101    public function getAllEnabledActionNames(): array {
102        $disabledActions = array_keys( array_filter(
103            $this->configActions,
104            static function ( $el ) {
105                return $el === false;
106            }
107        ) );
108        return array_values( array_diff( $this->getAllActionNames(), $disabledActions ) );
109    }
110}