MediaWiki  master
FileBackendGroup.php
Go to the documentation of this file.
1 <?php
29 use Wikimedia\ObjectFactory;
30 
42  protected $backends = [];
43 
45  private $options;
46 
48  private $srvCache;
49 
51  private $wanCache;
52 
54  private $mimeAnalyzer;
55 
57  private $lmgFactory;
58 
60  private $tmpFileFactory;
61 
63  private $objectFactory;
64 
68  public const CONSTRUCTOR_OPTIONS = [
69  'DirectoryMode',
70  'FileBackends',
71  'ForeignFileRepos',
72  'LocalFileRepo',
73  'fallbackWikiId',
74  ];
75 
82  public static function singleton(): FileBackendGroup {
83  wfDeprecated( __METHOD__, '1.35' );
84  return MediaWikiServices::getInstance()->getFileBackendGroup();
85  }
86 
91  public static function destroySingleton() {
92  wfDeprecated( __METHOD__, '1.35' );
93  MediaWikiServices::getInstance()->resetServiceForTesting( 'FileBackendGroup' );
94  }
95 
106  public function __construct(
108  ConfiguredReadOnlyMode $configuredReadOnlyMode,
114  ObjectFactory $objectFactory
115  ) {
116  $this->options = $options;
117  $this->srvCache = $srvCache;
118  $this->wanCache = $wanCache;
119  $this->mimeAnalyzer = $mimeAnalyzer;
120  $this->lmgFactory = $lmgFactory;
121  $this->tmpFileFactory = $tmpFileFactory;
122  $this->objectFactory = $objectFactory;
123 
124  // Register explicitly defined backends
125  $this->register( $options->get( 'FileBackends' ), $configuredReadOnlyMode->getReason() );
126 
127  $autoBackends = [];
128  // Automatically create b/c backends for file repos...
129  $repos = array_merge(
130  $options->get( 'ForeignFileRepos' ), [ $options->get( 'LocalFileRepo' ) ] );
131  foreach ( $repos as $info ) {
132  $backendName = $info['backend'];
133  if ( is_object( $backendName ) || isset( $this->backends[$backendName] ) ) {
134  continue; // already defined (or set to the object for some reason)
135  }
136  $repoName = $info['name'];
137  // Local vars that used to be FSRepo members...
138  $directory = $info['directory'];
139  $deletedDir = $info['deletedDir'] ?? false; // deletion disabled
140  $thumbDir = $info['thumbDir'] ?? "{$directory}/thumb";
141  $transcodedDir = $info['transcodedDir'] ?? "{$directory}/transcoded";
142  // Get the FS backend configuration
143  $autoBackends[] = [
144  'name' => $backendName,
145  'class' => FSFileBackend::class,
146  'lockManager' => 'fsLockManager',
147  'containerPaths' => [
148  "{$repoName}-public" => "{$directory}",
149  "{$repoName}-thumb" => $thumbDir,
150  "{$repoName}-transcoded" => $transcodedDir,
151  "{$repoName}-deleted" => $deletedDir,
152  "{$repoName}-temp" => "{$directory}/temp"
153  ],
154  'fileMode' => $info['fileMode'] ?? 0644,
155  'directoryMode' => $options->get( 'DirectoryMode' ),
156  ];
157  }
158 
159  // Register implicitly defined backends
160  $this->register( $autoBackends, $configuredReadOnlyMode->getReason() );
161  }
162 
170  protected function register( array $configs, $readOnlyReason = null ) {
171  foreach ( $configs as $config ) {
172  if ( !isset( $config['name'] ) ) {
173  throw new InvalidArgumentException( "Cannot register a backend with no name." );
174  }
175  $name = $config['name'];
176  if ( isset( $this->backends[$name] ) ) {
177  throw new LogicException( "Backend with name '$name' already registered." );
178  } elseif ( !isset( $config['class'] ) ) {
179  throw new InvalidArgumentException( "Backend with name '$name' has no class." );
180  }
181  $class = $config['class'];
182 
183  $config['domainId'] =
184  $config['domainId'] ?? $config['wikiId'] ?? $this->options->get( 'fallbackWikiId' );
185  $config['readOnly'] = $config['readOnly'] ?? $readOnlyReason;
186 
187  unset( $config['class'] ); // backend won't need this
188  $this->backends[$name] = [
189  'class' => $class,
190  'config' => $config,
191  'instance' => null
192  ];
193  }
194  }
195 
203  public function get( $name ) {
204  // Lazy-load the actual backend instance
205  if ( !isset( $this->backends[$name]['instance'] ) ) {
206  $config = $this->config( $name );
207 
208  $class = $config['class'];
209  if ( $class === FileBackendMultiWrite::class ) {
210  // @todo How can we test this? What's the intended use-case?
211  foreach ( $config['backends'] as $index => $beConfig ) {
212  if ( isset( $beConfig['template'] ) ) {
213  // Config is just a modified version of a registered backend's.
214  // This should only be used when that config is used only by this backend.
215  $config['backends'][$index] += $this->config( $beConfig['template'] );
216  }
217  }
218  }
219 
220  $this->backends[$name]['instance'] = new $class( $config );
221  }
222 
223  return $this->backends[$name]['instance'];
224  }
225 
233  public function config( $name ) {
234  if ( !isset( $this->backends[$name] ) ) {
235  throw new InvalidArgumentException( "No backend defined with the name '$name'." );
236  }
237 
238  $config = $this->backends[$name]['config'];
239 
240  return array_merge(
241  // Default backend parameters
242  [
243  'mimeCallback' => [ $this, 'guessMimeInternal' ],
244  'obResetFunc' => 'wfResetOutputBuffers',
245  'streamMimeFunc' => [ StreamFile::class, 'contentTypeFromPath' ],
246  'tmpFileFactory' => $this->tmpFileFactory,
247  'statusWrapper' => [ Status::class, 'wrap' ],
248  'wanCache' => $this->wanCache,
249  'srvCache' => $this->srvCache,
250  'logger' => LoggerFactory::getInstance( 'FileOperation' ),
251  'profiler' => static function ( $section ) {
252  return Profiler::instance()->scopedProfileIn( $section );
253  }
254  ],
255  // Configured backend parameters
256  $config,
257  // Resolved backend parameters
258  [
259  'class' => $this->backends[$name]['class'],
260  'lockManager' =>
261  $this->lmgFactory->getLockManagerGroup( $config['domainId'] )
262  ->get( $config['lockManager'] ),
263  'fileJournal' => isset( $config['fileJournal'] )
264  ? $this->objectFactory->createObject(
265  $config['fileJournal'] + [ 'backend' => $name ],
266  [ 'specIsArg' => true, 'assertClass' => FileJournal::class ] )
267  : new NullFileJournal
268  ]
269  );
270  }
271 
278  public function backendFromPath( $storagePath ) {
279  list( $backend, , ) = FileBackend::splitStoragePath( $storagePath );
280  if ( $backend !== null && isset( $this->backends[$backend] ) ) {
281  return $this->get( $backend );
282  }
283 
284  return null;
285  }
286 
294  public function guessMimeInternal( $storagePath, $content, $fsPath ) {
295  // Trust the extension of the storage path (caller must validate)
296  $ext = FileBackend::extensionFromPath( $storagePath );
297  $type = $this->mimeAnalyzer->getMimeTypeFromExtensionOrNull( $ext );
298  // For files without a valid extension (or one at all), inspect the contents
299  if ( !$type && $fsPath ) {
300  $type = $this->mimeAnalyzer->guessMimeType( $fsPath, false );
301  } elseif ( !$type && strlen( $content ) ) {
302  $tmpFile = $this->tmpFileFactory->newTempFSFile( 'mime_', '' );
303  file_put_contents( $tmpFile->getPath(), $content );
304  $type = $this->mimeAnalyzer->guessMimeType( $tmpFile->getPath(), false );
305  }
306  return $type ?: 'unknown/unknown';
307  }
308 }
FileBackendGroup\__construct
__construct(ServiceOptions $options, ConfiguredReadOnlyMode $configuredReadOnlyMode, BagOStuff $srvCache, WANObjectCache $wanCache, MimeAnalyzer $mimeAnalyzer, LockManagerGroupFactory $lmgFactory, TempFSFileFactory $tmpFileFactory, ObjectFactory $objectFactory)
Definition: FileBackendGroup.php:106
FileBackend\splitStoragePath
static splitStoragePath( $storagePath)
Split a storage path into a backend name, a container name, and a relative file path.
Definition: FileBackend.php:1537
FileBackendGroup
Class to handle file backend registration.
Definition: FileBackendGroup.php:37
Profiler\instance
static instance()
Singleton.
Definition: Profiler.php:69
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:186
MimeAnalyzer
Implements functions related to MIME types such as detection and mapping to file extension.
Definition: MimeAnalyzer.php:33
NullFileJournal
Simple version of FileJournal that does nothing.
Definition: NullFileJournal.php:6
MediaWiki\FileBackend\LockManager\LockManagerGroupFactory
Service to construct LockManagerGroups.
Definition: LockManagerGroupFactory.php:11
FileBackend\extensionFromPath
static extensionFromPath( $path, $case='lowercase')
Get the final extension from a storage or FS path.
Definition: FileBackend.php:1599
ConfiguredReadOnlyMode
A read-only mode service which does not depend on LoadBalancer.
Definition: ConfiguredReadOnlyMode.php:9
BagOStuff
Class representing a cache/ephemeral data store.
Definition: BagOStuff.php:86
FileBackendGroup\config
config( $name)
Get the config array for a backend object with a given name.
Definition: FileBackendGroup.php:233
ConfiguredReadOnlyMode\getReason
getReason()
Get the value of $wgReadOnly or the contents of $wgReadOnlyFile.
Definition: ConfiguredReadOnlyMode.php:42
FileBackendGroup\$objectFactory
ObjectFactory $objectFactory
Definition: FileBackendGroup.php:63
FileBackendGroup\guessMimeInternal
guessMimeInternal( $storagePath, $content, $fsPath)
Definition: FileBackendGroup.php:294
FileBackendGroup\singleton
static singleton()
Definition: FileBackendGroup.php:82
FileBackendGroup\destroySingleton
static destroySingleton()
Definition: FileBackendGroup.php:91
MediaWiki\Config\ServiceOptions
A class for passing options to services.
Definition: ServiceOptions.php:27
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:996
MediaWiki\Logger\LoggerFactory
PSR-3 logger instance factory.
Definition: LoggerFactory.php:45
FileBackendGroup\CONSTRUCTOR_OPTIONS
const CONSTRUCTOR_OPTIONS
Definition: FileBackendGroup.php:68
FileBackendGroup\$lmgFactory
LockManagerGroupFactory $lmgFactory
Definition: FileBackendGroup.php:57
MediaWiki\FileBackend\FSFile\TempFSFileFactory
Definition: TempFSFileFactory.php:10
$content
$content
Definition: router.php:76
WANObjectCache
Multi-datacenter aware caching interface.
Definition: WANObjectCache.php:128
FileBackendGroup\$options
ServiceOptions $options
Definition: FileBackendGroup.php:45
FileBackendGroup\$backends
array[] $backends
(name => ('class' => string, 'config' => array, 'instance' => object))
Definition: FileBackendGroup.php:42
FileBackendGroup\$tmpFileFactory
TempFSFileFactory $tmpFileFactory
Definition: FileBackendGroup.php:60
FileBackendGroup\backendFromPath
backendFromPath( $storagePath)
Get an appropriate backend object from a storage path.
Definition: FileBackendGroup.php:278
$ext
if(!is_readable( $file)) $ext
Definition: router.php:48
MediaWiki\Config\ServiceOptions\get
get( $key)
Definition: ServiceOptions.php:93
FileBackendGroup\$srvCache
BagOStuff $srvCache
Definition: FileBackendGroup.php:48
FileBackendGroup\$wanCache
WANObjectCache $wanCache
Definition: FileBackendGroup.php:51
$type
$type
Definition: testCompression.php:52
FileBackendGroup\$mimeAnalyzer
MimeAnalyzer $mimeAnalyzer
Definition: FileBackendGroup.php:54