MediaWiki  master
checkDependencies.php
Go to the documentation of this file.
1 <?php
23 require_once __DIR__ . '/Maintenance.php';
24 
31 
32  private $checkDev;
33 
34  public function __construct() {
35  parent::__construct();
36  $this->addDescription( 'Check dependencies for extensions' );
37  $this->addOption( 'extensions', 'Comma separated list of extensions to check', false, true );
38  $this->addOption( 'skins', 'Comma separated list of skins to check', false, true );
39  $this->addOption( 'json', 'Output in JSON' );
40  $this->addOption( 'dev', 'Check development dependencies too' );
41  }
42 
43  public function execute() {
44  $this->checkDev = $this->hasOption( 'dev' );
45  $extensions = $this->hasOption( 'extensions' )
46  ? explode( ',', $this->getOption( 'extensions' ) )
47  : [];
48  $skins = $this->hasOption( 'skins' )
49  ? explode( ',', $this->getOption( 'skins' ) )
50  : [];
51 
52  $dependencies = [];
53  // Note that we can only use the main registry if we are
54  // not checking development dependencies.
55  $registry = ExtensionRegistry::getInstance();
56  foreach ( $extensions as $extension ) {
57  if ( !$this->checkDev && $registry->isLoaded( $extension ) ) {
58  // If it's already loaded, we know all the dependencies resolved.
59  $this->addToDependencies( $dependencies, [ $extension ], [] );
60  continue;
61  }
62  $this->loadThing( $dependencies, $extension, [ $extension ], [] );
63  }
64 
65  foreach ( $skins as $skin ) {
66  if ( !$this->checkDev && $registry->isLoaded( $skin ) ) {
67  // If it's already loaded, we know all the dependencies resolved.
68  $this->addToDependencies( $dependencies, [], [ $skin ] );
69  continue;
70  }
71  $this->loadThing( $dependencies, $skin, [], [ $skin ] );
72  }
73 
74  if ( $this->hasOption( 'json' ) ) {
75  $this->output( json_encode( $dependencies ) . "\n" );
76  } else {
77  $this->output( $this->formatForHumans( $dependencies ) . "\n" );
78  }
79  }
80 
81  private function loadThing( &$dependencies, $name, $extensions, $skins ) {
82  $extDir = $this->getConfig()->get( 'ExtensionDirectory' );
83  $styleDir = $this->getConfig()->get( 'StyleDirectory' );
84  $queue = [];
85  $missing = false;
86  foreach ( $extensions as $extension ) {
87  $path = "$extDir/$extension/extension.json";
88  if ( file_exists( $path ) ) {
89  // 1 is ignored
90  $queue[$path] = 1;
91  $this->addToDependencies( $dependencies, [ $extension ], [], $name );
92  } else {
93  $missing = true;
94  $this->addToDependencies( $dependencies, [ $extension ], [], $name, 'missing' );
95  }
96  }
97 
98  foreach ( $skins as $skin ) {
99  $path = "$styleDir/$skin/skin.json";
100  if ( file_exists( $path ) ) {
101  $queue[$path] = 1;
102  $this->addToDependencies( $dependencies, [], [ $skin ], $name );
103  } else {
104  $missing = true;
105  $this->addToDependencies( $dependencies, [], [ $skin ], $name, 'missing' );
106  }
107  }
108 
109  if ( $missing ) {
110  // Stuff is missing, give up
111  return;
112  }
113 
114  $registry = new ExtensionRegistry();
115  $registry->setCheckDevRequires( $this->checkDev );
116  try {
117  $registry->readFromQueue( $queue );
118  } catch ( ExtensionDependencyError $e ) {
119  $reason = false;
120  if ( $e->incompatibleCore ) {
121  $reason = 'incompatible-core';
122  } elseif ( $e->incompatibleSkins ) {
123  $reason = 'incompatible-skins';
124  } elseif ( $e->incompatibleExtensions ) {
125  $reason = 'incompatible-extensions';
126  } elseif ( $e->missingExtensions || $e->missingSkins ) {
127  // There's an extension missing in the dependency tree,
128  // so add those to the dependency list and try again
129  return $this->loadThing(
130  $dependencies,
131  $name,
132  array_merge( $extensions, $e->missingExtensions ),
133  array_merge( $skins, $e->missingSkins )
134  );
135  } else {
136  // missing-phpExtension
137  // missing-ability
138  // XXX: ???
139  throw $e;
140  }
141 
142  $this->addToDependencies( $dependencies, $extensions, $skins, $name, $reason, $e->getMessage() );
143  }
144 
145  $this->addToDependencies( $dependencies, $extensions, $skins, $name );
146  }
147 
148  private function addToDependencies( &$dependencies, $extensions, $skins,
149  $why = null, $status = null, $message = null
150  ) {
151  $mainRegistry = ExtensionRegistry::getInstance();
152  $iter = [ 'extensions' => $extensions, 'skins' => $skins ];
153  foreach ( $iter as $type => $things ) {
154  foreach ( $things as $thing ) {
155  $preStatus = $dependencies[$type][$thing]['status'] ?? false;
156  if ( $preStatus !== 'loaded' ) {
157  // OK to overwrite
158  if ( $status ) {
159  $tStatus = $status;
160  } else {
161  $tStatus = $mainRegistry->isLoaded( $thing ) ? 'loaded' : 'present';
162  }
163  $dependencies[$type][$thing]['status'] = $tStatus;
164  }
165  if ( $why !== null ) {
166  $dependencies[$type][$thing]['why'][] = $why;
167  // XXX: this is a bit messy
168  $dependencies[$type][$thing]['why'] = array_unique(
169  $dependencies[$type][$thing]['why'] );
170  }
171 
172  if ( $message !== null ) {
173  $dependencies[$type][$thing]['message'] = trim( $message );
174  }
175 
176  }
177  }
178  }
179 
180  private function formatForHumans( $dependencies ) {
181  $text = '';
182  foreach ( $dependencies as $type => $things ) {
183  $text .= ucfirst( $type ) . "\n" . str_repeat( '=', strlen( $type ) ) . "\n";
184  foreach ( $things as $thing => $info ) {
185  $why = $info['why'] ?? [];
186  if ( $why ) {
187  $whyText = '(because: ' . implode( ',', $why ) . ') ';
188  } else {
189  $whyText = '';
190  }
191  $msg = isset( $info['message'] ) ? ", {$info['message']}" : '';
192 
193  $text .= "$thing: {$info['status']}{$msg} $whyText\n";
194  }
195  $text .= "\n";
196  }
197 
198  return trim( $text );
199  }
200 }
201 
202 $maintClass = CheckDependencies::class;
203 require_once RUN_MAINTENANCE_IF_MAIN;
const RUN_MAINTENANCE_IF_MAIN
Definition: Maintenance.php:39
Copyright (C) 2018 Kunal Mehta legoktm@member.fsf.org
getOption( $name, $default=null)
Get an option, or return the default.
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
Definition: Maintenance.php:86
hasOption( $name)
Checks to see if a particular option exists.
addToDependencies(&$dependencies, $extensions, $skins, $why=null, $status=null, $message=null)
addDescription( $text)
Set the description text.
loadThing(&$dependencies, $name, $extensions, $skins)
output( $out, $channel=null)
Throw some output to the user.
Checks dependencies for extensions, mostly without loading them.
formatForHumans( $dependencies)
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.