Make LocalisationCache a service
[lhc/web/wiklou.git] / includes / cache / localisation / LocalisationCache.php
index bb84f97..1031245 100644 (file)
 
 use CLDRPluralRuleParser\Evaluator;
 use CLDRPluralRuleParser\Error as CLDRPluralRuleError;
-use MediaWiki\MediaWikiServices;
+use MediaWiki\Config\ServiceOptions;
+use Psr\Log\LoggerInterface;
 
 /**
  * Class for caching the contents of localisation files, Messages*.php
  * and *.i18n.php.
  *
- * An instance of this class is available using Language::getLocalisationCache().
+ * An instance of this class is available using MediaWikiServices.
  *
  * The values retrieved from here are merged, containing items from extension
  * files, core messages files and the language fallback sequence (e.g. zh-cn ->
@@ -39,8 +40,8 @@ use MediaWiki\MediaWikiServices;
 class LocalisationCache {
        const VERSION = 4;
 
-       /** Configuration associative array */
-       private $conf;
+       /** @var ServiceOptions */
+       private $options;
 
        /**
         * True if recaching should only be done on an explicit call to recache().
@@ -49,11 +50,6 @@ class LocalisationCache {
         */
        private $manualRecache = false;
 
-       /**
-        * True to treat all files as expired until they are regenerated by this object.
-        */
-       private $forceRecache = false;
-
        /**
         * The cache data. 3-d array, where the first key is the language code,
         * the second key is the item key e.g. 'messages', and the third key is
@@ -69,6 +65,14 @@ class LocalisationCache {
         */
        private $store;
 
+       /**
+        * @var LoggerInterface
+        */
+       private $logger;
+
+       /** @var callable[] See comment for parameter in constructor */
+       private $clearStoreCallbacks;
+
        /**
         * A 2-d associative array, code/key, where presence indicates that the item
         * is loaded. Value arbitrary.
@@ -182,60 +186,49 @@ class LocalisationCache {
 
        private $mergeableKeys = null;
 
+       /**
+        * @todo Make this a const when HHVM support is dropped (T192166)
+        *
+        * @var array
+        * @since 1.34
+        */
+       public static $constructorOptions = [
+               // True to treat all files as expired until they are regenerated by this object.
+               'forceRecache',
+               'manualRecache',
+               'ExtensionMessagesFiles',
+               'MessagesDirs',
+       ];
+
        /**
         * For constructor parameters, see the documentation in DefaultSettings.php
         * for $wgLocalisationCacheConf.
         *
-        * @param array $conf
+        * Do not construct this directly. Use MediaWikiServices.
+        *
+        * @param ServiceOptions $options
+        * @param LCStore $store What backend to use for storage
+        * @param LoggerInterface $logger
+        * @param callable[] $clearStoreCallbacks To be called whenever the cache is cleared. Can be
+        *   used to clear other caches that depend on this one, such as ResourceLoader's
+        *   MessageBlobStore.
         * @throws MWException
         */
