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 requestd module name parsed from a URL. * * @var string * @access public */ public $rawModule; /** * 请求的原始方法名。 * The requested method name parsed from a URL. * * @var string * @access public */ public $rawMethod; /** * 当前页面所在的应用,用于左侧菜单栏判断。 * The current app code(url: '#app=?'), highlight left menu. * * @var string * @access public */ public $tab; /** * 用户使用的主题。 * 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; /** * 原始SESSIONID * SESSIONID * * @var int * @access public */ public $sessionID; /** * 网站代号。 * 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->setCookieSecure(); $this->setOpenApp(); $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(); $this->setEdition(); $this->setVision(); $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 static 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() { if(isset($_SERVER['REQUEST_URI'])) { $URI = $_SERVER['REQUEST_URI']; if(strpos($URI, '?') !== false) { $parsedURL = parse_url($URI); if(isset($parsedURL['query'])) { parse_str($parsedURL['query'], $parsedQuery); foreach($parsedQuery as $key => $value) { if(!isset($_GET[$key])) $_GET[$key] = $value; } } } } $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', $this->tab); unset($_REQUEST); /* Change for CSRF. */ if($this->config->framework->filterCSRF) { $httpType = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on') ? 'https' : 'http'; if(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) and strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https') $httpType = 'https'; if(isset($_SERVER['REQUEST_SCHEME']) and strtolower($_SERVER['REQUEST_SCHEME']) == 'https') $httpType = 'https'; $httpHost = zget($_SERVER, 'HTTP_HOST', ''); $apiMode = (defined('RUN_MODE') && RUN_MODE == 'api') || isset($_GET[$this->config->sessionVar]); if(!$apiMode && (empty($httpHost) or strpos($this->server->http_referer, "$httpType://$httpHost") !== 0)) $_FILES = $_POST = array(); } $_FILES = validater::filterFiles(); $_POST = validater::filterSuper($_POST); $_GET = validater::filterSuper($_GET); $_COOKIE = validater::filterSuper($_COOKIE); /* Filter common get and cookie vars. */ if($this->config->framework->filterParam == 2) { global $filter; foreach($filter->default->get as $key => $rules) { if(isset($_GET[$key]) and !validater::checkByRule($_GET[$key], $rules)) unset($_GET[$key]); } foreach($filter->default->cookie as $key => $rules) { if(isset($_COOKIE[$key]) and !validater::checkByRule($_COOKIE[$key], $rules)) unset($_COOKIE[$key]); } } } /** * Set cookieSecure config. * * @access public * @return void */ public function setCookieSecure() { $this->config->cookieSecure = false; if($this->config->framework->setCookieSecure and isHttps()) $this->config->cookieSecure = true; } /** * 设置Debug模式。 * set Debug. * * @access public * @return void */ public function setDebug() { if(!empty($this->config->debug)) error_reporting(E_ALL & ~ E_STRICT); } /** * 设置版本。 * Set edition. * * @access public * @return void */ public function setEdition() { if(isset($this->config->edition)) return $this->config->edition; $edition = substr($this->config->version, 0, 3); if(in_array($edition, array('pro', 'biz', 'max'))) return $this->config->edition = $edition; $this->config->edition = 'open'; } /** * 设置vision。 * set Debug. * * @access public * @return void */ public function setVision() { $account = isset($_SESSION['user']) ? $_SESSION['user']->account : ''; if(empty($account) and isset($_POST['account'])) $account = $_POST['account']; if(empty($account) and isset($_GET['account'])) $account = $_GET['account']; $vision = ''; if($this->config->installed and validater::checkAccount($account)) { $sql = new sql(); $account = $sql->quote($account); $vision = $this->dbh->query("SELECT * FROM " . TABLE_CONFIG . " WHERE owner = $account AND `key` = 'vision' LIMIT 1")->fetch(); if($vision) $vision = $vision->value; $user = $this->dbh->query("SELECT * FROM " . TABLE_USER . " WHERE account = $account AND deleted = '0' LIMIT 1")->fetch(); if(!empty($user->visions)) { $userVisions = explode(',', $user->visions); if(!in_array($vision, $userVisions)) $vision = ''; if(empty($vision)) list($vision) = $userVisions; } } list($defaultVision) = explode(',', trim($this->config->visions, ',')); if($vision and strpos($this->config->visions, ",{$vision},") === false) $vision = $defaultVision; $this->config->vision = $vision ? $vision : $defaultVision; } /** * Get installed version. * * @access public * @return string */ public function getInstalledVersion() { $version = $this->dbh->query("SELECT value FROM " . TABLE_CONFIG . " WHERE `owner` = 'system' AND `key` = 'version' AND `module` = 'common' AND `section` = 'global' LIMIT 1")->fetch(); $version = $version ? $version->value : '0.3.beta'; // No version, set as 0.3.beta. if($version == '3.0.stable') $version = '3.0'; // convert 3.0.stable to 3.0. return $version; } /** * 设置错误处理句柄。 * 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; } /** * 获取扩展根目录。 * Get the root of extension. * * @access public * @return string */ public function getExtensionRoot() { return $this->basePath . 'extension' . 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')) return; if(ini_get('session.save_handler') == 'files' and isset($_GET['tid'])) { $savePath = ini_get('session.save_path'); $writable = is_writable($savePath); if(!$writable) { $savePath = $this->getTmpRoot() . 'session'; if(!is_dir($savePath)) mkdir($savePath, 0777, true); $writable = is_writable($savePath); if($writable) session_save_path($this->getTmpRoot() . 'session'); } if($writable) { $ztSessionHandler = new ztSessionHandler($_GET['tid']); session_set_save_handler( array($ztSessionHandler, "open"), array($ztSessionHandler, "close"), array($ztSessionHandler, "read"), array($ztSessionHandler, "write"), array($ztSessionHandler, "destroy"), array($ztSessionHandler, "gc") ); } } $sessionName = $this->config->sessionVar; session_name($sessionName); session_set_cookie_params(0, $this->config->webRoot, '', $this->config->cookieSecure, true); if($this->config->customSession) session_save_path($this->getTmpRoot() . 'session'); if(!session_id()) session_start(); $this->sessionID = isset($ztSessionHandler) ? $ztSessionHandler->getSessionID() : session_id(); if(isset($_GET[$this->config->sessionVar])) { helper::restartSession($_GET[$this->config->sessionVar]); } else if(isset($_SERVER['HTTP_TOKEN'])) // If request header has token, use it as session for authentication. { helper::restartSession($_SERVER['HTTP_TOKEN']); $this->sessionID = isset($ztSessionHandler) ? $ztSessionHandler->getSessionID() : session_id(); } define('SESSION_STARTED', true); } /** * 从cookie中获取当前的group, 即URL锚链接'#tab=?'。 * Get current group from cookie, original source is url '#tab=?'. * * @access public * @return void */ public function setOpenApp() { $module = $this->rawModule; $this->tab = 'my'; if(isset($this->lang->navGroup)) $this->tab = zget($this->lang->navGroup, $module, 'my'); if(isset($_COOKIE['tab']) and $_COOKIE['tab'] and preg_match('/^\w+$/', $_COOKIE['tab'])) $this->tab = $_COOKIE['tab']; } /** * 根据用户浏览器的语言设置和服务器配置,选择显示的语言。 * 优先级:$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, '', $this->config->cookieSecure, false); 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, '', $this->config->cookieSecure, false); 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, '', $this->config->cookieSecure, true); if(!isset($_COOKIE['device'])) $_COOKIE['device'] = $this->clientDevice; return $this->clientDevice; } /** * 设置站点代号,可以针对不同的站点来加载不同的扩展。 * Set the code of current site, thus can load different extension of different 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 parsing 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; } /** * 获取$viewType变量。 * 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 example: * start session, check privilege 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,则打印错误日志 * The control file was not found: if the parameter is true, the program is terminated; * if false, the error log is printed. * @access public * @return bool */ public function setControlFile($exitIfNone = true) { $this->controlFile = $this->getModulePath() . '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); } /** * 设置要被调用方法的参数。 * Set the params of method calling. * * @access public * @return void */ public function setParams() { try { $appName = $this->appName; $moduleName = $this->moduleName; $methodName = $this->methodName; /* * 引入该模块的control文件。 * Include the control file of the module. **/ $isExt = $this->setActionExtFile(); if($isExt) { $controlFile = $this->controlFile; spl_autoload_register(function($class) use ($moduleName, $controlFile) { if($class == $moduleName) include $controlFile; }); } $file2Included = $isExt ? $this->extActionFile : $this->controlFile; chdir(dirname($file2Included)); helper::import($file2Included); /* Check file is encode by ioncube. */ $isEncrypted = false; if(strpos($file2Included, 'extension' . DS . $this->config->edition . DS) !== false) { $fp = fopen($file2Included, 'r'); $line1 = fgets($fp); $line2 = fgets($fp); fclose($fp); if(strpos($line1, '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(!$isEncrypted and $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 ('cli' === PHP_SAPI) { if ($this->params) { $this->params = array_merge($defaultParams, $this->params); } else { $this->params = $defaultParams; } } else { if($this->config->framework->filterParam == 2) { $_GET = validater::filterParam($_GET, 'get'); $_COOKIE = validater::filterParam($_COOKIE, 'cookie'); } } return true; } catch(EndResponseException $endResponseException) { echo $endResponseException->getContent(); return false; } } /** * 获取一个模块的路径。 * 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; $moduleName = strtolower($moduleName); if($this->checkModuleName($moduleName)) { $modulePath = $this->getExtensionRoot() . 'saas' . DS . $moduleName . DS; if(is_dir($modulePath) and (file_exists($modulePath . 'control.php') or file_exists($modulePath . 'model.php'))) return $modulePath; /* 1. 最后尝试在定制开发中寻找。 Finally, try to find the module in the custom dir. */ $modulePath = $this->getExtensionRoot() . 'custom' . DS . $moduleName . DS; if(is_dir($modulePath) and (file_exists($modulePath . 'control.php') or file_exists($modulePath . 'model.php'))) return $modulePath; /* 2. 如果设置过vision,尝试在vision中查找。 If vision is set, try to find the module in the vision. */ if($this->config->vision != 'rnd') { $modulePath = $this->getExtensionRoot() . $this->config->vision . DS . $moduleName . DS; if(is_dir($modulePath) and (file_exists($modulePath . 'control.php') or file_exists($modulePath . 'model.php'))) return $modulePath; } /* 3. 尝试查找商业版本是否有此模块。 Try to find the module in other editon. */ if($this->config->edition != 'open') { $modulePath = $this->getExtensionRoot() . $this->config->edition . DS . $moduleName . DS; if(is_dir($modulePath) and (file_exists($modulePath . 'control.php') or file_exists($modulePath . 'model.php'))) return $modulePath; } /* 4. 尝试查找喧喧是否有此模块。 Try to find the module in xuan. */ $modulePath = $this->getExtensionRoot() . 'xuan' . DS . $moduleName . DS; if(is_dir($modulePath) and (file_exists($modulePath . 'control.php') or file_exists($modulePath . 'model.php'))) return $modulePath; /* 5. 如果通用版本里有此模块,优先使用。 If module is in the open edition, use it. */ return $this->getModuleRoot($appName) . $moduleName . DS; } } /** * 获取一个模块的扩展路径。 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|zen|tao * @access public * @return string the extension path. */ public function getModuleExtPath($appName, $moduleName, $ext) { $saasExtPath = $this->getExtensionRoot() . 'saas' . DS . $moduleName . DS . 'ext' . DS . $ext . DS; /* 检查失败或者extensionLevel为0,直接返回空。If check failed or extensionLevel == 0, return empty array. */ if(!$this->checkModuleName($moduleName) or $this->config->framework->extensionLevel == 0) return array('saas' => $saasExtPath); $paths = array(); /* When extensionLevel == 1. */ $paths['common'] = $this->config->edition != 'open' ? $this->getExtensionRoot() . $this->config->edition . DS . $moduleName . DS . 'ext' . DS . $ext . DS : ''; $paths['xuan'] = $this->getExtensionRoot() . 'xuan' . DS . $moduleName . DS . 'ext' . DS . $ext . DS; $paths['vision'] = $this->config->vision == 'rnd' ? '' : $this->basePath . 'extension' . DS . $this->config->vision . DS . $moduleName . DS . 'ext' . DS . $ext . DS; $paths['custom'] = $this->getExtensionRoot() . 'custom' . DS . $moduleName . DS . 'ext' . DS . $ext . DS; if($this->config->framework->extensionLevel == 1) { $paths['saas'] = $saasExtPath; return $paths; } /* When extensionLevel == 2. */ $paths['site'] = empty($this->siteCode) ? '' : $this->getExtensionRoot() . $this->config->edition . DS . $moduleName . DS . 'ext' . DS . '_' . $this->siteCode . DS . $ext . DS; $paths['saas'] = $saasExtPath; return $paths; } /** * 检查模块中某一个变量必须为英文字母和数字组合。Check module a variable must be ascii. * * @param string $var * @param bool $exit * @access public * @return bool */ public function checkModuleName($var, $exit = true) { global $filter; static $checkedModule = array(); if(!isset($checkedModule[$var])) { $rule = $filter->default->moduleName; $result = validater::checkByRule($var, $rule); $checkedModule[$var] = $result; } if($checkedModule[$var]) return true; if(!$exit) return false; $this->triggerError("'$var' illegal. ", __FILE__, __LINE__, $exit = true); } /** * 检查方法中某一个变量必须为英文字母和数字组合。Check method a variable must be ascii. * * @param string $var * @param bool $exit * @access public * @return bool */ public function checkMethodName($var, $exit = true) { 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; if(!$exit) return false; $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 paths return false.*/ if(empty($moduleExtPaths)) return false; if(!empty($moduleExtPaths['saas'])) { $this->extActionFile = $moduleExtPaths['saas'] . $this->methodName . '.php'; if(file_exists($this->extActionFile)) return true; } /* 1. 如果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; } /* 2. 尝试在定制开发目录寻找扩展文件。Then try to find the custom extension file. */ $this->extActionFile = $moduleExtPaths['custom'] . $this->methodName . '.php'; if(file_exists($this->extActionFile)) return true;; /* 3. 如果设置过vision,尝试在vision中查找扩展文件。If vision is set, try to find the vision extension file. */ if($moduleExtPaths['vision']) $this->extActionFile = $moduleExtPaths['vision'] . $this->methodName . '.php'; if(file_exists($this->extActionFile)) return true;; /* 4. 在喧喧目录中查找扩展文件。Then try to find the xuan extension file. */ if($moduleExtPaths['xuan']) $this->extActionFile = $moduleExtPaths['xuan'] . $this->methodName . '.php'; if(file_exists($this->extActionFile)) return true;; /* 5. 最后尝试寻找公共扩展文件。Finally, try to find the common extension file. */ $this->extActionFile = $moduleExtPaths['common'] . $this->methodName . '.php'; if(empty($moduleExtPaths['common'])) return false; return file_exists($this->extActionFile); } /** * Check for API extend. * * @access public * @return bool */ public function checkAPIFile() { $moduleExtPaths = $this->getModuleExtPath('', $this->moduleName, 'control'); /* 如果扩展目录为空,不包含任何扩展文件。If there's no ext paths 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'])) { $locateFile = $moduleExtPaths['site'] . $this->methodName . '.302'; if(file_exists($locateFile)) $this->sendAPI($locateFile); $requestFile = $moduleExtPaths['site'] . $this->methodName . '.api'; if(file_exists($requestFile)) $this->sendAPI($requestFile); } /* 然后再尝试寻找公共扩展文件。Then try to find the common extension file. */ $locateFile = $moduleExtPaths['common'] . $this->methodName . '.302'; if(file_exists($locateFile)) $this->sendAPI($locateFile); $requestFile = $moduleExtPaths['common'] . $this->methodName . '.api'; if(file_exists($requestFile)) $this->sendAPI($requestFile); return false; } /** * 设置一个模块的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, 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(); /* 设置主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(); $extFiles = array(); $apiFiles = array(); $siteExtended = false; $targetExtPaths = $this->getModuleExtPath($appName, $moduleName, $class); foreach($targetExtPaths as $extType => $targetExtPath) { if(empty($targetExtPath)) continue; $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); if($extType == 'site' and (!empty($tmpHookFiles) or !empty($tmpExtFiles) or !empty($tmpAPIFiles))) $siteExtended = true; } /* 如果没有扩展文件,返回主文件。 If no extension or hook files, return the main file directly. */ if(empty($extFiles) and empty($hookFiles) and empty($apiFiles)) return $mainTargetFile; /* 计算合并之后的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; $mergedTargetDir = $this->getTmpRoot() . $class . DS . $extTargetPrefix; $mergedTargetFile = $mergedTargetDir . $moduleName . '.php'; if(!is_dir($mergedTargetDir)) mkdir($mergedTargetDir, 0755, true); /* 判断生成的缓存文件是否需要更新。 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. */ $targetLines = $this->mergeTargetExtFiles($moduleName, $extFiles, $mergedTargetDir, $class); $this->mergeTargetHookFiles($moduleName, $mainTargetFile, $targetLines, $hookFiles, $mergedTargetDir, $mergedTargetFile, $apiFiles, $class); return $mergedTargetFile; } /** * 检查合并之后的target文件是否需要更新。Check whether the merged target file need update or not. * * @param string $mergedTargetFile * @param array $extFiles * @param array $hookFiles * @param array $apiFiles * @param string $targetExtPaths * @param string $mainTargetFile * @access public * @return bool */ public function needTargetFileUpdate($mergedTargetFile, $extFiles, $hookFiles, $apiFiles, $targetExtPaths, $mainTargetFile) { $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; $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($targetExtPaths['site'])) { $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($mainTargetFile) > $lastTime) return true; return false; } /** * 将target的扩展文件合并在一起。Merge target ext files. * * @param string $moduleName * @param array $extFiles * @param string $mergedTargetDir * @param string $class model | zen | tao * @access public * @return void */ public function mergeTargetExtFiles($moduleName, $extFiles, $mergedTargetDir, $class = 'model') { /* 设置类名。Set the class names. */ $targetClass = $moduleName . ucfirst($class); $tmpTargetClass = "tmpExt$targetClass"; /* 开始拼装代码。Prepare the codes. */ $targetLines = "getModulePath('', '$moduleName') . " . "'$class.php');\n"; $targetLines .= "class $tmpTargetClass extends $targetClass \n{\n"; /* 将扩展文件的代码合并到代码中。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 = '//**//'; $targetLines .= "\n$replaceMark\n}"; /* 生成一个临时的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($tmpTargetClass)) include $tmpTargetFile; return $targetLines; } $this->triggerError("ERROR: $tmpTargetFile not writable.", __FILE__, __LINE__, true); } /** * 合并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 mergeTargetHookFiles($moduleName, $mainTargetFile, $targetLines, $hookFiles, $mergedTargetDir, $mergedTargetFile, $apiFiles, $class = 'model') { /* 定义相关变量。Init vars. */ $targetClass = $moduleName . ucfirst($class); $extTargetClass = 'ext' . $targetClass; $tmpTargetClass = 'tmpExt' . $targetClass; $tmpTargetFile = $mergedTargetDir . "tmp$moduleName.php"; $replaceMark = '//**//'; /* 读取hook文件。Get hook codes need to merge. */ $hookCodes = array(); foreach($apiFiles as $apiFile) { /* 通过文件名获得其对应的方法名。Get methods according it's filename. */ $fileName = baseName($apiFile); list($method) = explode('.', $fileName); $url = self::extractAPIURL($apiFile); if($url) $hookCodes[$method][] = "return helper::requestAPI('$url');"; } foreach($hookFiles as $hookFile) { /* 通过文件名获得其对应的方法名。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); $mainTargetCodes = file($mainTargetFile); $mergedTargetCodes = file($tmpTargetFile); foreach($hookedMethods as $method) { /* 通过反射获得hook脚本对应的方法所在的文件和起止行数。Reflection the hooked method to get it's defined position. */ 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 == $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 == $tmpTargetFile) $targetLines = str_replace($oldCodes, $newCodes, $targetLines); if($definedFile != $tmpTargetFile) $targetLines = str_replace($replaceMark, $newCodes . "\n$replaceMark", $targetLines); } /* 保存最终的Target文件。Save the last merged target file. */ $targetLines = str_replace($tmpTargetClass, $extTargetClass, $targetLines); file_put_contents($mergedTargetFile, $targetLines); unlink($tmpTargetFile); } /** * 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, '') !== false) $code = rtrim($code, '?' . '>'); return trim($code); } /** * Extract API url from api file. * * @param string $fileName * @static * @access public * @return string */ static public function extractAPIURL($fileName) { global $config; $url = ''; $lines = file($fileName); foreach($lines as $line) { $line = trim($line); if(empty($line)) continue; if(preg_match('/^https?\:\/\//', $line)) { $url = $line; break; } } if(empty($url)) return false; return $url; } /** * Send API. * * @param string $apiFile * @access public * @return void */ public function sendAPI($apiFile) { $extension = substr($apiFile, strrpos($apiFile, '.') + 1); if($extension != '302' and $extension != 'api') return false; $lines = file($apiFile); $url = ''; foreach($lines as $line) { $line = trim($line); if(empty($line)) continue; if(preg_match('/^https?\:\/\//', $line)) { $url = $line; break; } } if(empty($url)) return false; $url .= (strpos($url, '?') !== false ? '&' : '?') . $this->config->sessionVar . '=' . session_id() . '&account=' . $_SESSION['user']->account; if($extension == '302') { header("location: $url"); exit; } if($extension == 'api') { $response = common::http($url); $headFile = $this->moduleRoot . 'common/view/header.html.php'; $footFile = $this->moduleRoot . 'common/view/footer.html.php'; $obLevel = ob_get_level(); for($i = 0; $i < $obLevel; $i++) ob_end_clean(); $viewFiles = $this->control->setViewFile($this->moduleName, $this->methodName); if($css) $this->control->view->pageCSS = $css; if($js) $this->control->view->pageJS = $js; $output = ''; $output .= $this->control->printViewFile($headFile); $output .= $response; if(isset($viewFiles['hookFiles'])) foreach($viewFiles['hookFiles'] as $hookFile) $output .= $this->control->printViewFile($hookFile); $output .= $this->control->printViewFile($footFile); die($output); } } //-------------------- 路由相关方法(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 separator, 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 request separator, 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->setMethodName($methodName); $this->setControlFile(); } /** * 加载一个模块: * 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() { try { if(is_null($this->params) and !$this->setParams()) return false; /* 调用该方法 Call the method. */ $module = $this->control; call_user_func_array(array($module, $this->methodName), $this->params); $this->checkAPIFile(); return $module; } catch (EndResponseException $endResponseException) { echo $endResponseException->getContent(); } 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. * * @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, (string)$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; } /* Fix bug #3267. Param 'words' is not validated when searching. */ if($this->rawModule == 'search' and $this->rawMethod == 'index') unset($params['words']); $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 three params. */ unset($passedParams['onlybody']); unset($passedParams['tid']); 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 static, 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 * @access public * @return void */ public function loadModuleConfig($moduleName, $appName = '') { global $config; if($config and (!isset($config->$moduleName) or !is_object($config->$moduleName))) $config->$moduleName = new stdclass(); /* 将主配置文件和扩展配置文件合并在一起。Put the main config file and extension config files together. */ $configFiles = $this->getMainAndExtFiles($moduleName, $appName, 'config'); if(empty($configFiles)) return false; /* 加载每一个配置文件。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; $view->systemMode = $this->config->systemMode; $view->sprintConcept = zget($this->config->custom, 'sprintConcept', '0'); $view->URAndSR = zget($this->config->custom, 'URAndSR', '0'); $view->maxUploadSize = strtoupper(ini_get('upload_max_filesize')); $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); } /** * 获取主语言文件。 * Get main lang file. * * @param string $moduleName the module name * @param string $appName the app name * @access public * @return string. */ private function getMainLangFile($moduleName, $appName = '') { $path = $moduleName . DS . 'lang' . DS . $this->clientLang . '.php'; $modulePath = $this->getExtensionRoot() . 'saas' . DS; if(file_exists($modulePath . $path)) return $modulePath . $path; /* 1. 最后尝试在定制开发中寻找。 Finally, try to find the module in the custom dir. */ $modulePath = $this->getExtensionRoot() . 'custom' . DS; if(file_exists($modulePath . $path)) return $modulePath . $path; /* 2. 如果设置过vision,尝试在vision中查找。 If vision is set, try to find the module in the vision. */ if($this->config->vision != 'rnd') { $modulePath = $this->getExtensionRoot() . $this->config->vision . DS; if(file_exists($modulePath . $path)) return $modulePath . $path; } /* 3. 尝试查找商业版本是否有此模块。 Try to find the module in other editon. */ if($this->config->edition != 'open') { $modulePath = $this->getExtensionRoot() . $this->config->edition . DS; if(file_exists($modulePath . $path)) return $modulePath . $path; } /* 4. 尝试查找喧喧是否有此模块。 Try to find the module in other editon. */ $modulePath = $this->getExtensionRoot() . 'xuan' . DS; if(file_exists($modulePath . $path)) return $modulePath . $path; /* 5. 如果通用版本里有此模块,优先使用。 If module is in the open edition, use it. */ $modulePath = $this->getModuleRoot($appName); if(file_exists($modulePath . $path)) return $modulePath . $path; return ''; } /** * 加载语言文件,返回全局$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 = '') { /* 计算最终要加载的语言文件。 Get the lang files to be loaded. */ $langFilesToLoad = $this->getMainAndExtFiles($moduleName, $appName, 'lang'); 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 { $dbPassword = helper::decryptPassword($params->password); $dbh = new PDO($dsn, $params->user, $dbPassword, 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; if(defined('RUN_MODE') and RUN_MODE == 'api') $params->emulatePrepare = false; $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) { $message = $exception->getMessage(); if(empty($message)) { /* Try to repair table. */ header("location: {$this->config->webRoot}checktable.php"); exit; } self::triggerError($message, __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 occurs, 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 occurs * @param int $line 错误行 the line error occurs * @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 = htmlSpecialString($message); if(preg_match('/[^\x00-\x80]/', $message)) $message = helper::convertEncoding($message, 'gbk'); /* Only show error when debug is open. */ if(!$this->config->debug) die(); $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"; /* Change absolute path to relative path. */ $log = str_replace($this->basePath, '', $log); /* 触发错误(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)) return true; if(!is_writable($this->logRoot)) return true; /* * 删除设定时间之前的日志。 * 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. **/ if(preg_match('/[^\x00-\x80]/', $message)) $message = helper::convertEncoding($message, 'gbk'); $errorLog = "\n" . date('H:i:s') . " $message in $file on line $line "; $URI = $this->getURI(); $errorLog .= "when visiting " . (empty($URI) ? '' : htmlspecialchars($URI)) . "\n"; /* * 为了安全起见,对公网环境隐藏脚本路径。 * If the ip is pulic, hidden the full path of scripts. */ $remoteIP = helper::getRemoteIp(true); if(!defined('IN_SHELL') and !($remoteIP == '127.0.0.1' or filter_var($remoteIP, 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, "\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 "
$message: ";
                echo "
"; } } /* * 如果是严重错误,停止程序。 * 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 = ""; $htmlError .= "" . nl2br($errorLog) . ""; 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, "\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); } /** * Check app if it is run in a container. * * @access public * @return bool */ public function isContainer() { return strtolower(getenv('IS_CONTAINER')) == 'true'; } /** * Get main file and ext files. * * @param string $moduleName * @param string $appName * @param string $type lang|config|control|model * @access public * @return array */ public function getMainAndExtFiles($moduleName, $appName = '', $type = 'lang') { /* 初始化变量。Init vars. */ $modulePath = $this->getModulePath($appName, $moduleName); $extFiles = array(); $filesToLoad = array(); /* 判断主文件是否存在。Whether the main file exists or not. */ if($type == 'lang') { $mainFile = $this->getMainLangFile($moduleName, $appName); } else { $mainFile = $modulePath . $type . '.php'; } if($mainFile) $filesToLoad[] = $mainFile; /* 获取扩展文件。If extensionLevel > 0, get extension files. */ if($this->config->framework->extensionLevel > 0) { $commonExtFiles = array(); $siteExtFiles = array(); $extPath = $this->getModuleExtPath($appName, $moduleName, $type); if($this->config->framework->extensionLevel >= 1) { $clientLang = $type == 'lang' ? $this->clientLang : ''; if(!empty($extPath['common'])) $commonExtFiles = helper::ls($extPath['common'] . $clientLang, '.php'); if(!empty($extPath['xuan'])) $commonExtFiles = array_merge($commonExtFiles, helper::ls($extPath['xuan'] . $clientLang, '.php')); if(!empty($extPath['vision'])) $commonExtFiles = array_merge($commonExtFiles, helper::ls($extPath['vision'] . $clientLang, '.php')); if(!empty($extPath['custom'])) $commonExtFiles = array_merge($commonExtFiles, helper::ls($extPath['custom'] . $clientLang, '.php')); if(!empty($extPath['saas'])) $commonExtFiles = array_merge($commonExtFiles, helper::ls($extPath['saas'] . $clientLang, '.php')); } if($this->config->framework->extensionLevel == 2 and !empty($extPath['site'])) $siteExtFiles = helper::ls($extPath['site'] . $clientLang, '.php'); $extFiles = array_merge($commonExtFiles, $siteExtFiles); } /* 计算最终要加载的文件。 Get the files to be loaded. */ $filesToLoad = array_merge($filesToLoad, $extFiles); if(empty($filesToLoad)) return false; return $filesToLoad; } } /** * 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. * * * set('db.user', 'wwccss'); * ?> * * @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. * * * set('version', '1.0); * ?> * * @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, $tab = '') { $this->scope = $scope; $this->tab = $tab; } /** * 设置超级变量的成员值。 * Set one member value. * * @param string $key the key * @param mixed $value the value * @param string $tab * @access public * @return void */ public function set($key, $value, $tab = '') { 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') { if($tab) $_SESSION["app-$tab"][$key] = $value; $_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') { $tab = $this->tab; if($tab and isset($_SESSION["app-$tab"][$key])) return $_SESSION["app-$tab"][$key]; 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); } } class EndResponseException extends \Exception { /** * 响应内容 * * @var string */ private $content; /** * @param string $content * * @return self */ public static function create($content = '') { $exception = new self; $exception->content = $content; return $exception; } /** * Get 响应内容 * * @return string */ public function getContent() { return $this->content; } } /** * ZenTao session handler. * * @package framework */ class ztSessionHandler { public $sessSavePath; public $tagID; public $sessionFile; public $sessionID; public $rawID; public $rawFile; /** * Construct. * * @param string $tagID * @access public * @return void */ public function __construct($tagID = '') { $this->tagID = $tagID; ini_set('session.save_handler', 'files'); register_shutdown_function('session_write_close'); } /** * Get sessionID * * @access public * @return string */ public function getSessionID() { return $this->sessionID; } /** * Get session file. * * @param string $id * @access public * @return string */ public function getSessionFile($id) { if(!empty($this->sessionFile)) return $this->sessionFile; $sessionID = $id; if($this->tagID) $sessionID = md5($id . $this->tagID); $fileName = "sess_$sessionID"; $this->sessionFile = $this->sessSavePath . '/' . $fileName; $this->sessionID = $sessionID; $this->rawID = $id; $this->rawFile = $this->sessSavePath . '/' . "sess_$id"; return $this->sessionFile; } /** * Open * * @param string $savePath * @param string $sessionName * @access public * @return bool */ #[\ReturnTypeWillChange] public function open($savePath, $sessionName) { $this->sessSavePath = $savePath; return true; } /** * Close * * @access public * @return bool */ #[\ReturnTypeWillChange] public function close() { return true; } /** * Read * * @param string $id * @access public * @return bool */ #[\ReturnTypeWillChange] public function read($id) { $sessFile = $this->getSessionFile($id); if(!file_exists($sessFile)) { ($this->tagID and file_exists($this->rawFile)) ? copy($this->rawFile, $sessFile) : touch($sessFile); } return (string) file_get_contents($sessFile); } /** * Write * * @param string $id * @param string $sessData * @access public * @return bool */ #[\ReturnTypeWillChange] public function write($id, $sessData) { $sessFile = $this->getSessionFile($id); touch($sessFile); touch($this->rawFile); if(md5_file($sessFile) == md5($sessData)) return true; if(file_put_contents($sessFile, $sessData, LOCK_EX)) { if(strpos($sessData, 'user|') !== false) { $rawSessContent = (string) file_get_contents($this->rawFile, false, null, 0, 1024 * 2); if(strpos($rawSessContent, 'user|') === false) file_put_contents($this->rawFile, $sessData, LOCK_EX); } return true; } return false; } /** * Destroy * * @param string $id * @access public * @return bool */ #[\ReturnTypeWillChange] public function destroy($id) { $sessFile = $this->getSessionFile($id); if(file_exists($sessFile)) unlink($sessFile); if(file_exists($this->rawFile)) unlink($this->rawFile); touch($sessFile); touch($this->rawFile); return true; } /** * GC * * @param int $maxlifeTime * @access public * @return bool */ #[\ReturnTypeWillChange] public function gc($maxlifeTime) { $time = time(); foreach(glob("$this->sessSavePath/sess_*") as $fileName) { if(filemtime($fileName) + $maxlifeTime < $time) @unlink($fileName); } return true; } }