Module.php 19.8 KB
Newer Older
w  
Qiang Xue committed
1 2 3 4 5
<?php
/**
 * Module class file.
 *
 * @link http://www.yiiframework.com/
Qiang Xue committed
6
 * @copyright Copyright &copy; 2008 Yii Software LLC
w  
Qiang Xue committed
7 8 9 10 11
 * @license http://www.yiiframework.com/license/
 */

namespace yii\base;

Qiang Xue committed
12 13
use Yii;
use yii\util\StringHelper;
Qiang Xue committed
14 15
use yii\util\FileHelper;

w  
Qiang Xue committed
16 17 18
/**
 * Module is the base class for module and application classes.
 *
Qiang Xue committed
19 20 21 22 23 24 25
 * A module represents a sub-application which contains MVC elements by itself, such as
 * models, views, controllers, etc.
 *
 * A module may consist of [[modules|sub-modules]].
 *
 * [[components|Components]] may be registered with the module so that they are globally
 * accessible within the module.
w  
Qiang Xue committed
26
 *
Qiang Xue committed
27 28
 * @property string $uniqueId An ID that uniquely identifies this module among all modules within
 * the current application.
Qiang Xue committed
29
 * @property string $basePath The root directory of the module. Defaults to the directory containing the module class.
Qiang Xue committed
30 31 32
 * @property string $controllerPath The directory containing the controller classes. Defaults to "[[basePath]]/controllers".
 * @property string $viewPath The directory containing the view files within this module. Defaults to "[[basePath]]/views".
 * @property string $layoutPath The directory containing the layout view files within this module. Defaults to "[[viewPath]]/layouts".
Qiang Xue committed
33
 * @property array $modules The configuration of the currently installed modules (module ID => configuration).
Qiang Xue committed
34
 * @property array $components The components (indexed by their IDs) registered within this module.
Qiang Xue committed
35 36 37
 * @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.
 *
w  
Qiang Xue committed
38 39 40
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
Qiang Xue committed
41
abstract class Module extends Component
w  
Qiang Xue committed
42
{
m  
Qiang Xue committed
43 44 45 46
	/**
	 * @var array custom module parameters (name => value).
	 */
	public $params = array();
w  
Qiang Xue committed
47
	/**
Qiang Xue committed
48
	 * @var array the IDs of the components that should be preloaded when this module is created.
w  
Qiang Xue committed
49 50
	 */
	public $preload = array();
Qiang Xue committed
51
	/**
Qiang Xue committed
52
	 * @var string an ID that uniquely identifies this module among other modules which have the same [[module|parent]].
Qiang Xue committed
53 54 55 56 57 58
	 */
	public $id;
	/**
	 * @var Module the parent module of this module. Null if this module does not have a parent.
	 */
	public $module;
Qiang Xue committed
59
	/**
Qiang Xue committed
60
	 * @var string|boolean the layout that should be applied for views within this module. This refers to a view name
Qiang Xue committed
61 62 63 64
	 * relative to [[layoutPath]]. If this is not set, it means the layout value of the [[module|parent module]]
	 * will be taken. If this is false, layout will be disabled within this module.
	 */
	public $layout;
Qiang Xue committed
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
	/**
	 * @var array mapping from controller ID to controller configurations.
	 * Each name-value pair specifies the configuration of a single controller.
	 * A controller configuration can be either a string or an array.
	 * If the former, the string should be the class name or path alias of the controller.
	 * If the latter, the array must contain a 'class' element which specifies
	 * the controller's class name or path alias, and the rest of the name-value pairs
	 * in the array are used to initialize the corresponding controller properties. For example,
	 *
	 * ~~~
	 * array(
	 *   'account' => '@application/controllers/UserController',
	 *   'article' => array(
	 *      'class' => '@application/controllers/PostController',
	 *      'pageTitle' => 'something new',
	 *   ),
	 * )
	 * ~~~
	 */
Qiang Xue committed
84 85 86 87 88
	public $controllerMap = array();
	/**
	 * @var string the namespace that controller classes are in. Default is to use global namespace.
	 */
	public $controllerNamespace;
