Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 42
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
WikiPageConfig
0.00% covered (danger)
0.00%
0 / 42
0.00% covered (danger)
0.00%
0 / 7
272
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getConfigTitle
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 getConfigData
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
42
 get
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getWithFlags
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 has
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 hasWithFlags
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace AutoModerator\Config;
4
5use MediaWiki\Config\Config;
6use MediaWiki\Config\ConfigException;
7use MediaWiki\Title\Title;
8use MediaWiki\Title\TitleFactory;
9use Psr\Log\LoggerInterface;
10use RuntimeException;
11use StatusValue;
12
13class WikiPageConfig implements Config {
14
15    private ?Title $configTitle = null;
16
17    /**
18     * @param LoggerInterface $logger
19     * @param TitleFactory $titleFactory
20     * @param WikiPageConfigLoader $configLoader
21     * @param bool $isTestWithStorageDisabled Hack to disable DB access in non-database tests.
22     */
23    public function __construct(
24        private readonly LoggerInterface $logger,
25        private readonly TitleFactory $titleFactory,
26        private readonly WikiPageConfigLoader $configLoader,
27        private readonly bool $isTestWithStorageDisabled,
28    ) {
29    }
30
31    /**
32     * Helper to late-construct Title
33     *
34     * Config is initialized pretty early. This allows us to delay construction of
35     * Title (which may talk to the DB) until whenever config is first fetched,
36     * which should be much later, and probably after init sequence finished.
37     *
38     * @throws RuntimeException
39     * @return Title
40     */
41    private function getConfigTitle(): Title {
42        if ( $this->configTitle == null ) {
43            $configTitle = $this->titleFactory->makeTitleSafe(
44                NS_MEDIAWIKI,
45                'AutoModeratorConfig.json'
46            );
47            if (
48                $configTitle === null ||
49                !$configTitle->isSiteJsonConfigPage()
50            ) {
51                throw new RuntimeException( 'Invalid AutoModeratorWikiConfigPageTitle' );
52            }
53
54            $this->configTitle = $configTitle;
55        }
56
57        return $this->configTitle;
58    }
59
60    /**
61     * Helper function to fetch config data from wiki page
62     *
63     * This may sound expensive, but WikiPageConfigLoader is supposed
64     * to take care about caching.
65     *
66     * @param int $flags bit field, see IDBAccessObject::READ_XXX
67     *
68     * @throws ConfigException on an error
69     * @return array
70     */
71    private function getConfigData( int $flags = 0 ): array {
72        if ( $this->isTestWithStorageDisabled ) {
73            return [];
74        }
75        if ( !$this->getConfigTitle()->exists() ) {
76            // configLoader throws an exception for no-page case
77            return [];
78        }
79        // Load the multilingual configuration page if it exists
80        // in order to merge the multilingual configuration into the language agnostic configuration.
81        $multilingualConfigPage = $this->titleFactory->makeTitleSafe(
82            NS_MEDIAWIKI,
83            'AutoModeratorMultilingualConfig.json'
84        );
85        $multilingualResult = $multilingualConfigPage ? $this->configLoader->load(
86            $multilingualConfigPage, $flags ) : [];
87        if ( $multilingualResult instanceof StatusValue ) {
88            $multilingualResult = [];
89        }
90        $res = $this->configLoader->load( $this->getConfigTitle(), $flags );
91        if ( $res instanceof StatusValue ) {
92            // Loading config failed. This can happen in case of both a software error and
93            // an error made by an administrator (setting the JSON blob manually to something
94            // badly malformed, ie. set an array when a bool is expected). Log the error, and
95            // pretend there is nothing in the JSON blob.
96
97            $this->logger->error(
98                __METHOD__ . ' failed to load config from wiki: {error}',
99                [
100                    'error' => (string)$res,
101                    'impact' => 'Config stored in MediaWiki:AutoModeratorConfig.json ' .
102                        'is ignored, using sane fallbacks instead'
103                ]
104            );
105
106            // Throwing an exception would make _both_ get() and has() throw an exception,
107            //  while returning an empty array means has() finishes nicely (with a false),
108            //  while get still throws an exception (as calling get with has() returning false is unexpected).
109            return [];
110        }
111        // merge both language agnostic and multilingual configuration
112        return array_merge( $res, $multilingualResult );
113    }
114
115    /**
116     * @inheritDoc
117     */
118    public function get( $name ) {
119        return $this->getWithFlags( $name );
120    }
121
122    /**
123     * @param string $name
124     * @param int $flags bit field, see IDBAccessObject::READ_XXX
125     * @return mixed Config value
126     */
127    public function getWithFlags( string $name, int $flags = 0 ) {
128        $configData = $this->getConfigData( $flags );
129        if ( !array_key_exists( $name, $configData ) ) {
130            throw new ConfigException( 'Config key was not found in WikiPageConfig' );
131        }
132
133        return $configData[ $name ];
134    }
135
136    /**
137     * @inheritDoc
138     */
139    public function has( $name ): bool {
140        return $this->hasWithFlags( $name );
141    }
142
143    /**
144     * @param string $name
145     * @param int $flags bit field, see IDBAccessObject::READ_XXX
146     * @return bool
147     */
148    public function hasWithFlags( $name, int $flags = 0 ): bool {
149        return array_key_exists( $name, $this->getConfigData( $flags ) );
150    }
151}