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