Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.44% covered (success)
93.44%
57 / 61
72.73% covered (warning)
72.73%
8 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiModuleManager
95.00% covered (success)
95.00%
57 / 60
72.73% covered (warning)
72.73%
8 / 11
32
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 addModules
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 addModule
92.31% covered (success)
92.31%
12 / 13
0.00% covered (danger)
0.00%
0 / 1
5.01
 getModule
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
7.04
 instantiateModule
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 getNames
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 getNamesWithClasses
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
 getClassName
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 isDefined
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
3.33
 getModuleGroup
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getGroups
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
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
24namespace MediaWiki\Api;
25
26use InvalidArgumentException;
27use MediaWiki\Context\ContextSource;
28use MediaWiki\MediaWikiServices;
29use UnexpectedValueException;
30use 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 */
38class 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 */
270class_alias( ApiModuleManager::class, 'ApiModuleManager' );