MediaWiki 1.41.2
VersionChecker.php
Go to the documentation of this file.
1<?php
21use Composer\Semver\Constraint\Constraint;
22use Composer\Semver\VersionParser;
23
36 private $coreVersion = false;
37
41 private $phpVersion = false;
42
46 private $phpExtensions;
47
51 private $abilities;
52
56 private $abilityErrors;
57
61 private $loaded = [];
62
66 private $versionParser;
67
75 public function __construct(
76 $coreVersion, $phpVersion, array $phpExtensions,
77 array $abilities = [], array $abilityErrors = []
78 ) {
79 $this->versionParser = new VersionParser();
80 $this->setCoreVersion( $coreVersion );
81 $this->setPhpVersion( $phpVersion );
82 $this->phpExtensions = $phpExtensions;
83 $this->abilities = $abilities;
84 $this->abilityErrors = $abilityErrors;
85 }
86
93 public function setLoadedExtensionsAndSkins( array $credits ) {
94 $this->loaded = $credits;
95
96 return $this;
97 }
98
104 private function setCoreVersion( $coreVersion ) {
105 try {
106 $this->coreVersion = new Constraint(
107 '==',
108 $this->versionParser->normalize( $coreVersion )
109 );
110 $this->coreVersion->setPrettyString( $coreVersion );
111 } catch ( UnexpectedValueException $e ) {
112 // Non-parsable version, don't fatal.
113 }
114 }
115
120 private function setPhpVersion( $phpVersion ) {
121 // normalize to make this throw an exception if the version is invalid
122 $this->phpVersion = new Constraint(
123 '==',
124 $this->versionParser->normalize( $phpVersion )
125 );
126 $this->phpVersion->setPrettyString( $phpVersion );
127 }
128
154 public function checkArray( array $extDependencies ) {
155 $errors = [];
156 foreach ( $extDependencies as $extension => $dependencies ) {
157 foreach ( $dependencies as $dependencyType => $values ) {
158 switch ( $dependencyType ) {
159 case ExtensionRegistry::MEDIAWIKI_CORE:
160 $mwError = $this->handleDependency(
161 $this->coreVersion,
162 $values
163 );
164 if ( $mwError !== false ) {
165 $errors[] = [
166 'msg' =>
167 "{$extension} is not compatible with the current MediaWiki "
168 . "core (version {$this->coreVersion->getPrettyString()}), "
169 . "it requires: $values."
170 ,
171 'type' => 'incompatible-core',
172 ];
173 }
174 break;
175 case 'platform':
176 foreach ( $values as $dependency => $constraint ) {
177 if ( $dependency === 'php' ) {
178 // PHP version
179 $phpError = $this->handleDependency(
180 $this->phpVersion,
181 $constraint
182 );
183 if ( $phpError !== false ) {
184 $errors[] = [
185 'msg' =>
186 "{$extension} is not compatible with the current PHP "
187 . "version {$this->phpVersion->getPrettyString()}), "
188 . "it requires: $constraint."
189 ,
190 'type' => 'incompatible-php',
191 ];
192 }
193 } elseif ( substr( $dependency, 0, 4 ) === 'ext-' ) {
194 // PHP extensions
195 $phpExtension = substr( $dependency, 4 );
196 if ( $constraint !== '*' ) {
197 throw new UnexpectedValueException( 'Version constraints for '
198 . 'PHP extensions are not supported in ' . $extension );
199 }
200 if ( !in_array( $phpExtension, $this->phpExtensions, true ) ) {
201 $errors[] = [
202 'msg' =>
203 "{$extension} requires {$phpExtension} PHP extension "
204 . "to be installed."
205 ,
206 'type' => 'missing-phpExtension',
207 'missing' => $phpExtension,
208 ];
209 }
210 } elseif ( substr( $dependency, 0, 8 ) === 'ability-' ) {
211 // Other abilities the environment might provide.
212 $ability = substr( $dependency, 8 );
213 if ( !isset( $this->abilities[$ability] ) ) {
214 throw new UnexpectedValueException( 'Dependency type '
215 . $dependency . ' unknown in ' . $extension );
216 }
217 if ( !is_bool( $constraint ) ) {
218 throw new UnexpectedValueException( 'Only booleans are '
219 . 'allowed to to indicate the presence of abilities '
220 . 'in ' . $extension );
221 }
222
223 if ( $constraint &&
224 $this->abilities[$ability] !== true
225 ) {
226 // add custom error message for missing ability if specified
227 $customMessage = '';
228 if ( isset( $this->abilityErrors[$ability] ) ) {
229 $customMessage = ': ' . $this->abilityErrors[$ability];
230 }
231
232 $errors[] = [
233 'msg' =>
234 "{$extension} requires \"{$ability}\" ability"
235 . $customMessage
236 ,
237 'type' => 'missing-ability',
238 'missing' => $ability,
239 ];
240 }
241 } else {
242 // add other platform dependencies here
243 throw new UnexpectedValueException( 'Dependency type ' . $dependency .
244 ' unknown in ' . $extension );
245 }
246 }
247 break;
248 case 'extensions':
249 case 'skins':
250 foreach ( $values as $dependency => $constraint ) {
251 $extError = $this->handleExtensionDependency(
252 $dependency, $constraint, $extension, $dependencyType
253 );
254 if ( $extError !== false ) {
255 $errors[] = $extError;
256 }
257 }
258 break;
259 default:
260 throw new UnexpectedValueException( 'Dependency type ' . $dependencyType .
261 ' unknown in ' . $extension );
262 }
263 }
264 }
265
266 return $errors;
267 }
268
277 private function handleDependency( $version, $constraint ) {
278 if ( $version === false ) {
279 // Couldn't parse the version, so we can't check anything
280 return false;
281 }
282
283 // if the installed and required version are compatible, return an empty array
284 if ( $this->versionParser->parseConstraints( $constraint )
285 ->matches( $version ) ) {
286 return false;
287 }
288
289 return true;
290 }
291
301 private function handleExtensionDependency( $dependencyName, $constraint, $checkedExt,
302 $type
303 ) {
304 // Check if the dependency is even installed
305 if ( !isset( $this->loaded[$dependencyName] ) ) {
306 return [
307 'msg' => "{$checkedExt} requires {$dependencyName} to be installed.",
308 'type' => "missing-$type",
309 'missing' => $dependencyName,
310 ];
311 }
312 if ( $constraint === '*' ) {
313 // short-circuit since any version is OK.
314 return false;
315 }
316 // Check if the dependency has specified a version
317 if ( !isset( $this->loaded[$dependencyName]['version'] ) ) {
318 $msg = "{$dependencyName} does not expose its version, but {$checkedExt}"
319 . " requires: {$constraint}.";
320 return [
321 'msg' => $msg,
322 'type' => "incompatible-$type",
323 'incompatible' => $checkedExt,
324 ];
325 } else {
326 // Try to get a constraint for the dependency version
327 try {
328 $installedVersion = new Constraint(
329 '==',
330 $this->versionParser->normalize( $this->loaded[$dependencyName]['version'] )
331 );
332 } catch ( UnexpectedValueException $e ) {
333 // Non-parsable version, output an error message that the version
334 // string is invalid
335 return [
336 'msg' => "$dependencyName does not have a valid version string.",
337 'type' => 'invalid-version',
338 ];
339 }
340 // Check if the constraint actually matches...
341 if (
342 !$this->versionParser->parseConstraints( $constraint )->matches( $installedVersion )
343 ) {
344 $msg = "{$checkedExt} is not compatible with the current "
345 . "installed version of {$dependencyName} "
346 . "({$this->loaded[$dependencyName]['version']}), "
347 . "it requires: " . $constraint . '.';
348 return [
349 'msg' => $msg,
350 'type' => "incompatible-$type",
351 'incompatible' => $checkedExt,
352 ];
353 }
354 }
355
356 return false;
357 }
358}
Check whether extensions and their dependencies meet certain version requirements.
setLoadedExtensionsAndSkins(array $credits)
Set an array with credits of all loaded extensions and skins.
checkArray(array $extDependencies)
Check all given dependencies if they are compatible with the named installed extensions in the $credi...
__construct( $coreVersion, $phpVersion, array $phpExtensions, array $abilities=[], array $abilityErrors=[])