Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
67.14% |
47 / 70 |
|
36.84% |
7 / 19 |
CRAP | |
0.00% |
0 / 1 |
CompositeBlock | |
67.14% |
47 / 70 |
|
36.84% |
7 / 19 |
92.95 | |
0.00% |
0 / 1 |
createFromBlocks | |
91.67% |
11 / 12 |
|
0.00% |
0 / 1 |
4.01 | |||
__construct | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
1 | |||
propHasValue | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
methodReturnsValue | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
3.14 | |||
getOriginalBlocks | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
withOriginalBlocks | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
toArray | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getTimestamp | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
5 | |||
getExpiry | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
30 | |||
getIdentifier | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
appliesToRight | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
5 | |||
appliesToUsertalk | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
appliesToTitle | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
appliesToNamespace | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
appliesToPage | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
appliesToPasswordReset | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getBy | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getByName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getBlocker | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Class for blocks composed from multiple blocks. |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License along |
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | * http://www.gnu.org/copyleft/gpl.html |
19 | * |
20 | * @file |
21 | */ |
22 | |
23 | namespace MediaWiki\Block; |
24 | |
25 | use InvalidArgumentException; |
26 | use MediaWiki\Message\Message; |
27 | use MediaWiki\Title\Title; |
28 | use MediaWiki\User\UserIdentity; |
29 | |
30 | /** |
31 | * Multiple Block class. |
32 | * |
33 | * Multiple blocks exist to enforce restrictions from more than one block, if several |
34 | * blocks apply to a user/IP. Multiple blocks are created temporarily on enforcement. |
35 | * |
36 | * @since 1.34 |
37 | */ |
38 | class CompositeBlock extends AbstractBlock { |
39 | /** @var AbstractBlock[] */ |
40 | private $originalBlocks; |
41 | |
42 | /** |
43 | * Helper method for merging multiple blocks into a composite block. |
44 | * @param AbstractBlock ...$blocks |
45 | * @return self |
46 | */ |
47 | public static function createFromBlocks( AbstractBlock ...$blocks ): self { |
48 | $originalBlocks = []; |
49 | foreach ( $blocks as $block ) { |
50 | if ( $block instanceof self ) { |
51 | $originalBlocks = array_merge( $originalBlocks, $block->getOriginalBlocks() ); |
52 | } else { |
53 | $originalBlocks[] = $block; |
54 | } |
55 | } |
56 | if ( !$originalBlocks ) { |
57 | throw new InvalidArgumentException( 'No blocks given' ); |
58 | } |
59 | return new self( [ |
60 | 'address' => $originalBlocks[0]->target, |
61 | 'reason' => new Message( 'blockedtext-composite-reason' ), |
62 | 'originalBlocks' => $originalBlocks, |
63 | ] ); |
64 | } |
65 | |
66 | /** |
67 | * Create a new block with specified parameters on a user, IP or IP range. |
68 | * |
69 | * @param array $options Parameters of the block, with options supported by |
70 | * `AbstractBlock::__construct`, and also: |
71 | * - originalBlocks: (Block[]) Blocks that this block is composed from |
72 | */ |
73 | public function __construct( array $options = [] ) { |
74 | parent::__construct( $options ); |
75 | |
76 | $defaults = [ |
77 | 'originalBlocks' => [], |
78 | ]; |
79 | |
80 | $options += $defaults; |
81 | |
82 | $this->originalBlocks = $options[ 'originalBlocks' ]; |
83 | |
84 | $this->setHideName( $this->propHasValue( 'hideName', true ) ); |
85 | $this->isHardblock( $this->propHasValue( 'isHardblock', true ) ); |
86 | $this->isSitewide( $this->propHasValue( 'isSitewide', true ) ); |
87 | $this->isEmailBlocked( $this->propHasValue( 'blockEmail', true ) ); |
88 | $this->isCreateAccountBlocked( $this->propHasValue( 'blockCreateAccount', true ) ); |
89 | $this->isUsertalkEditAllowed( !$this->propHasValue( 'allowUsertalk', false ) ); |
90 | } |
91 | |
92 | /** |
93 | * Determine whether any original blocks have a particular property set to a |
94 | * particular value. |
95 | * |
96 | * @param string $prop |
97 | * @param mixed $value |
98 | * @return bool At least one block has the property set to the value |
99 | */ |
100 | private function propHasValue( $prop, $value ) { |
101 | foreach ( $this->originalBlocks as $block ) { |
102 | if ( $block->$prop == $value ) { |
103 | return true; |
104 | } |
105 | } |
106 | return false; |
107 | } |
108 | |
109 | /** |
110 | * Determine whether any original blocks have a particular method returning a |
111 | * particular value. |
112 | * |
113 | * @param string $method |
114 | * @param mixed $value |
115 | * @param mixed ...$params |
116 | * @return bool At least one block has the method returning the value |
117 | */ |
118 | private function methodReturnsValue( $method, $value, ...$params ) { |
119 | foreach ( $this->originalBlocks as $block ) { |
120 | if ( $block->$method( ...$params ) == $value ) { |
121 | return true; |
122 | } |
123 | } |
124 | return false; |
125 | } |
126 | |
127 | /** |
128 | * Get the original blocks from which this block is composed |
129 | * |
130 | * @since 1.34 |
131 | * @return AbstractBlock[] |
132 | */ |
133 | public function getOriginalBlocks() { |
134 | return $this->originalBlocks; |
135 | } |
136 | |
137 | /** |
138 | * Create a clone of the object with the original blocks array set to |
139 | * something else. |
140 | * |
141 | * @since 1.42 |
142 | * @param AbstractBlock[] $blocks |
143 | * @return self |
144 | */ |
145 | public function withOriginalBlocks( array $blocks ) { |
146 | $clone = clone $this; |
147 | $clone->originalBlocks = $blocks; |
148 | return $clone; |
149 | } |
150 | |
151 | public function toArray(): array { |
152 | return $this->originalBlocks; |
153 | } |
154 | |
155 | /** |
156 | * @inheritDoc |
157 | */ |
158 | public function getTimestamp(): string { |
159 | $minStart = null; |
160 | foreach ( $this->originalBlocks as $block ) { |
161 | $startTime = $block->getTimestamp(); |
162 | if ( $minStart === null || $startTime === '' || $startTime < $minStart ) { |
163 | $minStart = $startTime; |
164 | } |
165 | } |
166 | return $minStart ?? ''; |
167 | } |
168 | |
169 | /** |
170 | * @inheritDoc |
171 | */ |
172 | public function getExpiry(): string { |
173 | $maxExpiry = null; |
174 | foreach ( $this->originalBlocks as $block ) { |
175 | $expiry = $block->getExpiry(); |
176 | if ( $maxExpiry === null || $expiry === '' || $expiry > $maxExpiry ) { |
177 | $maxExpiry = $expiry; |
178 | } |
179 | } |
180 | return $maxExpiry ?? ''; |
181 | } |
182 | |
183 | /** |
184 | * @inheritDoc |
185 | */ |
186 | public function getIdentifier( $wikiId = self::LOCAL ) { |
187 | $identifier = []; |
188 | foreach ( $this->originalBlocks as $block ) { |
189 | $identifier[] = $block->getIdentifier( $wikiId ); |
190 | } |
191 | return $identifier; |
192 | } |
193 | |
194 | /** |
195 | * @inheritDoc |
196 | * |
197 | * Determines whether the CompositeBlock applies to a right by checking |
198 | * whether the original blocks apply to that right. Each block can report |
199 | * true (applies), false (does not apply) or null (unsure). Then: |
200 | * - If any original blocks apply, this block applies |
201 | * - If no original blocks apply but any are unsure, this block is unsure |
202 | * - If all blocks do not apply, this block does not apply |
203 | */ |
204 | public function appliesToRight( $right ) { |
205 | $isUnsure = false; |
206 | |
207 | foreach ( $this->originalBlocks as $block ) { |
208 | $appliesToRight = $block->appliesToRight( $right ); |
209 | |
210 | if ( $appliesToRight ) { |
211 | return true; |
212 | } elseif ( $appliesToRight === null ) { |
213 | $isUnsure = true; |
214 | } |
215 | } |
216 | |
217 | return $isUnsure ? null : false; |
218 | } |
219 | |
220 | /** |
221 | * @inheritDoc |
222 | */ |
223 | public function appliesToUsertalk( ?Title $usertalk = null ) { |
224 | return $this->methodReturnsValue( __FUNCTION__, true, $usertalk ); |
225 | } |
226 | |
227 | /** |
228 | * @inheritDoc |
229 | */ |
230 | public function appliesToTitle( Title $title ) { |
231 | return $this->methodReturnsValue( __FUNCTION__, true, $title ); |
232 | } |
233 | |
234 | /** |
235 | * @inheritDoc |
236 | */ |
237 | public function appliesToNamespace( $ns ) { |
238 | return $this->methodReturnsValue( __FUNCTION__, true, $ns ); |
239 | } |
240 | |
241 | /** |
242 | * @inheritDoc |
243 | */ |
244 | public function appliesToPage( $pageId ) { |
245 | return $this->methodReturnsValue( __FUNCTION__, true, $pageId ); |
246 | } |
247 | |
248 | /** |
249 | * @inheritDoc |
250 | */ |
251 | public function appliesToPasswordReset() { |
252 | return $this->methodReturnsValue( __FUNCTION__, true ); |
253 | } |
254 | |
255 | /** |
256 | * @inheritDoc |
257 | */ |
258 | public function getBy( $wikiId = self::LOCAL ): int { |
259 | $this->assertWiki( $wikiId ); |
260 | return 0; |
261 | } |
262 | |
263 | /** |
264 | * @inheritDoc |
265 | */ |
266 | public function getByName() { |
267 | return ''; |
268 | } |
269 | |
270 | /** |
271 | * @inheritDoc |
272 | */ |
273 | public function getBlocker(): ?UserIdentity { |
274 | return null; |
275 | } |
276 | } |