📄 正在查看:twcms/kongphp/base/core.class.php
大小:12,962 字节 · 修改:2014-05-05 17:38:38 · 行数:431
1<?php
2/**
3 * Copyright (C) 2013-2014 www.kongphp.com All rights reserved.
4 * Licensed http://www.gnu.org/licenses/lgpl.html
5 * Author: wuzhaohuan <kongphp@gmail.com>
6 */
7
8class core{
9 /**
10 * 开始加载框架
11 */
12 public static function start() {
13 debug::init();
14 self::ob_start();
15 self::init_set();
16 self::init_get();
17 self::init_control();
18 }
19
20 /**
21 * 打开输出控制缓冲
22 */
23 public static function ob_start() {
24 ob_start(array('core', 'ob_gzip'));
25 }
26
27 /**
28 * GZIP压缩处理
29 * @param string $s 数据
30 * @return string
31 */
32 public static function ob_gzip($s) {
33 $gzip = $_ENV['_config']['gzip'];
34 $isfirst = empty($_ENV['_isgzip']);
35
36 if($gzip) {
37 if(function_exists('ini_get') && ini_get('zlib.output_compression')) {
38 $isfirst && header("Content-Encoding: gzip");
39 }elseif(function_exists('gzencode') && strpos($_SERVER["HTTP_ACCEPT_ENCODING"], 'gzip') !== FALSE) {
40 $s = gzencode($s, 5);
41 if($isfirst) {
42 header("Content-Encoding: gzip");
43 // header("Content-Length: ".strlen($s));
44 }
45 }
46 }elseif($isfirst) {
47 header("Content-Encoding: none");
48 // header("Content-Length: ".strlen($s));
49 }
50 $isfirst && $_ENV['_isgzip'] = 1;
51 return $s;
52 }
53
54 /**
55 * 清空输出缓冲区
56 */
57 public static function ob_clean() {
58 !empty($_ENV['_isgzip']) && ob_clean();
59 }
60
61 /**
62 * 清空缓冲区并关闭输出缓冲
63 */
64 public static function ob_end_clean() {
65 !empty($_ENV['_isgzip']) && ob_end_clean();
66 }
67
68 /**
69 * 初始化基本设置
70 */
71 public static function init_set() {
72 date_default_timezone_set($_ENV['_config']['zone']); // php5.4 以后,不再支持 Etc/GMT+8 这种格式
73
74 spl_autoload_register(array('core', 'autoload_handler')); // 设置自动包含类文件方法
75
76 // GPC 安全过滤
77 if(get_magic_quotes_gpc()) {
78 _stripslashes($_GET);
79 _stripslashes($_POST);
80 _stripslashes($_COOKIE);
81 }
82
83 // 初始化全局变量
84 $_ENV['_sqls'] = array(); // debug 时使用
85 $_ENV['_include'] = array(); // autoload 时使用
86 $_ENV['_time'] = isset($_SERVER['REQUEST_TIME']) ? $_SERVER['REQUEST_TIME'] : time();
87 $_ENV['_ip'] = ip();
88 $_ENV['_sqlnum'] = 0;
89
90 // 某些IIS环境 fix
91 if(!isset($_SERVER['REQUEST_URI'])) {
92 if(isset($_SERVER['HTTP_X_REWRITE_URL'])) {
93 $_SERVER['REQUEST_URI'] = &$_SERVER['HTTP_X_REWRITE_URL'];
94 }else{
95 $_SERVER['REQUEST_URI'] = '';
96 $_SERVER['REQUEST_URI'] .= $_SERVER['REQUEST_URI'];
97 $_SERVER['REQUEST_URI'] .= isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '';
98 $_SERVER['REQUEST_URI'] .= empty($_SERVER['QUERY_STRING']) ? '' : '?'.$_SERVER['QUERY_STRING'];
99 }
100 }
101
102 // 输出 header 头
103 header("Expires: 0");
104 header("Cache-Control: private, post-check=0, pre-check=0, max-age=0");
105 header("Pragma: no-cache");
106 header('Content-Type: text/html; charset=UTF-8');
107 //header('X-Powered-By: KongPHP');
108 }
109
110 /**
111 * 自动包含类文件
112 * @param string $classname 类名
113 * @return boot
114 */
115 public static function autoload_handler($classname) {
116 if(substr($classname, 0, 3) == 'db_') {
117 include KONG_PATH.'db/'.$classname.'.class.php';
118 }elseif(substr($classname, 0, 6) == 'cache_') {
119 include KONG_PATH.'cache/'.$classname.'.class.php';
120 }elseif(is_file(KONG_PATH.'ext/'.$classname.'.class.php')) {
121 include KONG_PATH.'ext/'.$classname.'.class.php';
122 }else{
123 throw new Exception("类 $classname 不存在");
124 }
125 DEBUG && $_ENV['_include'][] = $classname.' 类';
126 return class_exists($classname, false);
127 }
128
129 /**
130 * 初始化 $_GET 变量 (可通过 parseurl_control.class.php 自定义解析 URl)
131 */
132 public static function init_get() {
133 if(!empty($_ENV['_config'][APP_NAME.'_parseurl'])) {
134 self::parseurl_control();
135 }else{
136 // 提示:为了满足各种需求,这里搞了三种方式,可能有点乱,没办法迫不得已。
137 if(isset($_GET['u'])) {
138 $u = $_GET['u'];
139 unset($_GET['u']);
140 }elseif(!empty($_SERVER['PATH_INFO'])) {
141 $u = $_SERVER['PATH_INFO'];
142 }else{
143 $_GET = array();
144 $u = $_SERVER["QUERY_STRING"];
145 }
146
147 //清除URL后缀
148 $url_suffix = C('url_suffix');
149 if($url_suffix) {
150 $suf_len = strlen($url_suffix);
151 if(substr($u, -($suf_len)) == $url_suffix) $u = substr($u, 0, -($suf_len));
152 }
153
154 $uarr = explode('-', $u);
155
156 if(isset($uarr[0])) {
157 $_GET['control'] = $uarr[0];
158 array_shift($uarr);
159 }
160
161 if(isset($uarr[0])) {
162 $_GET['action'] = $uarr[0];
163 array_shift($uarr);
164 }
165
166 $num = count($uarr);
167 for($i=0; $i<$num; $i+=2){
168 isset($uarr[$i+1]) && $_GET[$uarr[$i]] = $uarr[$i+1];
169 }
170 }
171
172 $_GET['control'] = isset($_GET['control']) && preg_match('/^\w+$/', $_GET['control']) ? $_GET['control'] : 'index';
173 $_GET['action'] = isset($_GET['action']) && preg_match('/^\w+$/', $_GET['action']) ? $_GET['action'] : 'index';
174
175 // 限制访问特殊控制器 直接转为错误404
176 if(in_array($_GET['control'], array('parseurl', 'error404'))) {
177 $_GET['control'] = 'error404';
178 $_GET['action'] = 'index';
179 }
180 }
181
182 /**
183 * 执行解析 URL 为 $_GET 的控制器
184 */
185 public static function parseurl_control() {
186 $controlname = 'parseurl_control.class.php';
187 $objfile = RUNTIME_CONTROL.$controlname;
188
189 if(DEBUG || !is_file($objfile)) {
190 $controlfile = self::get_original_file($controlname, CONTROL_PATH);
191
192 if(!$controlfile) {
193 $_GET['control'] = 'parseurl';
194 throw new Exception("访问的 URL 不正确,$controlname 文件不存在");
195 }
196
197 self::parse_all($controlfile, $objfile, "写入 control 编译文件 $controlname 失败");
198 }
199
200 include $objfile;
201 $obj = new parseurl_control();
202 $obj->index();
203 }
204
205 /**
206 * 初始化控制器,并实例化
207 */
208 public static function init_control() {
209 $control = &$_GET['control'];
210 $action = &$_GET['action'];
211 $controlname = "{$control}_control.class.php";
212 $objfile = RUNTIME_CONTROL.$controlname;
213
214 // 如果缓存文件不存在,则搜索原始文件,并编译后,写入缓存文件
215 if(DEBUG || !is_file($objfile)) {
216 $controlfile = self::get_original_file($controlname, CONTROL_PATH);
217 if($controlfile) {
218 self::parse_all($controlfile, $objfile, "写入 control 编译文件 $controlname 失败");
219 }elseif(DEBUG > 0) {
220 throw new Exception("访问的 URL 不正确,$controlname 文件不存在");
221 }else{
222 self::error404();
223 }
224 }
225
226 include $objfile;
227 $class_name = $control.'_control';
228 $obj = new $class_name();
229 $obj->$action();
230 }
231
232 /**
233 * 执行错误404控制器
234 */
235 public static function error404() {
236 log::write('404错误,访问的 URL 不存在', 'php_error404.php');
237
238 $errorname = 'error404_control.class.php';
239 $objfile = RUNTIME_CONTROL.$errorname;
240
241 if(DEBUG || !is_file($objfile)) {
242 $errorfile = self::get_original_file($errorname, CONTROL_PATH);
243
244 if(!$errorfile) {
245 throw new Exception("控制器加载失败,$errorname 文件不存在");
246 }
247
248 self::parse_all($errorfile, $objfile, "写入 control 编译文件 $errorname 失败");
249 }
250
251 include $objfile;
252 $obj = new error404_control();
253 $obj->index();
254 exit();
255 }
256
257 /**
258 * 将原始程序代码解析并写入缓存文件中
259 * @param string $readfile 原始路径
260 * @param string $writefile 缓存路径
261 * @param string $errorstr 写入出错提示
262 */
263 public static function parse_all($readfile, $writefile, $errorstr) {
264 $s = file_get_contents($readfile);
265 $s = self::parse_extends($s);
266 $s = preg_replace_callback('#\t*\/\/\s*hook\s+([\w\.]+)[\r\n]#', array('core', 'parse_hook'), $s); // 处理 hook
267 if(!FW($writefile, $s)) {
268 throw new Exception($errorstr);
269 }
270 }
271
272 /**
273 * 递归解析继承的控制器类 (不好理解?递归在 parse_all)
274 * @param string $s 文件内容
275 * @return string
276 */
277 public static function parse_extends($s) {
278 if(preg_match('#class\s+\w+\s+extends\s+(\w+)\s*\{#', $s, $m)) {
279 if($m[1] != 'control') {
280 $controlname = $m[1].'.class.php';
281 $realfile = CONTROL_PATH.$controlname;
282 if(is_file($realfile)) {
283 $objfile = RUNTIME_CONTROL.$controlname;
284 self::parse_all($realfile, $objfile, "写入继承的类的编译文件 $controlname 失败");
285 $s = str_replace_once($m[0], 'include RUNTIME_CONTROL.\''.$controlname."'; ".$m[0], $s);
286 }else{
287 throw new Exception("您继承的类文件 $controlname 不存在");
288 }
289 }
290 }
291 return $s;
292 }
293
294 /**
295 * 创建模型中的数据库操作对象
296 * @param string $model 类名或表名
297 * @return object 数据库连接对象
298 */
299 public static function model($model) {
300 $modelname = $model.'_model.class.php';
301 if(isset($_ENV['_models'][$modelname])) {
302 return $_ENV['_models'][$modelname];
303 }
304 $objfile = RUNTIME_MODEL.$modelname;
305
306 // 如果缓存文件不存在,则搜索原始文件,并编译后,写入缓存文件
307 if(DEBUG || !is_file($objfile)) {
308 $modelfile = core::get_original_file($modelname, MODEL_PATH);
309
310 if(!$modelfile) {
311 throw new Exception("模型 $modelname 文件不存在");
312 }
313
314 $s = file_get_contents($modelfile);
315 $s = preg_replace_callback('#\t*\/\/\s*hook\s+([\w\.]+)[\r\n]#', array('core', 'parse_hook'), $s); // 处理 hook
316 if(!FW($objfile, $s)) {
317 throw new Exception("写入 model 编译文件 $modelname 失败");
318 }
319 }
320
321 include $objfile;
322 $mod = new $model();
323 $_ENV['_models'][$modelname] = $mod;
324 return $mod;
325 }
326
327 /**
328 * 获取原始文件路径 (注意:插件最大,插件可代替程序核心功能)
329 * 支持 block control model view (目的:统一设计思路,方便记忆和理解)
330 * @param string $filename 文件名
331 * @param string $path 绝对路径
332 * @return string 获取成功返回路径, 获取失败返回false
333 */
334 public static function get_original_file($filename, $path) {
335 if(empty($_ENV['_config']['plugin_disable'])) {
336 $plugins = self::get_plugins();
337 if(isset($plugins['enable']) && is_array($plugins['enable'])) {
338 $plugin_enable = array_keys($plugins['enable']);
339 foreach($plugin_enable as $p) {
340 // 第1步 查找 plugin/xxx/APP_NAME/xxx.(php|htm)
341 if(is_file(PLUGIN_PATH.$p.'/'.APP_NAME.'/'.$filename)) {
342 return PLUGIN_PATH.$p.'/'.APP_NAME.'/'.$filename;
343 }
344 // 第2步 查找 plugin/xxx/xxx.(php|htm)
345 if(is_file(PLUGIN_PATH.$p.'/'.$filename)) {
346 return PLUGIN_PATH.$p.'/'.$filename;
347 }
348 }
349 }
350 }
351
352 // 第3步 查找 (block|control|model|view)/xxx.(php|htm)
353 if(is_file($path.$filename)) {
354 return $path.$filename;
355 }
356 return FALSE;
357 }
358
359 /**
360 * 获取所有插件
361 * @param boolean $force 强制重新获取
362 * @return array('not_install', 'disable', 'enable')
363 */
364 public static function get_plugins($force = 0) {
365 static $plugins = array();
366 if(!empty($plugins) && !$force) return $plugins;
367
368 if(!is_dir(PLUGIN_PATH)) return array();
369 $plugin_dirs = get_dirs(PLUGIN_PATH);
370
371 $plugin_arr = is_file(CONFIG_PATH.'plugin.inc.php') ? (array)include(CONFIG_PATH.'plugin.inc.php') : array();
372 foreach($plugin_dirs as $dir) {
373 $cfg = is_file(PLUGIN_PATH.$dir.'/conf.php') ? (array)include(PLUGIN_PATH.$dir.'/conf.php') : array();
374
375 $cfg['rank'] = isset($cfg['rank']) ? $cfg['rank'] : 100;
376
377 if(empty($plugin_arr[$dir])) {
378 $plugins['not_install'][$dir] = $cfg;
379 }elseif(empty($plugin_arr[$dir]['enable'])) {
380 $plugins['disable'][$dir] = $cfg;
381 }else{
382 $plugins['enable'][$dir] = $cfg;
383 }
384 }
385
386 //排序规则 rank升序 -> 插件名升序
387 _array_multisort($plugins['enable'], 'rank');
388 _array_multisort($plugins['disable'], 'rank');
389 _array_multisort($plugins['not_install'], 'rank');
390
391 return $plugins;
392 }
393
394 /**
395 * 解析启用插件目录,是否有 hook
396 * @param array $matches 参数数组
397 * @return string
398 */
399 public static function parse_hook($matches) {
400 $str = "\n";
401 if(!is_dir(PLUGIN_PATH) || !empty($_ENV['_config']['plugin_disable'])) return $str;
402
403 $plugins = core::get_plugins();
404 if(empty($plugins['enable'])) return $str;
405
406 $plugin_enable = array_keys($plugins['enable']);
407 foreach($plugin_enable as $p) {
408 $file = PLUGIN_PATH.$p.'/'.$matches[1];
409 if(!is_file($file)) continue;
410
411 $s = file_get_contents($file);
412 $str .= self::clear_code($s);
413 }
414 return $str;
415 }
416
417 /**
418 * 清除头尾不需要的代码
419 * @param array $s 字符串
420 * @return string
421 */
422 public static function clear_code($s) {
423 $s = trim($s);
424 if(substr($s, 0, 5) == '<?php') $s = substr($s, 5);
425 $s = ltrim($s);
426 if(substr($s, 0, 29) == 'defined(\'KONG_PATH\') || exit;') $s = substr($s, 29);
427 if(substr($s, -2, 2) == '?>') $s = substr($s, 0, -2);
428 return $s;
429 }
430}
431