Think.class.php 13 KB

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