diff --git a/framework/base/control.class.php b/framework/base/control.class.php index be1d4dbc12..cc4853fe49 100644 --- a/framework/base/control.class.php +++ b/framework/base/control.class.php @@ -251,6 +251,15 @@ class baseControl * Set super vars. */ $this->setSuperVars(); + + /** + * 读取当前模块的zen类。 + * Load the zen file auto. + */ + $zenClass = $this->moduleName . 'Zen'; + $selfClass = get_class($this); + $parentClasses = class_parents($this); + if($selfClass != $zenClass && !isset($parentClasses[$zenClass])) $this->loadZen($this->moduleName, $appName); } //-------------------- Model相关方法(Model related methods) --------------------// @@ -282,34 +291,25 @@ class baseControl } /** - * 加载指定模块的model文件。 - * Load the model file of one module. + * 加载一个模块的model对象。加载完成后,使用$this->$moduleName来访问这个model对象。 + * 比如:loadModel('user')引入user模块的model实例对象,可以通过$this->user来访问它。 + * + * Load the model object of one module. After loaded, can use $this->$moduleName to visit the model object. * * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name. - * @param string $appName The app name, if empty, use current app's name. - * @access public - * @return object|bool 如果没有model文件,返回false,否则返回model对象。If no model file, return false, else return the model object. + * @param string $appName 应用名,如果为空,使用当前应用。The app name, if empty, use current app's name. + * @access public + * @return object|bool 如果没有model文件,返回false,否则返回model对象。If no model file, return false, else return the model object. */ public function loadModel($moduleName = '', $appName = '') { - if(empty($moduleName)) $moduleName = $this->moduleName; - if(empty($appName)) $appName = $this->appName; - - global $loadedModels; - if(isset($loadedModels[$appName][$moduleName])) - { - $this->$moduleName = $loadedModels[$appName][$moduleName]; - $this->dao = $this->$moduleName->dao; - return $this->$moduleName; - } - - $modelFile = $this->app->setModelFile($moduleName, $appName); + $model = $this->app->loadTarget($moduleName, $appName); /** - * 如果没有model文件,尝试加载config配置信息。 - * If no model file, try load config. + * 如果加载model失败,尝试加载config, lang配置信息。 + * If model is not loaded, try load config and lang. */ - if(!helper::import($modelFile)) + if(!$model) { $this->app->loadModuleConfig($moduleName, $appName); $this->app->loadLang($moduleName, $appName); @@ -317,25 +317,32 @@ class baseControl return false; } - /** - * 如果没有扩展文件,model类名是$moduleName + 'model',如果有扩展,还需要增加ext前缀。 - * If no extension file, model class name is $moduleName + 'model', else with 'ext' as the prefix. - */ - $modelClass = class_exists('ext' . $appName . $moduleName . 'model') ? 'ext' . $appName . $moduleName . 'model' : $appName . $moduleName . 'model'; - if(!class_exists($modelClass)) - { - $modelClass = class_exists('ext' . $moduleName . 'model') ? 'ext' . $moduleName . 'model' : $moduleName . 'model'; - if(!class_exists($modelClass)) $this->app->triggerError(" The model $modelClass not found", __FILE__, __LINE__, $exit = true); - } + $this->{$moduleName} = $model; + $this->dao = $model->dao; + return $model; + } - /** - * 初始化model对象,在control对象中可以通过$this->$moduleName来引用。同时将dao对象赋为control对象的成员变量,方便引用。 - * Init the model object thus you can try $this->$moduleName to access it. Also assign the $dao object as a member of control object. - */ - $loadedModels[$appName][$moduleName] = new $modelClass($appName); - $this->$moduleName = $loadedModels[$appName][$moduleName]; - $this->dao = $this->$moduleName->dao; - return $this->$moduleName; + /** + * 加载一个模块的zen对象。加载完成后,使用$this->{$moduleName}Zen来访问这个zen对象。 + * 比如:loadZen('user')引入user模块的zen实例对象,可以通过$this->userZen来访问它。 + * + * Load the zen object of one module. After loaded, can use $this->{$moduleName}Zen to visit the zen object. + * + * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name. + * @param string $appName 应用名,如果为空,使用当前应用。The app name, if empty, use current app's name. + * @access public + * @return object|bool 如果没有zen文件,返回false,否则返回zen对象。If no zen file, return false, else return the zen object. + */ + public function loadZen($moduleName = '', $appName = '') + { + $zen = $this->app->loadTarget($moduleName, $appName, 'zen'); + if(!$zen) return false; + + $zen->view = $this->view; + + $zenObjectName = $moduleName . 'Zen'; + $this->{$zenObjectName} = $zen; + return $zen; } /** @@ -770,7 +777,6 @@ class baseControl $currentModuleName = $this->moduleName; $currentMethodName = $this->methodName; - $currentAppName = $this->appName; $currentParams = $this->app->getParams(); /** diff --git a/framework/base/model.class.php b/framework/base/model.class.php index 37f53b06e5..bf4a0136ab 100644 --- a/framework/base/model.class.php +++ b/framework/base/model.class.php @@ -150,6 +150,15 @@ class baseModel $this->loadDAO(); $this->setSuperVars(); + + /** + * 读取当前模块的tao类。 + * Load the tao file auto. + */ + $taoClass = $moduleName . 'Tao'; + $selfClass = get_class($this); + $parentClasses = class_parents($this); + if($selfClass != $taoClass && !isset($parentClasses[$taoClass])) $this->loadTao($moduleName, $this->appName); } /** @@ -170,11 +179,11 @@ class baseModel */ public function getModuleName() { - $parentClass = get_parent_class($this); - $selfClass = get_class($this); - $className = $parentClass == 'model' ? $selfClass : $parentClass; - if($className == 'extensionModel') return 'extension'; - return strtolower(str_ireplace(array('ext', 'Model'), '', $className)); + $className = get_class($this); + $parentClasses = class_parents($this); + if(count($parentClasses) > 2) $className = current(array_slice($parentClasses, -3, 1)); + if(strtolower(substr($className, -5)) == 'model') $className = strtolower(substr($className, 0, strlen($className) - 5)); + return $className; } /** @@ -194,41 +203,44 @@ class baseModel } /** - * 加载一个模块的model。加载完成后,使用$this->$moduleName来访问这个model对象。 + * 加载一个模块的model对象。加载完成后,使用$this->$moduleName来访问这个model对象。 * 比如:loadModel('user')引入user模块的model实例对象,可以通过$this->user来访问它。 * - * Load the model of one module. After loaded, can use $this->$moduleName to visit the model object. + * Load the model object of one module. After loaded, can use $this->$moduleName to visit the model object. * - * @param string $moduleName - * @access public - * @return object|bool the model object or false if model file not exists. + * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name. + * @param string $appName 应用名,如果为空,使用当前应用。The app name, if empty, use current app's name. + * @access public + * @return object|bool 如果没有model文件,返回false,否则返回model对象。If no model file, return false, else return the model object. */ public function loadModel($moduleName, $appName = '') { - if(empty($moduleName)) return false; - if(empty($appName)) $appName = $this->appName; - $moduleName = strtolower($moduleName); + $model = $this->app->loadTarget($moduleName, $appName); + if(!$model) return false; - global $loadedModels; - if(isset($loadedModels[$appName][$moduleName])) - { - $this->$moduleName = $loadedModels[$appName][$moduleName]; - return $this->$moduleName; - } + $this->{$moduleName} = $model; + return $model; + } - $modelFile = $this->app->setModelFile($moduleName, $appName); + /** + * 加载一个模块的tao对象。加载完成后,使用$this->{$moduleName}Tao来访问这个tao对象。 + * 比如:loadTao('user')引入user模块的tao实例对象,可以通过$this->userTao来访问它。 + * + * Load the tao object of one module. After loaded, can use $this->{$moduleName}Tao to visit the tao object. + * + * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name. + * @param string $appName 应用名,如果为空,使用当前应用。The app name, if empty, use current app's name. + * @access public + * @return object|bool 如果没有tao文件,返回false,否则返回tao对象。If no tao file, return false, else return the tao object. + */ + public function loadTao($moduleName, $appName = '') + { + $tao = $this->app->loadTarget($moduleName, $appName, 'tao'); + if(!$tao) return false; - if(!helper::import($modelFile)) return false; - $modelClass = class_exists('ext' . $appName . $moduleName. 'model') ? 'ext' . $appName . $moduleName . 'model' : $appName . $moduleName . 'model'; - if(!class_exists($modelClass)) - { - $modelClass = class_exists('ext' . $moduleName. 'model') ? 'ext' . $moduleName . 'model' : $moduleName . 'model'; - if(!class_exists($modelClass)) $this->app->triggerError(" The model $modelClass not found", __FILE__, __LINE__, $exit = true); - } - - $loadedModels[$appName][$moduleName] = new $modelClass($appName); - $this->$moduleName = $loadedModels[$appName][$moduleName]; - return $this->$moduleName; + $taoObjectName = $moduleName . 'Tao'; + $this->{$taoObjectName} = $tao; + return $tao; } /** @@ -252,17 +264,22 @@ class baseModel public function loadExtension($extensionName, $moduleName = '') { if(empty($extensionName)) return false; + if(empty($moduleName)) $moduleName = $this->getModuleName(); - $moduleName = $moduleName ? $moduleName : $this->getModuleName(); $moduleName = strtolower($moduleName); $extensionName = strtolower($extensionName); + $type = 'model'; + $className = strtolower(get_class($this)); + if($className == $moduleName . 'tao' || $className == 'ext' . $moduleName . 'tao') $type = 'tao'; + /* 设置扩展类的名字。Set the extension class name. */ $extensionClass = $extensionName . ucfirst($moduleName); + if($type != 'model') $extensionClass .= ucfirst($type); if(isset($this->$extensionClass)) return $this->$extensionClass; /* 设置扩展的名字和相应的文件。Set extenson name and extension file. */ - $moduleExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'model'); + $moduleExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, $type); if(!empty($moduleExtPath['site'])) $extensionFile = $moduleExtPath['site'] . 'class/' . $extensionName . '.class.php'; if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['custom'] . 'class/' . $extensionName . '.class.php'; if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['saas'] . 'class/' . $extensionName . '.class.php'; @@ -271,13 +288,13 @@ class baseModel if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['common'] . 'class/' . $extensionName . '.class.php'; /* 载入父类。Try to import parent model file auto and then import the extension file. */ - if(!class_exists($moduleName . 'Model')) helper::import($this->app->getModulePath($this->appName, $moduleName) . 'model.php'); + if(!class_exists($moduleName . ucfirst($type))) helper::import($this->app->getModulePath($this->appName, $moduleName) . $type . '.php'); if(!helper::import($extensionFile)) return false; if(!class_exists($extensionClass)) return false; /* 实例化扩展类。Create an instance of the extension class and return it. */ $extensionObject = new $extensionClass; - $extensionClass = str_replace('Model', '', $extensionClass); + if($type == 'model') $extensionClass = str_replace(ucfirst($type), '', $extensionClass); $this->$extensionClass = $extensionObject; return $extensionObject; } diff --git a/framework/base/router.class.php b/framework/base/router.class.php index 5caa5e3582..cceaba51cd 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -1623,7 +1623,7 @@ class baseRouter * * @param string $appName the app name * @param string $moduleName the module name - * @param string $ext the extension type, can be control|model|view|lang|config + * @param string $ext the extension type, can be control|model|view|lang|config|zen|tao * @access public * @return string the extension path. */ @@ -1775,19 +1775,33 @@ class baseRouter * 设置一个模块的model文件,如果存在model扩展,一起合并。 * Set the model file of one module. If there's an extension file, merge it with the main model file. * - * @param string $moduleName the module name - * @param string $appName the app name - * @static - * @access public - * @return string the model file + * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name. + * @param string $appName 应用名,如果为空,使用当前应用。The app name, if empty, use current app's name. + * @access public + * @return string the model file */ public function setModelFile($moduleName, $appName = '') + { + return $this->setTargetFile($moduleName, $appName); + } + + /** + * 设置一个模块的target文件,如果存在target扩展,一起合并。 + * Set the target file of one module. If there's an extension file, merge it with the main target file. + * + * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name. + * @param string $appName 应用名,如果为空,使用当前应用。The app name, if empty, use current app's name. + * @param string $class 对象的类型,可选值 target、zen、tao,默认为 target。The type of the object, optional values target, zen, tao, the default is target. + * @access public + * @return string the target file + */ + public function setTargetFile($moduleName, $appName = '', $class = 'model') { if($appName == '') $appName = $this->getAppName(); - /* 设置主model文件。 Set the main model file. */ - $mainModelFile = $this->getModulePath($appName, $moduleName) . 'model.php'; - if($this->config->framework->extensionLevel == 0) return $mainModelFile; + /* 设置主target文件。 Set the main target file. */ + $mainTargetFile = $this->getModulePath($appName, $moduleName) . "{$class}.php"; + if($this->config->framework->extensionLevel == 0) return $mainTargetFile; /* 计算扩展的文件和hook文件。Compute the extension files and hook files. */ $hookFiles = array(); @@ -1795,14 +1809,14 @@ class baseRouter $apiFiles = array(); $siteExtended = false; - $modelExtPaths = $this->getModuleExtPath($appName, $moduleName, 'model'); - foreach($modelExtPaths as $extType => $modelExtPath) + $targetExtPaths = $this->getModuleExtPath($appName, $moduleName, $class); + foreach($targetExtPaths as $extType => $targetExtPath) { - if(empty($modelExtPath)) continue; + if(empty($targetExtPath)) continue; - $tmpHookFiles = helper::ls($modelExtPath . 'hook/', '.php'); - $tmpExtFiles = helper::ls($modelExtPath, '.php'); - $tmpAPIFiles = helper::ls($modelExtPath, '.api'); + $tmpHookFiles = helper::ls($targetExtPath . 'hook/', '.php'); + $tmpExtFiles = helper::ls($targetExtPath, '.php'); + $tmpAPIFiles = helper::ls($targetExtPath, '.api'); $hookFiles = array_merge($hookFiles, $tmpHookFiles); $extFiles = array_merge($extFiles, $tmpExtFiles); $apiFiles = array_merge($apiFiles, $tmpAPIFiles); @@ -1811,119 +1825,127 @@ class baseRouter } /* 如果没有扩展文件,返回主文件。 If no extension or hook files, return the main file directly. */ - if(empty($extFiles) and empty($hookFiles) and empty($apiFiles)) return $mainModelFile; + if(empty($extFiles) and empty($hookFiles) and empty($apiFiles)) return $mainTargetFile; - /* 计算合并之后的modelFile路径。Compute the merged model file path. */ - $extModelPrefix = $this->config->edition . DS . $this->config->vision . DS; - if($siteExtended and !empty($this->siteCode)) $extModelPrefix .= $this->siteCode[0] . DS . $this->siteCode; + /* 计算合并之后的targetFile路径。Compute the merged target file path. */ + $extTargetPrefix = $this->config->edition . DS . $this->config->vision . DS; + if($siteExtended and !empty($this->siteCode)) $extTargetPrefix .= $this->siteCode[0] . DS . $this->siteCode; - $mergedModelDir = $this->getTmpRoot() . 'model' . DS . $extModelPrefix; - $mergedModelFile = $mergedModelDir . $moduleName . '.php'; - if(!is_dir($mergedModelDir)) mkdir($mergedModelDir, 0755, true); + $mergedTargetDir = $this->getTmpRoot() . $class . DS . $extTargetPrefix; + $mergedTargetFile = $mergedTargetDir . $moduleName . '.php'; + if(!is_dir($mergedTargetDir)) mkdir($mergedTargetDir, 0755, true); - /* 判断生成的缓存文件是否需要更新。 Judge whether the merged model file needed update or not. */ - if(!$this->needModelFileUpdate($mergedModelFile, $extFiles, $hookFiles, $apiFiles, $modelExtPaths, $mainModelFile)) return $mergedModelFile; + /* 判断生成的缓存文件是否需要更新。 Judge whether the merged target file needed update or not. */ + if(!$this->needTargetFileUpdate($mergedTargetFile, $extFiles, $hookFiles, $apiFiles, $targetExtPaths, $mainTargetFile)) return $mergedTargetFile; /* 合并扩展和hook文件。Merge the extension and hook files. */ - $modelLines = $this->mergeModelExtFiles($moduleName, $mainModelFile, $extFiles, $mergedModelDir); - $this->mergeModelHookFiles($moduleName, $mainModelFile, $modelLines, $hookFiles, $mergedModelDir, $mergedModelFile, $apiFiles); + $targetLines = $this->mergeTargetExtFiles($moduleName, $extFiles, $mergedTargetDir, $class); + $this->mergeTargetHookFiles($moduleName, $mainTargetFile, $targetLines, $hookFiles, $mergedTargetDir, $mergedTargetFile, $apiFiles, $class); - return $mergedModelFile; + return $mergedTargetFile; } /** - * 检查合并之后的model文件是否需要更新。Check whether the merged model file need update or not. + * 检查合并之后的target文件是否需要更新。Check whether the merged target file need update or not. * - * @param string $mergedModelFile + * @param string $mergedTargetFile * @param array $extFiles * @param array $hookFiles * @param array $apiFiles - * @param string $modelExtPaths - * @param string $mainModelFile + * @param string $targetExtPaths + * @param string $mainTargetFile * @access public * @return bool */ - public function needModelFileUpdate($mergedModelFile, $extFiles, $hookFiles, $apiFiles, $modelExtPaths, $mainModelFile) + public function needTargetFileUpdate($mergedTargetFile, $extFiles, $hookFiles, $apiFiles, $targetExtPaths, $mainTargetFile) { - $lastTime = file_exists($mergedModelFile) ? filemtime($mergedModelFile) : 0; + $lastTime = file_exists($mergedTargetFile) ? filemtime($mergedTargetFile) : 0; foreach($extFiles as $extFile) if(filemtime($extFile) > $lastTime) return true; foreach($hookFiles as $hookFile) if(filemtime($hookFile) > $lastTime) return true; foreach($apiFiles as $apiFile) if(filemtime($apiFile) > $lastTime) return true; - $modelExtPath = $modelExtPaths['common']; - $modelHookPath = $modelExtPaths['common'] . 'hook/'; - if(is_dir($modelExtPath ) and filemtime($modelExtPath) > $lastTime) return true; - if(is_dir($modelHookPath) and filemtime($modelHookPath) > $lastTime) return true; + $targetExtPath = $targetExtPaths['common']; + $targetHookPath = $targetExtPaths['common'] . 'hook/'; + if(is_dir($targetExtPath ) and filemtime($targetExtPath) > $lastTime) return true; + if(is_dir($targetHookPath) and filemtime($targetHookPath) > $lastTime) return true; - if(!empty($modelExtPaths['site'])) + if(!empty($targetExtPaths['site'])) { - $modelExtPath = $modelExtPaths['site']; - $modelHookPath = $modelExtPaths['site'] . 'hook/'; - if(is_dir($modelExtPath ) and filemtime($modelExtPath) > $lastTime) return true; - if(is_dir($modelHookPath) and filemtime($modelHookPath) > $lastTime) return true; + $targetExtPath = $targetExtPaths['site']; + $targetHookPath = $targetExtPaths['site'] . 'hook/'; + if(is_dir($targetExtPath ) and filemtime($targetExtPath) > $lastTime) return true; + if(is_dir($targetHookPath) and filemtime($targetHookPath) > $lastTime) return true; } - if(filemtime($mainModelFile) > $lastTime) return true; + if(filemtime($mainTargetFile) > $lastTime) return true; return false; } /** - * 将model的扩展文件合并在一起。Merge model ext files. + * 将target的扩展文件合并在一起。Merge target ext files. * * @param string $moduleName - * @param string $mainModelFile * @param array $extFiles - * @param string $mergedModelDir + * @param string $mergedTargetDir + * @param string $class model | zen | tao * @access public * @return void */ - public function mergeModelExtFiles($moduleName, $mainModelFile, $extFiles, $mergedModelDir) + public function mergeTargetExtFiles($moduleName, $extFiles, $mergedTargetDir, $class = 'model') { /* 设置类名。Set the class names. */ - $modelClass = "{$moduleName}Model"; - $tmpModelClass = "tmpExt$modelClass"; + $targetClass = $moduleName . ucfirst($class); + $tmpTargetClass = "tmpExt$targetClass"; /* 开始拼装代码。Prepare the codes. */ - $modelLines = "getModulePath('', '$moduleName') . " . "'model.php');\n"; - $modelLines .= "class $tmpModelClass extends $modelClass \n{\n"; + $targetLines = "getModulePath('', '$moduleName') . " . "'$class.php');\n"; + $targetLines .= "class $tmpTargetClass extends $targetClass \n{\n"; - /* 将扩展文件的代码合并到代码中。Cycle all the extension files and merge them into model lines. */ - $extModels = array(); - foreach($extFiles as $extFile) $extModels[basename($extFile)] = $extFile; - foreach($extModels as $extModel) $modelLines .= self::removePHPTAG($extModel); + /* 将扩展文件的代码合并到代码中。Cycle all the extension files and merge them into target lines. */ + $extTargets = array(); + foreach($extFiles as $extFile) $extTargets[basename($extFile)] = $extFile; + foreach($extTargets as $extTarget) $targetLines .= self::removePHPTAG($extTarget); /* 做个标记,方便后面替换代码使用。Make a mark for replacing codes. */ $replaceMark = '//**//'; - $modelLines .= "\n$replaceMark\n}"; + $targetLines .= "\n$replaceMark\n}"; - /* 生成一个临时的model扩展文件,并加载,用于后续的hook文件加载使用。Create a tmp merged model file and import it for merge hook codes using. */ - $tmpModelFile = $mergedModelDir . "tmp$moduleName.php"; - if(@file_put_contents($tmpModelFile, $modelLines)) + /* 生成一个临时的target扩展文件,并加载,用于后续的hook文件加载使用。Create a tmp merged target file and import it for merge hook codes using. */ + $tmpTargetFile = $mergedTargetDir . "tmp$moduleName.php"; + if(@file_put_contents($tmpTargetFile, $targetLines)) { - if(!class_exists($tmpModelClass)) include $tmpModelFile; - return $modelLines; + if(!class_exists($tmpTargetClass)) include $tmpTargetFile; + return $targetLines; } - $this->triggerError("ERROR: $tmpModelFile not writable.", __FILE__, __LINE__, true); + $this->triggerError("ERROR: $tmpTargetFile not writable.", __FILE__, __LINE__, true); } /** - * 合并model的hook脚本。Merge hook files for a model. + * 合并target的hook脚本。Merge hook files for a target. * + * @param string $moduleName + * @param string $mainTargetFile + * @param string $targetLines + * @param array $hookFiles + * @param string $mergedTargetDir + * @param string $mergedTargetFile + * @param array $apiFiles + * @param string $class model | zen | tao * @access public * @return void */ - public function mergeModelHookFiles($moduleName, $mainModelFile, $modelLines, $hookFiles, $mergedModelDir, $mergedModelFile, $apiFiles) + public function mergeTargetHookFiles($moduleName, $mainTargetFile, $targetLines, $hookFiles, $mergedTargetDir, $mergedTargetFile, $apiFiles, $class = 'model') { /* 定义相关变量。Init vars. */ - $modelClass = $moduleName . 'Model'; - $extModelClass = 'ext' . $modelClass; - $tmpModelClass = 'tmpExt' . $modelClass; - $tmpModelFile = $mergedModelDir . "tmp$moduleName.php"; + $targetClass = $moduleName . ucfirst($class); + $extTargetClass = 'ext' . $targetClass; + $tmpTargetClass = 'tmpExt' . $targetClass; + $tmpTargetFile = $mergedTargetDir . "tmp$moduleName.php"; $replaceMark = '//**//'; /* 读取hook文件。Get hook codes need to merge. */ @@ -1947,31 +1969,31 @@ class baseRouter /* 合并Hook文件。Cycle the hook methods and merge hook codes. */ $hookedMethods = array_keys($hookCodes); - $mainModelCodes = file($mainModelFile); - $mergedModelCodes = file($tmpModelFile); + $mainTargetCodes = file($mainTargetFile); + $mergedTargetCodes = file($tmpTargetFile); foreach($hookedMethods as $method) { /* 通过反射获得hook脚本对应的方法所在的文件和起止行数。Reflection the hooked method to get it's defined position. */ - if(!method_exists($tmpModelClass, $method)) continue; - $methodRelfection = new reflectionMethod($tmpModelClass, $method); + if(!method_exists($tmpTargetClass, $method)) continue; + $methodRelfection = new reflectionMethod($tmpTargetClass, $method); $definedFile = $methodRelfection->getFileName(); $startLine = $methodRelfection->getStartLine(); $endLine = $methodRelfection->getEndLine(); /* 将Hook脚本和老的代码合并在一起,并替换原来的定义。Merge hook codes with old codes and replace back. */ - $oldCodes = $definedFile == $tmpModelFile ? $mergedModelCodes : $mainModelCodes; + $oldCodes = $definedFile == $tmpTargetFile ? $mergedTargetCodes : $mainTargetCodes; $oldCodes = join("", array_slice($oldCodes, $startLine - 1, $endLine - $startLine + 1)); $openBrace = strpos($oldCodes, '{'); $newCodes = substr($oldCodes, 0, $openBrace + 1) . "\n" . join("\n", $hookCodes[$method]) . substr($oldCodes, $openBrace + 1); - if($definedFile == $tmpModelFile) $modelLines = str_replace($oldCodes, $newCodes, $modelLines); - if($definedFile != $tmpModelFile) $modelLines = str_replace($replaceMark, $newCodes . "\n$replaceMark", $modelLines); + if($definedFile == $tmpTargetFile) $targetLines = str_replace($oldCodes, $newCodes, $targetLines); + if($definedFile != $tmpTargetFile) $targetLines = str_replace($replaceMark, $newCodes . "\n$replaceMark", $targetLines); } - /* 保存最终的Model文件。Save the last merged model file. */ - $modelLines = str_replace($tmpModelClass, $extModelClass, $modelLines); - file_put_contents($mergedModelFile, $modelLines); - unlink($tmpModelFile); + /* 保存最终的Target文件。Save the last merged target file. */ + $targetLines = str_replace($tmpTargetClass, $extTargetClass, $targetLines); + file_put_contents($mergedTargetFile, $targetLines); + unlink($tmpTargetFile); } /** @@ -2181,6 +2203,57 @@ class baseRouter return isset($module) ? $module : false; } + /** + * 加载指定模块下的某种对象。 + * Load the target object of one module. + * + * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name. + * @param string $appName 应用名,如果为空,使用当前应用。The app name, if empty, use current app's name. + * @param string $class 对象的类型,可选值 model、zen、tao,默认为 model。The type of the object, optional values model, zen, tao, the default is model. + * @access public + * @return object|bool 如果没有model文件,返回false,否则返回model对象。If no model file, return false, else return the model object. + */ + public function loadTarget($moduleName = '', $appName = '', $class = 'model') + { + if(empty($moduleName)) $moduleName = $this->moduleName; + if(empty($appName)) $appName = $this->appName; + + global $loadedTargets; + if(isset($loadedTargets[$class][$appName][$moduleName])) return $loadedTargets[$class][$appName][$moduleName]; + + $targetFile = $this->setTargetFile($moduleName, $appName, $class); + + /** + * 如果没有target文件,返回false。 + * If no target file, return false. + */ + if(!helper::import($targetFile)) return false; + + /** + * 如果没有扩展文件,target类名是$moduleName + $class,如果有扩展,还需要增加ext前缀。 + * If no extension file, target class name is $moduleName + $class, else with 'ext' as the prefix. + */ + $targetClass = class_exists('ext' . $appName . $moduleName. $class) ? 'ext' . $appName . $moduleName . $class : $appName . $moduleName . $class; + if(!class_exists($targetClass)) + { + $targetClass = class_exists('ext' . $moduleName. $class) ? 'ext' . $moduleName . $class : $moduleName . $class; + if(!class_exists($targetClass)) $this->triggerError(" The $class $targetClass not found", __FILE__, __LINE__, $exit = true); + } + + /** + * 因为zen继承自control,tao继承自model,构造函数里会调用loadTarget方法,赋默认值值防止递归调用。 + */ + if($class == 'zen' || $class == 'tao') $loadedTargets[$class][$appName][$moduleName] = false; + + /** + * 初始化target 对象并返回。 + * Init the target object and return it. + */ + $target = new $targetClass($appName); + $loadedTargets[$class][$appName][$moduleName] = $target; + return $target; + } + /** * 设置请求的参数(PATH_INFO 方式)。 * Set the params by PATH_INFO. diff --git a/framework/control.class.php b/framework/control.class.php index 85aa1099ee..d8f81a4ead 100644 --- a/framework/control.class.php +++ b/framework/control.class.php @@ -42,48 +42,71 @@ class control extends baseControl /* Code for task #9224. Set requiredFields for workflow. */ if($this->dbh and (defined('IN_USE') or (defined('RUN_MODE') and RUN_MODE == 'api'))) { - if(isset($this->config->{$this->moduleName}) and strpos($this->methodName, 'export') !== false) - { - if(isset($this->config->{$this->moduleName}->exportFields) or isset($this->config->{$this->moduleName}->list->exportFields)) - { - $exportFields = $this->dao->select('*')->from(TABLE_WORKFLOWFIELD)->where('module')->eq($this->moduleName)->andWhere('canExport')->eq('1')->andWhere('buildin')->eq('0')->fetchAll('field'); - - if(isset($this->config->{$this->moduleName}->exportFields)) - { - foreach($exportFields as $field) $this->config->{$this->moduleName}->exportFields .= ",{$field->field}"; - } - - if(isset($this->config->{$this->moduleName}->list->exportFields)) - { - foreach($exportFields as $field) $this->config->{$this->moduleName}->list->exportFields .= ",{$field->field}"; - } - - foreach($exportFields as $flowField => $exportField) - { - if(!isset($this->lang->{$this->moduleName}->$flowField)) $this->lang->{$this->moduleName}->$flowField = $exportField->name; - } - } - } - - /* Append editor field to this module config from workflow. */ - $textareaFields = $this->dao->select('*')->from(TABLE_WORKFLOWFIELD)->where('module')->eq($this->moduleName)->andWhere('control')->eq('richtext')->andWhere('buildin')->eq('0')->fetchAll('field'); - if($textareaFields) - { - $editorIdList = array(); - foreach($textareaFields as $textareaField) $editorIdList[] = $textareaField->field; - - if(!isset($this->config->{$this->moduleName})) $this->config->{$this->moduleName} = new stdclass(); - if(!isset($this->config->{$this->moduleName}->editor)) $this->config->{$this->moduleName}->editor = new stdclass(); - if(!isset($this->config->{$this->moduleName}->editor->{$this->methodName})) $this->config->{$this->moduleName}->editor->{$this->methodName} = array('id' => '', 'tools' => 'simpleTools'); - $this->config->{$this->moduleName}->editor->{$this->methodName}['id'] .= ',' . join(',', $editorIdList); - trim($this->config->{$this->moduleName}->editor->{$this->methodName}['id'], ','); - } + $this->extendExportFields(); + $this->extendEditorFields(); /* If workflow is created by a normal user, set priv. */ if(isset($this->app->user) and !$this->app->user->admin) $this->setDefaultPrivByWorkflow(); } } + /** + * Append export fields to the config of this module from workflow. + * + * @access public + * @return void + */ + public function extendExportFields() + { + if(isset($this->config->{$this->moduleName}) and strpos($this->methodName, 'export') !== false) + { + if(isset($this->config->{$this->moduleName}->exportFields) or isset($this->config->{$this->moduleName}->list->exportFields)) + { + $exportFields = $this->dao->select('*')->from(TABLE_WORKFLOWFIELD)->where('module')->eq($this->moduleName)->andWhere('canExport')->eq('1')->andWhere('buildin')->eq('0')->fetchAll('field'); + + if(isset($this->config->{$this->moduleName}->exportFields)) + { + foreach($exportFields as $field) $this->config->{$this->moduleName}->exportFields .= ",{$field->field}"; + } + + if(isset($this->config->{$this->moduleName}->list->exportFields)) + { + foreach($exportFields as $field) $this->config->{$this->moduleName}->list->exportFields .= ",{$field->field}"; + } + + foreach($exportFields as $flowField => $exportField) + { + if(!isset($this->lang->{$this->moduleName}->$flowField)) $this->lang->{$this->moduleName}->$flowField = $exportField->name; + } + } + } + } + + /** + * Append editor fields to the config of this module from workflow. + * + * @access public + * @return void + */ + public function extendEditorFields() + { + $moduleName = $this->moduleName; + $methodName = $this->methodName; + + $textareaFields = $this->dao->select('*')->from(TABLE_WORKFLOWFIELD)->where('module')->eq($this->moduleName)->andWhere('control')->eq('richtext')->andWhere('buildin')->eq('0')->fetchAll('field'); + if($textareaFields) + { + $editorIdList = array(); + foreach($textareaFields as $textareaField) $editorIdList[] = $textareaField->field; + + if(!isset($this->config->{$moduleName})) $this->config->{$moduleName} = new stdclass(); + if(!isset($this->config->{$moduleName}->editor)) $this->config->{$moduleName}->editor = new stdclass(); + if(!isset($this->config->{$moduleName}->editor->{$methodName})) $this->config->{$moduleName}->editor->{$methodName} = array('id' => '', 'tools' => 'simpleTools'); + $this->config->{$moduleName}->editor->{$methodName}['id'] .= ',' . join(',', $editorIdList); + trim($this->config->{$moduleName}->editor->{$methodName}['id'], ','); + } + } + /** * Det default priv by workflow. * @@ -146,21 +169,94 @@ class control extends baseControl } /** - * 企业版部分功能是从然之合并过来的。然之代码中调用loadModel方法时传递了一个非空的appName,在禅道中会导致错误。 + * 企业版部分功能是从然之合并过来的。ZDOO代码中调用loadModel方法时传递了一个非空的appName,在禅道中会导致错误。 * 调用父类的loadModel方法来避免这个错误。 - * Some codes merged from ranzhi called the function loadModel with a non-empty appName which causes an error in zentao. + * Some codes merged from ZDOO called the function loadModel with a non-empty appName which causes an error in zentao. * Call the parent function with empty appName to avoid this error. * - * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name. - * @param string $appName The app name, if empty, use current app's name. - * @access public - * @return object|bool 如果没有model文件,返回false,否则返回model对象。If no model file, return false, else return the model object. + * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name. + * @param string $appName 应用名,如果为空,使用当前应用。The app name, if empty, use current app's name. + * @access public + * @return object|bool 如果没有model文件,返回false,否则返回model对象。If no model file, return false, else return the model object. */ public function loadModel($moduleName = '', $appName = '') { return parent::loadModel($moduleName); } + /** + * 企业版部分功能是从然之合并过来的。ZDOO代码中调用loadZen方法时传递了一个非空的appName,在禅道中会导致错误。 + * 调用父类的loadZen方法来避免这个错误。 + * Some codes merged from ZDOO called the function loadZen with a non-empty appName which causes an error in zentao. + * Call the parent function with empty appName to avoid this error. + * + * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name. + * @param string $appName 应用名,如果为空,使用当前应用。The app name, if empty, use current app's name. + * @access public + * @return object|bool 如果没有model文件,返回false,否则返回model对象。If no model file, return false, else return the model object. + */ + public function loadZen($moduleName = '', $appName = '') + { + return parent::loadZen($moduleName); + } + + /** + * 加载model的class扩展,主要是为了开发加密代码使用。 + * 可以将主要的逻辑存放到$moduleName/ext/model/class/$extensionName.class.php中。 + * 然后在ext/model/$extension.php的扩展里面使用$this->loadExtension()来调用相应的方法。 + * ext/model/class/*.class.php代码可以加密。而ext/model/*.php可以不用加密。 + * 因为框架对model的扩展是采取合并文件的方式,ext/model/*.php文件不能加密。 + * + * Load extension class of a model thus user can encrypt the code. + * You can put the main extension logic codes in $moduleName/ext/model/class/$extensionName.class.php. + * And call them by the ext/model/$extension.php like this: $this->loadExtension('myextension')->method(). + * You can encrypt the code in ext/model/class/*.class.php. + * Because the framework will merge the extension files in ext/model/*.php to the module/model.php. + * + * @param string $extensionName + * @param string $moduleName + * @access public + * @return void + */ + public function loadExtension($extensionName, $moduleName = '') + { + if(empty($extensionName)) return false; + if(empty($moduleName)) $moduleName = $this->moduleName; + + $moduleName = strtolower($moduleName); + $extensionName = strtolower($extensionName); + + $type = 'model'; + $className = strtolower(get_class($this)); + if($className == $moduleName . 'zen' || $className == 'ext' . $moduleName . 'zen') $type = 'zen'; + + /* 设置扩展类的名字。Set the extension class name. */ + $extensionClass = $extensionName . ucfirst($moduleName); + if($type != 'model') $extensionClass .= ucfirst($type); + if(isset($this->$extensionClass)) return $this->$extensionClass; + + /* 设置扩展的名字和相应的文件。Set extenson name and extension file. */ + $moduleExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, $type); + if(!empty($moduleExtPath['site'])) $extensionFile = $moduleExtPath['site'] . 'class/' . $extensionName . '.class.php'; + if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['custom'] . 'class/' . $extensionName . '.class.php'; + if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['saas'] . 'class/' . $extensionName . '.class.php'; + if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['vision'] . 'class/' . $extensionName . '.class.php'; + if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['xuan'] . 'class/' . $extensionName . '.class.php'; + if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['common'] . 'class/' . $extensionName . '.class.php'; + + /* 载入父类。Try to import parent model file auto and then import the extension file. */ + if(!class_exists($moduleName . ucfirst($type))) helper::import($this->app->getModulePath($this->appName, $moduleName) . $type . '.php'); + if(!helper::import($extensionFile)) return false; + if(!class_exists($extensionClass)) return false; + + /* 实例化扩展类。Create an instance of the extension class and return it. */ + $extensionObject = new $extensionClass; + if($type == 'model') $extensionClass = str_replace(ucfirst($type), '', $extensionClass); + $this->$extensionClass = $extensionObject; + $this->$extensionClass->view = $this->view; + return $extensionObject; + } + /** * 设置视图文件:主视图文件,扩展视图文件, 站点扩展视图文件,以及钩子脚本。 * Set view files: the main file, extension view file, site extension view file and hook files. @@ -553,4 +649,42 @@ class control extends baseControl if($message) $this->send(array('result' => 'fail', 'message' => $message)); } + /** + * Call the functions declared in the tao files. + * + * @param string $method + * @param array $arguments + * @access public + * @return mixed + */ + public function __call($method, $arguments) + { + $moduleName = $this->app->getModuleName(); + $zenClass = $moduleName . 'Zen'; + + if(is_callable(array($this->{$zenClass}, $method))) return call_user_func_array(array($this->{$zenClass}, $method), $arguments); + + $this->app->triggerError("the module {$moduleName} has no {$method} method", __FILE__, __LINE__, $exit = true); + } + + /** + * Call the static functions declared in the tao files. + * + * @param string $method + * @param array $arguments + * @access public + * @return mixed + */ + public static function __callStatic($method, $arguments) + { + global $app; + $moduleName = $app->getModuleName(); + $zenClass = 'ext' . $moduleName . 'Zen'; + if(is_callable("{$zenClass}::{$method}")) return call_user_func_array("{$zenClass}::{$method}", $arguments); + + $zenClass = $moduleName . 'Zen'; + if(is_callable("{$zenClass}::{$method}")) return call_user_func_array("{$zenClass}::{$method}", $arguments); + + $app->triggerError("the module {$moduleName} has no {$method} method", __FILE__, __LINE__, $exit = true); + } } diff --git a/framework/model.class.php b/framework/model.class.php index 45c14dba1e..2287fb1103 100644 --- a/framework/model.class.php +++ b/framework/model.class.php @@ -21,20 +21,37 @@ include dirname(__FILE__) . '/base/model.class.php'; class model extends baseModel { /** - * 企业版部分功能是从然之合并过来的。然之代码中调用loadModel方法时传递了一个非空的appName,在禅道中会导致错误。 + * 企业版部分功能是从然之合并过来的。ZDOO代码中调用loadModel方法时传递了一个非空的appName,在禅道中会导致错误。 * 调用父类的loadModel方法来避免这个错误。 - * Some codes merged from ranzhi called the function loadModel with a non-empty appName which causes an error in zentao. + * Some codes merged from ZDOO called the function loadModel with a non-empty appName which causes an error in zentao. * Call the parent function with empty appName to avoid this error. * - * @param string $moduleName - * @access public - * @return object|bool the model object or false if model file not exists. + * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name. + * @param string $appName 应用名,如果为空,使用当前应用。The app name, if empty, use current app's name. + * @access public + * @return object|bool the model object or false if model file not exists. */ public function loadModel($moduleName, $appName = '') { return parent::loadModel($moduleName); } + /** + * 企业版部分功能是从然之合并过来的。ZDOO代码中调用loadTao方法时传递了一个非空的appName,在禅道中会导致错误。 + * 调用父类的loadTao方法来避免这个错误。 + * Some codes merged from ZDOO called the function loadTao with a non-empty appName which causes an error in zentao. + * Call the parent function with empty appName to avoid this error. + * + * @param string $moduleName 模块名,如果为空,使用当前模块。The module name, if empty, use current module's name. + * @param string $appName 应用名,如果为空,使用当前应用。The app name, if empty, use current app's name. + * @access public + * @return object|bool the model object or false if model file not exists. + */ + public function loadTao($moduleName, $appName = '') + { + return parent::loadTao($moduleName); + } + /** * 删除记录 * Delete one record. @@ -320,4 +337,59 @@ class model extends baseModel $flow = $this->loadModel('workflow')->getByModule($moduleName); if($flow && $action) return $this->loadModel('workflowhook')->execute($flow, $action, $objectID); } + + /** + * Call the functions declared in the tao files. + * + * @param string $method + * @param array $arguments + * @access public + * @return mixed + */ + public function __call($method, $arguments) + { + $moduleName = $this->getModuleName(); + $taoClass = $moduleName . 'Tao'; + + if(is_callable(array($this->{$taoClass}, $method))) return call_user_func_array(array($this->{$taoClass}, $method), $arguments); + + $this->app->triggerError("the module {$moduleName} has no {$method} method", __FILE__, __LINE__, $exit = true); + } + + /** + * Call the static functions declared in the tao files. + * + * @param string $method + * @param array $arguments + * @access public + * @return mixed + */ + public static function __callStatic($method, $arguments) + { + global $app; + + $moduleName = strtolower(get_called_class()); + + preg_match_all('/^(ext)?(\w+)model/', $moduleName, $matches); + if(isset($matches[2][0])) + { + $moduleName = $matches[2][0]; + } + else + { + preg_match_all('/^(ext)?(\w+)tao/', $moduleName, $matches); + if(isset($matches[2][0])) $moduleName = $matches[2][0]; + } + + $modelClass = 'ext' . $moduleName . 'Model'; + if(method_exists($modelClass, $method)) return call_user_func_array("{$modelClass}::{$method}", $arguments); + + $taoClass = 'ext' . $moduleName . 'Tao'; + if(method_exists($taoClass, $method)) return call_user_func_array("{$taoClass}::{$method}", $arguments); + + $taoClass = $moduleName . 'Tao'; + if(method_exists($taoClass, $method)) return call_user_func_array("{$taoClass}::{$method}", $arguments); + + $app->triggerError("the module {$moduleName} has no {$method} method", __FILE__, __LINE__, $exit = true); + } }