31use Wikimedia\ObjectFactory\ObjectFactory;
57 private $mimeAnalyzer;
63 private $tmpFileFactory;
66 private $objectFactory;
72 MainConfigNames::DirectoryMode,
73 MainConfigNames::FileBackends,
74 MainConfigNames::ForeignFileRepos,
75 MainConfigNames::LocalFileRepo,
94 MimeAnalyzer $mimeAnalyzer,
97 ObjectFactory $objectFactory
99 $this->options = $options;
100 $this->srvCache = $srvCache;
101 $this->wanCache = $wanCache;
102 $this->mimeAnalyzer = $mimeAnalyzer;
103 $this->lmgFactory = $lmgFactory;
104 $this->tmpFileFactory = $tmpFileFactory;
105 $this->objectFactory = $objectFactory;
112 $repos = array_merge(
113 $options->
get( MainConfigNames::ForeignFileRepos ), [ $options->
get( MainConfigNames::LocalFileRepo ) ] );
114 foreach ( $repos as $info ) {
115 $backendName = $info[
'backend'];
116 if ( is_object( $backendName ) || isset( $this->backends[$backendName] ) ) {
119 $repoName = $info[
'name'];
121 $directory = $info[
'directory'];
122 $deletedDir = $info[
'deletedDir'] ??
false;
123 $thumbDir = $info[
'thumbDir'] ??
"{$directory}/thumb";
124 $transcodedDir = $info[
'transcodedDir'] ??
"{$directory}/transcoded";
125 $lockManager = $info[
'lockManager'] ??
'fsLockManager';
128 'name' => $backendName,
129 'class' => FSFileBackend::class,
130 'lockManager' => $lockManager,
131 'containerPaths' => [
132 "{$repoName}-public" =>
"{$directory}",
133 "{$repoName}-thumb" => $thumbDir,
134 "{$repoName}-transcoded" => $transcodedDir,
135 "{$repoName}-deleted" => $deletedDir,
136 "{$repoName}-temp" =>
"{$directory}/temp"
138 'fileMode' => $info[
'fileMode'] ?? 0644,
139 'directoryMode' => $options->
get( MainConfigNames::DirectoryMode ),
154 protected function register( array $configs, $readOnlyReason = null ) {
155 foreach ( $configs as $config ) {
156 if ( !isset( $config[
'name'] ) ) {
157 throw new InvalidArgumentException(
"Cannot register a backend with no name." );
159 $name = $config[
'name'];
160 if ( isset( $this->backends[$name] ) ) {
161 throw new LogicException(
"Backend with name '$name' already registered." );
162 } elseif ( !isset( $config[
'class'] ) ) {
163 throw new InvalidArgumentException(
"Backend with name '$name' has no class." );
165 $class = $config[
'class'];
167 $config[
'domainId'] ??= $config[
'wikiId'] ?? $this->options->get(
'fallbackWikiId' );
168 $config[
'readOnly'] ??= $readOnlyReason;
170 unset( $config[
'class'] );
171 $this->backends[$name] = [
186 public function get( $name ) {
188 if ( !isset( $this->backends[$name][
'instance'] ) ) {
189 $config = $this->
config( $name );
191 $class = $config[
'class'];
192 if ( $class === FileBackendMultiWrite::class ) {
194 foreach ( $config[
'backends'] as $index => $beConfig ) {
195 if ( isset( $beConfig[
'template'] ) ) {
198 $config[
'backends'][$index] += $this->
config( $beConfig[
'template'] );
203 $this->backends[$name][
'instance'] =
new $class( $config );
206 return $this->backends[$name][
'instance'];
217 if ( !isset( $this->backends[$name] ) ) {
218 throw new InvalidArgumentException(
"No backend defined with the name '$name'." );
221 $config = $this->backends[$name][
'config'];
226 'mimeCallback' => [ $this,
'guessMimeInternal' ],
227 'obResetFunc' =>
'wfResetOutputBuffers',
228 'streamMimeFunc' => [ StreamFile::class,
'contentTypeFromPath' ],
229 'tmpFileFactory' => $this->tmpFileFactory,
230 'statusWrapper' => [ Status::class,
'wrap' ],
231 'wanCache' => $this->wanCache,
232 'srvCache' => $this->srvCache,
233 'logger' => LoggerFactory::getInstance(
'FileOperation' ),
234 'profiler' =>
static function ( $section ) {
242 'class' => $this->backends[$name][
'class'],
244 $this->lmgFactory->getLockManagerGroup( $config[
'domainId'] )
245 ->get( $config[
'lockManager'] ),
258 if ( $backend !==
null && isset( $this->backends[$backend] ) ) {
259 return $this->
get( $backend );
275 $type = $this->mimeAnalyzer->getMimeTypeFromExtensionOrNull(
$ext );
277 if ( !$type && $fsPath ) {
278 $type = $this->mimeAnalyzer->guessMimeType( $fsPath,
false );
280 $tmpFile = $this->tmpFileFactory->newTempFSFile(
'mime_',
'' );
281 file_put_contents( $tmpFile->getPath(),
$content );
282 $type = $this->mimeAnalyzer->guessMimeType( $tmpFile->getPath(),
false );
284 return $type ?:
'unknown/unknown';
Class representing a cache/ephemeral data store.
Class to handle file backend registration.
__construct(ServiceOptions $options, ReadOnlyMode $readOnlyMode, BagOStuff $srvCache, WANObjectCache $wanCache, MimeAnalyzer $mimeAnalyzer, LockManagerGroupFactory $lmgFactory, TempFSFileFactory $tmpFileFactory, ObjectFactory $objectFactory)
config( $name)
Get the config array for a backend object with a given name.
guessMimeInternal( $storagePath, $content, $fsPath)
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