You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

344 rivejä
12 KiB

3 vuotta sitten
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: liu21st <liu21st@gmail.com>
  10. // +----------------------------------------------------------------------
  11. namespace Think;
  12. /**
  13. * ThinkPHP 引导类
  14. */
  15. class Think {
  16. // 类映射
  17. private static $_map = array();
  18. // 实例化对象
  19. private static $_instance = array();
  20. /**
  21. * 应用程序初始化
  22. * @access public
  23. * @return void
  24. */
  25. static public function start() {
  26. // 注册AUTOLOAD方法
  27. spl_autoload_register('Think\Think::autoload');
  28. // 设定错误和异常处理
  29. register_shutdown_function('Think\Think::fatalError');
  30. set_error_handler('Think\Think::appError');
  31. set_exception_handler('Think\Think::appException');
  32. // 初始化文件存储方式
  33. Storage::connect(STORAGE_TYPE);
  34. $runtimefile = RUNTIME_PATH.APP_MODE.'~runtime.php';
  35. if(!APP_DEBUG && Storage::has($runtimefile)){
  36. Storage::load($runtimefile);
  37. }else{
  38. if(Storage::has($runtimefile))
  39. Storage::unlink($runtimefile);
  40. $content = '';
  41. // 读取应用模式
  42. $mode = include is_file(CONF_PATH.'core.php')?CONF_PATH.'core.php':MODE_PATH.APP_MODE.'.php';
  43. // 加载核心文件
  44. foreach ($mode['core'] as $file){
  45. if(is_file($file)) {
  46. include $file;
  47. if(!APP_DEBUG) $content .= compile($file);
  48. }
  49. }
  50. // 加载应用模式配置文件
  51. foreach ($mode['config'] as $key=>$file){
  52. is_numeric($key)?C(load_config($file)):C($key,load_config($file));
  53. }
  54. // 读取当前应用模式对应的配置文件
  55. if('common' != APP_MODE && is_file(CONF_PATH.'config_'.APP_MODE.CONF_EXT))
  56. C(load_config(CONF_PATH.'config_'.APP_MODE.CONF_EXT));
  57. // 加载模式别名定义
  58. if(isset($mode['alias'])){
  59. self::addMap(is_array($mode['alias'])?$mode['alias']:include $mode['alias']);
  60. }
  61. // 加载应用别名定义文件
  62. if(is_file(CONF_PATH.'alias.php'))
  63. self::addMap(include CONF_PATH.'alias.php');
  64. // 加载模式行为定义
  65. if(isset($mode['tags'])) {
  66. Hook::import(is_array($mode['tags'])?$mode['tags']:include $mode['tags']);
  67. }
  68. // 加载应用行为定义
  69. if(is_file(CONF_PATH.'tags.php'))
  70. // 允许应用增加开发模式配置定义
  71. Hook::import(include CONF_PATH.'tags.php');
  72. // 加载框架底层语言包
  73. L(include THINK_PATH.'Lang/'.strtolower(C('DEFAULT_LANG')).'.php');
  74. if(!APP_DEBUG){
  75. $content .= "\nnamespace { Think\\Think::addMap(".var_export(self::$_map,true).");";
  76. $content .= "\nL(".var_export(L(),true).");\nC(".var_export(C(),true).');Think\Hook::import('.var_export(Hook::get(),true).');}';
  77. Storage::put($runtimefile,strip_whitespace('<?php '.$content));
  78. }else{
  79. // 调试模式加载系统默认的配置文件
  80. C(include THINK_PATH.'Conf/debug.php');
  81. // 读取应用调试配置文件
  82. if(is_file(CONF_PATH.'debug'.CONF_EXT))
  83. C(include CONF_PATH.'debug'.CONF_EXT);
  84. }
  85. }
  86. // 读取当前应用状态对应的配置文件
  87. if(APP_STATUS && is_file(CONF_PATH.APP_STATUS.CONF_EXT))
  88. C(include CONF_PATH.APP_STATUS.CONF_EXT);
  89. // 设置系统时区
  90. date_default_timezone_set(C('DEFAULT_TIMEZONE'));
  91. // 检查应用目录结构 如果不存在则自动创建
  92. if(C('CHECK_APP_DIR')) {
  93. $module = defined('BIND_MODULE') ? BIND_MODULE : C('DEFAULT_MODULE');
  94. if(!is_dir(APP_PATH.$module) || !is_dir(LOG_PATH)){
  95. // 检测应用目录结构
  96. Build::checkDir($module);
  97. }
  98. }
  99. // 记录加载文件时间
  100. G('loadTime');
  101. // 运行应用
  102. App::run();
  103. }
  104. // 注册classmap
  105. static public function addMap($class, $map=''){
  106. if(is_array($class)){
  107. self::$_map = array_merge(self::$_map, $class);
  108. }else{
  109. self::$_map[$class] = $map;
  110. }
  111. }
  112. // 获取classmap
  113. static public function getMap($class=''){
  114. if(''===$class){
  115. return self::$_map;
  116. }elseif(isset(self::$_map[$class])){
  117. return self::$_map[$class];
  118. }else{
  119. return null;
  120. }
  121. }
  122. /**
  123. * 类库自动加载
  124. * @param string $class 对象类名
  125. * @return void
  126. */
  127. public static function autoload($class) {
  128. // 检查是否存在映射
  129. if(isset(self::$_map[$class])) {
  130. include self::$_map[$class];
  131. }elseif(false !== strpos($class,'\\')){
  132. $name = strstr($class, '\\', true);
  133. if(in_array($name,array('Think','Org','Behavior','Com','Vendor')) || is_dir(LIB_PATH.$name)){
  134. // Library目录下面的命名空间自动定位
  135. $path = LIB_PATH;
  136. }else{
  137. // 检测自定义命名空间 否则就以模块为命名空间
  138. $namespace = C('AUTOLOAD_NAMESPACE');
  139. $path = isset($namespace[$name])? dirname($namespace[$name]).'/' : APP_PATH;
  140. }
  141. $filename = $path . str_replace('\\', '/', $class) . EXT;
  142. if(is_file($filename)) {
  143. // Win环境下面严格区分大小写
  144. if (IS_WIN && false === strpos(str_replace('/', '\\', realpath($filename)), $class . EXT)){
  145. return ;
  146. }
  147. include $filename;
  148. }
  149. }elseif (!C('APP_USE_NAMESPACE')) {
  150. // 自动加载的类库层
  151. foreach(explode(',',C('APP_AUTOLOAD_LAYER')) as $layer){
  152. if(substr($class,-strlen($layer))==$layer){
  153. if(require_cache(MODULE_PATH.$layer.'/'.$class.EXT)) {
  154. return ;
  155. }
  156. }
  157. }
  158. // 根据自动加载路径设置进行尝试搜索
  159. foreach (explode(',',C('APP_AUTOLOAD_PATH')) as $path){
  160. if(import($path.'.'.$class))
  161. // 如果加载类成功则返回
  162. return ;
  163. }
  164. }
  165. }
  166. /**
  167. * 取得对象实例 支持调用类的静态方法
  168. * @param string $class 对象类名
  169. * @param string $method 类的静态方法名
  170. * @return object
  171. */
  172. static public function instance($class,$method='') {
  173. $identify = $class.$method;
  174. if(!isset(self::$_instance[$identify])) {
  175. if(class_exists($class)){
  176. $o = new $class();
  177. if(!empty($method) && method_exists($o,$method))
  178. self::$_instance[$identify] = call_user_func(array(&$o, $method));
  179. else
  180. self::$_instance[$identify] = $o;
  181. }
  182. else
  183. self::halt(L('_CLASS_NOT_EXIST_').':'.$class);
  184. }
  185. return self::$_instance[$identify];
  186. }
  187. /**
  188. * 自定义异常处理
  189. * @access public
  190. * @param mixed $e 异常对象
  191. */
  192. static public function appException($e) {
  193. $error = array();
  194. $error['message'] = $e->getMessage();
  195. $trace = $e->getTrace();
  196. if('E'==$trace[0]['function']) {
  197. $error['file'] = $trace[0]['file'];
  198. $error['line'] = $trace[0]['line'];
  199. }else{
  200. $error['file'] = $e->getFile();
  201. $error['line'] = $e->getLine();
  202. }
  203. $error['trace'] = $e->getTraceAsString();
  204. Log::record($error['message'],Log::ERR);
  205. // 发送404信息
  206. //header('HTTP/1.1 404 Not Found');
  207. //header('Status:404 Not Found');
  208. self::halt($error);
  209. }
  210. /**
  211. * 自定义错误处理
  212. * @access public
  213. * @param int $errno 错误类型
  214. * @param string $errstr 错误信息
  215. * @param string $errfile 错误文件
  216. * @param int $errline 错误行数
  217. * @return void
  218. */
  219. static public function appError($errno, $errstr, $errfile, $errline) {
  220. switch ($errno) {
  221. case E_ERROR:
  222. case E_PARSE:
  223. case E_CORE_ERROR:
  224. case E_COMPILE_ERROR:
  225. case E_USER_ERROR:
  226. ob_end_clean();
  227. $errorStr = "$errstr ".$errfile."$errline 行.";
  228. if(C('LOG_RECORD')) Log::write("[$errno] ".$errorStr,Log::ERR);
  229. self::halt($errorStr);
  230. break;
  231. default:
  232. $errorStr = "[$errno] $errstr ".$errfile."$errline 行.";
  233. self::trace($errorStr,'','NOTIC');
  234. break;
  235. }
  236. }
  237. // 致命错误捕获
  238. static public function fatalError() {
  239. Log::save();
  240. if ($e = error_get_last()) {
  241. switch($e['type']){
  242. case E_ERROR:
  243. case E_PARSE:
  244. case E_CORE_ERROR:
  245. case E_COMPILE_ERROR:
  246. case E_USER_ERROR:
  247. ob_end_clean();
  248. self::halt($e);
  249. break;
  250. }
  251. }
  252. }
  253. /**
  254. * 错误输出
  255. * @param mixed $error 错误
  256. * @return void
  257. */
  258. static public function halt($error) {
  259. $e = array();
  260. if (APP_DEBUG || IS_CLI) {
  261. //调试模式下输出错误信息
  262. if (!is_array($error)) {
  263. $trace = debug_backtrace();
  264. $e['message'] = $error;
  265. $e['file'] = $trace[0]['file'];
  266. $e['line'] = $trace[0]['line'];
  267. ob_start();
  268. debug_print_backtrace();
  269. $e['trace'] = ob_get_clean();
  270. } else {
  271. $e = $error;
  272. }
  273. if(IS_CLI){
  274. exit(iconv('UTF-8','gbk',$e['message']).PHP_EOL.'FILE: '.$e['file'].'('.$e['line'].')'.PHP_EOL.$e['trace']);
  275. }
  276. } else {
  277. //否则定向到错误页面
  278. $error_page = C('ERROR_PAGE');
  279. if (!empty($error_page)) {
  280. redirect($error_page);
  281. } else {
  282. $message = is_array($error) ? $error['message'] : $error;
  283. $e['message'] = C('SHOW_ERROR_MSG')? $message : C('ERROR_MESSAGE');
  284. }
  285. }
  286. // 包含异常页面模板
  287. $exceptionFile = C('TMPL_EXCEPTION_FILE',null,THINK_PATH.'Tpl/think_exception.tpl');
  288. include $exceptionFile;
  289. exit;
  290. }
  291. /**
  292. * 添加和获取页面Trace记录
  293. * @param string $value 变量
  294. * @param string $label 标签
  295. * @param string $level 日志级别(或者页面Trace的选项卡)
  296. * @param boolean $record 是否记录日志
  297. * @return void|array
  298. */
  299. static public function trace($value='[think]',$label='',$level='DEBUG',$record=false) {
  300. static $_trace = array();
  301. if('[think]' === $value){ // 获取trace信息
  302. return $_trace;
  303. }else{
  304. $info = ($label?$label.':':'').print_r($value,true);
  305. $level = strtoupper($level);
  306. if((defined('IS_AJAX') && IS_AJAX) || !C('SHOW_PAGE_TRACE') || $record) {
  307. Log::record($info,$level,$record);
  308. }else{
  309. if(!isset($_trace[$level]) || count($_trace[$level])>C('TRACE_MAX_RECORD')) {
  310. $_trace[$level] = array();
  311. }
  312. $_trace[$level][] = $info;
  313. }
  314. }
  315. }
  316. }