Qiang Xue committed
89 90 91 92 93 94 95 96 97 98 99
	/**
	 * @return string the default route of this module. Defaults to 'default'.
	 * The route may consist of child module ID, controller ID, and/or action ID.
	 * For example, `help`, `post/create`, `admin/post/create`.
	 * If action ID is not given, it will take the default value as specified in
	 * [[Controller::defaultAction]].
	 */
	public $defaultRoute = 'default';
	/**
	 * @var string the root directory of the module.
	 */
Qiang Xue committed
100
	private $_basePath;
Qiang Xue committed
101
	/**
Qiang Xue committed
102
	 * @var string the root directory that contains view files for this module
Qiang Xue committed
103
	 */
Qiang Xue committed
104
	private $_viewPath;
Qiang Xue committed
105
	/**
Qiang Xue committed
106
	 * @var string the root directory that contains layout view files for this module.
Qiang Xue committed
107
	 */
Qiang Xue committed
108
	private $_layoutPath;
Qiang Xue committed
109 110 111
	/**
	 * @var string the directory containing controller classes in the module.
	 */
Qiang Xue committed
112
	private $_controllerPath;
Qiang Xue committed
113 114 115
	/**
	 * @var array child modules of this module
	 */
Qiang Xue committed
116
	private $_modules = array();
Qiang Xue committed
117
	/**
Qiang Xue committed
118
	 * @var array components registered under this module
Qiang Xue committed
119
	 */
Qiang Xue committed
120
	private $_components = array();
w  
Qiang Xue committed
121 122 123 124

	/**
	 * Constructor.
	 * @param string $id the ID of this module
Qiang Xue committed
125
	 * @param Module $parent the parent module (if any)
Qiang Xue committed
126
	 * @param array $config name-value pairs that will be used to initialize the object properties
w  
Qiang Xue committed
127
	 */
Qiang Xue committed
128
	public function __construct($id, $parent = null, $config = array())
w  
Qiang Xue committed
129
	{
Qiang Xue committed
130 131
		$this->id = $id;
		$this->module = $parent;
Qiang Xue committed
132
		parent::__construct($config);
w  
Qiang Xue committed
133 134 135 136
	}

	/**
	 * Getter magic method.
Qiang Xue committed
137
	 * This method is overridden to support accessing components
w  
Qiang Xue committed
138
	 * like reading module properties.
Qiang Xue committed
139
	 * @param string $name component or property name
w  
Qiang Xue committed
140 141 142 143
	 * @return mixed the named property value
	 */
	public function __get($name)
	{
w  
Qiang Xue committed
144
		if ($this->hasComponent($name)) {
w  
Qiang Xue committed
145
			return $this->getComponent($name);
Qiang Xue committed
146
		} else {
w  
Qiang Xue committed
147
			return parent::__get($name);
w  
Qiang Xue committed
148
		}
w  
Qiang Xue committed
149 150 151 152 153
	}

	/**
	 * Checks if a property value is null.
	 * This method overrides the parent implementation by checking
Qiang Xue committed
154
	 * if the named component is loaded.
w  
Qiang Xue committed
155 156 157 158 159
	 * @param string $name the property name or the event name
	 * @return boolean whether the property value is null
	 */
	public function __isset($name)
	{
w  
Qiang Xue committed
160
		if ($this->hasComponent($name)) {
w  
Qiang Xue committed
161
			return $this->getComponent($name) !== null;
Qiang Xue committed
162
		} else {
w  
Qiang Xue committed
163
			return parent::__isset($name);
w  
Qiang Xue committed
164 165 166 167
		}
	}

	/**
Qiang Xue committed
168 169
	 * Initializes the module.
	 * This method is called after the module is created and initialized with property values
.  
Qiang Xue committed
170 171
	 * 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]].
w  
Qiang Xue committed
172
	 */
Qiang Xue committed
173
	public function init()
