Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 37
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 / 37
0.00% covered (danger)
0.00%
0 / 7
210
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
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 / 16
0.00% covered (danger)
0.00%
0 / 1
20
 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 LoggerInterface $logger;
16    private TitleFactory $titleFactory;
17    private ?WikiPageConfigLoader $configLoader;
18    private ?Title $configTitle = null;
19    /**
20     * @var bool Hack to disable DB access in non-database tests.
21     */
22    private bool $isTestWithStorageDisabled;
23
24    /**
25     * @param LoggerInterface $logger
26     * @param TitleFactory $titleFactory
27     * @param WikiPageConfigLoader $configLoader
28     * @param bool $isTestWithStorageDisabled
29     */
30    public function __construct(
31        LoggerInterface $logger,
32        TitleFactory $titleFactory,
33        WikiPageConfigLoader $configLoader,
34        bool $isTestWithStorageDisabled
35    ) {
36        $this->logger = $logger;
37        $this->titleFactory = $titleFactory;
38        $this->configLoader = $configLoader;
39        $this->isTestWithStorageDisabled = $isTestWithStorageDisabled;
40    }
41
42    /**
43     * Helper to late-construct Title
44     *
45     * Config is initialized pretty early. This allows us to delay construction of
46     * Title (which may talk to the DB) until whenever config is first fetched,
47     * which should be much later, and probably after init sequence finished.
48     *
49     * @throws RuntimeException
50     * @return Title
51     */
52    private function getConfigTitle(): Title {
53        if ( $this->configTitle == null ) {
54            $configTitle = $this->titleFactory->makeTitleSafe(
55                NS_MEDIAWIKI,
56                'AutoModeratorConfig.json'
57            );
58            if (
59                $configTitle === null ||
60                !$configTitle->isSiteJsonConfigPage()
61            ) {
62                throw new RuntimeException( 'Invalid AutoModeratorWikiConfigPageTitle' );
63            }
64
65            $this->configTitle = $configTitle;
66        }
67
68        return $this->configTitle;
69    }
70
71    /**
72     * Helper function to fetch config data from wiki page
73     *
74     * This may sound expensive, but WikiPageConfigLoader is supposed
75     * to take care about caching.
76     *
77     * @param int $flags bit field, see IDBAccessObject::READ_XXX
78     *
79     * @throws ConfigException on an error
80     * @return array
81     */
82    private function getConfigData( int $flags = 0 ): array {
83        if ( $this->isTestWithStorageDisabled ) {
84            return [];
85        }
86        if ( !$this->getConfigTitle()->exists() ) {
87            // configLoader throws an exception for no-page case
88            return [];
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        return $res;
112    }
113
114    /**
115     * @inheritDoc
116     */
117    public function get( $name ) {
118        return $this->getWithFlags( $name );
119    }
120
121    /**
122     * @param string $name
123     * @param int $flags bit field, see IDBAccessObject::READ_XXX
124     * @return mixed Config value
125     */
126    public function getWithFlags( $name, int $flags = 0 ) {
127        $configData = $this->getConfigData( $flags );
128        if ( !array_key_exists( $name, $configData ) ) {
129            throw new ConfigException( 'Config key was not found in WikiPageConfig' );
130        }
131
132        return $configData[ $name ];
133    }
134
135    /**
136     * @inheritDoc
137     */
138    public function has( $name ) {
139        return $this->hasWithFlags( $name );
140    }
141
142    /**
143     * @param string $name
144     * @param int $flags bit field, see IDBAccessObject::READ_XXX
145     * @return bool
146     */
147    public function hasWithFlags( $name, int $flags = 0 ) {
148        return array_key_exists( $name, $this->getConfigData( $flags ) );
149    }
150}