Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
LeximorphFactory
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
3 / 3
5
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getManager
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 getProvider
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\Language;
8
9use MediaWiki\Config\ServiceOptions;
10use MediaWiki\MainConfigNames;
11use Psr\Log\LoggerInterface;
12use Wikimedia\Bcp47Code\Bcp47Code;
13use Wikimedia\Leximorph\Manager;
14use Wikimedia\Leximorph\Provider;
15use Wikimedia\ObjectCache\MapCacheLRU;
16
17/**
18 * Create and cache Manager and Provider instances.
19 *
20 * This is the centralized entry point for Leximorph services in MediaWiki.
21 *
22 * This caches instances in-process, because each instance
23 * has to load rule files, parse data, and prepare lookup maps.
24 *
25 * Example usage:
26 *
27 * @code
28 *    $factory = MediaWikiServices::getInstance()->getLeximorphFactory();
29 *    $bcp = new \Wikimedia\Bcp47Code\Bcp47CodeValue('en');
30 *    $manager = $factory->getManager( $bcp );
31 *    if ( $manager ) {
32 *        $pluralHandler = $manager->getPlural();
33 *    }
34 *
35 *    $provider = $factory->getProvider( $bcp );
36 *    if ( $provider ) {
37 *        $pluralRules = $provider->getPluralProvider();
38 *    }
39 * @endcode
40 *
41 * @since 1.45
42 * @author Doğu Abaris (abaris@null.net)
43 */
44class LeximorphFactory {
45    /** @internal Only public for service wiring use. */
46    public const CONSTRUCTOR_OPTIONS = [
47        MainConfigNames::UseLeximorph,
48    ];
49
50    /** @var int Maximum number of cached instances */
51    private const CACHE_SIZE = 100;
52
53    /** @var MapCacheLRU Cache for Manager instances */
54    private MapCacheLRU $managerCache;
55
56    /** @var MapCacheLRU Cache for Provider instances */
57    private MapCacheLRU $providerCache;
58
59    /** @var bool Whether UseLeximorph is enabled */
60    private bool $useLeximorph;
61
62    /**
63     * @since 1.45
64     * @param ServiceOptions $options Reads MainConfigNames::UseLeximorph
65     * @param LoggerInterface $logger Logger passed to created Manager/Provider instances.
66     */
67    public function __construct(
68        ServiceOptions $options,
69        private readonly LoggerInterface $logger,
70    ) {
71        $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
72        $this->useLeximorph = (bool)$options->get( MainConfigNames::UseLeximorph );
73
74        $this->managerCache = new MapCacheLRU( self::CACHE_SIZE );
75        $this->providerCache = new MapCacheLRU( self::CACHE_SIZE );
76    }
77
78    /**
79     * Get a Manager for the given language code.
80     *
81     * @param Bcp47Code $code BCP‑47 language code
82     * @return ?Manager Manager, or null if Leximorph is disabled
83     */
84    public function getManager( Bcp47Code $code ): ?Manager {
85        if ( !$this->useLeximorph ) {
86            return null;
87        }
88        $langCode = LanguageCode::bcp47ToInternal( $code );
89
90        return $this->managerCache->getWithSetCallback(
91            $langCode,
92            fn () => new Manager( $langCode, $this->logger )
93        );
94    }
95
96    /**
97     * Get a Provider for the given language code.
98     *
99     * @param Bcp47Code $code BCP‑47 language code
100     * @return ?Provider Provider, or null if Leximorph is disabled
101     */
102    public function getProvider( Bcp47Code $code ): ?Provider {
103        if ( !$this->useLeximorph ) {
104            return null;
105        }
106        $langCode = LanguageCode::bcp47ToInternal( $code );
107
108        return $this->providerCache->getWithSetCallback(
109            $langCode,
110            fn () => new Provider( $langCode, $this->logger )
111        );
112    }
113}