Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
93.44% |
57 / 61 |
|
72.73% |
8 / 11 |
CRAP | |
0.00% |
0 / 1 |
ApiModuleManager | |
95.00% |
57 / 60 |
|
72.73% |
8 / 11 |
32 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
addModules | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
addModule | |
92.31% |
12 / 13 |
|
0.00% |
0 / 1 |
5.01 | |||
getModule | |
90.91% |
10 / 11 |
|
0.00% |
0 / 1 |
7.04 | |||
instantiateModule | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
1 | |||
getNames | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
4 | |||
getNamesWithClasses | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
4 | |||
getClassName | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
isDefined | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
3.33 | |||
getModuleGroup | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getGroups | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * Copyright © 2012 Yuri Astrakhan "<Firstname><Lastname>@gmail.com" |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License along |
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | * http://www.gnu.org/copyleft/gpl.html |
19 | * |
20 | * @file |
21 | * @since 1.21 |
22 | */ |
23 | |
24 | namespace MediaWiki\Api; |
25 | |
26 | use InvalidArgumentException; |
27 | use MediaWiki\Context\ContextSource; |
28 | use MediaWiki\MediaWikiServices; |
29 | use UnexpectedValueException; |
30 | use Wikimedia\ObjectFactory\ObjectFactory; |
31 | |
32 | /** |
33 | * This class holds a list of modules and handles instantiation |
34 | * |
35 | * @since 1.21 |
36 | * @ingroup API |
37 | */ |
38 | class ApiModuleManager extends ContextSource { |
39 | |
40 | /** |
41 | * @var ApiBase |
42 | */ |
43 | private $mParent; |
44 | /** |
45 | * @var ApiBase[] |
46 | */ |
47 | private $mInstances = []; |
48 | /** |
49 | * @var null[] |
50 | */ |
51 | private $mGroups = []; |
52 | /** |
53 | * @var array[] |
54 | */ |
55 | private $mModules = []; |
56 | /** |
57 | * @var ObjectFactory |
58 | */ |
59 | private $objectFactory; |
60 | |
61 | /** |
62 | * Construct new module manager |
63 | * |
64 | * @param ApiBase $parentModule Parent module instance will be used during instantiation |
65 | * @param ObjectFactory|null $objectFactory Object factory to use when instantiating modules |
66 | */ |
67 | public function __construct( ApiBase $parentModule, ?ObjectFactory $objectFactory = null ) { |
68 | $this->mParent = $parentModule; |
69 | $this->objectFactory = $objectFactory ?? MediaWikiServices::getInstance()->getObjectFactory(); |
70 | } |
71 | |
72 | /** |
73 | * Add a list of modules to the manager. Each module is described |
74 | * by an ObjectFactory spec. |
75 | * |
76 | * This simply calls `addModule()` for each module in `$modules`. |
77 | * |
78 | * @see ApiModuleManager::addModule() |
79 | * @param array $modules A map of ModuleName => ModuleSpec |
80 | * @param string $group Which group modules belong to (action,format,...) |
81 | */ |
82 | public function addModules( array $modules, $group ) { |
83 | foreach ( $modules as $name => $moduleSpec ) { |
84 | $this->addModule( $name, $group, $moduleSpec ); |
85 | } |
86 | } |
87 | |
88 | /** |
89 | * Add or overwrite a module in this ApiMain instance. Intended for use by extending |
90 | * classes who wish to add their own modules to their lexicon or override the |
91 | * behavior of inherent ones. |
92 | * |
93 | * ObjectFactory is used to instantiate the module when needed. The parent module |
94 | * (`$parentModule` from `__construct()`) and the `$name` are passed as extraArgs. |
95 | * |
96 | * @since 1.34, accepts an ObjectFactory spec as the third parameter. The old calling convention, |
97 | * passing a class name as parameter #3 and an optional factory callable as parameter #4, is |
98 | * deprecated. |
99 | * @param string $name The identifier for this module. |
100 | * @param string $group Name of the module group |
101 | * @param string|array $spec The ObjectFactory spec for instantiating the module, |
102 | * or a class name to instantiate. |
103 | * @param callable|null $factory Callback for instantiating the module (deprecated). |
104 | */ |
105 | public function addModule( string $name, string $group, $spec, $factory = null ) { |
106 | if ( is_string( $spec ) ) { |
107 | $spec = [ |
108 | 'class' => $spec |
109 | ]; |
110 | |
111 | if ( is_callable( $factory ) ) { |
112 | wfDeprecated( __METHOD__ . ' with $class and $factory', '1.34' ); |
113 | $spec['factory'] = $factory; |
114 | } |
115 | } elseif ( !is_array( $spec ) ) { |
116 | throw new InvalidArgumentException( '$spec must be a string or an array' ); |
117 | } elseif ( !isset( $spec['class'] ) ) { |
118 | throw new InvalidArgumentException( '$spec must define a class name' ); |
119 | } |
120 | |
121 | $this->mGroups[$group] = null; |
122 | $this->mModules[$name] = [ $group, $spec ]; |
123 | } |
124 | |
125 | /** |
126 | * Get module instance by name, or instantiate it if it does not exist |
127 | * |
128 | * @param string $moduleName |
129 | * @param string|null $group Optionally validate that the module is in a specific group |
130 | * @param bool $ignoreCache If true, force-creates a new instance and does not cache it |
131 | * |
132 | * @return ApiBase|null The new module instance, or null if failed |
133 | */ |
134 | public function getModule( $moduleName, $group = null, $ignoreCache = false ) { |
135 | if ( !isset( $this->mModules[$moduleName] ) ) { |
136 | return null; |
137 | } |
138 | |
139 | [ $moduleGroup, $spec ] = $this->mModules[$moduleName]; |
140 | |
141 | if ( $group !== null && $moduleGroup !== $group ) { |
142 | return null; |
143 | } |
144 | |
145 | if ( !$ignoreCache && isset( $this->mInstances[$moduleName] ) ) { |
146 | // already exists |
147 | return $this->mInstances[$moduleName]; |
148 | } else { |
149 | // new instance |
150 | $instance = $this->instantiateModule( $moduleName, $spec ); |
151 | |
152 | if ( !$ignoreCache ) { |
153 | // cache this instance in case it is needed later |
154 | $this->mInstances[$moduleName] = $instance; |
155 | } |
156 | |
157 | return $instance; |
158 | } |
159 | } |
160 | |
161 | /** |
162 | * Instantiate the module using the given class or factory function. |
163 | * |
164 | * @param string $name The identifier for this module. |
165 | * @param array $spec The ObjectFactory spec for instantiating the module. |
166 | * |
167 | * @throws UnexpectedValueException |
168 | * @return ApiBase |
169 | */ |
170 | private function instantiateModule( $name, $spec ) { |
171 | return $this->objectFactory->createObject( |
172 | $spec, |
173 | [ |
174 | 'extraArgs' => [ |
175 | $this->mParent, |
176 | $name |
177 | ], |
178 | 'assertClass' => $spec['class'] |
179 | ] |
180 | ); |
181 | } |
182 | |
183 | /** |
184 | * Get an array of modules in a specific group or all if no group is set. |
185 | * @param string|null $group Optional group filter |
186 | * @return string[] List of module names |
187 | */ |
188 | public function getNames( $group = null ) { |
189 | if ( $group === null ) { |
190 | return array_keys( $this->mModules ); |
191 | } |
192 | $result = []; |
193 | foreach ( $this->mModules as $name => $groupAndSpec ) { |
194 | if ( $groupAndSpec[0] === $group ) { |
195 | $result[] = $name; |
196 | } |
197 | } |
198 | |
199 | return $result; |
200 | } |
201 | |
202 | /** |
203 | * Create an array of (moduleName => moduleClass) for a specific group or for all. |
204 | * @param string|null $group Name of the group to get or null for all |
205 | * @return array Name=>class map |
206 | */ |
207 | public function getNamesWithClasses( $group = null ) { |
208 | $result = []; |
209 | foreach ( $this->mModules as $name => $groupAndSpec ) { |
210 | if ( $group === null || $groupAndSpec[0] === $group ) { |
211 | $result[$name] = $groupAndSpec[1]['class']; |
212 | } |
213 | } |
214 | |
215 | return $result; |
216 | } |
217 | |
218 | /** |
219 | * Returns the class name of the given module |
220 | * |
221 | * @param string $module Module name |
222 | * @return string|false class name or false if the module does not exist |
223 | * @since 1.24 |
224 | */ |
225 | public function getClassName( $module ) { |
226 | if ( isset( $this->mModules[$module] ) ) { |
227 | return $this->mModules[$module][1]['class']; |
228 | } |
229 | |
230 | return false; |
231 | } |
232 | |
233 | /** |
234 | * Returns true if the specific module is defined at all or in a specific group. |
235 | * @param string $moduleName |
236 | * @param string|null $group Group name to check against, or null to check all groups, |
237 | * @return bool True if defined |
238 | */ |
239 | public function isDefined( $moduleName, $group = null ) { |
240 | if ( isset( $this->mModules[$moduleName] ) ) { |
241 | return $group === null || $this->mModules[$moduleName][0] === $group; |
242 | } |
243 | |
244 | return false; |
245 | } |
246 | |
247 | /** |
248 | * Returns the group name for the given module |
249 | * @param string $moduleName |
250 | * @return string|null Group name or null if missing |
251 | */ |
252 | public function getModuleGroup( $moduleName ) { |
253 | if ( isset( $this->mModules[$moduleName] ) ) { |
254 | return $this->mModules[$moduleName][0]; |
255 | } |
256 | |
257 | return null; |
258 | } |
259 | |
260 | /** |
261 | * Get a list of groups this manager contains. |
262 | * @return array |
263 | */ |
264 | public function getGroups() { |
265 | return array_keys( $this->mGroups ); |
266 | } |
267 | } |
268 | |
269 | /** @deprecated class alias since 1.43 */ |
270 | class_alias( ApiModuleManager::class, 'ApiModuleManager' ); |