MediaWiki  1.34.0
VersionChecker.php
Go to the documentation of this file.
1 <?php
2 
23 use Composer\Semver\VersionParser;
24 use Composer\Semver\Constraint\Constraint;
25 
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(
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 
122  private function setPhpVersion( $phpVersion ) {
123  // normalize to make this throw an exception if the version is invalid
124  $this->phpVersion = new Constraint(
125  '==',
126  $this->versionParser->normalize( $phpVersion )
127  );
128  $this->phpVersion->setPrettyString( $phpVersion );
129  }
130 
156  public function checkArray( array $extDependencies ) {
157  $errors = [];
158  foreach ( $extDependencies as $extension => $dependencies ) {
159  foreach ( $dependencies as $dependencyType => $values ) {
160  switch ( $dependencyType ) {
162  $mwError = $this->handleDependency(
163  $this->coreVersion,
164  $values,
165  $extension
166  );
167  if ( $mwError !== false ) {
168  $errors[] = [
169  'msg' =>
170  "{$extension} is not compatible with the current MediaWiki "
171  . "core (version {$this->coreVersion->getPrettyString()}), "
172  . "it requires: $values."
173  ,
174  'type' => 'incompatible-core',
175  ];
176  }
177  break;
178  case 'platform':
179  foreach ( $values as $dependency => $constraint ) {
180  if ( $dependency === 'php' ) {
181  // PHP version
182  $phpError = $this->handleDependency(
183  $this->phpVersion,
184  $constraint,
185  $extension
186  );
187  if ( $phpError !== false ) {
188  $errors[] = [
189  'msg' =>
190  "{$extension} is not compatible with the current PHP "
191  . "version {$this->phpVersion->getPrettyString()}), "
192  . "it requires: $constraint."
193  ,
194  'type' => 'incompatible-php',
195  ];
196  }
197  } elseif ( substr( $dependency, 0, 4 ) === 'ext-' ) {
198  // PHP extensions
199  $phpExtension = substr( $dependency, 4 );
200  if ( $constraint !== '*' ) {
201  throw new UnexpectedValueException( 'Version constraints for '
202  . 'PHP extensions are not supported in ' . $extension );
203  }
204  if ( !in_array( $phpExtension, $this->phpExtensions, true ) ) {
205  $errors[] = [
206  'msg' =>
207  "{$extension} requires {$phpExtension} PHP extension "
208  . "to be installed."
209  ,
210  'type' => 'missing-phpExtension',
211  'missing' => $phpExtension,
212  ];
213  }
214  } elseif ( substr( $dependency, 0, 8 ) === 'ability-' ) {
215  // Other abilities the environment might provide.
216  $ability = substr( $dependency, 8 );
217  if ( !isset( $this->abilities[$ability] ) ) {
218  throw new UnexpectedValueException( 'Dependency type '
219  . $dependency . ' unknown in ' . $extension );
220  }
221  if ( !is_bool( $constraint ) ) {
222  throw new UnexpectedValueException( 'Only booleans are '
223  . 'allowed to to indicate the presence of abilities '
224  . 'in ' . $extension );
225  }
226 
227  if ( $constraint === true &&
228  $this->abilities[$ability] !== true
229  ) {
230  // add custom error message for missing ability if specified
231  $customMessage = '';
232  if ( isset( $this->abilityErrors[$ability] ) ) {
233  $customMessage = ': ' . $this->abilityErrors[$ability];
234  }
235 
236  $errors[] = [
237  'msg' =>
238  "{$extension} requires \"{$ability}\" ability"
239  . $customMessage
240  ,
241  'type' => 'missing-ability',
242  'missing' => $ability,
243  ];
244  }
245  } else {
246  // add other platform dependencies here
247  throw new UnexpectedValueException( 'Dependency type ' . $dependency .
248  ' unknown in ' . $extension );
249  }
250  }
251  break;
252  case 'extensions':
253  case 'skins':
254  foreach ( $values as $dependency => $constraint ) {
255  $extError = $this->handleExtensionDependency(
256  $dependency, $constraint, $extension, $dependencyType
257  );
258  if ( $extError !== false ) {
259  $errors[] = $extError;
260  }
261  }
262  break;
263  default:
264  throw new UnexpectedValueException( 'Dependency type ' . $dependencyType .
265  ' unknown in ' . $extension );
266  }
267  }
268  }
269 
270  return $errors;
271  }
272 
282  private function handleDependency( $version, $constraint, $checkedExt ) {
283  if ( $version === false ) {
284  // Couldn't parse the version, so we can't check anything
285  return false;
286  }
287 
288  // if the installed and required version are compatible, return an empty array
289  if ( $this->versionParser->parseConstraints( $constraint )
290  ->matches( $version ) ) {
291  return false;
292  }
293 
294  return true;
295  }
296 
306  private function handleExtensionDependency( $dependencyName, $constraint, $checkedExt,
307  $type
308  ) {
309  // Check if the dependency is even installed
310  if ( !isset( $this->loaded[$dependencyName] ) ) {
311  return [
312  'msg' => "{$checkedExt} requires {$dependencyName} to be installed.",
313  'type' => "missing-$type",
314  'missing' => $dependencyName,
315  ];
316  }
317  if ( $constraint === '*' ) {
318  // short-circuit since any version is OK.
319  return false;
320  }
321  // Check if the dependency has specified a version
322  if ( !isset( $this->loaded[$dependencyName]['version'] ) ) {
323  $msg = "{$dependencyName} does not expose its version, but {$checkedExt}"
324  . " requires: {$constraint}.";
325  return [
326  'msg' => $msg,
327  'type' => "incompatible-$type",
328  'incompatible' => $checkedExt,
329  ];
330  } else {
331  // Try to get a constraint for the dependency version
332  try {
333  $installedVersion = new Constraint(
334  '==',
335  $this->versionParser->normalize( $this->loaded[$dependencyName]['version'] )
336  );
337  } catch ( UnexpectedValueException $e ) {
338  // Non-parsable version, output an error message that the version
339  // string is invalid
340  return [
341  'msg' => "$dependencyName does not have a valid version string.",
342  'type' => 'invalid-version',
343  ];
344  }
345  // Check if the constraint actually matches...
346  if (
347  !$this->versionParser->parseConstraints( $constraint )->matches( $installedVersion )
348  ) {
349  $msg = "{$checkedExt} is not compatible with the current "
350  . "installed version of {$dependencyName} "
351  . "({$this->loaded[$dependencyName]['version']}), "
352  . "it requires: " . $constraint . '.';
353  return [
354  'msg' => $msg,
355  'type' => "incompatible-$type",
356  'incompatible' => $checkedExt,
357  ];
358  }
359  }
360 
361  return false;
362  }
363 }
VersionChecker\$abilityErrors
string[] $abilityErrors
List of provided ability errors.
Definition: VersionChecker.php:56
VersionChecker\__construct
__construct( $coreVersion, $phpVersion, array $phpExtensions, array $abilities=[], array $abilityErrors=[])
Definition: VersionChecker.php:75
VersionChecker\$abilities
bool[] $abilities
List of provided abilities.
Definition: VersionChecker.php:51
VersionChecker\setCoreVersion
setCoreVersion( $coreVersion)
Set MediaWiki core version.
Definition: VersionChecker.php:104
VersionChecker\checkArray
checkArray(array $extDependencies)
Check all given dependencies if they are compatible with the named installed extensions in the $credi...
Definition: VersionChecker.php:156
VersionChecker\handleExtensionDependency
handleExtensionDependency( $dependencyName, $constraint, $checkedExt, $type)
Handle a dependency to another extension.
Definition: VersionChecker.php:306
VersionChecker\$loaded
array $loaded
Loaded extensions.
Definition: VersionChecker.php:61
VersionChecker\$coreVersion
Constraint bool $coreVersion
representing $wgVersion
Definition: VersionChecker.php:36
VersionChecker\handleDependency
handleDependency( $version, $constraint, $checkedExt)
Handle a simple dependency to MediaWiki core or PHP.
Definition: VersionChecker.php:282
ExtensionRegistry\MEDIAWIKI_CORE
const MEDIAWIKI_CORE
"requires" key that applies to MediaWiki core/$wgVersion
Definition: ExtensionRegistry.php:23
VersionChecker\$phpVersion
Constraint bool $phpVersion
representing PHP version
Definition: VersionChecker.php:41
VersionChecker\setPhpVersion
setPhpVersion( $phpVersion)
Set PHP version.
Definition: VersionChecker.php:122
VersionChecker\$versionParser
VersionParser $versionParser
Definition: VersionChecker.php:66
VersionChecker
Provides functions to check a set of extensions with dependencies against a set of loaded extensions ...
Definition: VersionChecker.php:32
VersionChecker\setLoadedExtensionsAndSkins
setLoadedExtensionsAndSkins(array $credits)
Set an array with credits of all loaded extensions and skins.
Definition: VersionChecker.php:93
VersionChecker\$phpExtensions
string[] $phpExtensions
List of installed PHP extensions.
Definition: VersionChecker.php:46
$type
$type
Definition: testCompression.php:48