xxb/framework/base/router.class.php
2023-10-23 15:51:36 +08:00

2536 lines
78 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* 此文件包括ZenTaoPHP框架的三个类baseRouter, config, lang。
* The router, config and lang class file of ZenTaoPHP framework.
*
* @package framework
*
* The author disclaims copyright to this source code. In place of
* a legal notice, here is a blessing:
*
* May you do good and not evil.
* May you find forgiveness for yourself and forgive others.
* May you share freely, never taking more than you give.
*/
class baseRouter
{
/**
* ZenTaoPHP的基础目录一般是程序的根目录。
* The base path of the ZenTaoPHP framework.
*
* @var string
* @access public
*/
public $basePath;
/**
* 框架的根目录。
* The root directory of the framwork($this->basePath/framework)
*
* @var string
* @access public
*/
public $frameRoot;
/**
* 类库的根目录。{$this->basePath/lib}
* The root directory of the library($this->basePath/lib).
*
* @var string
* @access public
*/
public $coreLibRoot;
/**
* 应用名称
* The appName.
*
* @var string
* @access public
*/
public $appName = '';
/**
* 应用程序的根目录。
* The root directory of the app.
*
* @var string
* @access public
*/
public $appRoot;
/**
* 临时文件的根目录。
* The root directory of temp.
*
* @var string
* @access public
*/
public $tmpRoot;
/**
* 缓存的根目录。
* The root directory of cache.
*
* @var string
* @access public
*/
public $cacheRoot;
/**
* WWW目录。
* The root directory of www.
*
* @var string
* @access public
*/
public $wwwRoot;
/**
* 附件存放目录。
* The root directory of data.
*
* @var string
* @access public
*/
public $dataRoot;
/**
* 日志文件的根目录。
* The root directory of log.
*
* @var string
* @access public
*/
public $logRoot;
/**
* 配置文件的根目录。
* The root directory of config.
*
* @var string
* @access public
*/
public $configRoot;
/**
* 模块的根目录。
* The root directory of module.
*
* @var string
* @access public
*/
public $moduleRoot;
/**
* 主题的根目录。
* The root directory of theme.
*
* @var string
* @access public
*/
public $themeRoot;
/**
* 用户使用的语言。
* The lang of the client user.
*
* @var string
* @access public
*/
public $clientLang;
/**
* 用户使用的主题。
* The theme of the client user.
*
* @var string
* @access public
*/
public $clientTheme;
/**
* 客户端设备类型。
* The device type of client.
*
* @var string
* @access public
*/
public $clientDevice;
/**
* 当前模块的control对象。
* The control object of current module.
*
* @var object
* @access public
*/
public $control;
/**
* 模块名。
* The module name
*
* @var string
* @access public
*/
public $moduleName;
/**
* 当前访问模块的control文件。
* The control file of the module current visiting.
*
* @var string
* @access public
*/
public $controlFile;
/**
* 当前访问的方法名。
* The name of the method current visiting.
*
* @var string
* @access public
*/
public $methodName;
/**
* 当前方法的扩展文件。
* The action extension file of current method.
*
* @var string
* @access public
*/
public $extActionFile;
/**
* 访问的URI。
* The URI.
*
* @var string
* @access public
*/
public $URI;
/**
* url地址传递的参数。
* The params passed in through url.
*
* @var array
* @access public
*/
public $params;
/**
* 视图类型。
* The view type.
*
* @var string
* @access public
*/
public $viewType;
/**
* 全局$config对象。
* The global $config object.
*
* @var object
* @access public
*/
public $config;
/**
* 全局$lang对象。
* The global $lang object.
*
* @var object
* @access public
*/
public $lang;
/**
* 全局$dbh对象数据库连接句柄。
* The global $dbh object, the database connection handler.
*
* @var object
* @access public
*/
public $dbh;
/**
* 从数据库的句柄。
* The slave database handler.
*
* @var object
* @access public
*/
public $slaveDBH;
/**
* $post对象用于访问$_POST变量。
* The $post object, used to access the $_POST var.
*
* @var object
* @access public
*/
public $post;
/**
* $get对象用于访问$_GET变量。
* The $get object, used to access the $_GET var.
*
* @var object
* @access public
*/
public $get;
/**
* $session对象用于访问$_SESSION变量。
* The $session object, used to access the $_SESSION var.
*
* @var object
* @access public
*/
public $session;
/**
* $server对象用于访问$_SERVER变量。
* The $server object, used to access the $_SERVER var.
*
* @var object
* @access public
*/
public $server;
/**
* $cookie对象用于访问$_COOKIE变量。
* The $cookie object, used to access the $_COOKIE var.
*
* @var object
* @access public
*/
public $cookie;
/**
* 网站代号。
* The code of current site.
*
* @var string
* @access public
*/
public $siteCode;
/**
* 构造方法, 设置路径,类,超级变量等。注意:
* 1.应该使用createApp()方法实例化router类
* 2.如果$appRoot为空框架会根据$appName计算应用路径。
*
* The construct function.
* Prepare all the paths, classes, super objects and so on.
* Notice:
* 1. You should use the createApp() method to get an instance of the router.
* 2. If the $appRoot is empty, the framework will compute the appRoot according the $appName
*
* @param string $appName the name of the app
* @param string $appRoot the root path of the app
* @access public
* @return void
*/
public function __construct($appName = 'demo', $appRoot = '')
{
$this->setPathFix();
$this->setBasePath();
$this->setFrameRoot();
$this->setCoreLibRoot();
$this->setAppRoot($appName, $appRoot);
$this->setTmpRoot();
$this->setCacheRoot();
$this->setLogRoot();
$this->setConfigRoot();
$this->setModuleRoot();
$this->setWwwRoot();
$this->setThemeRoot();
$this->setDataRoot();
$this->loadMainConfig();
$this->loadClass('front', $static = true);
$this->loadClass('filter', $static = true);
$this->loadClass('dao', $static = true);
$this->loadClass('mobile', $static = true);
$this->setSuperVars();
$this->setDebug();
$this->setErrorHandler();
$this->setTimezone();
$this->startSession();
if($this->config->framework->multiSite) $this->setSiteCode() && $this->loadExtraConfig();
if($this->config->framework->autoConnectDB) $this->connectDB();
if($this->config->framework->multiLanguage) $this->setClientLang();
$needDetectDevice = zget($this->config->framework->detectDevice, $this->clientLang, false);
$this->clientDevice = $needDetectDevice ? $this->setClientDevice() : 'desktop';
if($this->config->framework->multiLanguage) $this->loadLang('common');
if($this->config->framework->multiTheme) $this->setClientTheme();
}
/**
* 创建一个应用。
* Create an application.
*
* @param string $appName 应用名称。 The name of the app.
* @param string $appRoot 应用根路径。The root path of the app.
* @param string $className 应用类名如果对router类做了扩展需要指定类名。When extends router class, you should pass in the child router class name.
* @static
* @access public
* @return object the app object
*/
public static function createApp($appName = 'demo', $appRoot = '', $className = '')
{
if(empty($className)) $className = __CLASS__;
return new $className($appName, $appRoot);
}
//-------------------- 路径相关方法(Path related methods)--------------------//
/**
* 设置应用名称。
* Set app name.
*
* @param string $appName
* @access public
* @return void
*/
public function setAppName($appName)
{
$this->appName = $appName;
}
/**
* 设置目录分隔符。
* Set the path directory separator.
*
* @access public
* @return void
*/
public function setPathFix()
{
define('DS', DIRECTORY_SEPARATOR);
}
/**
* 设置基础目录。
* Set the base path.
*
* @access public
* @return void
*/
public function setBasePath()
{
$this->basePath = realpath(dirname(dirname(dirname(__FILE__)))) . DS;
}
/**
* 设置框架根目录。
* Set the frame root.
*
* @access public
* @return void
*/
public function setFrameRoot()
{
$this->frameRoot = $this->basePath . 'framework' . DS;
}
/**
* 设置类库的根目录。
* Set the app lib root.
*
* @access public
* @return void
*/
public function setCoreLibRoot()
{
$this->coreLibRoot = $this->basePath . 'lib' . DS;
}
/**
* 设置应用的根目录。
* Set the app root.
*
* @param string $appName
* @param string $appRoot
* @access public
* @return void
*/
public function setAppRoot($appName = 'demo', $appRoot = '')
{
if(empty($appRoot)) $this->appRoot = $this->basePath . 'app' . DS . $appName . DS;
if(!empty($appRoot)) $this->appRoot = realpath($appRoot) . DS;
if(!is_dir($this->appRoot)) $this->triggerError("The app you call not found in {$this->appRoot}", __FILE__, __LINE__, $exit = true);
}
/**
* 设置临时文件的根目录。
* Set the tmp root.
*
* @access public
* @return void
*/
public function setTmpRoot()
{
$this->tmpRoot = $this->basePath . 'tmp' . DS;
}
/**
* 设置缓存的根目录。
* Set the cache root.
*
* @access public
* @return void
*/
public function setCacheRoot()
{
$this->cacheRoot = $this->tmpRoot . 'cache' . DS;
}
/**
* 设置log的根目录。
* Set the log root.
*
* @access public
* @return void
*/
public function setLogRoot()
{
$this->logRoot = $this->tmpRoot . 'log' . DS;
}
/**
* 设置config配置文件的根目录。
* Set the config root.
*
* @access public
* @return void
*/
public function setConfigRoot()
{
$this->configRoot = $this->basePath . 'config' . DS;
}
/**
* 设置模块的根目录。
* Set the module root.
*
* @access public
* @return void
*/
public function setModuleRoot()
{
$this->moduleRoot = $this->basePath . 'module' . DS;
}
/**
* 设置www的根目录。
* Set the www root.
*
* @access public
* @return void
*/
public function setWwwRoot()
{
$this->wwwRoot = rtrim(dirname($_SERVER['SCRIPT_FILENAME']), DS) . DS;
}
/**
* 设置主题根目录。
* Set the theme root.
*
* @access public
* @return void
*/
public function setThemeRoot()
{
$this->themeRoot = $this->wwwRoot . 'theme' . DS;
}
/**
* 设置data根目录。
* Set the data root.
*
* @access public
* @return void
*/
public function setDataRoot()
{
$this->dataRoot = $this->wwwRoot . 'data' . DS;
}
/**
* 设置超级变量。
* Set the super vars.
*
* @access public
* @return void
*/
public function setSuperVars()
{
$this->post = new super('post');
$this->get = new super('get');
$this->server = new super('server');
$this->cookie = new super('cookie');
$this->session = new super('session');
unset($GLOBALS);
unset($_REQUEST);
$_FILES = validater::filterFiles();
$_POST = validater::filterSuper($_POST);
$_GET = validater::filterSuper($_GET);
$_COOKIE = validater::filterSuper($_COOKIE);
}
/**
* 设置Debug模式。
* set Debug.
*
* @access public
* @return void
*/
public function setDebug()
{
if(!empty($this->config->debug)) error_reporting(E_ALL & ~ E_STRICT);
}
/**
* 设置错误处理句柄。
* Set the error handler.
*
* @access public
* @return void
*/
public function setErrorHandler()
{
set_error_handler(array($this, 'saveError'));
register_shutdown_function(array($this, 'shutdown'));
}
/**
* 获取应用名称
* Get app name
*
* @access public
* @return string
*/
public function getAppName()
{
return $this->appName;
}
/**
* 获取$basePath即基础路径。
* Get the $basePath var.
*
* @access public
* @return string
*/
public function getBasePath()
{
return $this->basePath;
}
/**
* 获取$frameRoot即框架根目录。
* Get the $frameRoot var.
*
* @access public
* @return string
*/
public function getFrameRoot()
{
return $this->frameRoot;
}
/**
* 获取$appRoot变量即应用的根目录。
* Get the $appRoot var.
*
* @access public
* @return string
*/
public function getAppRoot()
{
return $this->appRoot;
}
/**
* 获取$wwwRoot变量。
* Get the $wwwRoot var
*
* @access public
* @return string
*/
public function getWwwRoot()
{
return $this->wwwRoot;
}
/**
* 获取$coreLibRoot变量即应用类库的根目录。
* Get the $coreLibRoot var.
*
* @access public
* @return string
*/
public function getCoreLibRoot()
{
return $this->coreLibRoot;
}
/**
* 获取$tmpRoot变量即临时文件的根目录。
* Get the $tmpRoot var.
*
* @access public
* @return string
*/
public function getTmpRoot()
{
return $this->tmpRoot;
}
/**
* 获取$cacheRoot变量即缓存文件的根目录。
* Get the $cacheRoot var.
*
* @access public
* @return string
*/
public function getCacheRoot()
{
return $this->cacheRoot;
}
/**
* 获取$logRoot变量即日志文件的根目录。
* Get the $logRoot var.
*
* @access public
* @return string
*/
public function getLogRoot()
{
return $this->logRoot;
}
/**
* 获取$configRoot变量即配置文件的根目录。
* Get the $configRoot var.
*
* @access public
* @return string
*/
public function getConfigRoot()
{
return $this->configRoot;
}
/**
* 获取$moduleRoot变量即应用模块的根目录。
* Get the $moduleRoot var.
*
* @param string $appName
* @access public
* @return string
*/
public function getModuleRoot($appName = '')
{
if($appName == '') return $this->moduleRoot;
return dirname($this->moduleRoot) . DS . $appName . DS;
}
/**
* 获取$webRoot即应用的路径。
* Get the $webRoot var.
*
* @access public
* @return string
*/
public function getWebRoot()
{
return $this->config->webRoot;
}
/**
* 获取$themeRoot变量即主题的根目录。
* Get the $themeRoot var.
*
* @access public
* @return string
*/
public function getThemeRoot()
{
return $this->themeRoot;
}
/**
* 获取$dataRoot目录
* Get the $dataRoot var
*
* @access public
* @return string
*/
public function getDataRoot()
{
return $this->dataRoot;
}
//------ 客户端环境有关的函数(Client environment related functions) ------//
/**
* 根据配置设置当前时区。
* Set the time zone according to the config.
*
* @access public
* @return void
*/
public function setTimezone()
{
if(isset($this->config->timezone)) date_default_timezone_set($this->config->timezone);
}
/**
* 开启 session
* Start the session.
*
* @access public
* @return void
*/
public function startSession()
{
if(!defined('SESSION_STARTED') && RUN_MODE != 'install')
{
$sessionName = $this->config->sessionVar;
session_name($sessionName);
if(isset($_GET[$this->config->sessionVar])) session_id($_GET[$this->config->sessionVar]);
session_start();
define('SESSION_STARTED', true);
}
}
/**
* 根据用户浏览器的语言设置和服务器配置,选择显示的语言。
* 优先级:$lang参数 > session > cookie > 浏览器 > 配置文件。
*
* Set the language.
* Using the order of method $lang param, session, cookie, browser and the default lang.
*
* @param string $lang zh-cn|zh-tw|zh-hk|en
* @access public
* @return void
*/
public function setClientLang($lang = '')
{
if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) $this->clientLang = $this->parseHttpAcceptLang();
if(isset($_COOKIE['lang'])) $this->clientLang = $_COOKIE['lang'];
if(isset($_SESSION['lang'])) $this->clientLang = $_SESSION['lang'];
if(!empty($lang)) $this->clientLang = $lang;
if(!empty($this->clientLang))
{
$this->clientLang = strtolower($this->clientLang);
if(!isset($this->config->langs[$this->clientLang])) $this->clientLang = $this->config->default->lang;
}
else
{
$this->clientLang = $this->config->default->lang;
}
setcookie('lang', $this->clientLang, $this->config->cookieLife, $this->config->webRoot);
if(!isset($_COOKIE['lang'])) $_COOKIE['lang'] = $this->clientLang;
return true;
}
/**
* 从HTTP_ACCEPT_LANGUAGE中提出去支持的语言。
* Parse the lang str from HTTP_ACCEPT_LANGUAGE header.
*
* @access public
* @return string
*/
public function parseHttpAcceptLang()
{
if(empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) return '';
$raw = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
$pos = strpos($raw, ',');
$lang = $pos === false ? $raw : substr($raw, 0, $pos);
/* Fix clientLang for ie >= 10. https://www.drupal.org/node/365615. */
if(stripos($lang, 'hans')) $lang = 'zh-cn';
if(stripos($lang, 'hant')) $lang = 'zh-tw';
return $lang;
}
/**
* 设置客户端使用的主题,判断逻辑与客户端的语言相同。
* 主题的css和图片文件应该存放在www/theme/$themeName路径。
*
* Set the theme the client user using. The logic is same as the clientLang.
* The css and images files of an theme should saved at www/theme/$themeName
*
* @param string $theme
* @access public
* @return void
*/
public function setClientTheme($theme = '')
{
if(isset($this->config->client->theme)) $this->clientTheme = $this->config->client->theme;
if(isset($_COOKIE['theme'])) $this->clientTheme = $_COOKIE['theme'];
if(!empty($theme)) $this->clientTheme = $theme;
if(!empty($this->clientTheme))
{
$this->clientTheme = strtolower($this->clientTheme);
if(!isset($this->lang->themes[$this->clientTheme])) $this->clientTheme = $this->config->default->theme;
}
else
{
$this->clientTheme = $this->config->default->theme;
}
setcookie('theme', $this->clientTheme, $this->config->cookieLife, $this->config->webRoot);
if(!isset($_COOKIE['theme'])) $_COOKIE['theme'] = $this->clientTheme;
return true;
}
/**
* 设置客户端的设备类型。
* Set client device.
*
* @access public
* @return void
*/
public function setClientDevice()
{
$this->clientDevice = 'desktop';
if($this->cookie->device == 'mobile') $this->clientDevice = 'mobile';
if($this->cookie->device == 'desktop') $this->clientDevice = 'desktop';
if(empty($this->cookie->device) || strpos('mobile,desktop', $this->cookie->device) === false)
{
$mobile = new mobile();
$this->clientDevice = ($mobile->isMobile() and !$mobile->isTablet()) ? 'mobile' : 'desktop';
}
setcookie('device', $this->clientDevice, $this->config->cookieLife, $this->config->webRoot);
if(!isset($_COOKIE['device'])) $_COOKIE['device'] = $this->clientDevice;
return $this->clientDevice;
}
/**
* 设置站点代号,可以针对不同的站点来加载不同的扩展。
* Set the code of current site, thus can load diffrent extension of diffrent site.
*
* @access public
* @return void
*/
public function setSiteCode()
{
return $this->siteCode = helper::parseSiteCode($this->server->http_host);
}
/**
* 获取$clientLang变量即客户端的语言。
* Get the $clientLang var.
*
* @access public
* @return string
*/
public function getClientLang()
{
return $this->clientLang;
}
/**
* 获取$clientTheme变量。
* Get the $clientTheme var.
*
* @access public
* @return string
*/
public function getClientTheme()
{
return $this->config->webRoot . 'theme/' . $this->clientTheme . '/';
}
/**
* 获得客户端的终端设备。
* Get the client device.
*
* @access public
* @return void
*/
public function getClientDevice()
{
return $this->clientDevice;
}
//-------------------- 请求相关的方法(Request related methods) --------------------//
/**
* 解析本次请求的入口方法,根据请求的类型(PATH_INFO GET),调用相应的方法。
* The entrance of parseing request. According to the requestType, call related methods.
*
* @access public
* @return void
*/
public function parseRequest()
{
if($this->config->requestType == 'PATH_INFO' or $this->config->requestType == 'PATH_INFO2')
{
$this->parsePathInfo();
$this->setRouteByPathInfo();
}
elseif($this->config->requestType == 'GET')
{
$this->parseGET();
$this->setRouteByGET();
}
else
{
$this->triggerError("The request type {$this->config->requestType} not supported", __FILE__, __LINE__, $exit = true);
}
}
/**
* PATH_INFO方式解析获取$URI和$viewType。
* Parse PATH_INFO, get the $URI and $viewType.
*
* @access public
* @return void
*/
public function parsePathInfo()
{
$pathInfo = $this->getPathInfo();
if(!empty($pathInfo))
{
$dotPos = strrpos($pathInfo, '.');
if($dotPos)
{
$this->URI = substr($pathInfo, 0, $dotPos);
$this->viewType = substr($pathInfo, $dotPos + 1);
if(strpos($this->config->views, ',' . $this->viewType . ',') === false)
{
$this->viewType = $this->config->default->view;
}
}
else
{
$this->URI = $pathInfo;
$this->viewType = $this->config->default->view;
}
}
else
{
$this->viewType = $this->config->default->view;
}
}
/**
* 从$_SERVER或者$_ENV全局变量根据pathinfo变量名获取$PATH_INFO值。
* PATH_INFO的变量名几乎都是'PATH_INFO'但也有可能是ORIG_PATH_INFO。
*
* Get $PATH_INFO from $_SERVER or $_ENV by the pathinfo var name.
* Mostly, the var name of PATH_INFO is PATH_INFO, but may be ORIG_PATH_INFO.
*
* @access public
* @return string the PATH_INFO
*/
public function getPathInfo()
{
if(isset($_SERVER['PATH_INFO']))
{
$value = $_SERVER['PATH_INFO'];
}
elseif(isset($_SERVER['ORIG_PATH_INFO']))
{
$value = $_SERVER['ORIG_PATH_INFO'];
}
elseif(isset($this->URI))
{
$value = $this->URI;
$subpath = str_replace($_SERVER['DOCUMENT_ROOT'], '', dirname($_SERVER['SCRIPT_FILENAME']));
if($subpath != '/') $subpath = '/' . $subpath;
if($subpath != '' and $subpath != '/' and strpos($value, $subpath) === 0) $value = substr($value, strlen($subpath));
}
else
{
$value = @getenv('PATH_INFO');
if(empty($value)) $value = @getenv('ORIG_PATH_INFO');
}
if(strpos($value, $_SERVER['SCRIPT_NAME']) !== false) $value = str_replace($_SERVER['SCRIPT_NAME'], '', $value);
if(strpos($value, '?') === false) return trim($value, '/');
$value = parse_url($value);
$pathInfo = trim(zget($value, 'path', ''), '/');
if(trim($pathInfo, '/') == trim($this->config->webRoot, '/')) $pathInfo = '';
return $pathInfo;
}
/**
* GET请求方式解析获取$URI和$viewType。
* Parse GET, get $URI and $viewType.
*
* @access public
* @return void
*/
public function parseGET()
{
if(isset($_GET[$this->config->viewVar]))
{
$this->viewType = $_GET[$this->config->viewVar];
if(strpos($this->config->views, ',' . $this->viewType . ',') === false) $this->viewType = $this->config->default->view;
}
else
{
$this->viewType = $this->config->default->view;
}
$this->URI = $_SERVER['REQUEST_URI'];
}
/**
* 获取$URL。
* Get the $URL.
*
* @param bool $full true, the URI contains the webRoot, else only hte URI.
* @access public
* @return string
*/
public function getURI($full = false)
{
if($full and $this->config->requestType == 'PATH_INFO')
{
if($this->URI) return $this->config->webRoot . $this->URI . '.' . $this->viewType;
return $this->config->webRoot;
}
return $this->URI;
}
/**
* 获取$vewType变量。
* Get the $viewType var.
*
* @access public
* @return string
*/
public function getViewType()
{
return $this->viewType;
}
//-------------------- 模块及扩展设置(Module and extension) --------------------//
/**
* 加载common模块。
*
* common模块比较特别它会执行几乎每次请求都需要执行的操作例如
* 打开session检查权限等等。
* 加载完$lang, $config, $dbh后需要在入口文件(www/index.php)中手动调用该方法。
*
* Load the common module
*
* The common module is a special module, which can be used to do some common things. For examle:
* start session, check priviledge and so on.
* This method should called manually in the router file(www/index.php) after the $lang, $config, $dbh loaded.
*
* @access public
* @return object|bool the common model object or false if not exits.
*/
public function loadCommon()
{
$this->setModuleName('common');
$commonModelFile = $this->setModelFile('common');
if(!file_exists($commonModelFile)) return false;
helper::import($commonModelFile);
if($this->config->framework->extensionLevel == 0 and class_exists('commonModel')) return new commonModel();
if($this->config->framework->extensionLevel > 0 and class_exists('extCommonModel')) return new extCommonModel();
if(class_exists('commonModel')) return new commonModel();
return false;
}
/**
* 设置要被调用的模块名。
* Set the name of the module to be called.
*
* @param string $moduleName the module name
* @access public
* @return void
*/
public function setModuleName($moduleName = '')
{
if($this->checkModuleName($moduleName)) $this->moduleName = strtolower($moduleName);
}
/**
* 设置要被调用的控制器文件。
* Set the control file of the module to be called.
*
* @param bool $exitIfNone 没有找到该控制器文件的情况如果该参数为true则终止程序如果为false则打印错误日志
* If control file not foundde, how to do. True, die the whole app. false, log error.
* @access public
* @return bool
*/
public function setControlFile($exitIfNone = true)
{
$this->controlFile = $this->moduleRoot . $this->moduleName . DS . 'control.php';
if(file_exists($this->controlFile)) return true;
$this->triggerError("the control file $this->controlFile not found.", __FILE__, __LINE__, $exitIfNone);
}
/**
* 设置要被调用的方法名。
* Set the name of the method calling.
*
* @param string $methodName
* @access public
* @return void
*/
public function setMethodName($methodName = '')
{
if($this->checkMethodName($methodName)) $this->methodName = strtolower($methodName);
}
/**
* 获取一个模块的路径。
* Get the path of one module.
*
* @param string $appName the app name
* @param string $moduleName the module name
* @access public
* @return string the module path
*/
public function getModulePath($appName = '', $moduleName = '')
{
if($moduleName == '') $moduleName = $this->moduleName;
if($this->checkModuleName($moduleName))
{
$modulePath = $this->getModuleRoot($appName) . strtolower($moduleName) . DS;
return $modulePath;
}
}
/**
* 获取一个模块的扩展路径。 Get extension path of one module.
*
* If the extensionLevel == 0, return empty array.
* If the extensionLevel == 1, return the common extension directory.
* If the extensionLevel == 2, return the common and site extension directories.
*
* @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
* @access public
* @return string the extension path.
*/
public function getModuleExtPath($appName, $moduleName, $ext)
{
/* 检查失败或者extensionLevel为0直接返回空。If check failed or extensionLevel == 0, return empty array. */
if(!$this->checkModuleName($moduleName) or $this->config->framework->extensionLevel == 0) return array();
/* When extensionLevel == 1. */
$modulePath = $this->getModulePath($appName, $moduleName);
$paths = array();
$paths['common'] = $modulePath . 'ext' . DS . $ext . DS;
if($this->config->framework->extensionLevel == 1) return $paths;
/* When extensionLevel == 2. */
$paths['site'] = empty($this->siteCode) ? '' : $modulePath . 'ext' . DS . '_' . $this->siteCode . DS . $ext . DS;
return $paths;
}
/**
* 检查模块中某一个变量必须为英文字母和数字组合。Check module a variable must be ascii.
*
* @param string $var
* @access public
* @return bool
*/
public function checkModuleName($var)
{
global $filter;
$rule = $filter->default->moduleName;
if(validater::checkByRule($var, $rule)) return true;
$this->triggerError("'$var' illegal. ", __FILE__, __LINE__, $exit = true);
}
/**
* 检查方法中某一个变量必须为英文字母和数字组合。Check method a variable must be ascii.
*
* @param string $var
* @access public
* @return bool
*/
public function checkMethodName($var)
{
global $filter;
$rule = $filter->default->methodName;
if($this->config->framework->filterParam == 2 and isset($filter->{$this->moduleName}->methodName)) $rule = $filter->{$this->moduleName}->methodName;
if(validater::checkByRule($var, $rule)) return true;
$this->triggerError("'$var' illegal. ", __FILE__, __LINE__, $exit = true);
}
/**
* 设置Action的扩展文件。 Set the action extension file.
*
* @access public
* @return bool
*/
public function setActionExtFile()
{
$moduleExtPaths = $this->getModuleExtPath('', $this->moduleName, 'control');
/* 如果扩展目录为空不包含任何扩展文件。If there's no ext pathes return false.*/
if(empty($moduleExtPaths)) return false;
/* 如果extensionLevel == 2且扩展文件存在返回该站点扩展文件。If extensionLevel == 2 and site extensionFile exists, return it. */
if($this->config->framework->extensionLevel == 2 and !empty( $moduleExtPaths['site']))
{
$this->extActionFile = $moduleExtPaths['site'] . $this->methodName . '.php';
if(file_exists($this->extActionFile)) return true;
}
/* 然后再尝试寻找公共扩展文件。Then try to find the common extension file. */
$this->extActionFile = $moduleExtPaths['common'] . $this->methodName . '.php';
return file_exists($this->extActionFile);
}
/**
* 设置一个模块的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
*/
public function setModelFile($moduleName, $appName = '')
{
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;
/* 计算扩展的文件和hook文件。Compute the extension files and hook files. */
$hookFiles = array();
$extFiles = array();
$siteExtended = false;
$modelExtPaths = $this->getModuleExtPath($appName, $moduleName, 'model');
foreach($modelExtPaths as $extType => $modelExtPath)
{
if(empty($modelExtPath)) continue;
$tmpHookFiles = helper::ls($modelExtPath . 'hook/', '.php');
$tmpExtFiles = helper::ls($modelExtPath, '.php');
$hookFiles = array_merge($hookFiles, $tmpHookFiles);
$extFiles = array_merge($extFiles, $tmpExtFiles);
if($extType == 'site' and (!empty($tmpHookFiles) or !empty($tmpExtFiles))) $siteExtended = true;
}
/* 如果没有扩展文件,返回主文件。 If no extension or hook files, return the main file directly. */
if(empty($extFiles) and empty($hookFiles)) return $mainModelFile;
/* 计算合并之后的modelFile路径。Compute the merged model file path. */
$extModelPrefix = ($siteExtended and !empty($this->siteCode)) ? $this->siteCode[0] . DS . $this->siteCode : '';
$mergedModelDir = $this->getTmpRoot() . 'model' . DS . ($extModelPrefix ? $extModelPrefix . DS : '');
$mergedModelFile = $mergedModelDir . $moduleName . '.php';
if(!is_dir($mergedModelDir)) mkdir($mergedModelDir, 0755, true);
/* 判断缓存文件目录是否存在以及是否可写。 Judge whether the merged model dir exist and writable. */
if(!is_dir($mergedModelDir)) $this->triggerError("$mergedModelDir not exist.", __FILE__, __LINE__, true);
if(!is_writable($mergedModelDir)) $this->triggerError("$mergedModelDir not writable.", __FILE__, __LINE__, true);
/* 判断生成的缓存文件是否需要更新。 Judge whether the merged model file needed update or not. */
if(!$this->needModelFileUpdate($mergedModelFile, $extFiles, $hookFiles, $modelExtPaths, $mainModelFile)) return $mergedModelFile;
/* 合并扩展和hook文件。Merge the extension and hook files. */
$modelLines = $this->mergeModelExtFiles($moduleName, $mainModelFile, $extFiles, $mergedModelDir);
$this->mergeModelHookFiles($moduleName, $mainModelFile, $modelLines, $hookFiles, $mergedModelDir, $mergedModelFile);
return $mergedModelFile;
}
/**
* 检查合并之后的model文件是否需要更新。Check whether the merged model file need update or not.
*
* @param string $mainModelFile
* @param string $mergedModelFile
* @param string $modelExtPaths
* @param array $extFiles
* @param array $hookFiles
* @access public
* @return bool
*/
public function needModelFileUpdate($mergedModelFile, $extFiles, $hookFiles, $modelExtPaths, $mainModelFile)
{
$lastTime = file_exists($mergedModelFile) ? filemtime($mergedModelFile) : 0;
foreach($extFiles as $extFile) if(filemtime($extFile) > $lastTime) return true;
foreach($hookFiles as $hookFile) if(filemtime($hookFile) > $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;
if(!empty($modelExtPaths['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;
}
if(filemtime($mainModelFile) > $lastTime) return true;
return false;
}
/**
* 将model的扩展文件合并在一起。Merge model ext files.
*
* @param string $moduleName
* @param string $mainModelFile
* @param array $extFiles
* @param string $mergedModelDir
* @access public
* @return void
*/
public function mergeModelExtFiles($moduleName, $mainModelFile, $extFiles, $mergedModelDir)
{
/* 设置类名。Set the class names. */
$modelClass = "{$moduleName}Model";
$tmpModelClass = "tmpExt$modelClass";
$modelFilePath = substr($mainModelFile, strlen($this->basePath));
/* 开始拼装代码。Prepare the codes. */
$modelLines = "<?php\n";
$modelLines .= "global \$app;\n";
$modelLines .= "helper::cd(\$app->getBasePath());\n";
$modelLines .= "helper::import('$modelFilePath');\n";
$modelLines .= "helper::cd();\n";
$modelLines .= "class $tmpModelClass extends $modelClass \n{\n";
/* 将扩展文件的代码合并到代码中。Cycle all the extension files and merge them into model lines. */
foreach($extFiles as $extFile) $modelLines .= self::removePHPTAG($extFile);
/* 做个标记方便后面替换代码使用。Make a mark for replacing codes. */
$replaceMark = '//**//';
$modelLines .= "\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))
{
if(!class_exists($tmpModelClass)) include $tmpModelFile;
return $modelLines;
}
$this->triggerError("ERROR: $tmpModelFile not writable.", __FILE__, __LINE__, true);
}
/**
* 合并model的hook脚本。Merge hook files for a model.
*
* @access public
* @return void
*/
public function mergeModelHookFiles($moduleName, $mainModelFile, $modelLines, $hookFiles, $mergedModelDir, $mergedModelFile)
{
/* 定义相关变量。Init vars. */
$modelClass = $moduleName . 'Model';
$extModelClass = 'ext' . $modelClass;
$tmpModelClass = 'tmpExt' . $modelClass;
$tmpModelFile = $mergedModelDir . "tmp$moduleName.php";
$replaceMark = '//**//';
/* 读取hook文件。Get hook codes need to merge. */
$hookCodes = array();
foreach($hookFiles as $hookFile)
{
/* 跳过空的 hook 文件。Skip empty hook file. */
$hookContent = file_get_contents($hookFile);
if(empty($hookContent)) continue;
/* 通过文件名获得其对应的方法名。Get methods according it's filename. */
$fileName = baseName($hookFile);
list($method) = explode('.', $fileName);
$hookCodes[$method][] = self::removePHPTAG($hookFile);
}
/* 合并Hook文件。Cycle the hook methods and merge hook codes. */
$hookedMethods = array_keys($hookCodes);
$mainModelCodes = file($mainModelFile);
$mergedModelCodes = file($tmpModelFile);
foreach($hookedMethods as $method)
{
/* 通过反射获得hook脚本对应的方法所在的文件和起止行数。Reflection the hooked method to get it's defined position. */
$methodRelfection = new reflectionMethod($tmpModelClass, $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 = 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);
}
/* 保存最终的Model文件。Save the last merged model file. */
$modelLines = str_replace($tmpModelClass, $extModelClass, $modelLines);
file_put_contents($mergedModelFile, $modelLines);
unlink($tmpModelFile);
}
/**
* Remove tags of PHP
*
* @param string $fileName
* @static
* @access public
* @return string
*/
static public function removePHPTAG($fileName)
{
$code = trim(file_get_contents($fileName));
if(strpos($code, '<?php') === 0) $code = ltrim($code, '<?php');
if(strrpos($code, '?>') !== false) $code = rtrim($code, '?>');
return trim($code);
}
//-------------------- 路由相关方法(Routing related methods) --------------------//
/**
* 设置路由(PATH_INFO 方式)
* 1.设置模块名;
* 2.设置方法名;
* 3.设置控制器文件。
*
* Set the route according to PATH_INFO.
* 1. set the module name.
* 2. set the method name.
* 3. set the control file.
*
* @access public
* @return void
*/
public function setRouteByPathInfo()
{
if(!empty($this->URI))
{
/*
* 根据$requestFix分割符分割网址。
* There's the request seperator, split the URI by it.
**/
if(strpos($this->URI, $this->config->requestFix) !== false)
{
$items = explode($this->config->requestFix, $this->URI);
$this->setModuleName($items[0]);
$this->setMethodName($items[1]);
}
/*
* 如果网址中没有分隔符,使用默认的方法。
* No reqeust seperator, use the default method name.
**/
else
{
$this->setModuleName($this->URI);
$this->setMethodName($this->config->default->method);
}
}
else
{
$this->setModuleName($this->config->default->module); // 使用默认模块 use the default module.
$this->setMethodName($this->config->default->method); // 使用默认方法 use the default method.
}
$this->setControlFile();
}
/**
* 设置路由(GET 方式)
* 1.设置模块名;
* 2.设置方法名;
* 3.设置控制器文件。
*
* Set the route according to GET.
* 1. set the module name.
* 2. set the method name.
* 3. set the control file.
*
* @access public
* @return void
*/
public function setRouteByGET()
{
$moduleName = isset($_GET[$this->config->moduleVar]) ? strtolower($_GET[$this->config->moduleVar]) : $this->config->default->module;
$methodName = isset($_GET[$this->config->methodVar]) ? strtolower($_GET[$this->config->methodVar]) : $this->config->default->method;
$this->setModuleName($moduleName);
$this->setControlFile();
$this->setMethodName($methodName);
}
/**
* 加载一个模块:
* 1. 引入控制器文件或扩展的方法文件;
* 2. 创建control对象
* 3. 解析url得到请求的参数
* 4. 使用call_user_function_array调用相应的方法。
*
* Load a module.
* 1. include the control file or the extension action file.
* 2. create the control object.
* 3. set the params passed in through url.
* 4. call the method by call_user_function_array
*
* @access public
* @return bool|object if the module object of die.
*/
public function loadModule()
{
$appName = $this->appName;
$moduleName = $this->moduleName;
$methodName = $this->methodName;
/*
* 引入该模块的control文件。
* Include the control file of the module.
**/
$file2Included = $this->setActionExtFile() ? $this->extActionFile : $this->controlFile;
chdir(dirname($file2Included));
include $file2Included;
/*
* 设置control的类名。
* Set the class name of the control.
**/
$className = class_exists("my$moduleName") ? "my$moduleName" : $moduleName;
if(!class_exists($className)) $this->triggerError("the control $className not found", __FILE__, __LINE__, $exit = true);
/*
* 创建control类的实例。
* Create a instance of the control.
**/
$module = new $className();
if(!method_exists($module, $methodName)) $this->triggerError("the module $moduleName has no $methodName method", __FILE__, __LINE__, $exit = true);
$this->control = $module;
/* include default value for module*/
$defaultValueFiles = glob($this->getTmpRoot() . "defaultvalue/*.php");
if($defaultValueFiles) foreach($defaultValueFiles as $file) include $file;
/*
* 使用反射机制获取函数参数的默认值。
* Get the default settings of the method to be called using the reflecting.
*
* */
$defaultParams = array();
$methodReflect = new reflectionMethod($className, $methodName);
foreach($methodReflect->getParameters() as $param)
{
$name = $param->getName();
$default = '_NOT_SET';
if(isset($paramDefaultValue[$appName][$className][$methodName][$name]))
{
$default = $paramDefaultValue[$appName][$className][$methodName][$name];
}
elseif(isset($paramDefaultValue[$className][$methodName][$name]))
{
$default = $paramDefaultValue[$className][$methodName][$name];
}
elseif($param->isDefaultValueAvailable())
{
$default = $param->getDefaultValue();
}
$defaultParams[$name] = $default;
}
/**
* 根据PATH_INFO或者GET方式设置请求的参数。
* Set params according PATH_INFO or GET.
*/
if($this->config->requestType != 'GET')
{
$this->setParamsByPathInfo($defaultParams);
}
else
{
$this->setParamsByGET($defaultParams);
}
if($this->config->framework->filterParam == 2)
{
$_GET = validater::filterParam($_GET, 'get');
$_COOKIE = validater::filterParam($_COOKIE, 'cookie');
}
/* 调用该方法 Call the method. */
call_user_func_array(array($module, $methodName), $this->params);
return $module;
}
/**
* 设置请求的参数(PATH_INFO 方式)。
* Set the params by PATH_INFO.
*
* @param array $defaultParams the default settings of the params.
* @param string $type
* @access public
* @return void
*/
public function setParamsByPathInfo($defaultParams = array(), $type = '')
{
$params = array();
if($type != 'fetch')
{
/* 分割URI。 Spit the URI. */
$items = explode($this->config->requestFix, $this->URI);
$itemCount = count($items);
/**
* 前两项为模块名和方法名参数从下标2开始。
* The first two item is moduleName and methodName. So the params should begin at 2.
**/
for($i = 2; $i < $itemCount; $i ++)
{
$key = key($defaultParams); // Get key from the $defaultParams.
if(empty($key)) continue;
$params[$key] = $items[$i];
next($defaultParams);
}
}
$this->params = $this->mergeParams($defaultParams, $params);
}
/**
* 设置请求的参数(GET 方式)。
* Set the params by GET.
*
* @param array $defaultParams the default settings of the params.
* @param string $type
* @access public
* @return void
*/
public function setParamsByGET($defaultParams, $type = '')
{
$params = array();
if($type != 'fetch')
{
/* Unset moduleVar, methodVar, viewVar and session 变量, 剩下的作为参数。 */
/* Unset the moduleVar, methodVar, viewVar and session var, all the left are the params. */
unset($_GET[$this->config->moduleVar]);
unset($_GET[$this->config->methodVar]);
unset($_GET[$this->config->viewVar]);
unset($_GET[$this->config->sessionVar]);
$params = $_GET;
}
$this->params = $this->mergeParams($defaultParams, $params);
}
/**
* 合并请求的参数和默认参数,这样就可以省略已经有默认值的参数了。
* Merge the params passed in and the default params. Thus the params which have default values needn't pass value, just like a function.
*
* @param array $defaultParams the default params defined by the method.
* @param array $passedParams the params passed in through url.
* @access public
* @return array the merged params.
*/
public function mergeParams($defaultParams, $passedParams)
{
global $filter;
/* Remove these two params. */
unset($passedParams['onlybody']);
unset($passedParams['HTTP_X_REQUESTED_WITH']);
/* Check params from URL. */
$nameRule = isset($filter->{$this->moduleName}->{$this->methodName}->paramName) ? $filter->{$this->moduleName}->{$this->methodName}->paramName : $filter->default->paramName;
foreach($passedParams as $param => $value)
{
if(!validater::checkByRule($param, $nameRule)) die('Bad Request!');
$valueRule = $filter->default->paramValue;
if(isset($filter->{$this->moduleName}->{$this->methodName}->paramValue[$param]))
{
$valueRule = $filter->{$this->moduleName}->{$this->methodName}->paramValue[$param];
}
if($value and !validater::checkByRule($value, $valueRule)) die('Bad Request!');
}
$passedParams = array_values($passedParams);
$i = 0;
foreach($defaultParams as $key => $defaultValue)
{
if(isset($passedParams[$i]))
{
$defaultParams[$key] = strip_tags($passedParams[$i]);
}
else
{
if($defaultValue === '_NOT_SET') $this->triggerError("The param '$key' should pass value. ", __FILE__, __LINE__, $exit = true);
}
$i ++;
}
return $defaultParams;
}
/**
* 获取$moduleName变量。
* Get the $moduleName var.
*
* @access public
* @return string
*/
public function getModuleName()
{
return $this->moduleName;
}
/**
* 获取$controlFile变量。
* Get the $controlFile var.
*
* @access public
* @return string
*/
public function getControlFile()
{
return $this->controlFile;
}
/**
* 获取$methodName变量。
* Get the $methodName var.
*
* @access public
* @return string
*/
public function getMethodName()
{
return $this->methodName;
}
/**
* 获取$param变量。
* Get the $param var.
*
* @access public
* @return string
*/
public function getParams()
{
return $this->params;
}
//-------------------- 常用的工具方法(Tool methods) ------------------//
/**
* 从类库中加载一个类文件。
*
* Load a class file.
*
* @param string $className the class name
* @param bool $static statis class or not
* @access public
* @return object|bool the instance of the class or just true.
*/
public function loadClass($className, $static = false)
{
$className = strtolower($className);
/* 搜索$coreLibRoot(Search in $coreLibRoot) */
$classFile = $this->coreLibRoot . $className;
if(is_dir($classFile)) $classFile .= DS . $className;
$classFile .= '.class.php';
if(!helper::import($classFile)) $this->triggerError("class file $classFile not found", __FILE__, __LINE__, $exit = true);
/* 如果是静态调用,则返回(If staitc, return) */
if($static) return true;
/* 实例化该类(Instance it) */
global $$className;
if(!class_exists($className)) $this->triggerError("the class $className not found in $classFile", __FILE__, __LINE__, $exit = true);
if(!is_object($$className)) $$className = new $className();
return $$className;
}
/**
* 加载整个应用公共的配置文件。
* Load the common config files for the app.
*
* @access public
* @return void
*/
public function loadMainConfig()
{
/* 初始化$config对象。Init the $config object. */
global $config, $filter;
if(!is_object($config)) $config = new config();
$this->config = $config;
/* 加载主配置文件。 Load the main config file. */
$mainConfigFile = $this->configRoot . 'config.php';
if(!file_exists($mainConfigFile)) $this->triggerError("The main config file $mainConfigFile not found", __FILE__, __LINE__, $exit = true);
include $mainConfigFile;
}
/**
* 当multiSite功能打开的时候加载额外的配置文件。
* When multiSite feature enabled, load extra config file.
*
* @access public
* @return void
*/
public function loadExtraConfig()
{
global $config;
$multiConfigFile = $this->configRoot . 'multi.php';
if(file_exists($multiConfigFile)) include $multiConfigFile;
$siteConfigFile = $this->configRoot . "sites/{$this->siteCode}.php";
if(file_exists($siteConfigFile)) include $siteConfigFile;
}
/**
* 加载模块的config文件返回全局$config对象。
* 如果该模块是common加载$configRoot的配置文件其他模块则加载其模块的配置文件。
*
* Load config and return it as the global config object.
* If the module is common, search in $configRoot, else in $modulePath.
*
* @param string $moduleName module name
* @param string $appName app name
* @param bool $exitIfNone exit or not
* @access public
* @return object|bool the config object or false.
*/
public function loadModuleConfig($moduleName, $appName = '')
{
global $config;
if($config and (!isset($config->$moduleName) or !is_object($config->$moduleName))) $config->$moduleName = new stdclass();
/* 初始化数组。Init the variables. */
$extConfigFiles = array();
$commonExtConfigFiles = array();
$siteExtConfigFiles = array();
/* 先获得模块的主配置文件。Get the main config file for current module first. */
$mainConfigFile = $this->getModulePath($appName, $moduleName) . 'config.php';
/* 查找扩展配置文件。Get extension config files. */
if($config->framework->extensionLevel > 0) $extConfigPath = $this->getModuleExtPath($appName, $moduleName, 'config');
if($config->framework->extensionLevel >= 1 and !empty($extConfigPath['common'])) $commonExtConfigFiles = helper::ls($extConfigPath['common'], '.php');
if($config->framework->extensionLevel == 2 and !empty($extConfigPath['site'])) $siteExtConfigFiles = helper::ls($extConfigPath['site'], '.php');
$extConfigFiles = array_merge($commonExtConfigFiles, $siteExtConfigFiles);
/* 将主配置文件和扩展配置文件合并在一起。Put the main config file and extension config files together. */
$configFiles = array_merge(array($mainConfigFile), $extConfigFiles);
/* 加载每一个配置文件。Load every config file. */
static $loadedConfigs = array();
foreach($configFiles as $configFile)
{
if(in_array($configFile, $loadedConfigs)) continue;
if(file_exists($configFile)) include $configFile;
$loadedConfigs[] = $configFile;
}
/* 加载数据库中与本模块相关的配置项。Merge from the db configs. */
if($moduleName != 'common')
{
if(isset($config->system->$moduleName)) $this->mergeConfig($config->system->$moduleName, $moduleName);
if(isset($config->personal->$moduleName)) $this->mergeConfig($config->personal->$moduleName, $moduleName);
}
}
/**
* Merge db config.
*
* @param array $dbConfig
* @param string $moduleName
* @access public
* @return void
*/
public function mergeConfig($dbConfig, $moduleName = 'common')
{
global $config;
/* 如果没有设置本模块配置则首先进行初始化。Init the $config->$moduleName if not set.*/
if($moduleName != 'common' and !isset($config->$moduleName)) $config->$moduleName = new stdclass();
$config2Merge = $config;
if($moduleName != 'common') $config2Merge = $config->$moduleName;
foreach($dbConfig as $item)
{
if($item->section)
{
if(!isset($config2Merge->{$item->section})) $config2Merge->{$item->section} = new stdclass();
if(is_object($config2Merge->{$item->section}))
{
$config2Merge->{$item->section}->{$item->key} = $item->value;
}
}
else
{
$config2Merge->{$item->key} = $item->value;
}
}
}
/**
* 向客户端输出配置参数,客户端可以根据这些参数实现和调整请求的逻辑。
* Export the config params to the client, thus the client can adjust it's logic according the config.
*
* @access public
* @return void
*/
public function exportConfig()
{
$view = new stdclass();
$view->version = $this->config->version;
$view->requestType = $this->config->requestType;
$view->requestFix = $this->config->requestFix;
$view->moduleVar = $this->config->moduleVar;
$view->methodVar = $this->config->methodVar;
$view->viewVar = $this->config->viewVar;
$view->sessionVar = $this->config->sessionVar;
$this->session->set('random', mt_rand(0, 10000));
$view->sessionName = session_name();
$view->sessionID = session_id();
$view->random = $this->session->random;
$view->expiredTime = ini_get('session.gc_maxlifetime');
$view->serverTime = time();
echo json_encode($view);
}
/**
* 加载语言文件,返回全局$lang对象。
* Load lang and return it as the global lang object.
*
* @param string $moduleName the module name
* @param string $appName the app name
* @access public
* @return bool|object the lang object or false.
*/
public function loadLang($moduleName, $appName = '')
{
/* 初始化变量。Init vars. */
$modulePath = $this->getModulePath($appName, $moduleName);
$extLangFiles = array();
$langFilesToLoad = array();
/* 判断主语言文件是否存在。Whether the main lang file exists or not. */
$mainLangFile = $modulePath . 'lang' . DS . $this->clientLang . '.php';
if(file_exists($mainLangFile)) $langFilesToLoad[] = $mainLangFile;
/* 获取扩展语言文件。If extensionLevel > 0, get extension lang files. */
if($this->config->framework->extensionLevel > 0)
{
$commonExtLangFiles = array();
$siteExtLangFiles = array();
$extLangPath = $this->getModuleExtPath($appName, $moduleName, 'lang');
if($this->config->framework->extensionLevel >= 1 and !empty($extLangPath['common'])) $commonExtLangFiles = helper::ls($extLangPath['common'] . $this->clientLang, '.php');
if($this->config->framework->extensionLevel == 2 and !empty($extLangPath['site'])) $siteExtLangFiles = helper::ls($extLangPath['site'] . $this->clientLang, '.php');
$extLangFiles = array_merge($commonExtLangFiles, $siteExtLangFiles);
}
/* 计算最终要加载的语言文件。 Get the lang files to be loaded. */
$langFilesToLoad = array_merge($langFilesToLoad, $extLangFiles);
if(empty($langFilesToLoad)) return false;
/* 加载语言文件。Load lang files. */
global $lang;
if(!is_object($lang)) $lang = new language();
if(!isset($lang->$moduleName)) $lang->$moduleName = new stdclass();
static $loadedLangs = array();
foreach($langFilesToLoad as $langFile)
{
if(in_array($langFile, $loadedLangs)) continue;
include $langFile;
$loadedLangs[] = $langFile;
}
$this->lang = $lang;
return $lang;
}
/**
* 连接数据库。
* Connect to database.
*
* @access public
* @return void
*/
public function connectDB()
{
global $config, $dbh, $slaveDBH;
if(!isset($config->installed) or !$config->installed) return;
if(isset($config->db->host)) $this->dbh = $dbh = $this->connectByPDO($config->db);
if(isset($config->slaveDB->host)) $this->slaveDBH = $slaveDBH = $this->connectByPDO($config->slaveDB);
}
/**
* 使用PDO连接数据库。
* Connect database by PDO.
*
* @param object $params the database params.
* @access public
* @return object|bool
*/
public function connectByPDO($params)
{
if(!isset($params->driver)) self::triggerError('no pdo driver defined, it should be mysql or sqlite', __FILE__, __LINE__, $exit = true);
if(!isset($params->user)) return false;
if($params->driver == 'mysql')
{
$dsn = "mysql:host={$params->host}; port={$params->port}; dbname={$params->name}";
}
try
{
$dbh = new PDO($dsn, $params->user, $params->password, array(PDO::ATTR_PERSISTENT => $params->persistant));
$dbh->exec("SET NAMES {$params->encoding}");
/*
* 如果系统是Linux开启仿真预处理和缓冲查询。
* If run on linux, set emulatePrepare and bufferQuery to true.
**/
if(!isset($params->emulatePrepare) and PHP_OS == 'Linux') $params->emulatePrepare = true;
if(!isset($params->bufferQuery) and PHP_OS == 'Linux') $params->bufferQuery = true;
$dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
if(isset($params->strictMode) and $params->strictMode == false) $dbh->exec("SET @@sql_mode= ''");
if(isset($params->emulatePrepare)) $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, $params->emulatePrepare);
if(isset($params->bufferQuery)) $dbh->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, $params->bufferQuery);
return $dbh;
}
catch (PDOException $exception)
{
self::triggerError($exception->getMessage(), __FILE__, __LINE__, $exit = true);
}
}
//-------------------- 错误处理方法(Error methods) ------------------//
/**
* 程序停止时执行的函数。
* The shutdown handler.
*
* @access public
* @return void
*/
public function shutdown()
{
/* 如果debug模式开启保存sql语句(If debug on, save sql queries) */
if(!empty($this->config->debug)) $this->saveSQL();
/*
* 发现错误,保存到日志中。
* If any error occers, save it.
* */
if(!function_exists('error_get_last')) return;
$error = error_get_last();
if($error) $this->saveError($error['type'], $error['message'], $error['file'], $error['line']);
}
/**
* 触发一个错误。
* Trigger an error.
*
* @param string $message 错误信息 error message
* @param string $file 所在文件 the file error occers
* @param int $line 错误行 the line error occers
* @param bool $exit 是否停止程序 exit the program or not
* @access public
* @return void
*/
public function triggerError($message, $file, $line, $exit = false)
{
/* 设置错误信息(Set the error info) */
$message = htmlspecialchars($message);
$log = "ERROR: $message in $file on line $line";
if(isset($_SERVER['SCRIPT_URI'])) $log .= ", request: $_SERVER[SCRIPT_URI]";;
$trace = debug_backtrace();
extract($trace[0]);
extract($trace[1]);
$log .= ", last called by $file on line $line through function $function.\n";
/* 触发错误(Trigger the error) */
trigger_error($log, $exit ? E_USER_ERROR : E_USER_WARNING);
}
/**
* 保存错误信息。
* Save error info.
*
* @param int $level
* @param string $message
* @param string $file
* @param int $line
* @access public
* @return void
*/
public function saveError($level, $message, $file, $line)
{
if(empty($this->config->debug)) return true;
if(!is_dir($this->logRoot)) die("$this->logRoot not exist.");
if(!is_writable($this->logRoot)) die("$this->logRoot not writable.");
/*
* 删除设定时间之前的日志。
* Delete the log before the set time.
**/
if(mt_rand(0, 10) == 1)
{
$logDays = isset($this->config->framework->logDays) ? $this->config->framework->logDays : 14;
$dayTime = time() - $logDays * 24 * 3600;
foreach(glob($this->getLogRoot() . '*') as $logFile)
{
if(filemtime($logFile) <= $dayTime) unlink($logFile);
}
}
/*
* 忽略该错误Redefining already defined constructor。
* Skip the error: Redefining already defined constructor.
**/
if(strpos($message, 'Redefining') !== false) return true;
/*
* 设置错误信息。
* Set the error info.
**/
$errorLog = "\n" . date('H:i:s') . " $message in <strong>$file</strong> on line <strong>$line</strong> ";
$errorLog .= "when visiting <strong>" . htmlspecialchars($this->getURI()) . "</strong>\n";
/*
* 当 debug >=3 时打印调用栈信息
* Print stack trace information when debug >= 3
**/
if($this->config->debug > 2)
{
$e = new Exception();
$errorLog .= "==== Stacktrace =====================================\n" . $e->getTraceAsString() . "\n";
}
/*
* 为了安全起见,对公网环境隐藏脚本路径。
* If the ip is pulic, hidden the full path of scripts.
*/
if(!defined('IN_SHELL') and !($this->server->remote_addr == '127.0.0.1' or filter_var($this->server->remote_addr, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE) === false))
{
$errorLog = str_replace($this->getBasePath(), '', $errorLog);
}
/* 保存到日志文件(Save to log file) */
$errorFile = $this->logRoot . 'php.' . date('Ymd') . '.log.php';
if(!is_file($errorFile)) file_put_contents($errorFile, "<?php\n die();\n?>\n");
$fh = fopen($errorFile, 'a');
if($fh) fwrite($fh, strip_tags($errorLog)) and fclose($fh);
/*
* 如果debug > 1显示warning, notice级别的错误。
* If the debug > 1, show warning, notice error.
**/
if($level == E_NOTICE or $level == E_WARNING or $level == E_STRICT or $level == 8192) // 8192: E_DEPRECATED
{
if(!empty($this->config->debug) and $this->config->debug > 1)
{
$cmd = "vim +$line $file";
$size = strlen($cmd);
echo "<pre class='alert alert-danger'>$message: ";
echo "<input type='text' value='$cmd' size='$size' style='border:none; background:none;' onclick='this.select();' /></pre>";
}
}
/*
* 如果是严重错误,停止程序。
* If error level is serious, die.
* */
if($level == E_ERROR or $level == E_PARSE or $level == E_CORE_ERROR or $level == E_COMPILE_ERROR or $level == E_USER_ERROR)
{
if(empty($this->config->debug)) die();
if(PHP_SAPI == 'cli') die($errorLog);
$htmlError = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8' /></head>";
$htmlError .= "<body>" . nl2br($errorLog) . "</body></html>";
die($htmlError);
}
}
/**
* 保存sql语句。
* Save the sql.
*
* @access public
* @return void
*/
public function saveSQL()
{
if(!$this->config->debug) return true;
if(!class_exists('dao')) return;
$sqlLog = $this->getLogRoot() . 'sql.' . date('Ymd') . '.log.php';
if(!is_file($sqlLog)) file_put_contents($sqlLog, "<?php\n die();\n?>\n");
$fh = @fopen($sqlLog, 'a');
if(!$fh) return false;
fwrite($fh, date('Ymd H:i:s') . ": " . $this->getURI() . "\n");
foreach(dao::$querys as $query) fwrite($fh, " $query\n");
fwrite($fh, "\n");
fclose($fh);
}
}
/**
* config类。
* The config class.
*
* @package framework
*/
class config
{
/**
* 设置成员变量,成员可以是'db.user'类似的格式。
* Set the value of a member. the member can be the format like db.user.
*
* <code>
* <?php
* $config->set('db.user', 'wwccss');
* ?>
* </code>
* @param string $key the key of the member
* @param mixed $value the value
* @access public
* @return void
*/
public function set($key, $value)
{
helper::setMember('config', $key, $value);
}
}
/**
* lang类。
* The lang class.
*
* @package framework
*/
class language
{
/**
* 设置成员变量,成员可以是'db.user'类似的格式。
* Set the value of a member. the member can be the foramt like db.user.
*
* <code>
* <?php
* $lang->set('version', '1.0);
* ?>
* </code>
* @param string $key 成员的键名可以是father.child的形式。
* the key of the member, can be father.child
* @param mixed $value the value
* @access public
* @return void
*/
public function set($key, $value)
{
helper::setMember('lang', $key, $value);
}
/**
* 显示一个成员的值。
* Show a member.
*
* @param object $obj the object
* @param string $key the key
* @access public
* @return void
*/
public function show($obj, $key)
{
$obj = (array)$obj;
echo isset($obj[$key]) ? $obj[$key] : '';
}
}
/**
* 超级对象类,转化超级全局变量。
* The super object class.
*
* @package framework
*/
class super
{
/**
* 构造函数,设置超级变量名。
* Construct, set the var scope.
*
* @param string $scope the scope, can be server, post, get, cookie, session, global
* @access public
* @return void
*/
public function __construct($scope)
{
$this->scope = $scope;
}
/**
* 设置超级变量的成员值。
* Set one member value.
*
* @param string the key
* @param mixed $value the value
* @access public
* @return void
*/
public function set($key, $value)
{
if($this->scope == 'post')
{
$_POST[$key] = $value;
}
elseif($this->scope == 'get')
{
$_GET[$key] = $value;
}
elseif($this->scope == 'server')
{
$_SERVER[$key] = $value;
}
elseif($this->scope == 'cookie')
{
$_COOKIE[$key] = $value;
}
elseif($this->scope == 'session')
{
$_SESSION[$key] = $value;
}
elseif($this->scope == 'env')
{
$_ENV[$key] = $value;
}
elseif($this->scope == 'global')
{
$GLOBALS[$key] = $value;
}
}
/**
* 超级变量的魔术方法,比如用$post->key访问$_POST['key']。
* The magic get method.
*
* @param string $key the key
* @access public
* @return mixed|bool return the value of the key or false.
*/
public function __get($key)
{
if($this->scope == 'post')
{
if(isset($_POST[$key])) return $_POST[$key];
return false;
}
elseif($this->scope == 'get')
{
if(isset($_GET[$key])) return $_GET[$key];
return false;
}
elseif($this->scope == 'server')
{
if($key == 'ajax') return isset($_SERVER['HTTP_X_REQUESTED_WITH']) ? true : false;
if(isset($_SERVER[$key])) return $_SERVER[$key];
$key = strtoupper($key);
if(isset($_SERVER[$key])) return $_SERVER[$key];
return false;
}
elseif($this->scope == 'cookie')
{
if(isset($_COOKIE[$key])) return $_COOKIE[$key];
return false;
}
elseif($this->scope == 'session')
{
if(isset($_SESSION[$key])) return $_SESSION[$key];
return false;
}
elseif($this->scope == 'env')
{
if(isset($_ENV[$key])) return $_ENV[$key];
return false;
}
elseif($this->scope == 'global')
{
if(isset($GLOBALS[$key])) return $GLOBALS[$key];
return false;
}
else
{
return false;
}
}
/**
* 打印变量的详细结构。
* Print the structure.
*
* @access public
* @return void
*/
public function a()
{
if($this->scope == 'post') a($_POST);
if($this->scope == 'get') a($_GET);
if($this->scope == 'server') a($_SERVER);
if($this->scope == 'cookie') a($_COOKIE);
if($this->scope == 'session') a($_SESSION);
if($this->scope == 'env') a($_ENV);
if($this->scope == 'global') a($GLOBALS);
}
}