w  
Qiang Xue committed
174
	{
Qiang Xue committed
175
		Yii::setAlias('@' . $this->id, $this->getBasePath());
Qiang Xue committed
176
		$this->preloadComponents();
w  
Qiang Xue committed
177 178 179
	}

	/**
Qiang Xue committed
180
	 * Returns an ID that uniquely identifies this module among all modules within the current application.
Qiang Xue committed
181
	 * Note that if the module is an application, an empty string will be returned.
Qiang Xue committed
182
	 * @return string the unique ID of the module.
w  
Qiang Xue committed
183
	 */
Qiang Xue committed
184
	public function getUniqueId()
w  
Qiang Xue committed
185
	{
Qiang Xue committed
186 187 188 189
		if ($this instanceof Application) {
			return '';
		} elseif ($this->module) {
			return $this->module->getUniqueId() . '/' . $this->id;
Qiang Xue committed
190 191 192
		} else {
			return $this->id;
		}
w  
Qiang Xue committed
193 194 195 196
	}

	/**
	 * Returns the root directory of the module.
Qiang Xue committed
197 198
	 * It defaults to the directory containing the module class file.
	 * @return string the root directory of the module.
w  
Qiang Xue committed
199 200 201
	 */
	public function getBasePath()
	{
m  
Qiang Xue committed
202
		if ($this->_basePath === null) {
Qiang Xue committed
203
			$class = new \ReflectionClass($this);
w  
Qiang Xue committed
204 205 206 207 208 209 210 211
			$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.
Qiang Xue committed
212
	 * @param string $path the root directory of the module. This can be either a directory name or a path alias.
m  
Qiang Xue committed
213
	 * @throws Exception if the directory does not exist.
w  
Qiang Xue committed
214 215 216
	 */
	public function setBasePath($path)
	{
Qiang Xue committed
217
		$this->_basePath = FileHelper::ensureDirectory($path);
w  
Qiang Xue committed
218 219
	}

Qiang Xue committed
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
	/**
	 * Returns the directory that contains the controller classes.
	 * Defaults to "[[basePath]]/controllers".
	 * @return string the directory that contains the controller classes.
	 */
	public function getControllerPath()
	{
		if ($this->_controllerPath !== null) {
			return $this->_controllerPath;
		} else {
			return $this->_controllerPath = $this->getBasePath() . DIRECTORY_SEPARATOR . 'controllers';
		}
	}

	/**
	 * Sets the directory that contains the controller classes.
	 * @param string $path the directory that contains the controller classes.
	 * This can be either a directory name or a path alias.
	 * @throws Exception if the directory is invalid
	 */
	public function setControllerPath($path)
	{
Qiang Xue committed
242
		$this->_controllerPath = FileHelper::ensureDirectory($path);
Qiang Xue committed
243 244
	}

Qiang Xue committed
245
	/**
Qiang Xue committed
246 247
	 * Returns the directory that contains the view files for this module.
	 * @return string the root directory of view files. Defaults to "[[basePath]]/view".
Qiang Xue committed
248 249 250 251 252 253 254 255 256 257 258
	 */
	public function getViewPath()
	{
		if ($this->_viewPath !== null) {
			return $this->_viewPath;
		} else {
			return $this->_viewPath = $this->getBasePath() . DIRECTORY_SEPARATOR . 'views';
		}
	}

	/**
Qiang Xue committed
259
	 * Sets the directory that contains the view files.
Qiang Xue committed
260
	 * @param string $path the root directory of view files.
Qiang Xue committed
261
	 * @throws Exception if the directory is invalid
Qiang Xue committed
262 263 264
	 */
	public function setViewPath($path)
	{
Qiang Xue committed
265
		$this->_viewPath = FileHelper::ensureDirectory($path);
Qiang Xue committed
266 267 268
	}

	/**
Qiang Xue committed
269 270
	 * Returns the directory that contains layout view files for this module.
	 * @return string the root directory of layout files. Defaults to "[[viewPath]]/layouts".
Qiang Xue committed
271 272 273 274 275 276 277 278 279 280 281
	 */
	public function getLayoutPath()
	{
		if ($this->_layoutPath !== null) {
			return $this->_layoutPath;
		} else {
			return $this->_layoutPath = $this->getViewPath() . DIRECTORY_SEPARATOR . 'layouts';
		}
	}

	/**
Qiang Xue committed
282
	 * Sets the directory that contains the layout files.
Qiang Xue committed
283
	 * @param string $path the root directory of layout files.
Qiang Xue committed
284
	 * @throws Exception if the directory is invalid
Qiang Xue committed
285 286 287
	 */
	public function setLayoutPath($path)
	{
Qiang Xue committed
288
		$this->_layoutPath = FileHelper::ensureDirectory($path);
Qiang Xue committed
289 290
	}

w  
Qiang Xue committed
291
	/**
m  
Qiang Xue committed
292
	 * Imports the specified path aliases.
Qiang Xue committed
293
	 * This method is provided so that you can import a set of path aliases when configuring a module.
Qiang Xue committed
294
	 * The path aliases will be imported by calling [[Yii::import()]].
m  
Qiang Xue committed
295
	 * @param array $aliases list of path aliases to be imported
w  
Qiang Xue committed
296 297 298
	 */
	public function setImport($aliases)
	{
m  
Qiang Xue committed
299
		foreach ($aliases as $alias) {
Qiang Xue committed
300
			Yii::import($alias);
m  
Qiang Xue committed
301
		}
w  
Qiang Xue committed
302 303 304
	}

	/**
w  
Qiang Xue committed
305
	 * Defines path aliases.
Qiang Xue committed
306
	 * This method calls [[Yii::setAlias()]] to register the path aliases.
Qiang Xue committed
307
	 * This method is provided so that you can define path aliases when configuring a module.
w  
Qiang Xue committed
308
	 * @param array $aliases list of path aliases to be defined. The array keys are alias names
Qiang Xue committed
309
	 * (must start with '@') and the array values are the corresponding paths or aliases.
w  
Qiang Xue committed
310
	 * For example,
w  
Qiang Xue committed
311 312
	 *
	 * ~~~
w  
Qiang Xue committed
313
	 * array(
Qiang Xue committed
314
	 *	'@models' => '@application/models', // an existing alias
Qiang Xue committed
315
	 *	'@backend' => __DIR__ . '/../backend',  // a directory
w  
Qiang Xue committed
316
	 * )
w  
Qiang Xue committed
317
	 * ~~~
w  
Qiang Xue committed
318
	 */
w  
Qiang Xue committed
319
	public function setAliases($aliases)
w  
Qiang Xue committed
320
	{
w  
Qiang Xue committed
321
		foreach ($aliases as $name => $alias) {
Qiang Xue committed
322
			Yii::setAlias($name, $alias);
w  
Qiang Xue committed
323 324 325 326
		}
	}

	/**
Qiang Xue committed
327 328 329 330
	 * 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.
w  
Qiang Xue committed
331
	 */
Qiang Xue committed
332
	public function hasModule($id)
w  
Qiang Xue committed
333
	{
Qiang Xue committed
334 335 336 337 338 339
		return isset($this->_modules[$id]);
	}

	/**
	 * Retrieves the named module.
	 * @param string $id module ID (case-sensitive)
340
	 * @param boolean $load whether to load the module if it is not yet loaded.
Qiang Xue committed
341 342 343 344
	 * @return Module|null the module instance, null if the module
	 * does not exist.
	 * @see hasModule()
	 */
345
	public function getModule($id, $load = true)
Qiang Xue committed
346 347 348 349
	{
		if (isset($this->_modules[$id])) {
			if ($this->_modules[$id] instanceof Module) {
				return $this->_modules[$id];
350
			} elseif ($load) {
Qiang Xue committed
351 352
				Yii::trace("Loading module: $id", __CLASS__);
				return $this->_modules[$id] = Yii::createObject($this->_modules[$id], $id, $this);
w  
Qiang Xue committed
353 354
			}
		}
Qiang Xue committed
355
		return null;
w  
Qiang Xue committed
356 357 358
	}

	/**
Qiang Xue committed
359 360 361 362 363 364 365 366 367
	 * 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
w  
Qiang Xue committed
368
	 */
Qiang Xue committed
369
	public function setModule($id, $module)
w  
Qiang Xue committed
370
	{
Qiang Xue committed
371 372 373 374 375
		if ($module === null) {
			unset($this->_modules[$id]);
		} else {
			$this->_modules[$id] = $module;
		}
w  
Qiang Xue committed
376 377 378
	}

	/**
Qiang Xue committed
379 380 381 382 383
	 * 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)
w  
Qiang Xue committed
384
	 */
Qiang Xue committed
385
	public function getModules($loadedOnly = false)
w  
Qiang Xue committed
386
	{
Qiang Xue committed
387 388 389 390 391 392 393 394 395 396 397
		if ($loadedOnly) {
			$modules = array();
			foreach ($this->_modules as $module) {
				if ($module instanceof Module) {
					$modules[] = $module;
				}
			}
			return $modules;
		} else {
			return $this->_modules;
		}
w  
Qiang Xue committed
398 399 400
	}

	/**
Qiang Xue committed
401
	 * Registers sub-modules in the current module.
w  
Qiang Xue committed
402
	 *
Qiang Xue committed
403 404
	 * 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
Qiang Xue committed
405
	 * array that can be used to create the module. In the latter case, [[Yii::createObject()]]
Qiang Xue committed
406
	 * will be used to create the module.
w  
Qiang Xue committed
407
	 *
Qiang Xue committed
408
	 * If a new sub-module has the same ID as an existing one, the existing one will be overwritten silently.
w  
Qiang Xue committed
409
	 *
Qiang Xue committed
410
	 * The following is an example for registering two sub-modules:
w  
Qiang Xue committed
411
	 *
Qiang Xue committed
412 413 414 415 416 417 418 419 420 421 422
	 * ~~~
	 * array(
	 *     'comment' => array(
	 *         'class' => 'app\modules\CommentModule',
	 *         'connectionID' => 'db',
	 *     ),
	 *     'booking' => array(
	 *         'class' => 'app\modules\BookingModule',
	 *     ),
	 * )
	 * ~~~
w  
Qiang Xue committed
423
	 *
Qiang Xue committed
424
	 * @param array $modules modules (id => module configuration or instances)
w  
Qiang Xue committed
425 426 427
	 */
	public function setModules($modules)
	{
Qiang Xue committed
428 429
		foreach ($modules as $id => $module) {
			$this->_modules[$id] = $module;
w  
Qiang Xue committed
430 431
		}
	}
432

w  
Qiang Xue committed
433 434
	/**
	 * Checks whether the named component exists.
Qiang Xue committed
435 436
	 * @param string $id component ID
	 * @return boolean whether the named component exists. Both loaded and unloaded components
Qiang Xue committed
437
	 * are considered.
w  
Qiang Xue committed
438 439 440
	 */
	public function hasComponent($id)
	{
Qiang Xue committed
441
		return isset($this->_components[$id]);
w  
Qiang Xue committed
442 443 444
	}

	/**
Qiang Xue committed
445 446
	 * Retrieves the named component.
	 * @param string $id component ID (case-sensitive)
447
	 * @param boolean $load whether to load the component if it is not yet loaded.
Qiang Xue committed
448
	 * @return Component|null the component instance, null if the component does not exist.
Qiang Xue committed
449
	 * @see hasComponent()
w  
Qiang Xue committed
450
	 */
451
	public function getComponent($id, $load = true)
w  
Qiang Xue committed
452
	{
Qiang Xue committed
453
		if (isset($this->_components[$id])) {
454
			if ($this->_components[$id] instanceof Component) {
Qiang Xue committed
455
				return $this->_components[$id];
456
			} elseif ($load) {
Qiang Xue committed
457 458
				Yii::trace("Loading component: $id", __CLASS__);
				return $this->_components[$id] = Yii::createObject($this->_components[$id]);
w  
Qiang Xue committed
459 460
			}
		}
Qiang Xue committed
461
		return null;
w  
Qiang Xue committed
462 463 464
	}

	/**
Qiang Xue committed
465
	 * Registers a component with this module.
w  
Qiang Xue committed
466
	 * @param string $id component ID
Qiang Xue committed
467
	 * @param Component|array|null $component the component to be registered with the module. This can
Qiang Xue committed
468 469
	 * be one of the followings:
	 *
470
	 * - a [[Component]] object
Qiang Xue committed
471
	 * - a configuration array: when [[getComponent()]] is called initially for this component, the array
Qiang Xue committed
472
	 *   will be used to instantiate the component via [[Yii::createObject()]].
Qiang Xue committed
473
	 * - null: the named component will be removed from the module
w  
Qiang Xue committed
474 475 476
	 */
	public function setComponent($id, $component)
	{
Qiang Xue committed
477
		if ($component === null) {
w  
Qiang Xue committed
478
			unset($this->_components[$id]);
Qiang Xue committed
479
		} else {
w  
Qiang Xue committed
480 481 482 483 484
			$this->_components[$id] = $component;
		}
	}

	/**
Qiang Xue committed
485
	 * Returns the registered components.
w  
Qiang Xue committed
486 487 488
	 * @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.
Qiang Xue committed
489
	 * @return array the components (indexed by their IDs)
w  
Qiang Xue committed
490
	 */
Qiang Xue committed
491
	public function getComponents($loadedOnly = false)
w  
Qiang Xue committed
492
	{
Qiang Xue committed
493
		if ($loadedOnly) {
Qiang Xue committed
494 495
			$components = array();
			foreach ($this->_components as $component) {
496
				if ($component instanceof Component) {
Qiang Xue committed
497 498 499 500
					$components[] = $component;
				}
			}
			return $components;
Qiang Xue committed
501
		} else {
Qiang Xue committed
502
			return $this->_components;
Qiang Xue committed
503
		}
w  
Qiang Xue committed
504 505 506
	}

	/**
Qiang Xue committed
507
	 * Registers a set of components in this module.
w  
Qiang Xue committed
508
	 *
Qiang Xue committed
509
	 * Each component should be specified as a name-value pair, where
Qiang Xue committed
510
	 * name refers to the ID of the component and value the component or a configuration
Qiang Xue committed
511
	 * array that can be used to create the component. In the latter case, [[Yii::createObject()]]
Qiang Xue committed
512
	 * will be used to create the component.
w  
Qiang Xue committed
513
	 *
Qiang Xue committed
514
	 * If a new component has the same ID as an existing one, the existing one will be overwritten silently.
w  
Qiang Xue committed
515
	 *
Qiang Xue committed
516 517 518
	 * The following is an example for setting two components:
	 *
	 * ~~~
w  
Qiang Xue committed
519
	 * array(
Qiang Xue committed
520
	 *     'db' => array(
Qiang Xue committed
521
	 *         'class' => 'yii\db\Connection',
Qiang Xue committed
522 523 524 525 526 527
	 *         'dsn' => 'sqlite:path/to/file.db',
	 *     ),
	 *     'cache' => array(
	 *         'class' => 'yii\caching\DbCache',
	 *         'connectionID' => 'db',
	 *     ),
w  
Qiang Xue committed
528
	 * )
Qiang Xue committed
529
	 * ~~~
w  
Qiang Xue committed
530
	 *
Qiang Xue committed
531
	 * @param array $components components (id => component configuration or instance)
w  
Qiang Xue committed
532
	 */
Qiang Xue committed
533
	public function setComponents($components)
w  
Qiang Xue committed
534
	{
Qiang Xue committed
535 536
		foreach ($components as $id => $component) {
			$this->_components[$id] = $component;
w  
Qiang Xue committed
537 538 539 540
		}
	}

	/**
Qiang Xue committed
541
	 * Loads components that are declared in [[preload]].
w  
Qiang Xue committed
542
	 */
w  
Qiang Xue committed
543
	public function preloadComponents()
w  
Qiang Xue committed
544
	{
Qiang Xue committed
545
		foreach ($this->preload as $id) {
w  
Qiang Xue committed
546
			$this->getComponent($id);
Qiang Xue committed
547
		}
w  
Qiang Xue committed
548
	}
Qiang Xue committed
549 550

	/**
Qiang Xue committed
551 552 553 554
	 * Runs a controller action specified by a route.
	 * This method parses the specified route and creates the corresponding child module(s), controller and action
	 * instances. It then calls [[Controller::runAction()]] to run the action with the given parameters.
	 * If the route is empty, the method will use [[defaultRoute]].
Qiang Xue committed
555 556
	 * @param string $route the route that specifies the action.
	 * @param array $params the parameters to be passed to the action
Qiang Xue committed
557 558
	 * @return integer the status code returned by the action execution. 0 means normal, and other values mean abnormal.
	 * @throws InvalidRouteException if the requested route cannot be resolved into an action successfully
Qiang Xue committed
559 560 561
	 */
	public function runAction($route, $params = array())
	{
Qiang Xue committed
562 563 564 565
		$result = $this->createController($route);
		if (is_array($result)) {
			/** @var $controller Controller */
			list($controller, $actionID) = $result;
Qiang Xue committed
566 567
			$oldController = Yii::$application->controller;
			Yii::$application->controller = $controller;
Qiang Xue committed
568
			$status = $controller->runAction($actionID, $params);
Qiang Xue committed
569 570 571
			Yii::$application->controller = $oldController;
			return $status;
		} else {
Qiang Xue committed
572
			throw new InvalidRouteException('Unable to resolve the request: ' . trim($this->getUniqueId() . '/' . $route, '/'));
Qiang Xue committed
573
		}
Qiang Xue committed
574 575 576 577 578
	}

	/**
	 * Creates a controller instance based on the controller ID.
	 *
Qiang Xue committed
579
	 * The controller is created within this module. The method first attempts to
Qiang Xue committed
580 581 582 583
	 * create the controller based on the [[controllerMap]] of the module. If not available,
	 * it will look for the controller class under the [[controllerPath]] and create an
	 * instance of it.
	 *
Qiang Xue committed
584 585 586
	 * @param string $route the route consisting of module, controller and action IDs.
	 * @return array|boolean if the controller is created successfully, it will be returned together
	 * with the remainder of the route which represents the action ID. Otherwise false will be returned.
Qiang Xue committed
587
	 */
Qiang Xue committed
588
	public function createController($route)
Qiang Xue committed
589
	{
Qiang Xue committed
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
		if ($route === '') {
			$route = $this->defaultRoute;
		}
		if (($pos = strpos($route, '/')) !== false) {
			$id = substr($route, 0, $pos);
			$route = substr($route, $pos + 1);
		} else {
			$id = $route;
			$route = '';
		}

		$module = $this->getModule($id);
		if ($module !== null) {
			return $module->createController($route);
		}

Qiang Xue committed
606
		if (isset($this->controllerMap[$id])) {
Qiang Xue committed
607
			$controller = Yii::createObject($this->controllerMap[$id], $id, $this);
Qiang Xue committed
608 609 610 611 612 613 614 615 616
		} elseif (preg_match('/^[a-z0-9\\-_]+$/', $id)) {
			$className = StringHelper::id2camel($id) . 'Controller';
			$classFile = $this->controllerPath . DIRECTORY_SEPARATOR . $className . '.php';
			if (is_file($classFile)) {
				$className = $this->controllerNamespace . '\\' . $className;
				if (!class_exists($className, false)) {
					require($classFile);
				}
				if (class_exists($className, false) && is_subclass_of($className, '\yii\base\Controller')) {
Qiang Xue committed
617
					$controller = new $className($id, $this);
Qiang Xue committed
618 619 620
				}
			}
		}
Qiang Xue committed
621 622

		return isset($controller) ? array($controller, $route) : false;
Qiang Xue committed
623
	}
w  
Qiang Xue committed
624
}