Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
EditConstraintRunner
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
5 / 5
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
 addConstraint
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 checkConstraints
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
3
 getConstraintName
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 getFailedConstraint
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20
21namespace MediaWiki\EditPage\Constraint;
22
23use MediaWiki\Logger\LoggerFactory;
24use Psr\Log\LoggerInterface;
25use Wikimedia\Assert\Assert;
26
27/**
28 * Back end to process the edit constraints
29 *
30 * Constraints reflect possible errors that need to be checked
31 *
32 * @since 1.36
33 * @internal
34 * @author DannyS712
35 */
36class EditConstraintRunner {
37
38    private LoggerInterface $logger;
39
40    /**
41     * @var IEditConstraint[]
42     *
43     * Constraints to check.
44     */
45    private $constraints = [];
46
47    /**
48     * @var IEditConstraint|false
49     *
50     * The constraint that failed, so that its status can be fetched, or false if none failed.
51     */
52    private $failedConstraint = false;
53
54    /**
55     * Create a new runner
56     */
57    public function __construct() {
58        // TODO allow passing an array here as the starting constraints?
59        // TODO consider injecting this?
60        $this->logger = LoggerFactory::getInstance( 'EditConstraintRunner' );
61    }
62
63    /**
64     * Add a constraint to check
65     *
66     * Not all constraints are applicable to the action api or other methods of submitting
67     * an edit
68     *
69     * For constraints that have dependencies, use the EditConstraintFactory.
70     */
71    public function addConstraint( IEditConstraint $constraint ) {
72        $this->constraints[] = $constraint;
73    }
74
75    /**
76     * Run constraint checks
77     *
78     * Returns true if all constraints pass, false otherwise.
79     * Check getLegacyStatus for the reason
80     */
81    public function checkConstraints(): bool {
82        foreach ( $this->constraints as $constraint ) {
83            $result = $constraint->checkConstraint();
84            if ( $result !== IEditConstraint::CONSTRAINT_PASSED ) {
85                // Use `info` instead of `debug` for the one constraint that failed
86                $this->logger->info(
87                    'Checked {name}, got result: {result}',
88                    [
89                        'name' => $this->getConstraintName( $constraint ),
90                        'result' => $result
91                    ]
92                );
93
94                $this->failedConstraint = $constraint;
95                return false;
96            }
97
98            // Pass, log at `debug` level
99            $this->logger->debug(
100                'Checked {name}, got result: {result}',
101                [
102                    'name' => $this->getConstraintName( $constraint ),
103                    'result' => $result
104                ]
105            );
106        }
107        return true;
108    }
109
110    private function getConstraintName( IEditConstraint $constraint ): string {
111        // Used for debug logging
112        $fullClassName = explode( '\\', get_class( $constraint ) );
113        $constraintName = end( $fullClassName );
114        if ( $constraint instanceof PageSizeConstraint ) {
115            // TODO "When you need to do this instanceof, it's a code smell"
116            // Convert IEditConstraint to abstract class with a getName method
117            // once the initial migration to edit constraints is complete
118            // PageSizeConstraint is used twice, make sure they can be told apart
119            $constraintName .= ' ' . $constraint->getType();
120        }
121        return $constraintName;
122    }
123
124    /**
125     * Get the constraint that failed
126     */
127    public function getFailedConstraint(): IEditConstraint {
128        Assert::precondition(
129            $this->failedConstraint !== false,
130            'getFailedConstraint called with no failed constraint'
131        );
132        return $this->failedConstraint;
133    }
134
135}