Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 74
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
RestUtilTrait
0.00% covered (danger)
0.00%
0 / 74
0.00% covered (danger)
0.00%
0 / 6
240
0.00% covered (danger)
0.00%
0 / 1
 die
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 dieIf
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 requireMaxOneParameter
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
6
 requireAtLeastOneParameter
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
6
 requireOnlyOneParameter
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
12
 parameterNotEmpty
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace MediaWiki\Extension\ReadingLists\Rest;
4
5use MediaWiki\Message\Converter;
6use MediaWiki\Rest\LocalizedHttpException;
7use Message;
8use Wikimedia\Message\ListParam;
9use Wikimedia\Message\MessageValue;
10use Wikimedia\Message\ParamType;
11
12/**
13 * Trait for collecting utility code that is a candidate for moving to the REST infrastructure
14 * in MediaWiki core.
15 *
16 * Much of this code is related to parameter validation that cannot reasonably be expressed in the
17 * usual validation functions, especially as related to combinations of parameters. One open
18 * question: is validation of this sort actually RESTful? Would including these functions
19 * encourage a type of endpoint design that we'd rather not have?
20 */
21trait RestUtilTrait {
22    /**
23     * @param string|Message|MessageValue $msg
24     * @param array $params
25     * @param int $code
26     * @return never
27     * @throws LocalizedHttpException
28     */
29    protected function die( $msg, array $params = [], int $code = 400 ) {
30        if ( $msg instanceof Message ) {
31            $c = new Converter();
32            $mv = $c->convertMessage( $msg );
33        } elseif ( $msg instanceof MessageValue ) {
34            $mv = $msg;
35        } else {
36            $mv = MessageValue::new( $msg, $params );
37        }
38
39        throw new LocalizedHttpException( $mv, 400 );
40    }
41
42    /**
43     * @param bool $condition
44     * @param string|Message|MessageValue $msg
45     * @param array $params
46     * @param int $code
47     * @return void
48     * @throws LocalizedHttpException
49     */
50    protected function dieIf( bool $condition, $msg, array $params = [], int $code = 400 ) {
51        if ( $condition ) {
52            $this->die( $msg, $params, $code );
53        }
54    }
55
56    /**
57     * Dies if more than one parameter from a certain set of parameters are set and not false.
58     *
59     * @param array $params User provided parameters set, as from $this->getValidatedParams()
60     * @param string ...$required Parameter names that cannot have more than one set
61     */
62    protected function requireMaxOneParameter( array $params, ...$required ) {
63        $intersection = array_intersect( array_keys( array_filter( $params,
64            [ $this, 'parameterNotEmpty' ] ) ), $required );
65
66        if ( count( $intersection ) > 1 ) {
67            $lp = new ListParam(
68                ParamType::TEXT,
69                array_map(
70                    static function ( $paramName ) {
71                        return '<var>' . $paramName . '</var>';
72                    },
73                    array_values( $intersection )
74                )
75            );
76
77            $mv = MessageValue::new( 'apierror-invalidparammix' )
78                ->textListParams( [ $lp ] )
79                ->numParams( count( $intersection ) );
80            throw new LocalizedHttpException( $mv, 400 );
81        }
82    }
83
84    /**
85     * Die if 0 of a certain set of parameters is set and not false.
86     *
87     * @param array $params User provided parameters set, as from $this->extractRequestParams()
88     * @param string ...$required Names of parameters of which at least one must be set
89     */
90    protected function requireAtLeastOneParameter( $params, ...$required ) {
91        $intersection = array_intersect(
92            array_keys( array_filter( $params, [ $this, 'parameterNotEmpty' ] ) ),
93            $required
94        );
95
96        if ( count( $intersection ) == 0 ) {
97            $lp = new ListParam(
98                ParamType::TEXT,
99                array_map(
100                    static function ( $paramName ) {
101                        return '<var>' . $paramName . '</var>';
102                    },
103                    array_values( $required )
104                )
105            );
106
107            $mv = MessageValue::new( 'apierror-missingparam-at-least-one-of' )
108                ->textListParams( [ $lp ] )
109                ->numParams( count( $required ) );
110            throw new LocalizedHttpException( $mv, 400 );
111        }
112    }
113
114    /**
115     * Die if 0 or more than one of a certain set of parameters is set and not false.
116     *
117     * @param array $params User provided parameter set, as from $this->extractRequestParams()
118     * @param string ...$required Names of parameters of which exactly one must be set
119     * @throws LocalizedHttpException
120     */
121    protected function requireOnlyOneParameter( $params, ...$required ) {
122        $intersection = array_intersect( array_keys( array_filter( $params,
123            [ $this, 'parameterNotEmpty' ] ) ), $required );
124
125        if ( count( $intersection ) > 1 ) {
126            $lp = new ListParam(
127                ParamType::TEXT,
128                array_map(
129                    static function ( $paramName ) {
130                        return '<var>' . $paramName . '</var>';
131                    },
132                    array_values( $intersection )
133                )
134            );
135
136            $mv = MessageValue::new( 'apierror-invalidparammix' )
137                ->textListParams( [ $lp ] )
138                ->numParams( count( $intersection ) );
139            throw new LocalizedHttpException( $mv, 400 );
140        } elseif ( count( $intersection ) == 0 ) {
141            $lp = new ListParam(
142                ParamType::TEXT,
143                array_map(
144                    static function ( $paramName ) {
145                        return '<var>' . $paramName . '</var>';
146                    },
147                    array_values( $required )
148                )
149            );
150
151            $mv = MessageValue::new( 'apierror-missingparam-one-of' )
152                ->textListParams( [ $lp ] )
153                ->numParams( count( $required ) );
154            throw new LocalizedHttpException( $mv, 400 );
155        }
156    }
157
158    /**
159     * Callback function used in requireOnlyOneParameter to check whether required parameters are set.
160     *
161     * @param mixed $x Parameter to check is not null/false/empty string
162     * @return bool
163     */
164    protected function parameterNotEmpty( $x ): bool {
165        return $x !== null && $x !== false && $x !== '';
166    }
167}