1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
<?php
/**
* Module class file.
*
* @link http://www.yiiframework.com/
* @copyright Copyright © 2008-2012 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* Module is the base class for module and application classes.
*
* Module mainly manages application components and sub-modules that belongs to a module.
*
* @property string $id The module ID.
* @property string $basePath The root directory of the module. Defaults to the directory containing the module class.
* @property Module|null $parentModule The parent module. Null if this module does not have a parent.
* @property array $modules The configuration of the currently installed modules (module ID => configuration).
* @property array $components The application components (indexed by their IDs).
* @property array $import List of aliases to be imported. This property is write-only.
* @property array $aliases List of aliases to be defined. This property is write-only.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
abstract class Module extends Component implements Initable
{
/**
* @var array custom module parameters (name => value).
*/
public $params = array();
/**
* @var array the IDs of the application components that should be preloaded when this module is created.
*/
public $preload = array();
private $_id;
private $_basePath;
private $_parentModule;
private $_modules = array();
private $_components = array();
/**
* Constructor.
* @param string $id the ID of this module
* @param Module $parent the parent module (if any)
*/
public function __construct($id, $parent = null)
{
$this->_id = $id;
$this->_parentModule = $parent;
}
/**
* Getter magic method.
* This method is overridden to support accessing application components
* like reading module properties.
* @param string $name application component or property name
* @return mixed the named property value
*/
public function __get($name)
{
if ($this->hasComponent($name)) {
return $this->getComponent($name);
} else {
return parent::__get($name);
}
}
/**
* Checks if a property value is null.
* This method overrides the parent implementation by checking
* if the named application component is loaded.
* @param string $name the property name or the event name
* @return boolean whether the property value is null
*/
public function __isset($name)
{
if ($this->hasComponent($name)) {
return $this->getComponent($name) !== null;
} else {
return parent::__isset($name);
}
}
/**
* Initializes the module.
* This method is called after the module is created and initialized with property values
* given in configuration. The default implement will create a path alias using the module [[id]]
* and then call [[preloadComponents()]] to load components that are declared in [[preload]].
*/
public function init()
{
\Yii::setAlias('@' . $this->getId(), $this->getBasePath());
$this->preloadComponents();
}
/**
* Returns the module ID.
* @return string the module ID.
*/
public function getId()
{
return $this->_id;
}
/**
* Sets the module ID.
* @param string $id the module ID
*/
public function setId($id)
{
$this->_id = $id;
}
/**
* Returns the root directory of the module.
* @return string the root directory of the module. Defaults to the directory containing the module class file.
*/
public function getBasePath()
{
if ($this->_basePath === null) {
$class = new \ReflectionClass($this);
$this->_basePath = dirname($class->getFileName());
}
return $this->_basePath;
}
/**
* Sets the root directory of the module.
* This method can only be invoked at the beginning of the constructor.
* @param string $path the root directory of the module.
* @throws Exception if the directory does not exist.
*/
public function setBasePath($path)
{
if (($p = realpath($path)) === false || !is_dir($p)) {
throw new Exception('Invalid base path: ' . $path);
} else {
$this->_basePath = $p;
}
}
/**
* Imports the specified path aliases.
* This method is provided so that you can import a set of path aliases when configuring a module.
* The path aliases will be imported by calling [[\Yii::import()]].
* @param array $aliases list of path aliases to be imported
*/
public function setImport($aliases)
{
foreach ($aliases as $alias) {
\Yii::import($alias);
}
}
/**
* Defines path aliases.
* This method calls [[\Yii::setAlias()]] to register the path aliases.
* This method is provided so that you can define path aliases when configuring a module.
* @param array $aliases list of path aliases to be defined. The array keys are alias names
* (must start with '@') and the array values are the corresponding paths or aliases.
* For example,
*
* ~~~
* array(
* '@models' => '@app/models', // an existing alias
* '@backend' => __DIR__ . '/../backend', // a directory
* )
* ~~~
*/
public function setAliases($aliases)
{
foreach ($aliases as $name => $alias) {
\Yii::setAlias($name, $alias);
}
}
/**
* Returns the parent module.
* @return Module|null the parent module. Null is returned if this module does not have a parent.
*/
public function getParentModule()
{
return $this->_parentModule;
}
/**
* Checks whether the named module exists.
* @param string $id module ID
* @return boolean whether the named module exists. Both loaded and unloaded modules
* are considered.
*/
public function hasModule($id)
{
return isset($this->_modules[$id]);
}
/**
* Retrieves the named module.
* @param string $id module ID (case-sensitive)
* @param boolean $load whether to load the module if it is not yet loaded.
* @return Module|null the module instance, null if the module
* does not exist.
* @see hasModule()
*/
public function getModule($id, $load = true)
{
if (isset($this->_modules[$id])) {
if ($this->_modules[$id] instanceof Module) {
return $this->_modules[$id];
} elseif ($load) {
\Yii::trace("Loading \"$id\" module", __CLASS__);
return $this->_modules[$id] = \Yii::createObject($this->_modules[$id], $id, $this);
}
}
return null;
}
/**
* Adds a sub-module to this module.
* @param string $id module ID
* @param Module|array|null $module the sub-module to be added to this module. This can
* be one of the followings:
*
* - a [[Module]] object
* - a configuration array: when [[getModule()]] is called initially, the array
* will be used to instantiate the sub-module
* - null: the named sub-module will be removed from this module
*/
public function setModule($id, $module)
{
if ($module === null) {
unset($this->_modules[$id]);
} else {
$this->_modules[$id] = $module;
}
}
/**
* Returns the sub-modules in this module.
* @param boolean $loadedOnly whether to return the loaded sub-modules only. If this is set false,
* then all sub-modules registered in this module will be returned, whether they are loaded or not.
* Loaded modules will be returned as objects, while unloaded modules as configuration arrays.
* @return array the modules (indexed by their IDs)
*/
public function getModules($loadedOnly = false)
{
if ($loadedOnly) {
$modules = array();
foreach ($this->_modules as $module) {
if ($module instanceof Module) {
$modules[] = $module;
}
}
return $modules;
} else {
return $this->_modules;
}
}
/**
* Registers sub-modules in the current module.
*
* Each sub-module should be specified as a name-value pair, where
* name refers to the ID of the module and value the module or a configuration
* array that can be used to create the module. In the latter case, [[\Yii::createObject()]]
* will be used to create the module.
*
* If a new sub-module has the same ID as an existing one, the existing one will be overwritten silently.
*
* The following is an example for registering two sub-modules:
*
* ~~~
* array(
* 'comment' => array(
* 'class' => 'app\modules\CommentModule',
* 'connectionID' => 'db',
* ),
* 'booking' => array(
* 'class' => 'app\modules\BookingModule',
* ),
* )
* ~~~
*
* @param array $modules modules (id => module configuration or instances)
*/
public function setModules($modules)
{
foreach ($modules as $id => $module) {
$this->_modules[$id] = $module;
}
}
/**
* Checks whether the named component exists.
* @param string $id application component ID
* @return boolean whether the named application component exists. Both loaded and unloaded components
* are considered.
*/
public function hasComponent($id)
{
return isset($this->_components[$id]);
}
/**
* Retrieves the named application component.
* @param string $id application component ID (case-sensitive)
* @param boolean $load whether to load the component if it is not yet loaded.
* @return ApplicationComponent|null the application component instance, null if the application component
* does not exist.
* @see hasComponent()
*/
public function getComponent($id, $load = true)
{
if (isset($this->_components[$id])) {
if ($this->_components[$id] instanceof ApplicationComponent) {
return $this->_components[$id];
} elseif ($load) {
\Yii::trace("Loading \"$id\" application component", __CLASS__);
return $this->_components[$id] = \Yii::createObject($this->_components[$id]);
}
}
return null;
}
/**
* Registers an application component in this module.
* @param string $id component ID
* @param ApplicationComponent|array|null $component the component to be added to the module. This can
* be one of the followings:
*
* - an [[ApplicationComponent]] object
* - a configuration array: when [[getComponent()]] is called initially for this component, the array
* will be used to instantiate the component
* - null: the named component will be removed from the module
*/
public function setComponent($id, $component)
{
if ($component === null) {
unset($this->_components[$id]);
} else {
$this->_components[$id] = $component;
}
}
/**
* Returns the application components.
* @param boolean $loadedOnly whether to return the loaded components only. If this is set false,
* then all components specified in the configuration will be returned, whether they are loaded or not.
* Loaded components will be returned as objects, while unloaded components as configuration arrays.
* @return array the application components (indexed by their IDs)
*/
public function getComponents($loadedOnly = false)
{
if ($loadedOnly) {
$components = array();
foreach ($this->_components as $component) {
if ($component instanceof ApplicationComponent) {
$components[] = $component;
}
}
return $components;
} else {
return $this->_components;
}
}
/**
* Registers a set of application components in this module.
*
* Each application component should be specified as a name-value pair, where
* name refers to the ID of the component and value the component or a configuration
* array that can be used to create the component. In the latter case, [[\Yii::createObject()]]
* will be used to create the component.
*
* If a new component has the same ID as an existing one, the existing one will be overwritten silently.
*
* The following is an example for setting two components:
*
* ~~~
* array(
* 'db' => array(
* 'class' => 'yii\db\dao\Connection',
* 'dsn' => 'sqlite:path/to/file.db',
* ),
* 'cache' => array(
* 'class' => 'yii\caching\DbCache',
* 'connectionID' => 'db',
* ),
* )
* ~~~
*
* @param array $components application components (id => component configuration or instance)
*/
public function setComponents($components)
{
foreach ($components as $id => $component) {
$this->_components[$id] = $component;
}
}
/**
* Loads application components that are declared in [[preload]].
*/
public function preloadComponents()
{
foreach ($this->preload as $id) {
$this->getComponent($id);
}
}
}