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