MediaWiki  1.34.0
FileBackendGroup.php
Go to the documentation of this file.
1 <?php
26 
35  protected static $instance = null;
36 
38  protected $backends = [];
39 
40  protected function __construct() {
41  }
42 
46  public static function singleton() {
47  if ( self::$instance == null ) {
48  self::$instance = new self();
49  self::$instance->initFromGlobals();
50  }
51 
52  return self::$instance;
53  }
54 
58  public static function destroySingleton() {
59  self::$instance = null;
60  }
61 
65  protected function initFromGlobals() {
67 
68  // Register explicitly defined backends
69  $this->register( $wgFileBackends, wfConfiguredReadOnlyReason() );
70 
71  $autoBackends = [];
72  // Automatically create b/c backends for file repos...
73  $repos = array_merge( $wgForeignFileRepos, [ $wgLocalFileRepo ] );
74  foreach ( $repos as $info ) {
75  $backendName = $info['backend'];
76  if ( is_object( $backendName ) || isset( $this->backends[$backendName] ) ) {
77  continue; // already defined (or set to the object for some reason)
78  }
79  $repoName = $info['name'];
80  // Local vars that used to be FSRepo members...
81  $directory = $info['directory'];
82  $deletedDir = $info['deletedDir'] ?? false; // deletion disabled
83  $thumbDir = $info['thumbDir'] ?? "{$directory}/thumb";
84  $transcodedDir = $info['transcodedDir'] ?? "{$directory}/transcoded";
85  // Get the FS backend configuration
86  $autoBackends[] = [
87  'name' => $backendName,
88  'class' => FSFileBackend::class,
89  'lockManager' => 'fsLockManager',
90  'containerPaths' => [
91  "{$repoName}-public" => "{$directory}",
92  "{$repoName}-thumb" => $thumbDir,
93  "{$repoName}-transcoded" => $transcodedDir,
94  "{$repoName}-deleted" => $deletedDir,
95  "{$repoName}-temp" => "{$directory}/temp"
96  ],
97  'fileMode' => $info['fileMode'] ?? 0644,
98  'directoryMode' => $wgDirectoryMode,
99  ];
100  }
101 
102  // Register implicitly defined backends
103  $this->register( $autoBackends, wfConfiguredReadOnlyReason() );
104  }
105 
113  protected function register( array $configs, $readOnlyReason = null ) {
114  foreach ( $configs as $config ) {
115  if ( !isset( $config['name'] ) ) {
116  throw new InvalidArgumentException( "Cannot register a backend with no name." );
117  }
118  $name = $config['name'];
119  if ( isset( $this->backends[$name] ) ) {
120  throw new LogicException( "Backend with name '$name' already registered." );
121  } elseif ( !isset( $config['class'] ) ) {
122  throw new InvalidArgumentException( "Backend with name '$name' has no class." );
123  }
124  $class = $config['class'];
125 
126  if ( isset( $config['domainId'] ) ) {
127  $domainId = $config['domainId'];
128  } elseif ( isset( $config['wikiId'] ) ) {
129  $domainId = $config['wikiId']; // b/c
130  } else {
131  // Only use the raw database/prefix for backwards compatibility
133  $domainId = strlen( $ld->getTablePrefix() )
134  ? "{$ld->getDatabase()}-{$ld->getTablePrefix()}"
135  : $ld->getDatabase();
136  // If the local wiki ID and local domain ID do not match, probably due to a
137  // non-default schema, issue a warning. A non-default schema indicates that
138  // it might be used to disambiguate different wikis.
139  $wikiId = WikiMap::getWikiIdFromDbDomain( $ld );
140  if ( $ld->getSchema() !== null && $domainId !== $wikiId ) {
141  wfWarn(
142  "\$wgFileBackend entry '$name' should have 'domainId' set.\n" .
143  "Legacy default 'domainId' is '$domainId' but wiki ID is '$wikiId'."
144  );
145  }
146  }
147  $config['domainId'] = $domainId;
148  $config['readOnly'] = $config['readOnly'] ?? $readOnlyReason;
149 
150  unset( $config['class'] ); // backend won't need this
151  $this->backends[$name] = [
152  'class' => $class,
153  'config' => $config,
154  'instance' => null
155  ];
156  }
157  }
158 
166  public function get( $name ) {
167  // Lazy-load the actual backend instance
168  if ( !isset( $this->backends[$name]['instance'] ) ) {
169  $config = $this->config( $name );
170 
171  $class = $config['class'];
172  if ( $class === FileBackendMultiWrite::class ) {
173  // @todo How can we test this? What's the intended use-case?
174  foreach ( $config['backends'] as $index => $beConfig ) {
175  if ( isset( $beConfig['template'] ) ) {
176  // Config is just a modified version of a registered backend's.
177  // This should only be used when that config is used only by this backend.
178  $config['backends'][$index] += $this->config( $beConfig['template'] );
179  }
180  }
181  }
182 
183  $this->backends[$name]['instance'] = new $class( $config );
184  }
185 
186  return $this->backends[$name]['instance'];
187  }
188 
196  public function config( $name ) {
197  if ( !isset( $this->backends[$name] ) ) {
198  throw new InvalidArgumentException( "No backend defined with the name '$name'." );
199  }
200 
201  $config = $this->backends[$name]['config'];
202  $services = MediaWikiServices::getInstance();
203 
204  return array_merge(
205  // Default backend parameters
206  [
207  'mimeCallback' => [ $this, 'guessMimeInternal' ],
208  'obResetFunc' => 'wfResetOutputBuffers',
209  'streamMimeFunc' => [ StreamFile::class, 'contentTypeFromPath' ],
210  'tmpFileFactory' => $services->getTempFSFileFactory(),
211  'statusWrapper' => [ Status::class, 'wrap' ],
212  'wanCache' => $services->getMainWANObjectCache(),
213  'srvCache' => ObjectCache::getLocalServerInstance( 'hash' ),
214  'logger' => LoggerFactory::getInstance( 'FileOperation' ),
215  'profiler' => function ( $section ) {
216  return Profiler::instance()->scopedProfileIn( $section );
217  }
218  ],
219  // Configured backend parameters
220  $config,
221  // Resolved backend parameters
222  [
223  'class' => $this->backends[$name]['class'],
224  'lockManager' =>
225  LockManagerGroup::singleton( $config['domainId'] )
226  ->get( $config['lockManager'] ),
227  'fileJournal' => isset( $config['fileJournal'] )
228  ? FileJournal::factory( $config['fileJournal'], $name )
229  : FileJournal::factory( [ 'class' => NullFileJournal::class ], $name )
230  ]
231  );
232  }
233 
240  public function backendFromPath( $storagePath ) {
241  list( $backend, , ) = FileBackend::splitStoragePath( $storagePath );
242  if ( $backend !== null && isset( $this->backends[$backend] ) ) {
243  return $this->get( $backend );
244  }
245 
246  return null;
247  }
248 
256  public function guessMimeInternal( $storagePath, $content, $fsPath ) {
257  $magic = MediaWiki\MediaWikiServices::getInstance()->getMimeAnalyzer();
258  // Trust the extension of the storage path (caller must validate)
259  $ext = FileBackend::extensionFromPath( $storagePath );
260  $type = $magic->guessTypesForExtension( $ext );
261  // For files without a valid extension (or one at all), inspect the contents
262  if ( !$type && $fsPath ) {
263  $type = $magic->guessMimeType( $fsPath, false );
264  } elseif ( !$type && strlen( $content ) ) {
265  $tmpFile = MediaWikiServices::getInstance()->getTempFSFileFactory()
266  ->newTempFSFile( 'mime_', '' );
267  file_put_contents( $tmpFile->getPath(), $content );
268  $type = $magic->guessMimeType( $tmpFile->getPath(), false );
269  }
270  return $type ?: 'unknown/unknown';
271  }
272 }
FileBackend\splitStoragePath
static splitStoragePath( $storagePath)
Split a storage path into a backend name, a container name, and a relative file path.
Definition: FileBackend.php:1520
WikiMap\getCurrentWikiDbDomain
static getCurrentWikiDbDomain()
Definition: WikiMap.php:292
FileBackendGroup
Class to handle file backend registration.
Definition: FileBackendGroup.php:33
Profiler\instance
static instance()
Singleton.
Definition: Profiler.php:63
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:117
$wgLocalFileRepo
$wgLocalFileRepo
File repository structures.
Definition: DefaultSettings.php:539
wfConfiguredReadOnlyReason
wfConfiguredReadOnlyReason()
Get the value of $wgReadOnly or the contents of $wgReadOnlyFile.
Definition: GlobalFunctions.php:1195
LockManagerGroup\singleton
static singleton( $domain=false)
Definition: LockManagerGroup.php:79
FileBackend\extensionFromPath
static extensionFromPath( $path, $case='lowercase')
Get the final extension from a storage or FS path.
Definition: FileBackend.php:1579
FileBackendGroup\config
config( $name)
Get the config array for a backend object with a given name.
Definition: FileBackendGroup.php:196
FileBackendGroup\guessMimeInternal
guessMimeInternal( $storagePath, $content, $fsPath)
Definition: FileBackendGroup.php:256
FileBackendGroup\singleton
static singleton()
Definition: FileBackendGroup.php:46
FileBackendGroup\destroySingleton
static destroySingleton()
Destroy the singleton instance.
Definition: FileBackendGroup.php:58
MediaWiki\MediaWikiServices\getInstance
static getInstance()
Returns the global default instance of the top level service locator.
Definition: MediaWikiServices.php:138
MediaWiki\Logger\LoggerFactory
PSR-3 logger instance factory.
Definition: LoggerFactory.php:45
WikiMap\getWikiIdFromDbDomain
static getWikiIdFromDbDomain( $domain)
Get the wiki ID of a database domain.
Definition: WikiMap.php:268
$wgFileBackends
$wgFileBackends
File backend structure configuration.
Definition: DefaultSettings.php:779
$content
$content
Definition: router.php:78
$wgDirectoryMode
$wgDirectoryMode
Default value for chmoding of new directories.
Definition: DefaultSettings.php:1585
FileBackendGroup\initFromGlobals
initFromGlobals()
Register file backends from the global variables.
Definition: FileBackendGroup.php:65
FileBackendGroup\$backends
array $backends
(name => ('class' => string, 'config' => array, 'instance' => object))
Definition: FileBackendGroup.php:38
FileBackendGroup\backendFromPath
backendFromPath( $storagePath)
Get an appropriate backend object from a storage path.
Definition: FileBackendGroup.php:240
$ext
if(!is_readable( $file)) $ext
Definition: router.php:48
wfWarn
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
Definition: GlobalFunctions.php:1065
FileBackendGroup\$instance
static FileBackendGroup $instance
Definition: FileBackendGroup.php:35
FileBackendGroup\__construct
__construct()
Definition: FileBackendGroup.php:40
FileJournal\factory
static factory(array $config, $backend)
Create an appropriate FileJournal object from config.
Definition: FileJournal.php:64
$wgForeignFileRepos
$wgForeignFileRepos
Enable the use of files from one or more other wikis.
Definition: DefaultSettings.php:554
ObjectCache\getLocalServerInstance
static getLocalServerInstance( $fallback=CACHE_NONE)
Factory function for CACHE_ACCEL (referenced from DefaultSettings.php)
Definition: ObjectCache.php:268
$type
$type
Definition: testCompression.php:48