diff --git a/docs/guide/structure-modules.md b/docs/guide/structure-modules.md
index 507db664ad..e09e473e00 100644
--- a/docs/guide/structure-modules.md
+++ b/docs/guide/structure-modules.md
@@ -3,8 +3,9 @@ Modules
Modules are self-contained software units that consist of [models](structure-models.md), [views](structure-views.md),
[controllers](structure-controllers.md), and other supporting components. End users can access the controllers
-of a module when it is installed in [application](structure-applications.md). Modules differ from
-[applications](structure-applications.md) in that the former cannot be deployed alone and must reside within the latter.
+of a module when it is installed in [application](structure-applications.md). For these reasons, modules are
+often viewed as mini-applications. Modules differ from [applications](structure-applications.md) in that
+modules cannot be deployed alone and must reside within applications.
## Creating Modules
@@ -135,7 +136,7 @@ the [[yii\base\Application::modules|modules]] property of the application. The f
```
The [[yii\base\Application::modules|modules]] property takes an array of module configurations. Each array key
-represents a module ID which uniquely identifies the module among all modules in the application, and the corresponding
+represents a *module ID* which uniquely identifies the module among all modules in the application, and the corresponding
array value is a [configuration](concept-configurations.md) for creating the module.
@@ -152,8 +153,24 @@ controller in the `forum` module.
### Accessing Modules
-A module is instantiated when one of its controllers is accessed by end users. You may access the instance of a module
-using the approaches shown in the following example:
+Within a module, you may often need to get the instance of the [module class](#module-classes) so that you can
+access the module ID, module parameters, module components, etc. You can do so by using the following statement:
+
+```php
+$module = MyModuleClass::getInstance();
+```
+
+where `MyModuleClass` refers to the name of the module class that you are interested in. The `getInstance()` method
+will return the currently requested instance of the module class. If the module is not requested, the method will
+return null. Note that You do not want to manually create a new instance of the module class because it will be
+different from the one created by Yii in response to a request.
+
+> Info: When developing a module, you should not assume the module will use a fixed ID. This is because a module
+ can be associated with an arbitrary ID when used in an application or within another module. In order to get
+ the module ID, you should use the above approach to get the module instance first, and then get the ID via
+ `$module->id`.
+
+You may also access the instance of a module using the following approaches:
```php
// get the module whose ID is "forum"
@@ -163,8 +180,8 @@ $module = \Yii::$app->getModule('forum');
$module = \Yii::$app->controller->module;
```
-The first approach is only useful in application code which has knowledge about the module ID, while the second approach
-is best used by the code within the module.
+The first approach is only useful when you know the module ID, while the second approach is best used when you
+know about the controllers being requested.
Once getting hold of a module instance, you can access parameters or components registered with the module. For example,
@@ -230,5 +247,5 @@ a set of closely related features. Each such feature group can be developed as a
maintained by a specific developer or team.
Modules are also a good way of reusing code at the feature group level. Some commonly used features, such as
-user management, comment management, can all be developed in terms of modules so that they can be resued easily
+user management, comment management, can all be developed in terms of modules so that they can be reused easily
in future projects.
diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index 82884be721..dd16e15a45 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -66,6 +66,7 @@ Yii Framework 2 Change Log
- Bug: Fixed the bug that requesting protected or private action methods would cause 500 error instead of 404 (qiangxue)
- Bug: Fixed Object of class Imagick could not be converted to string in CaptchaAction (eXprojects, cebe)
- Enh #422: Added Support for BIT(M) data type default values in Schema (cebe)
+- Enh #1452: Added `Module::getInstance()` to allow accessing the module instance from anywhere within the module (qiangxue)
- Enh #2264: `CookieCollection::has()` will return false for expired or removed cookies (qiangxue)
- Enh #2435: `yii\db\IntegrityException` is now thrown on database integrity errors instead of general `yii\db\Exception` (samdark)
- Enh #2558: Enhanced support for memcached by adding `yii\caching\MemCache::persistentId` and `yii\caching\MemCache::options` (qiangxue)
@@ -110,12 +111,14 @@ Yii Framework 2 Change Log
- Enh #3774: Added `FileValidator::checkExtensionByMimeType` to support validating file types against file mime-types (Ragazzo)
- Enh #3801: Base migration controller `yii\console\controllers\BaseMigrateController` extracted (klimov-paul)
- Enh #3811: Now Gii model generator makes autocomplete for model class field (mitalcoi)
+- Enh #3926: `yii\widgets\Breadcrumbs::$links`. Allows individual link to have its own `template` (creocoder, umneeq)
- Enh #3939: `\yii\Inflector::slug()` improvements (samdark)
- Added protected `\yii\Inflector::transliterate()` that could be replaced with custom translit implementation.
- Added proper tests for both intl-based slug and PHP fallback.
- Removed character maps for non-latin languages.
- Improved overall slug results.
- Added note about the fact that intl is required for non-latin languages to requirements checker.
+- Enh #4028: Added ability to `yii\widgets\Menu` to encode each item's label separately (creocoder, umneeq)
- Enh: Added support for using sub-queries when building a DB query with `IN` condition (qiangxue)
- Enh: Supported adding a new response formatter without the need to reconfigure existing formatters (qiangxue)
- Enh: Added `yii\web\UrlManager::addRules()` to simplify adding new URL rules (qiangxue)
@@ -129,8 +132,6 @@ Yii Framework 2 Change Log
- Enh: Added param `hideOnSinglePage` to `yii\widgets\LinkPager` (arturf)
- Enh: Added support for array attributes in `in` validator (creocoder)
- Enh: Improved `yii\helpers\Inflector::slug` to support more cases for Russian, Hebrew and special characters (samdark)
-- Enh #3926: `yii\widgets\Breadcrumbs::$links`. Allows individual link to have its own `template` (creocoder, umneeq)
-- Enh #4028: Added ability to `yii\widgets\Menu` to encode each item's label separately (creocoder, umneeq)
- Chg #2287: Split `yii\db\ColumnSchema::typecast()` into two methods `phpTypecast()` and `dbTypecast()` to allow specifying PDO type explicitly (cebe)
- Chg #2898: `yii\console\controllers\AssetController` is now using hashes instead of timestamps (samdark)
- Chg #2913: RBAC `DbManager` is now initialized via migration (samdark)
diff --git a/framework/base/Application.php b/framework/base/Application.php
index 77e7525ec7..f94e36f184 100644
--- a/framework/base/Application.php
+++ b/framework/base/Application.php
@@ -189,6 +189,7 @@ abstract class Application extends Module
public function __construct($config = [])
{
Yii::$app = $this;
+ $this->setInstance($this);
$this->state = self::STATE_BEGIN;
diff --git a/framework/base/Module.php b/framework/base/Module.php
index 01b6add170..8d4379869b 100644
--- a/framework/base/Module.php
+++ b/framework/base/Module.php
@@ -122,7 +122,10 @@ class Module extends ServiceLocator
* @var array child modules of this module
*/
private $_modules = [];
-
+ /**
+ * @var array list of currently requested modules indexed by their class names
+ */
+ private static $_instances = [];
/**
* Constructor.
@@ -137,6 +140,32 @@ class Module extends ServiceLocator
parent::__construct($config);
}
+ /**
+ * Returns the currently requested instance of this module class.
+ * If the module class is not currently requested, null will be returned.
+ * This method is provided so that you access the module instance from anywhere within the module.
+ * @return static|null the currently requested instance of this module class, or null if the module class is not requested.
+ */
+ public static function getInstance()
+ {
+ $class = get_called_class();
+ return isset(self::$_instances[$class]) ? self::$_instances[$class] : null;
+ }
+
+ /**
+ * Sets the currently requested instance of this module class.
+ * @param Module|null $instance the currently requested instance of this module class.
+ * If it is null, the instance of the calling class will be removed, if any.
+ */
+ public static function setInstance($instance)
+ {
+ if ($instance === null) {
+ unset(self::$_instances[get_class()]);
+ } else {
+ self::$_instances[get_class($instance)] = $instance;
+ }
+ }
+
/**
* Initializes the module.
*
@@ -326,8 +355,10 @@ class Module extends ServiceLocator
if (is_array($this->_modules[$id]) && !isset($this->_modules[$id]['class'])) {
$this->_modules[$id]['class'] = 'yii\base\Module';
}
-
- return $this->_modules[$id] = Yii::createObject($this->_modules[$id], [$id, $this]);
+ /** @var $module Module */
+ $module = Yii::createObject($this->_modules[$id], [$id, $this]);
+ $module->setInstance($module);
+ return $this->_modules[$id] = $module;
}
}