29use Wikimedia\ObjectFactory\ObjectFactory;
54 private $mimeAnalyzer;
60 private $tmpFileFactory;
63 private $objectFactory;
69 MainConfigNames::DirectoryMode,
70 MainConfigNames::FileBackends,
71 MainConfigNames::ForeignFileRepos,
72 MainConfigNames::LocalFileRepo,
91 MimeAnalyzer $mimeAnalyzer,
94 ObjectFactory $objectFactory
96 $this->options = $options;
97 $this->srvCache = $srvCache;
98 $this->wanCache = $wanCache;
99 $this->mimeAnalyzer = $mimeAnalyzer;
100 $this->lmgFactory = $lmgFactory;
101 $this->tmpFileFactory = $tmpFileFactory;
102 $this->objectFactory = $objectFactory;
105 $this->
register( $options->
get( MainConfigNames::FileBackends ), $configuredReadOnlyMode->
getReason() );
109 $repos = array_merge(
110 $options->
get( MainConfigNames::ForeignFileRepos ), [ $options->
get( MainConfigNames::LocalFileRepo ) ] );
111 foreach ( $repos as $info ) {
112 $backendName = $info[
'backend'];
113 if ( is_object( $backendName ) || isset( $this->backends[$backendName] ) ) {
116 $repoName = $info[
'name'];
118 $directory = $info[
'directory'];
119 $deletedDir = $info[
'deletedDir'] ??
false;
120 $thumbDir = $info[
'thumbDir'] ??
"{$directory}/thumb";
121 $transcodedDir = $info[
'transcodedDir'] ??
"{$directory}/transcoded";
122 $lockManager = $info[
'lockManager'] ??
'fsLockManager';
125 'name' => $backendName,
126 'class' => FSFileBackend::class,
127 'lockManager' => $lockManager,
128 'containerPaths' => [
129 "{$repoName}-public" =>
"{$directory}",
130 "{$repoName}-thumb" => $thumbDir,
131 "{$repoName}-transcoded" => $transcodedDir,
132 "{$repoName}-deleted" => $deletedDir,
133 "{$repoName}-temp" =>
"{$directory}/temp"
135 'fileMode' => $info[
'fileMode'] ?? 0644,
136 'directoryMode' => $options->
get( MainConfigNames::DirectoryMode ),
141 $this->
register( $autoBackends, $configuredReadOnlyMode->
getReason() );
151 protected function register( array $configs, $readOnlyReason = null ) {
152 foreach ( $configs as $config ) {
153 if ( !isset( $config[
'name'] ) ) {
154 throw new InvalidArgumentException(
"Cannot register a backend with no name." );
156 $name = $config[
'name'];
157 if ( isset( $this->backends[$name] ) ) {
158 throw new LogicException(
"Backend with name '$name' already registered." );
159 } elseif ( !isset( $config[
'class'] ) ) {
160 throw new InvalidArgumentException(
"Backend with name '$name' has no class." );
162 $class = $config[
'class'];
164 $config[
'domainId'] =
165 $config[
'domainId'] ?? $config[
'wikiId'] ?? $this->options->get(
'fallbackWikiId' );
166 $config[
'readOnly'] = $config[
'readOnly'] ?? $readOnlyReason;
168 unset( $config[
'class'] );
169 $this->backends[$name] = [
184 public function get( $name ) {
186 if ( !isset( $this->backends[$name][
'instance'] ) ) {
187 $config = $this->
config( $name );
189 $class = $config[
'class'];
190 if ( $class === FileBackendMultiWrite::class ) {
192 foreach ( $config[
'backends'] as $index => $beConfig ) {
193 if ( isset( $beConfig[
'template'] ) ) {
196 $config[
'backends'][$index] += $this->
config( $beConfig[
'template'] );
201 $this->backends[$name][
'instance'] =
new $class( $config );
204 return $this->backends[$name][
'instance'];
215 if ( !isset( $this->backends[$name] ) ) {
216 throw new InvalidArgumentException(
"No backend defined with the name '$name'." );
219 $config = $this->backends[$name][
'config'];
224 'mimeCallback' => [ $this,
'guessMimeInternal' ],
225 'obResetFunc' =>
'wfResetOutputBuffers',
226 'streamMimeFunc' => [ StreamFile::class,
'contentTypeFromPath' ],
227 'tmpFileFactory' => $this->tmpFileFactory,
228 'statusWrapper' => [ Status::class,
'wrap' ],
229 'wanCache' => $this->wanCache,
230 'srvCache' => $this->srvCache,
231 'logger' => LoggerFactory::getInstance(
'FileOperation' ),
232 'profiler' =>
static function ( $section ) {
233 return Profiler::instance()->scopedProfileIn( $section );
240 'class' => $this->backends[$name][
'class'],
242 $this->lmgFactory->getLockManagerGroup( $config[
'domainId'] )
243 ->get( $config[
'lockManager'] ),
256 if ( $backend !==
null && isset( $this->backends[$backend] ) ) {
257 return $this->
get( $backend );
273 $type = $this->mimeAnalyzer->getMimeTypeFromExtensionOrNull(
$ext );
275 if ( !
$type && $fsPath ) {
276 $type = $this->mimeAnalyzer->guessMimeType( $fsPath,
false );
278 $tmpFile = $this->tmpFileFactory->newTempFSFile(
'mime_',
'' );
279 file_put_contents( $tmpFile->getPath(),
$content );
280 $type = $this->mimeAnalyzer->guessMimeType( $tmpFile->getPath(),
false );
282 return $type ?:
'unknown/unknown';
Class representing a cache/ephemeral data store.
Class to handle file backend registration.
config( $name)
Get the config array for a backend object with a given name.
guessMimeInternal( $storagePath, $content, $fsPath)
__construct(ServiceOptions $options, ConfiguredReadOnlyMode $configuredReadOnlyMode, BagOStuff $srvCache, WANObjectCache $wanCache, MimeAnalyzer $mimeAnalyzer, LockManagerGroupFactory $lmgFactory, TempFSFileFactory $tmpFileFactory, ObjectFactory $objectFactory)
const CONSTRUCTOR_OPTIONS
backendFromPath( $storagePath)
Get an appropriate backend object from a storage path.
array[] $backends
(name => ('class' => string, 'config' => array, 'instance' => object))
static splitStoragePath( $storagePath)
Split a storage path into a backend name, a container name, and a relative file path.
static extensionFromPath( $path, $case='lowercase')
Get the final extension from a storage or FS path.
A class containing constants representing the names of configuration variables.
Multi-datacenter aware caching interface.
if(!is_readable( $file)) $ext