* @link http://www.yiiframework.com/ * @copyright Copyright © 2008-2009 Yii Software LLC * @license http://www.yiiframework.com/license/ */ /** * CModule is the base class for module and application classes. * * CModule mainly manages application components and sub-modules. * * @author Qiang Xue * @version $Id: CModule.php 1091 2009-06-05 00:39:02Z qiang.xue $ * @package system.base * @since 1.0.4 */ abstract class CModule extends CComponent { /** * @var array the IDs of the application components that should be preloaded. */ public $preload=array(); /** * @var array the behaviors that should be attached to the module. * The behaviors will be attached to the module when {@link init} is called. * Please refer to {@link CModel::behaviors} on how to specify the value of this property. */ public $behaviors=array(); private $_id; private $_parentModule; private $_basePath; private $_modulePath; private $_params; private $_modules=array(); private $_moduleConfig=array(); private $_components=array(); private $_componentConfig=array(); /** * Constructor. * @param string the ID of this module * @param CModule the parent module (if any) * @param mixed the module configuration. It can be either an array or * the path of a PHP file returning the configuration array. */ public function __construct($id,$parent,$config=null) { $this->_id=$id; $this->_parentModule=$parent; // set basePath at early as possible to avoid trouble if(is_string($config)) $config=require($config); if(isset($config['basePath'])) { $this->setBasePath($config['basePath']); unset($config['basePath']); } else { $class=new ReflectionClass(get_class($this)); $this->setBasePath(dirname($class->getFileName())); } Yii::setPathOfAlias($id,$this->getBasePath()); $this->preinit(); $this->configure($config); $this->attachBehaviors($this->behaviors); $this->preloadComponents(); $this->init(); } /** * Getter magic method. * This method is overridden to support accessing application components * like reading module properties. * @param string 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 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); } /** * @return string the module ID. */ public function getId() { return $this->_id; } /** * @param string the module ID */ public function setId($id) { $this->_id=$id; } /** * @return string the root directory of the module. Defaults to the directory containing the module class. */ public function getBasePath() { if($this->_basePath===null) { $class=new ReflectionClass(get_class($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 the root directory of the module. * @throws CException if the directory does not exist. */ public function setBasePath($path) { if(($this->_basePath=realpath($path))===false || !is_dir($this->_basePath)) throw new CException(Yii::t('yii','Base path "{path}" is not a valid directory.', array('{path}'=>$path))); } /** * @return CAttributeCollection the list of user-defined parameters */ public function getParams() { if($this->_params!==null) return $this->_params; else return $this->_params=new CAttributeCollection; } /** * @param array user-defined parameters. This should be in name-value pairs. */ public function setParams($value) { $params=$this->getParams(); foreach($value as $k=>$v) $params->add($k,$v); } /** * @return string the directory that contains the application modules. Defaults to the 'modules' subdirectory of {@link basePath}. */ public function getModulePath() { if($this->_modulePath!==null) return $this->_modulePath; else return $this->_modulePath=$this->getBasePath().DIRECTORY_SEPARATOR.'modules'; } /** * @param string the directory that contains the application modules. * @throws CException if the directory is invalid */ public function setModulePath($value) { if(($this->_modulePath=realpath($value))===false || !is_dir($this->_modulePath)) throw new CException(Yii::t('yii','The module path "{path}" is not a valid directory.', array('{path}'=>$value))); } /** * Sets the aliases that are used in the module. * @param array list of aliases to be imported */ public function setImport($aliases) { foreach($aliases as $alias) Yii::import($alias); } /** * Defines the root aliases. * @param array list of aliases to be defined. The array keys are root aliases, * while the array values are paths or aliases corresponding to the root aliases. * For example, *
	 * array(
	 *    'models'=>'application.models',              // an existing alias
	 *    'extensions'=>'application.extensions',      // an existing alias
	 *    'backend'=>dirname(__FILE__).'/../backend',  // a directory
	 * )
	 * 
