Make LocalisationCache a service
[lhc/web/wiklou.git] / includes / cache / localisation / LocalisationCache.php
index c6d6b8f..0f186b6 100644 (file)
 
 use CLDRPluralRuleParser\Evaluator;
 use CLDRPluralRuleParser\Error as CLDRPluralRuleError;
-use MediaWiki\Logger\LoggerFactory;
-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 ->
@@ -40,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().
@@ -50,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
@@ -71,16 +66,20 @@ class LocalisationCache {
        private $store;
 
        /**
-        * @var \Psr\Log\LoggerInterface
+        * @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.
         *
         * For split items, if set, this indicates that all of the subitems have been
         * loaded.
+        *
         */
        private $loadedItems = [];
 
@@ -189,59 +188,81 @@ class LocalisationCache {
        private $mergeableKeys = null;
 
        /**
-        * For constructor parameters, see the documentation in DefaultSettings.php
-        * for $wgLocalisationCacheConf.
+        * Return a suitable LCStore as specified by the given configuration.
         *
-        * @param array $conf
-        * @throws MWException
+        * @since 1.34
+        * @param array $conf In the format of $wgLocalisationCacheConf
+        * @param string|false|null $fallbackCacheDir In case 'storeDirectory' isn't specified
+        * @return LCStore
         */
-       function __construct( $conf ) {
-               global $wgCacheDirectory;
-
-               $this->conf = $conf;
-               $this->logger = LoggerFactory::getInstance( 'localisation' );
-
-               $directory = !empty( $conf['storeDirectory'] ) ? $conf['storeDirectory'] : $wgCacheDirectory;
+       public static function getStoreFromConf( array $conf, $fallbackCacheDir ) : LCStore {
                $storeArg = [];
-               $storeArg['directory'] = $directory;
+               $storeArg['directory'] =
+                       $conf['storeDirectory'] ?: $fallbackCacheDir;
 
                if ( !empty( $conf['storeClass'] ) ) {
                        $storeClass = $conf['storeClass'];
+               } elseif ( $conf['store'] === 'files' || $conf['store'] === 'file' ||
+                       ( $conf['store'] === 'detect' && $storeArg['directory'] )
+               ) {
+                       $storeClass = LCStoreCDB::class;
+               } elseif ( $conf['store'] === 'db' || $conf['store'] === 'detect' ) {
+                       $storeClass = LCStoreDB::class;
+                       $storeArg['server'] = $conf['storeServer'] ?? [];
+               } elseif ( $conf['store'] === 'array' ) {
+                       $storeClass = LCStoreStaticArray::class;
                } 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.'
-                                       );
-                       }
+                       throw new MWException(
+                               'Please set $wgLocalisationCacheConf[\'store\'] to something sensible.'
+                       );
                }
-               $this->logger->debug( static::class . ": using store $storeClass" );
 
-               $this->store = new $storeClass( $storeArg );
-               foreach ( [ 'manualRecache', 'forceRecache' ] as $var ) {
-                       if ( isset( $conf[$var] ) ) {
-                               $this->$var = $conf[$var];
-                       }
-               }
+               return new $storeClass( $storeArg );
+       }
+
+       /**
+        * @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.
+        *
+        * 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(
+               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' );
        }
 
        /**
@@ -406,7 +427,7 @@ class LocalisationCache {
         * @return bool
         */
        public function isExpired( $code ) {
-               if ( $this->forceRecache && !isset( $this->recachedLangs[$code] ) ) {
+               if ( $this->options->get( 'forceRecache' ) && !isset( $this->recachedLangs[$code] ) ) {
                        $this->logger->debug( __METHOD__ . "($code): forced reload" );
 
                        return true;
@@ -796,14 +817,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' );
        }
 
        /**
@@ -813,8 +832,6 @@ class LocalisationCache {
         * @throws MWException
         */
        public function recache( $code ) {
-               global $wgExtensionMessagesFiles;
-
                if ( !$code ) {
                        throw new MWException( "Invalid language code requested" );
                }
@@ -861,7 +878,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;
@@ -1023,8 +1040,9 @@ class LocalisationCache {
                # HACK: If using a null (i.e. disabled) storage backend, we
                # can't write to the MessageBlobStore either
                if ( !$this->store instanceof LCStoreNull ) {
-                       $blobStore = MediaWikiServices::getInstance()->getResourceLoader()->getMessageBlobStore();
-                       $blobStore->clear();
+                       foreach ( $this->clearStoreCallbacks as $callback ) {
+                               $callback();
+                       }
                }
        }
 
@@ -1085,5 +1103,4 @@ class LocalisationCache {
                $this->store = new LCStoreNull;
                $this->manualRecache = false;
        }
-
 }