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     * @param IEditConstraint $constraint
72     */
73    public function addConstraint( IEditConstraint $constraint ) {
74        $this->constraints[] = $constraint;
75    }
76
77    /**
78     * Run constraint checks
79     *
80     * Returns true if all constraints pass, false otherwise.
81     * Check getLegacyStatus for the reason
82     *
83     * @return bool
84     */
85    public function checkConstraints(): bool {
86        foreach ( $this->constraints as $constraint ) {
87            $result = $constraint->checkConstraint();
88            if ( $result !== IEditConstraint::CONSTRAINT_PASSED ) {
89                // Use `info` instead of `debug` for the one constraint that failed
90                $this->logger->info(
91                    'Checked {name}, got result: {result}',
92                    [
93                        'name' => $this->getConstraintName( $constraint ),
94                        'result' => $result
95                    ]
96                );
97
98                $this->failedConstraint = $constraint;
99                return false;
100            }
101
102            // Pass, log at `debug` level
103            $this->logger->debug(
104                'Checked {name}, got result: {result}',
105                [
106                    'name' => $this->getConstraintName( $constraint ),
107                    'result' => $result
108                ]
109            );
110        }
111        return true;
112    }
113
114    /**
115     * @param IEditConstraint $constraint
116     * @return string
117     */
118    private function getConstraintName( IEditConstraint $constraint ): string {
119        // Used for debug logging
120        $fullClassName = explode( '\\', get_class( $constraint ) );
121        $constraintName = end( $fullClassName );
122        if ( $constraint instanceof PageSizeConstraint ) {
123            // TODO "When you need to do this instanceof, it's a code smell"
124            // Convert IEditConstraint to abstract class with a getName method
125            // once the initial migration to edit constraints is complete
126            // PageSizeConstraint is used twice, make sure they can be told apart
127            $constraintName .= ' ' . $constraint->getType();
128        }
129        return $constraintName;
130    }
131
132    /**
133     * Get the constraint that failed
134     *
135     * @return IEditConstraint
136     */
137    public function getFailedConstraint(): IEditConstraint {
138        Assert::precondition(
139            $this->failedConstraint !== false,
140            'getFailedConstraint called with no failed constraint'
141        );
142        return $this->failedConstraint;
143    }
144
145}