* @since 1.0.5 */ public function setAliases($mappings) { foreach($mappings as $name=>$alias) { if(($path=Yii::getPathOfAlias($alias))!==false) Yii::setPathOfAlias($name,$path); else Yii::setPathOfAlias($name,$alias); } } /** * @return CModule the parent module. Null if this module does not have a parent. */ public function getParentModule() { return $this->_parentModule; } /** * Retrieves the named application module. * The module has to be declared in {@link modules}. A new instance will be created * when calling this method with the given ID for the first time. * @param string application module ID (case-sensitive) * @return CModule the module instance, null if the module is disabled or does not exist. */ public function getModule($id) { if(array_key_exists($id,$this->_modules)) return $this->_modules[$id]; else if(isset($this->_moduleConfig[$id])) { $config=$this->_moduleConfig[$id]; if(!isset($config['enabled']) || $config['enabled']) { Yii::trace("Loading \"$id\" module",'system.base.CModule'); $class=$config['class']; unset($config['class'], $config['enabled']); if($this===Yii::app()) $module=Yii::createComponent($class,$id,null,$config); else $module=Yii::createComponent($class,$this->getId().'/'.$id,$this,$config); return $this->_modules[$id]=$module; } } } /** * @return array the configuration of the currently installed modules (module ID => configuration) */ public function getModules() { return $this->_moduleConfig; } /** * Configures the sub-modules of this module. * * Call this method to declare sub-modules and configure them with their initial property values. * The parameter should be an array of module configurations. Each array element represents a single module, * which can be either a string representing the module ID or an ID-configuration pair representing * a module with the specified ID and the initial property values. * * For example, the following array declares two modules: *
	 * array(
	 *     'admin',                // a single module ID
	 *     'payment'=>array(       // ID-configuration pair
	 *         'server'=>'paymentserver.com',
	 *     ),
	 * )
	 * 
* * By default, the module class is determined using the expression ucfirst($moduleID).'Module'. * And the class file is located under modules/$moduleID. * You may override this default by explicitly specifying the 'class' option in the configuration. * * You may also enable or disable a module by specifying the 'enabled' option in the configuration. * * @param array module configurations. */ public function setModules($modules) { foreach($modules as $id=>$module) { if(is_int($id)) { $id=$module; $module=array(); } if(!isset($module['class'])) { Yii::setPathOfAlias($id,$this->getModulePath().DIRECTORY_SEPARATOR.$id); $module['class']=$id.'.'.ucfirst($id).'Module'; } if(isset($this->_moduleConfig[$id])) $this->_moduleConfig[$id]=CMap::mergeArray($this->_moduleConfig[$id],$module); else $this->_moduleConfig[$id]=$module; } } /** * @param string application component ID * @return boolean whether the named application component exists (including both loaded and disabled.) */ public function hasComponent($id) { return isset($this->_components[$id]) || isset($this->_componentConfig[$id]); } /** * Retrieves the named application component. * @param string application component ID (case-sensitive) * @param boolean whether to create the component if it doesn't exist yet. This parameter * has been available since version 1.0.6. * @return IApplicationComponent the application component instance, null if the application component is disabled or does not exist. * @see hasComponent */ public function getComponent($id,$createIfNull=true) { if(isset($this->_components[$id])) return $this->_components[$id]; else if(isset($this->_componentConfig[$id]) && $createIfNull) { $config=$this->_componentConfig[$id]; unset($this->_componentConfig[$id]); if(!isset($config['enabled']) || $config['enabled']) { Yii::trace("Loading \"$id\" application component",'system.web.CModule'); unset($config['enabled']); $component=Yii::createComponent($config); $component->init(); return $this->_components[$id]=$component; } } } /** * Puts a component under the management of the module. * The component will be initialized (by calling its {@link CApplicationComponent::init() init()} * method if it has not done so. * @param string component ID * @param IApplicationComponent the component */ public function setComponent($id,$component) { $this->_components[$id]=$component; if(!$component->getIsInitialized()) $component->init(); } /** * @return array the currently loaded components (indexed by their IDs) */ public function getComponents() { return $this->_components; } /** * Sets the application components. * * When a configuration is used to specify a component, it should consist of * the component's initial property values (name-value pairs). Additionally, * a component can be enabled (default) or disabled by specifying the 'enabled' value * in the configuration. * * If a configuration is specified with an ID that is the same as an existing * component or configuration, the existing one will be replaced silently. * * The following is the configuration for two components: *
	 * array(
	 *     'db'=>array(
	 *         'class'=>'CDbConnection',
	 *         'connectionString'=>'sqlite:path/to/file.db',
	 *     ),
	 *     'cache'=>array(
	 *         'class'=>'CDbCache',
	 *         'connectionID'=>'db',
	 *         'enabled'=>!YII_DEBUG,  // enable caching in non-debug mode
	 *     ),
	 * )
	 * 
* * @param array application components(id=>component configuration or instances) */ public function setComponents($components) { foreach($components as $id=>$component) { if($component instanceof IApplicationComponent) $this->setComponent($id,$component); else if(isset($this->_componentConfig[$id])) $this->_componentConfig[$id]=CMap::mergeArray($this->_componentConfig[$id],$component); else $this->_componentConfig[$id]=$component; } } /** * Configures the module with the specified configuration. * @param array the configuration array */ public function configure($config) { if(is_array($config)) { foreach($config as $key=>$value) $this->$key=$value; } } /** * Loads static application components. */ protected function preloadComponents() { foreach($this->preload as $id) $this->getComponent($id); } /** * Preinitializes the module. * This method is called at the beginning of the module constructor. * You may override this method to do some customized preinitialization work. * Note that at this moment, the module is not configured yet. * @see init */ protected function preinit() { } /** * Initializes the module. * This method is called at the end of the module constructor. * Note that at this moment, the module has been configured, the behaviors * have been attached and the application components have been registered. * @see preinit */ protected function init() { } }