MediaWiki  master
AutoLoader.php
Go to the documentation of this file.
1 <?php
23 // NO_AUTOLOAD -- file scope code, can't load self
24 
29 require_once __DIR__ . '/../autoload.php';
30 
31 class AutoLoader {
32 
41  public const CORE_NAMESPACES = [
42  'MediaWiki\\' => __DIR__ . '/',
43  'MediaWiki\\Actions\\' => __DIR__ . '/actions/',
44  'MediaWiki\\Api\\' => __DIR__ . '/api/',
45  'MediaWiki\\Auth\\' => __DIR__ . '/auth/',
46  'MediaWiki\\Block\\' => __DIR__ . '/block/',
47  'MediaWiki\\Cache\\' => __DIR__ . '/cache/',
48  'MediaWiki\\ChangeTags\\' => __DIR__ . '/changetags/',
49  'MediaWiki\\Config\\' => __DIR__ . '/config/',
50  'MediaWiki\\Content\\' => __DIR__ . '/content/',
51  'MediaWiki\\DB\\' => __DIR__ . '/db/',
52  'MediaWiki\\Deferred\\LinksUpdate\\' => __DIR__ . '/deferred/LinksUpdate/',
53  'MediaWiki\\Diff\\' => __DIR__ . '/diff/',
54  'MediaWiki\\EditPage\\' => __DIR__ . '/editpage/',
55  'MediaWiki\\Edit\\' => __DIR__ . '/edit/',
56  'MediaWiki\\FileBackend\\LockManager\\' => __DIR__ . '/filebackend/lockmanager/',
57  'MediaWiki\\Http\\' => __DIR__ . '/http/',
58  'MediaWiki\\Installer\\' => __DIR__ . '/installer/',
59  'MediaWiki\\Interwiki\\' => __DIR__ . '/interwiki/',
60  'MediaWiki\\JobQueue\\' => __DIR__ . '/jobqueue/',
61  'MediaWiki\\Json\\' => __DIR__ . '/json/',
62  'MediaWiki\\Languages\\Data\\' => __DIR__ . '/languages/data/',
63  'MediaWiki\\Linker\\' => __DIR__ . '/linker/',
64  'MediaWiki\\Logger\\' => __DIR__ . '/debug/logger/',
65  'MediaWiki\\Logger\\Monolog\\' => __DIR__ . '/debug/logger/monolog/',
66  'MediaWiki\\Mail\\' => __DIR__ . '/mail/',
67  'MediaWiki\\Page\\' => __DIR__ . '/page/',
68  'MediaWiki\\Parser\\' => __DIR__ . '/parser/',
69  'MediaWiki\\Preferences\\' => __DIR__ . '/preferences/',
70  'MediaWiki\\Search\\' => __DIR__ . '/search/',
71  'MediaWiki\\Search\\SearchWidgets\\' => __DIR__ . '/search/searchwidgets/',
72  'MediaWiki\\Session\\' => __DIR__ . '/session/',
73  'MediaWiki\\Shell\\' => __DIR__ . '/shell/',
74  'MediaWiki\\Site\\' => __DIR__ . '/site/',
75  'MediaWiki\\Sparql\\' => __DIR__ . '/sparql/',
76  'MediaWiki\\SpecialPage\\' => __DIR__ . '/specialpage/',
77  'MediaWiki\\Tidy\\' => __DIR__ . '/tidy/',
78  'MediaWiki\\User\\' => __DIR__ . '/user/',
79  'MediaWiki\\Utils\\' => __DIR__ . '/utils/',
80  'MediaWiki\\Widget\\' => __DIR__ . '/widget/',
81  'Wikimedia\\' => __DIR__ . '/libs/',
82  'Wikimedia\\Http\\' => __DIR__ . '/libs/http/',
83  'Wikimedia\\Rdbms\\Platform\\' => __DIR__ . '/libs/rdbms/platform/',
84  'Wikimedia\\UUID\\' => __DIR__ . '/libs/uuid/',
85  ];
86 
91  private static $autoloadLocalClassesLower = null;
92 
97  public static $psr4Namespaces = self::CORE_NAMESPACES;
98 
102  private static $classFiles = [];
103 
112  public static function registerNamespaces( array $dirs ): void {
113  self::$psr4Namespaces += $dirs;
114  }
115 
122  public static function registerClasses( array $files ): void {
123  self::$classFiles += $files;
124  }
125 
142  public static function loadFile( string $file ): void {
143  require_once $file;
144  }
145 
155  public static function loadFiles( array $files ): void {
156  foreach ( $files as $f ) {
157  self::loadFile( $f );
158  }
159  }
160 
167  public static function find( $className ): ?string {
169 
170  // NOTE: $wgAutoloadClasses is supported for compatibility with old-style extension
171  // registration files.
172 
173  $filename = $wgAutoloadLocalClasses[$className] ??
174  self::$classFiles[$className] ??
175  $wgAutoloadClasses[$className] ??
176  false;
177 
178  if ( !$filename && $wgAutoloadAttemptLowercase ) {
179  // Try a different capitalisation.
180  //
181  // PHP 4 objects are always serialized with the classname coerced to lowercase,
182  // and we are plagued with several legacy uses created by MediaWiki < 1.5, see
183  // https://wikitech.wikimedia.org/wiki/Text_storage_data
184  if ( self::$autoloadLocalClassesLower === null ) {
185  self::$autoloadLocalClassesLower = array_change_key_case( $wgAutoloadLocalClasses, CASE_LOWER );
186  }
187  $lowerClass = strtolower( $className );
188  if ( isset( self::$autoloadLocalClassesLower[$lowerClass] ) ) {
189  if ( function_exists( 'wfDebugLog' ) ) {
190  wfDebugLog( 'autoloader', "Class {$className} was loaded using incorrect case" );
191  }
192  // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
193  $filename = self::$autoloadLocalClassesLower[$lowerClass];
194  }
195  }
196 
197  if ( !$filename && strpos( $className, '\\' ) !== false ) {
198  // This class is namespaced, so look in the namespace map
199  $prefix = $className;
200  while ( ( $pos = strrpos( $prefix, '\\' ) ) !== false ) {
201  // Check to see if this namespace prefix is in the map
202  $prefix = substr( $className, 0, $pos + 1 );
203  if ( isset( self::$psr4Namespaces[$prefix] ) ) {
204  $relativeClass = substr( $className, $pos + 1 );
205  // Build the expected filename, and see if it exists
206  $file = self::$psr4Namespaces[$prefix] .
207  '/' .
208  strtr( $relativeClass, '\\', '/' ) .
209  '.php';
210  if ( is_file( $file ) ) {
211  $filename = $file;
212  break;
213  }
214  }
215 
216  // Remove trailing separator for next iteration
217  $prefix = rtrim( $prefix, '\\' );
218  }
219  }
220 
221  if ( !$filename ) {
222  // Class not found; let the next autoloader try to find it
223  return null;
224  }
225 
226  // Make an absolute path, this improves performance by avoiding some stat calls
227  // Optimisation: use string offset access instead of substr
228  if ( $filename[0] !== '/' && $filename[1] !== ':' ) {
229  $filename = __DIR__ . '/../' . $filename;
230  }
231 
232  return $filename;
233  }
234 
240  public static function autoload( $className ) {
241  $filename = self::find( $className );
242 
243  if ( $filename !== null ) {
244  require $filename;
245  }
246  }
247 
252  public static function resetAutoloadLocalClassesLower() {
253  self::$autoloadLocalClassesLower = null;
254  }
255 
257  private static function assertTesting( $method ) {
258  if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
259  throw new LogicException( "$method is not supported outside phpunit tests!" );
260  }
261  }
262 
268  public static function getClassFiles(): array {
270 
271  self::assertTesting( __METHOD__ );
272 
273  // NOTE: ensure the order of preference is the same as used by find().
274  return array_merge(
276  self::$classFiles,
278  );
279  }
280 
286  public static function getNamespaceDirectories(): array {
287  self::assertTesting( __METHOD__ );
288  return self::$psr4Namespaces;
289  }
290 
298  public static function getState(): array {
299  self::assertTesting( __METHOD__ );
300  return [
301  'classFiles' => self::$classFiles,
302  'psr4Namespaces' => self::$psr4Namespaces,
303  ];
304  }
305 
314  public static function restoreState( $state ): void {
315  self::assertTesting( __METHOD__ );
316 
317  self::$classFiles = $state['classFiles'];
318  self::$psr4Namespaces = $state['psr4Namespaces'];
319  }
320 
321 }
322 
323 spl_autoload_register( [ 'AutoLoader', 'autoload' ] );
324 
325 // Load composer's autoloader if present
326 if ( is_readable( __DIR__ . '/../vendor/autoload.php' ) ) {
327  require_once __DIR__ . '/../vendor/autoload.php';
328 } elseif ( file_exists( __DIR__ . '/../vendor/autoload.php' ) ) {
329  die( __DIR__ . '/../vendor/autoload.php exists but is not readable' );
330 }
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
$wgAutoloadClasses
Definition: Setup.php:141
global $wgAutoloadLocalClasses
Definition: autoload.php:4
Locations of core classes Extension classes are specified with $wgAutoloadClasses.
Definition: AutoLoader.php:31
$wgAutoloadAttemptLowercase
Config variable stub for the AutoloadAttemptLowercase setting, for use by phpdoc and IDEs.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42