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    /** @var LoggerInterface */
39    private $logger;
40
41    /**
42     * @var IEditConstraint[]
43     *
44     * Constraints to check.
45     */
46    private $constraints = [];
47
48    /**
49     * @var IEditConstraint|false
50     *
51     * The constraint that failed, so that its status can be fetched, or false if none failed.
52     */
53    private $failedConstraint = false;
54
55    /**
56     * Create a new runner
57     */
58    public function __construct() {
59        // TODO allow passing an array here as the starting constraints?
60        // TODO consider injecting this?
61        $this->logger = LoggerFactory::getInstance( 'EditConstraintRunner' );
62    }
63
64    /**
65     * Add a constraint to check
66     *
67     * Not all constraints are applicable to the action api or other methods of submitting
68     * an edit
69     *
70     * For constraints that have dependencies, use the EditConstraintFactory.
71     *
72     * @param IEditConstraint $constraint
73     */
74    public function addConstraint( IEditConstraint $constraint ) {
75        $this->constraints[] = $constraint;
76    }
77
78    /**
79     * Run constraint checks
80     *
81     * Returns true if all constraints pass, false otherwise.
82     * Check getLegacyStatus for the reason
83     *
84     * @return bool
85     */
86    public function checkConstraints(): bool {
87        foreach ( $this->constraints as $constraint ) {
88            $result = $constraint->checkConstraint();
89            if ( $result !== IEditConstraint::CONSTRAINT_PASSED ) {
90                // Use `info` instead of `debug` for the one constraint that failed
91                $this->logger->info(
92                    'Checked {name}, got result: {result}',
93                    [
94                        'name' => $this->getConstraintName( $constraint ),
95                        'result' => $result
96                    ]
97                );
98
99                $this->failedConstraint = $constraint;
100                return false;
101            }
102
103            // Pass, log at `debug` level
104            $this->logger->debug(
105                'Checked {name}, got result: {result}',
106                [
107                    'name' => $this->getConstraintName( $constraint ),
108                    'result' => $result
109                ]
110            );
111        }
112        return true;
113    }
114
115    /**
116     * @param IEditConstraint $constraint
117     * @return string
118     */
119    private function getConstraintName( IEditConstraint $constraint ): string {
120        // Used for debug logging
121        $fullClassName = explode( '\\', get_class( $constraint ) );
122        $constraintName = end( $fullClassName );
123        if ( $constraint instanceof PageSizeConstraint ) {
124            // TODO "When you need to do this instanceof, it's a code smell"
125            // Convert IEditConstraint to abstract class with a getName method
126            // once the initial migration to edit constraints is complete
127            // PageSizeConstraint is used twice, make sure they can be told apart
128            $constraintName .= ' ' . $constraint->getType();
129        }
130        return $constraintName;
131    }
132
133    /**
134     * Get the constraint that failed
135     *
136     * @return IEditConstraint
137     */
138    public function getFailedConstraint(): IEditConstraint {
139        Assert::precondition(
140            $this->failedConstraint !== false,
141            'getFailedConstraint called with no failed constraint'
142        );
143        return $this->failedConstraint;
144    }
145
146}