-       function __construct( $conf ) {
-               global $wgCacheDirectory;
-
-               $this->conf = $conf;
-
-               $directory = !empty( $conf['storeDirectory'] ) ? $conf['storeDirectory'] : $wgCacheDirectory;
-               $storeArg = [];
-               $storeArg['directory'] = $directory;
-
-               if ( !empty( $conf['storeClass'] ) ) {
-                       $storeClass = $conf['storeClass'];
-               } else {
-                       switch ( $conf['store'] ) {
-                               case 'files':
-                               case 'file':
-                                       $storeClass = LCStoreCDB::class;
-                                       break;
-                               case 'db':
-                                       $storeClass = LCStoreDB::class;
-                                       $storeArg['server'] = $conf['storeServer'] ?? [];
-                                       break;
-                               case 'array':
-                                       $storeClass = LCStoreStaticArray::class;
-                                       break;
-                               case 'detect':
-                                       if ( $directory ) {
-                                               $storeClass = LCStoreCDB::class;
-                                       } else {
-                                               $storeClass = LCStoreDB::class;
-                                               $storeArg['server'] = $conf['storeServer'] ?? [];
-                                       }
-                                       break;
-                               default:
-                                       throw new MWException(
-                                               'Please set $wgLocalisationCacheConf[\'store\'] to something sensible.'
-                                       );
-                       }
-               }
-
-               wfDebugLog( 'caches', static::class . ": using store $storeClass" );
-
-               $this->store = new $storeClass( $storeArg );
-               foreach ( [ 'manualRecache', 'forceRecache' ] as $var ) {
-                       if ( isset( $conf[$var] ) ) {
-                               $this->$var = $conf[$var];
-                       }
-               }
+       function __construct(
+               ServiceOptions $options,
+               LCStore $store,
+               LoggerInterface $logger,
+               array $clearStoreCallbacks = []
+       ) {
+               $options->assertRequiredOptions( self::$constructorOptions );
+
+               $this->options = $options;
+               $this->store = $store;
+               $this->logger = $logger;
+               $this->clearStoreCallbacks = $clearStoreCallbacks;
+
+               // Keep this separate from $this->options so it can be mutable
+               $this->manualRecache = $options->get( 'manualRecache' );
        }
 
        /**
@@ -400,8 +393,8 @@ class LocalisationCache {
         * @return bool
         */
        public function isExpired( $code ) {
-               if ( $this->forceRecache && !isset( $this->recachedLangs[$code] ) ) {
-                       wfDebug( __METHOD__ . "($code): forced reload\n" );
+               if ( $this->options->get( 'forceRecache' ) && !isset( $this->recachedLangs[$code] ) ) {
+                       $this->logger->debug( __METHOD__ . "($code): forced reload" );
 
                        return true;
                }
@@ -411,7 +404,7 @@ class LocalisationCache {
                $preload = $this->store->get( $code, 'preload' );
                // Different keys may expire separately for some stores
                if ( $deps === null || $keys === null || $preload === null ) {
-                       wfDebug( __METHOD__ . "($code): cache missing, need to make one\n" );
+                       $this->logger->debug( __METHOD__ . "($code): cache missing, need to make one" );
 
                        return true;
                }
@@ -422,8 +415,8 @@ class LocalisationCache {
                        // anymore (e.g. uninstalled extensions)
                        // When this happens, always expire the cache
                        if ( !$dep instanceof CacheDependency || $dep->isExpired() ) {
-                               wfDebug( __METHOD__ . "($code): cache for $code expired due to " .
-                                       get_class( $dep ) . "\n" );
+                               $this->logger->debug( __METHOD__ . "($code): cache for $code expired due to " .
+                                       get_class( $dep ) );
 
                                return true;
                        }
@@ -590,7 +583,7 @@ class LocalisationCache {
                try {
                        $compiledRules = Evaluator::compile( $rules );
                } catch ( CLDRPluralRuleError $e ) {
-                       wfDebugLog( 'l10n', $e->getMessage() );
+                       $this->logger->debug( $e->getMessage() );
 
                        return [];
                }
@@ -798,14 +791,12 @@ class LocalisationCache {
        public function getMessagesDirs() {
                global $IP;
 
-               $config = MediaWikiServices::getInstance()->getMainConfig();
-               $messagesDirs = $config->get( 'MessagesDirs' );
                return [
                        'core' => "$IP/languages/i18n",
                        'exif' => "$IP/languages/i18n/exif",
                        'api' => "$IP/includes/api/i18n",
                        'oojs-ui' => "$IP/resources/lib/ooui/i18n",
-               ] + $messagesDirs;
+               ] + $this->options->get( 'MessagesDirs' );
        }
 
        /**
@@ -815,8 +806,6 @@ class LocalisationCache {
         * @throws MWException
         */
        public function recache( $code ) {
-               global $wgExtensionMessagesFiles;
-
                if ( !$code ) {
                        throw new MWException( "Invalid language code requested" );
                }
@@ -830,10 +819,10 @@ class LocalisationCache {
                # Load the primary localisation from the source file
                $data = $this->readSourceFilesAndRegisterDeps( $code, $deps );
                if ( $data === false ) {
-                       wfDebug( __METHOD__ . ": no localisation file for $code, using fallback to en\n" );
+                       $this->logger->debug( __METHOD__ . ": no localisation file for $code, using fallback to en" );
                        $coreData['fallback'] = 'en';
                } else {
-                       wfDebug( __METHOD__ . ": got localisation for $code from source\n" );
+                       $this->logger->debug( __METHOD__ . ": got localisation for $code from source" );
 
                        # Merge primary localisation
                        foreach ( $data as $key => $value ) {
@@ -868,7 +857,7 @@ class LocalisationCache {
 
                # Load non-JSON localisation data for extensions
                $extensionData = array_fill_keys( $codeSequence, $initialData );
-               foreach ( $wgExtensionMessagesFiles as $extension => $fileName ) {
+               foreach ( $this->options->get( 'ExtensionMessagesFiles' ) as $extension => $fileName ) {
                        if ( isset( $messageDirs[$extension] ) ) {
                                # This extension has JSON message data; skip the PHP shim
                                continue;
@@ -998,8 +987,8 @@ class LocalisationCache {
                        $allData['list'][$key] = array_keys( $allData[$key] );
                }
                # Run hooks
-               $purgeBlobs = true;
-               Hooks::run( 'LocalisationCacheRecache', [ $this, $code, &$allData, &$purgeBlobs ] );
+               $unused = true; // Used to be $purgeBlobs, removed in 1.34
+               Hooks::run( 'LocalisationCacheRecache', [ $this, $code, &$allData, &$unused ] );
 
                if ( is_null( $allData['namespaceNames'] ) ) {
                        throw new MWException( __METHOD__ . ': Localisation data failed sanity check! ' .
@@ -1031,9 +1020,10 @@ class LocalisationCache {
                # Clear out the MessageBlobStore
                # HACK: If using a null (i.e. disabled) storage backend, we
                # can't write to the MessageBlobStore either
-               if ( $purgeBlobs && !$this->store instanceof LCStoreNull ) {
-                       $blobStore = MediaWikiServices::getInstance()->getResourceLoader()->getMessageBlobStore();
-                       $blobStore->clear();
+               if ( !$this->store instanceof LCStoreNull ) {
+                       foreach ( $this->clearStoreCallbacks as $callback ) {
+                               $callback();
+                       }
                }
        }
 
@@ -1094,5 +1084,4 @@ class LocalisationCache {
                $this->store = new LCStoreNull;
                $this->manualRecache = false;
        }
-
 }