PageRenderTime 59ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/core/classes/core.class.php

https://github.com/lewisliud/myqee
PHP | 2418 lines | 1663 code | 276 blank | 479 comment | 229 complexity | ebf3aecb88d40dc69b15c4ee00a5d398 MD5 | raw file
Possible License(s): LGPL-3.0, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * 返回一个静态资源的URL路径. Core::url_assets() 的别名
  4. *
  5. * echo url_assets('js/global.js');
  6. *
  7. * @param string $uri
  8. */
  9. function url_assets($uri='')
  10. {
  11. return Core::url_assets($uri);
  12. }
  13. /**
  14. * 返回一个URL. Core::url() 的别名
  15. *
  16. * echo url(); //返回首页地址
  17. *
  18. * @param string $uri
  19. * @return string
  20. */
  21. function url($uri='')
  22. {
  23. return Core::url($uri);
  24. }
  25. /**
  26. * 读取配置数据. Core::config() 的别名
  27. *
  28. * echo config('core'); //返回核心配置
  29. *
  30. * @param string $key
  31. * @return string
  32. */
  33. function config($key=null)
  34. {
  35. return Core::config($key);
  36. }
  37. /**
  38. * MyQEE 核心类
  39. *
  40. * @author 呼吸二氧化碳 <jonwang@myqee.com>
  41. * @category MyQEE
  42. * @package System
  43. * @subpackage Core
  44. * @copyright Copyright (c) 2008-2013 myqee.com
  45. * @license http://www.myqee.com/license.html
  46. */
  47. abstract class Core_Core extends Bootstrap
  48. {
  49. /**
  50. * MyQEE版本号
  51. *
  52. * @var string
  53. */
  54. const VERSION = '3.0';
  55. /**
  56. * 版本发布状态
  57. *
  58. * stable, rc1, rc2, beta1, beta2, ...
  59. *
  60. * @var string
  61. */
  62. const RELEASE = 'rc2';
  63. /**
  64. * 项目开发者
  65. *
  66. * @var string
  67. */
  68. const CODER = '呼吸二氧化碳 <jonwang@myqee.com>';
  69. /**
  70. * 页面编码
  71. *
  72. * @var string
  73. */
  74. public static $charset = 'utf-8';
  75. /**
  76. * 页面传入的PATHINFO参数
  77. *
  78. * @var array
  79. */
  80. public static $arguments = array();
  81. /**
  82. * 页面输出内容
  83. *
  84. * @var string
  85. */
  86. public static $output = '';
  87. /**
  88. * 自动加载时各个文件夹后缀
  89. *
  90. * 例如,classes目录为.class,则文件实际后缀为.class.php
  91. * 注意,只有对EXT常理设置的后缀文件有效,默认情况下EXT=.php
  92. *
  93. * @var array
  94. */
  95. public static $autoload_dir_ext = array
  96. (
  97. 'config' => '.config',
  98. 'classes' => '.class',
  99. 'controllers' => '.controller',
  100. 'models' => '.model',
  101. 'orm' => '.orm',
  102. 'views' => '.view',
  103. );
  104. /**
  105. * 缓冲区包含数
  106. * @var int
  107. */
  108. protected static $buffer_level = 0;
  109. /**
  110. * 页面在关闭前需要执行的方法列队
  111. * 通过Core::register_shutdown_function()设置
  112. * @var array
  113. */
  114. protected static $shutdown_function = array();
  115. /**
  116. * getFactory获取的对象寄存器
  117. * @var array
  118. */
  119. protected static $instances = array();
  120. /**
  121. * 执行Core::close_all_connect()方法时会关闭链接的类和方法名的列队
  122. *
  123. * 可通过Core::add_close_connect_class()方法进行设置增加
  124. *
  125. * array
  126. * (
  127. * 'Database' => 'close_all_connect',
  128. * );
  129. *
  130. * @var array
  131. */
  132. protected static $close_connect_class_list = array();
  133. /**
  134. * import_library回调函数列表
  135. *
  136. * @var array
  137. */
  138. protected static $import_library_callback = array();
  139. /**
  140. * change_project回调函数列表
  141. *
  142. * @var array
  143. */
  144. protected static $change_project_callback = array();
  145. /**
  146. * 使用 Core::url() 会附带的参数列表
  147. *
  148. * @var array
  149. */
  150. protected static $_url_auto_args = array();
  151. /**
  152. * 系统启动
  153. *
  154. * @param boolean $auto_execute 是否直接运行
  155. */
  156. public static function setup($auto_execute = true)
  157. {
  158. static $run = null;
  159. if (null===$run)
  160. {
  161. $run = true;
  162. Core::$charset = Core::$config['charset'];
  163. if (!IS_CLI)
  164. {
  165. # 输出powered by信息
  166. $x_powered_by = (isset(Core::$config['hide_x_powered_by_header']) && Core::$config['hide_x_powered_by_header']) ? Core::$config['hide_x_powered_by_header'] : false;
  167. if (is_string($x_powered_by))
  168. {
  169. $str = 'X-Powered-By: ' . trim(str_replace(array("\r", "\n"), '', $x_powered_by));
  170. }
  171. else if (!$x_powered_by)
  172. {
  173. $str = 'X-Powered-By: PHP/' . PHP_VERSION . ' MyQEE/' . Core::VERSION .'('. Core::RELEASE .')';
  174. }
  175. else
  176. {
  177. $str = null;
  178. }
  179. if ($str)
  180. {
  181. header($str);
  182. }
  183. }
  184. if (IS_DEBUG)
  185. {
  186. Core::debug()->info('SERVER IP:' . $_SERVER["SERVER_ADDR"] . (function_exists('php_uname')?'. SERVER NAME:' . php_uname('a') : ''));
  187. if (Core::$project)
  188. {
  189. Core::debug()->info('project: ' . Core::$project);
  190. }
  191. if (IS_ADMIN_MODE)
  192. {
  193. Core::debug()->info('admin mode');
  194. }
  195. if (IS_REST_MODE)
  196. {
  197. Core::debug()->info('RESTFul mode');
  198. }
  199. Core::debug()->group('include path');
  200. foreach ( Core::include_path() as $value )
  201. {
  202. Core::debug()->log(Core::debug_path($value));
  203. }
  204. Core::debug()->groupEnd();
  205. }
  206. if ((IS_CLI || IS_DEBUG) && class_exists('ErrException', true))
  207. {
  208. # 注册脚本
  209. register_shutdown_function(array('ErrException', 'shutdown_handler'));
  210. # 捕获错误
  211. set_exception_handler(array('ErrException', 'exception_handler'));
  212. set_error_handler(array('ErrException', 'error_handler'), error_reporting());
  213. }
  214. else
  215. {
  216. # 注册脚本
  217. register_shutdown_function(array('Core', 'shutdown_handler'));
  218. # 捕获错误
  219. set_exception_handler(array('Core', 'exception_handler'));
  220. set_error_handler(array('Core', 'error_handler'), error_reporting());
  221. }
  222. if (!IS_CLI)
  223. {
  224. # 初始化 HttpIO 对象
  225. HttpIO::setup();
  226. }
  227. # 注册输出函数
  228. register_shutdown_function(array('Core', '_output_body'));
  229. if (true===IS_SYSTEM_MODE)
  230. {
  231. if (false===Core::check_system_request_allow())
  232. {
  233. # 内部请求验证不通过
  234. Core::show_500('system request hash error');
  235. }
  236. }
  237. if (IS_DEBUG && isset($_REQUEST['debug']) && class_exists('Profiler', true))
  238. {
  239. Profiler::setup();
  240. }
  241. if (!defined('URL_ASSETS'))
  242. {
  243. /**
  244. * 静态文件URL地址前缀
  245. *
  246. * @var string
  247. */
  248. define('URL_ASSETS', rtrim(Core::config('url.assets', Core::url('/assets/')), '/') . '/');
  249. }
  250. }
  251. if ($auto_execute)
  252. {
  253. Core::run();
  254. }
  255. }
  256. protected static function run()
  257. {
  258. if (IS_CLI || IS_SYSTEM_MODE)
  259. {
  260. Core::execute(Core::$path_info);
  261. }
  262. else
  263. {
  264. ob_start();
  265. try
  266. {
  267. Core::execute(Core::$path_info);
  268. }
  269. catch (Exception $e)
  270. {
  271. $code = $e->getCode();
  272. if (404===$code || E_PAGE_NOT_FOUND===$code)
  273. {
  274. Core::show_404($e->getMessage());
  275. }
  276. elseif (500===$code)
  277. {
  278. Core::show_500($e->getMessage());
  279. }
  280. else
  281. {
  282. Core::show_500($e);
  283. }
  284. }
  285. Core::$output = ob_get_clean();
  286. }
  287. }
  288. /**
  289. * 获取指定key的配置
  290. *
  291. * 若不传key,则返回Core_Config对象,可获取动态配置,例如Core::config()->get();
  292. *
  293. * @param string $key
  294. * @param mixed $default 默认值
  295. * @return Config
  296. * @return array
  297. */
  298. public static function config($key = null, $default = null)
  299. {
  300. if (null===$key)
  301. {
  302. return Core::factory('Config');
  303. }
  304. $c = explode('.', $key);
  305. $cname = array_shift($c);
  306. if (strtolower($cname)=='core')
  307. {
  308. $v = Core::$core_config;
  309. }
  310. elseif (isset(Core::$config[$cname]))
  311. {
  312. $v = Core::$config[$cname];
  313. }
  314. else
  315. {
  316. return $default;
  317. }
  318. if ($c)foreach ($c as $i)
  319. {
  320. if (!isset($v[$i]))return $default;
  321. $v = $v[$i];
  322. }
  323. return $v;
  324. }
  325. /**
  326. * Cookie
  327. * @return Core_Cookie
  328. */
  329. public static function cookie()
  330. {
  331. return Core::factory('Cookie');
  332. }
  333. /**
  334. * 路由处理
  335. *
  336. * @return Core_Route
  337. */
  338. public static function route()
  339. {
  340. return Core::factory('Route');
  341. }
  342. /**
  343. * 返回URL路径
  344. *
  345. * 自3.0起可用 url() 直接快速调用此方法
  346. *
  347. * Core::url('test/');
  348. * url('test/');
  349. *
  350. * @param string $url URL
  351. * @param true|string $isfullurl_or_project 若传true,则返回当前项目的完整url(http(s)://开头),若传项目名,比如default,则返回指定项目的完整URL
  352. * @return string
  353. */
  354. public static function url($uri = '' , $isfullurl_or_project = false)
  355. {
  356. list($url, $query) = explode('?', $uri , 2);
  357. $url = Core::$base_url. ltrim($url, '/') . ($url!='' && substr($url,-1)!='/' && false===strpos($url, '.') && Core::$config['url_suffix']?Core::$config['url_suffix']:'') . ($query?'?'.$query:'');
  358. # 返回完整URL
  359. if (true===$isfullurl_or_project && !preg_match('#^http(s)?://#i', $url))
  360. {
  361. $url = HttpIO::PROTOCOL . $_SERVER["HTTP_HOST"] . $url;
  362. }
  363. # 添加自动追加的参数
  364. if (Core::$_url_auto_args)
  365. {
  366. list($url, $hash) = explode('#', $url, 2);
  367. list($u, $q) = explode('?', $url, 2);
  368. if (!$q)
  369. {
  370. $q = '';
  371. parse_str($q, $q);
  372. $q += Core::$_url_auto_args;
  373. }
  374. else
  375. {
  376. $q = Core::$_url_auto_args;
  377. }
  378. $url = $u .'?'. http_build_query($q, '', '&') . ($hash?'#'.$hash:'');
  379. }
  380. return $url;
  381. }
  382. /**
  383. * 返回静态资源URL路径
  384. *
  385. * @param string $uri
  386. */
  387. public static function url_assets($uri = '')
  388. {
  389. $url = ltrim($uri, './ ');
  390. if (IS_DEBUG & 1)
  391. {
  392. # 本地调试环境
  393. $url_asstes = Core::url('/assets-dev/');
  394. }
  395. else
  396. {
  397. $url_asstes = URL_ASSETS . Core::$project . '/' . (IS_ADMIN_MODE?'~admin/':'');
  398. list($file, $query) = explode('?', $uri.'?', 2);
  399. $uri = $file . '?' . (strlen($query)>0?$query.'&':'') . Core::assets_hash($file);
  400. }
  401. return $url_asstes . $url;
  402. }
  403. /**
  404. * 增加URL默认参数
  405. *
  406. * 比如用在URL跟踪访客统计上,不支持Session的时候通过URL传送的SessionID等
  407. *
  408. * 增加的参数在所有使用 `Core::url()` 返回的内容里都会附带这个参数
  409. *
  410. * Core::add_url_args('debug', 'test');
  411. *
  412. * Core::add_url_args(array('debug'=>'test', 't2'=>'v'2));
  413. *
  414. * @param $key 参数名称
  415. * @param $value 参数值
  416. * @since v3.0
  417. */
  418. public static function add_url_args($key, $value)
  419. {
  420. if (is_array($key))
  421. {
  422. Core::$_url_auto_args += $key;
  423. }
  424. else
  425. {
  426. Core::$_url_auto_args[$key] = $value;
  427. }
  428. }
  429. /**
  430. * Include一个指定URI的控制器
  431. *
  432. * @param string $uri
  433. * @return class_name | false
  434. */
  435. public static function load_controller($uri)
  436. {
  437. $found = Core::find_controller($uri);
  438. if ($found)
  439. {
  440. # 返回类的名称
  441. return $found['class'];
  442. }
  443. else
  444. {
  445. return false;
  446. }
  447. }
  448. /**
  449. * 是否系统设置禁用文件写入功能
  450. *
  451. * 可在 `config.php` 中设置 `$config['file_write_mode'] = 'disable';` 如果disable则返回true,否则返回false
  452. *
  453. * @return boolean
  454. */
  455. public static function is_file_write_disabled()
  456. {
  457. if (Core::config('core.file_write_mode')=='disable')
  458. {
  459. return true;
  460. }
  461. else
  462. {
  463. return false;
  464. }
  465. }
  466. /**
  467. * 记录日志
  468. *
  469. * @param string $msg 日志内容
  470. * @param string $type 类型,例如:log,error,debug 等
  471. * @param stirng $file 指定文件名,不指定则默认
  472. * @return boolean
  473. */
  474. public static function log($msg, $type = 'log', $file = null)
  475. {
  476. if (Core::is_file_write_disabled())return true;
  477. # log配置
  478. $log_config = Core::config('log');
  479. # 不记录日志
  480. if (isset($log_config['use']) && !$log_config['use'])
  481. {
  482. return true;
  483. }
  484. # 内容格式化
  485. if ($log_config['format'])
  486. {
  487. $format = $log_config['format'];
  488. }
  489. else
  490. {
  491. # 默认格式
  492. $format = ':time - :host::port - :url - :msg';
  493. }
  494. # 获取日志内容
  495. $data = Core::log_format($msg, $type, $format);
  496. if (IS_DEBUG)
  497. {
  498. # 如果有开启debug模式输出到浏览器
  499. Core::debug()->log($data, $type);
  500. }
  501. # 保存日志
  502. return Core::write_log($data, $type, $file);
  503. }
  504. /**
  505. * 执行指定URI的控制器
  506. *
  507. * @param string $uri
  508. */
  509. public static function execute($uri)
  510. {
  511. $found = Core::find_controller($uri);
  512. if ($found)
  513. {
  514. if (isset($found['route']))
  515. {
  516. $class_name = $found['class'];
  517. $class_exists = class_exists($class_name, true);
  518. $arguments = array();
  519. if (isset($found['route']['action']) && $found['route']['action'])
  520. {
  521. $arguments[] = $found['route']['action'];
  522. }
  523. }
  524. else
  525. {
  526. require $found['file'];
  527. if ($found['ns']=='team-library' || $found['ns']=='project')
  528. {
  529. $class_name = $found['class'];
  530. }
  531. else
  532. {
  533. $class_name = str_replace('.', '_', $found['ns']) . '_' . $found['class'];
  534. }
  535. $class_exists = class_exists($class_name, false);
  536. }
  537. if ($class_exists)
  538. {
  539. $controller = new $class_name();
  540. Controller::$controllers[] = $controller;
  541. # 是否有必要将action从$arguments中移出
  542. $need_shift_action = false;
  543. $arguments = $found['args'];
  544. if ($arguments)
  545. {
  546. $action = current($arguments);
  547. if (0===strlen($action))
  548. {
  549. $action = 'default';
  550. }
  551. else
  552. {
  553. $need_shift_action = true;
  554. }
  555. }
  556. else
  557. {
  558. $action = 'index';
  559. }
  560. $action_name = 'action_' . $action;
  561. if (!method_exists($controller, $action_name))
  562. {
  563. if ($action_name!='action_default' && method_exists($controller, 'action_default'))
  564. {
  565. $action_name = 'action_default';
  566. }
  567. elseif ($action_name!='' && (!$arguments || $arguments===array('')) && method_exists($controller, 'action_index'))
  568. {
  569. $action_name = 'action_index';
  570. }
  571. elseif (method_exists($controller, '__call'))
  572. {
  573. $controller->__call($action_name, $arguments);
  574. Core::rm_controoler($controller);
  575. return;
  576. }
  577. else
  578. {
  579. Core::rm_controoler($controller);
  580. throw new Exception(__('Page Not Found'), 404);
  581. }
  582. }
  583. elseif ($need_shift_action)
  584. {
  585. array_shift($arguments);
  586. }
  587. $ispublicmethod = new ReflectionMethod($controller, $action_name);
  588. if (!$ispublicmethod->isPublic())
  589. {
  590. Core::rm_controoler($controller);
  591. throw new Exception(__('Request Method Not Allowed.'), 405);
  592. }
  593. unset($ispublicmethod);
  594. # POST 方式自动CSRF判断
  595. if (HttpIO::METHOD=='POST')
  596. {
  597. $auto_check_post_method_referer = isset($controller->auto_check_post_method_referer)?$controller->auto_check_post_method_referer:Core::config('auto_check_post_method_referer', true);
  598. if ($auto_check_post_method_referer && !HttpIO::csrf_check())
  599. {
  600. throw new Exception(__('Not Acceptable.'), 406);
  601. }
  602. }
  603. if (isset($found['route']))
  604. {
  605. # 设置Route参数
  606. foreach ($found['route'] as $k => $v)
  607. {
  608. $controller->$k = $v;
  609. }
  610. }
  611. else
  612. {
  613. $controller->ids = $found['ids'];
  614. }
  615. # 将参数传递给控制器
  616. $controller->action = $action_name;
  617. $controller->controller = $found['class'];
  618. $controller->uri = $uri;
  619. $controller->directory = $found['dir'];
  620. if (IS_SYSTEM_MODE)
  621. {
  622. # 系统内部调用参数
  623. $controller->arguments = @unserialize(HttpIO::POST('data', HttpIO::PARAM_TYPE_OLDDATA));
  624. }
  625. else
  626. {
  627. $controller->arguments = $arguments;
  628. }
  629. # 设置 HttpIO 参数
  630. HttpIO::set_params_controller($controller);
  631. # 前置方法
  632. if (method_exists($controller, 'before'))
  633. {
  634. $controller->before();
  635. }
  636. # 执行方法
  637. $count_arguments = count($arguments);
  638. switch ($count_arguments)
  639. {
  640. case 0:
  641. $controller->$action_name();
  642. break;
  643. case 1:
  644. $controller->$action_name($arguments[0]);
  645. break;
  646. case 2:
  647. $controller->$action_name($arguments[0], $arguments[1]);
  648. break;
  649. case 3:
  650. $controller->$action_name($arguments[0], $arguments[1], $arguments[2]);
  651. break;
  652. case 4:
  653. $controller->$action_name($arguments[0], $arguments[1], $arguments[2], $arguments[3]);
  654. break;
  655. default:
  656. call_user_func_array(array($controller, $action_name), $arguments);
  657. break;
  658. }
  659. # 后置方法
  660. if (method_exists($controller, 'after'))
  661. {
  662. $controller->after();
  663. }
  664. # 移除控制器
  665. Core::rm_controoler($controller);
  666. unset($controller);
  667. }
  668. else
  669. {
  670. throw new Exception(__('Page Not Found'), 404);
  671. }
  672. }
  673. else
  674. {
  675. throw new Exception(__('Page Not Found'), 404);
  676. }
  677. }
  678. protected static function rm_controoler($controller)
  679. {
  680. foreach (Controller::$controllers as $k=>$c)
  681. {
  682. if ($c===$controller)unset(Controller::$controllers[$k]);
  683. }
  684. Controller::$controllers = array_values(Controller::$controllers);
  685. }
  686. /**
  687. * 寻找控制器
  688. *
  689. * @return array
  690. */
  691. protected static function find_controller($uri)
  692. {
  693. $uri = ltrim($uri, '/');
  694. if (Core::$config['url_suffix'] && substr(strtolower($uri), -strlen(Core::$config['url_suffix']))==Core::$config['url_suffix'])
  695. {
  696. $uri = substr($uri, 0, -strlen(Core::$config['url_suffix']));
  697. }
  698. if (!IS_SYSTEM_MODE && isset(Core::$config['route']) && Core::$config['route'])
  699. {
  700. # 有路由配置首先根据路由配置查询控制器
  701. $found_route = Route::get($uri);
  702. if ($found_route)
  703. {
  704. if (!isset($found_route['controller']) || !$found_route['controller'])
  705. {
  706. if (IS_DEBUG)Core::debug()->error('The route not match controller');
  707. Core::show_404();
  708. }
  709. return array
  710. (
  711. 'class' => 'Controller_' . preg_replace('#[^a-zA-Z0-9_]#', '_', trim($found_route['controller'])),
  712. 'route' => $found_route,
  713. );
  714. }
  715. else
  716. {
  717. unset($found_route);
  718. }
  719. }
  720. if ($uri!='')
  721. {
  722. $uri_arr = explode('/', strtolower($uri));
  723. }
  724. else
  725. {
  726. $uri_arr = array();
  727. }
  728. if (IS_DEBUG)
  729. {
  730. Core::debug()->log('/'. $uri, 'find controller uri');
  731. }
  732. $include_path = Core::$include_path;
  733. # log
  734. $find_log = $find_path_log = array();
  735. # 控制器目录
  736. $controller_dir = Core::$dir_setting['controller'][0];
  737. if (IS_SYSTEM_MODE)
  738. {
  739. $controller_dir .= '-system';
  740. }
  741. elseif (IS_ADMIN_MODE)
  742. {
  743. $controller_dir .= '-admin';
  744. }
  745. elseif (IS_REST_MODE)
  746. {
  747. $controller_dir .= '-rest';
  748. }
  749. elseif (IS_CLI)
  750. {
  751. $controller_dir .= '-shell';
  752. }
  753. # 首先找到存在的目录
  754. $found_path = array();
  755. foreach ($include_path as $ns => $ipath)
  756. {
  757. if($ipath)foreach ($ipath as $lib_ns => $path)
  758. {
  759. if ($ns==='library')
  760. {
  761. $tmp_ns = 'library_' . str_replace('.', '_', $lib_ns);
  762. }
  763. else
  764. {
  765. $tmp_ns = $ns;
  766. }
  767. $tmp_str = $real_path = $real_class = '';
  768. $tmp_path = $path . $controller_dir . DS;
  769. $ids = array();
  770. foreach ($uri_arr as $uri_path)
  771. {
  772. $ds = DS;
  773. if ($uri_path==='')
  774. {
  775. if (count($uri_arr)>1)
  776. {
  777. break;
  778. }
  779. $real_uri_path = '';
  780. $ds = '';
  781. }
  782. elseif (is_numeric($uri_path))
  783. {
  784. $real_uri_path = '_id';
  785. $ids[] = $uri_path;
  786. }
  787. elseif ($uri_path == '_id')
  788. {
  789. # 不允许直接在URL中使用_id
  790. break;
  791. }
  792. elseif (preg_match('#[^a-z0-9_]#i', $uri_path))
  793. {
  794. # 不允许非a-z0-9_的字符在控制中
  795. break;
  796. }
  797. else
  798. {
  799. $real_uri_path = $uri_path;
  800. }
  801. $tmpdir = $tmp_path . $real_path . $real_uri_path . $ds;
  802. if (IS_DEBUG)
  803. {
  804. $find_path_log[] = Core::debug_path($tmpdir);
  805. }
  806. $real_path .= $real_uri_path . DS;
  807. $real_class .= $real_uri_path . '_';
  808. $tmp_str .= $uri_path . DS;
  809. if (is_dir($tmpdir))
  810. {
  811. $found_path[$tmp_str][] = array
  812. (
  813. $tmp_ns,
  814. $tmpdir,
  815. ltrim($real_class, '_'),
  816. $ids,
  817. );
  818. }
  819. else
  820. {
  821. break;
  822. }
  823. }
  824. // 根目录的
  825. if (is_dir($tmp_path))
  826. {
  827. $found_path[''][] = array
  828. (
  829. $tmp_ns,
  830. $tmp_path,
  831. '',
  832. array(),
  833. );
  834. if (IS_DEBUG)
  835. {
  836. $find_path_log[] = Core::debug_path($tmp_path);
  837. }
  838. }
  839. }
  840. }
  841. unset($ids);
  842. $found = null;
  843. # 寻找可能的文件
  844. if ($found_path)
  845. {
  846. # 调整优先级
  847. krsort($found_path);
  848. foreach ($found_path as $path => $all_path)
  849. {
  850. $path_len = strlen($path);
  851. $tmp_p = substr($uri, $path_len);
  852. if (strlen($tmp_p)>0)
  853. {
  854. $args = explode('/', substr($uri, $path_len));
  855. }
  856. else
  857. {
  858. $args = array();
  859. }
  860. $the_id = array();
  861. $tmp_class = array_shift($args);
  862. $tmp_arg = $tmp_class;
  863. $directory = rtrim('/'. substr($uri, 0, $path_len) . $tmp_class, '/');
  864. if (0===strlen($tmp_class))
  865. {
  866. $tmp_class = 'index';
  867. }
  868. elseif (is_numeric($tmp_class))
  869. {
  870. $the_id = array
  871. (
  872. $tmp_class
  873. );
  874. $tmp_class = '_id';
  875. }
  876. elseif ($tmp_class == '_id')
  877. {
  878. continue;
  879. }
  880. $real_class = $tmp_class;
  881. $tmp_class = strtolower($tmp_class);
  882. // 记录找到的index.controller.php
  883. $found_index_class = null;
  884. if (IS_DEBUG)
  885. {
  886. $find_log2 = array();
  887. }
  888. foreach ($all_path as $tmp_arr)
  889. {
  890. list($ns, $tmp_path, $real_path, $ids) = $tmp_arr;
  891. $tmpfile = $tmp_path . $tmp_class . Core::$dir_setting['controller'][1] . EXT;
  892. if (IS_DEBUG)
  893. {
  894. $find_log[] = Core::debug_path($tmpfile);
  895. }
  896. if (is_file($tmpfile))
  897. {
  898. if ($the_id)
  899. {
  900. $ids = array_merge($ids, $the_id);
  901. }
  902. if ($directory && substr($directory, -1-strlen($tmp_class))=='/'.$tmp_class)
  903. {
  904. $directory = substr($directory, 0, -1-strlen($tmp_class));
  905. }
  906. $found = array
  907. (
  908. 'file' => $tmpfile,
  909. 'dir' => $directory,
  910. 'ns' => $ns,
  911. 'class' => 'Controller_' . $real_path . $real_class,
  912. 'args' => $args,
  913. 'ids' => $ids,
  914. );
  915. break 2;
  916. }
  917. elseif (!$found_index_class && $tmp_class!='default')
  918. {
  919. // 记录 index.controller.php 控制器
  920. $tmpfile = $tmp_path . 'default' . Core::$dir_setting['controller'][1] . EXT;
  921. if (IS_DEBUG)
  922. {
  923. $find_log2[] = Core::debug_path($tmpfile);
  924. }
  925. if (is_file($tmpfile))
  926. {
  927. if (null!==$tmp_arg)
  928. {
  929. array_unshift($args, $tmp_arg);
  930. if (strlen($tmp_arg)>0)
  931. {
  932. $directory = substr($directory, 0, -strlen($tmp_arg) - 1);
  933. }
  934. }
  935. $found_index_class = array
  936. (
  937. 'file' => $tmpfile,
  938. 'dir' => $directory,
  939. 'ns' => $ns,
  940. 'class' => 'Controller_' . $real_path . 'Default',
  941. 'args' => $args,
  942. 'ids' => $ids,
  943. );
  944. }
  945. }
  946. }
  947. if (IS_DEBUG && $find_log2)
  948. {
  949. $find_log = array_merge($find_log, $find_log2);
  950. }
  951. // index.controller.php 文件
  952. if (!$found && $found_index_class)
  953. {
  954. $found = $found_index_class;
  955. break;
  956. }
  957. }
  958. }
  959. if (IS_DEBUG)
  960. {
  961. Core::debug()->group('find controller path');
  962. foreach ($find_path_log as $value)
  963. {
  964. Core::debug()->log($value);
  965. }
  966. Core::debug()->groupEnd();
  967. Core::debug()->group('find controller files');
  968. foreach ($find_log as $value)
  969. {
  970. Core::debug()->log($value);
  971. }
  972. Core::debug()->groupEnd();
  973. if ($found)
  974. {
  975. $found2 = $found;
  976. $found2['file'] = Core::debug_path($found2['file']);
  977. Core::debug()->log($found2, 'found contoller');
  978. }
  979. else
  980. {
  981. Core::debug()->log('/'. $uri, 'not found contoller');
  982. }
  983. }
  984. if (is_array($found) && isset($found['class']))
  985. {
  986. $found['class'] = preg_replace('#[^a-zA-Z0-9_]#', '_', trim($found['class']));
  987. }
  988. return $found;
  989. }
  990. /**
  991. * 写入日志
  992. *
  993. * 若有特殊写入需求,可以扩展本方法(比如调用数据库类克写到数据库里)
  994. *
  995. * @param string $data
  996. * @param string $type 日志类型
  997. * @param stirng $file 指定文件名,不指定则默认
  998. * @return boolean
  999. */
  1000. protected static function write_log($data, $type = 'log', $file = null)
  1001. {
  1002. static $pro = null;
  1003. if (!$type)$type = 'log';
  1004. if (null===$pro)
  1005. {
  1006. if (preg_match('#^(db|cache)://([a-z0-9_]+)/([a-z0-9_]+)$#i', DIR_LOG , $m))
  1007. {
  1008. $pro = $m;
  1009. }
  1010. else
  1011. {
  1012. $pro = false;
  1013. }
  1014. }
  1015. # Log目录采用文件目录
  1016. if (false===$pro)
  1017. {
  1018. $write_mode = Core::config('core.file_write_mode');
  1019. # 禁用写入
  1020. if ($write_mode=='disable')return true;
  1021. # 再判断是否有转换储存处理
  1022. if (preg_match('#^(db|cache)://([a-z0-9_]+)/([a-z0-9_]+)$#i', $write_mode , $m))
  1023. {
  1024. $pro = $m;
  1025. }
  1026. }
  1027. if (false===$pro)
  1028. {
  1029. # 以文件的形式保存
  1030. $log_config = Core::config('log');
  1031. if (!$file)
  1032. {
  1033. if ($log_config['file'])
  1034. {
  1035. $file = date($log_config['file']);
  1036. }
  1037. else
  1038. {
  1039. $file = date('Y/m/d/');
  1040. }
  1041. $file .= $type . '.log';
  1042. }
  1043. $dir = trim(dirname($file), '/');
  1044. # 如果目录不存在则创建
  1045. if (!is_dir(DIR_LOG.$dir))
  1046. {
  1047. $temp = explode('/', str_replace('\\', '/', $dir) );
  1048. $cur_dir = '';
  1049. for($i=0; $i<count($temp); $i++)
  1050. {
  1051. $cur_dir .= $temp[$i] . "/";
  1052. if (!is_dir(DIR_LOG.$cur_dir))
  1053. {
  1054. @mkdir(DIR_LOG.$cur_dir, 0755);
  1055. }
  1056. }
  1057. }
  1058. return false===@file_put_contents(DIR_LOG . $file, $data . CRLF , FILE_APPEND)?false:true;
  1059. }
  1060. else
  1061. {
  1062. # 以db或cache方式保存
  1063. if ($pro[1]=='db')
  1064. {
  1065. $db_data = array
  1066. (
  1067. 'key' => md5($file),
  1068. 'key_str'=> substr($file, 0, 255),
  1069. 'type' => $type,
  1070. 'day' => date('Ymd'),
  1071. 'time' => TIME,
  1072. 'value' => $data,
  1073. );
  1074. $obj = new Database($pro[2]);
  1075. $status = $obj->insert($pro[3], $db_data) ? true:false;
  1076. }
  1077. else
  1078. {
  1079. if ($pro[3])
  1080. {
  1081. $pro[1]['prefix'] = trim($pro[3]) . '_';
  1082. }
  1083. $pro[1]['prefix'] .= $type.'_';
  1084. $obj = new Cache($pro[2]);
  1085. $status = $obj->set(date('Ymd').'_'.md5($file), $data, 86400*30); // 存1月
  1086. }
  1087. return $status;
  1088. }
  1089. }
  1090. /**
  1091. * 用于保存日志时格式化内容,如需要特殊格式可以自行扩展
  1092. *
  1093. * @param string $msg
  1094. * @param string $format
  1095. * @return string
  1096. */
  1097. protected static function log_format($msg,$type,$format)
  1098. {
  1099. $value = array
  1100. (
  1101. ':time' => date('Y-m-d H:i:s'), //当前时间
  1102. ':url' => $_SERVER['SCRIPT_URI'], //请求的URL
  1103. ':msg' => $msg, //日志信息
  1104. ':type' => $type, //日志类型
  1105. ':host' => $_SERVER["SERVER_ADDR"], //服务器
  1106. ':port' => $_SERVER["SERVER_PORT"], //端口
  1107. ':ip' => HttpIO::IP, //请求的IP
  1108. ':agent' => $_SERVER["HTTP_USER_AGENT"], //客户端信息
  1109. ':referer' => $_SERVER["HTTP_REFERER"], //来源页面
  1110. );
  1111. return strtr($format, $value);
  1112. }
  1113. /**
  1114. * 获取debug对象
  1115. * 可安全用于生产环境,在生产环境下将忽略所有debug信息
  1116. * @return Debug
  1117. */
  1118. public static function debug()
  1119. {
  1120. static $debug = null;
  1121. if (null === $debug)
  1122. {
  1123. if (!IS_CLI && ( IS_DEBUG || false!==strpos($_SERVER["HTTP_USER_AGENT"],'FirePHP') || isset($_SERVER["HTTP_X_FIREPHP_VERSION"]) ) && class_exists('Debug', true))
  1124. {
  1125. $debug = Debug::instance();
  1126. }
  1127. else
  1128. {
  1129. $debug = new __NoDebug();
  1130. }
  1131. }
  1132. return $debug;
  1133. }
  1134. /**
  1135. * 将真实路径地址输出为调试地址
  1136. *
  1137. * 显示结果类似 ./system/libraries/Database.class.php
  1138. *
  1139. * @param string path to debug
  1140. * @param boolean $highlight 是否返回高亮前缀,可以传字符颜色,比如#f00
  1141. * @return string
  1142. */
  1143. public static function debug_path($file, $highlight=false)
  1144. {
  1145. if ($highlight)
  1146. {
  1147. if (IS_CLI)
  1148. {
  1149. # 命令行下输出带色彩的前缀
  1150. $l = "\x1b[36m";
  1151. $r = "\x1b[39m";
  1152. }
  1153. else
  1154. {
  1155. $l = '<span style="color:'.(is_string($highlight) && preg_match('/^[a-z0-9#\(\)\.,]+$/i',$highlight) ?$highlight:'#a00').'">';
  1156. $r = '</span>';
  1157. }
  1158. }
  1159. else
  1160. {
  1161. $l = $r = '';
  1162. }
  1163. $file = str_replace('\\', DS, $file);
  1164. if (strpos($file, DIR_CORE) === 0)
  1165. {
  1166. $file = $l . './core/' . $r . substr($file, strlen(DIR_CORE));
  1167. }
  1168. elseif (strpos($file, DIR_TEAM_LIBRARY) === 0)
  1169. {
  1170. $file = $l . './team-library/' . $r . substr($file, strlen(DIR_TEAM_LIBRARY));
  1171. }
  1172. elseif (strpos($file, DIR_LIBRARY) === 0)
  1173. {
  1174. $file = $l . './libraries/' . $r . substr($file, strlen(DIR_LIBRARY));
  1175. }
  1176. elseif (strpos($file, DIR_MODULE) === 0)
  1177. {
  1178. $file = $l . './modules/' . $r . substr($file, strlen(DIR_MODULE));
  1179. }
  1180. elseif (strpos($file, DIR_DRIVER) === 0)
  1181. {
  1182. $file = $l . './drivers/' . $r . substr($file, strlen(DIR_DRIVER));
  1183. }
  1184. elseif (strpos($file, DIR_PROJECT) === 0)
  1185. {
  1186. $file = $l . './projects/' . $r . substr($file, strlen(DIR_PROJECT));
  1187. }
  1188. elseif (strpos($file, DIR_TEMP) === 0)
  1189. {
  1190. $file = $l . './data/temp/' . $r . substr($file, strlen(DIR_TEMP));
  1191. }
  1192. elseif (strpos($file, DIR_LOG) === 0)
  1193. {
  1194. $file = $l . './data/log/' . $r . substr($file, strlen(DIR_LOG));
  1195. }
  1196. elseif (strpos($file, DIR_CACHE) === 0)
  1197. {
  1198. $file = $l . './data/cache/' . $r . substr($file, strlen(DIR_CACHE));
  1199. }
  1200. elseif (strpos($file, DIR_DATA) === 0)
  1201. {
  1202. $file = $l . './data/' . $r . substr($file, strlen(DIR_DATA));
  1203. }
  1204. elseif (strpos($file, DIR_ASSETS) === 0)
  1205. {
  1206. $file = $l . './wwwroot/assets/' . $r . substr($file, strlen(DIR_ASSETS));
  1207. }
  1208. elseif (strpos($file, DIR_UPLOAD) === 0)
  1209. {
  1210. $file = $l . './wwwroot/upload/' . $r . substr($file, strlen(DIR_UPLOAD));
  1211. }
  1212. elseif (strpos($file, DIR_WWWROOT) === 0)
  1213. {
  1214. $file = $l . './wwwroot/' . $r . substr($file, strlen(DIR_WWWROOT));
  1215. }
  1216. elseif (strpos($file, DIR_SYSTEM) === 0)
  1217. {
  1218. $file = $l . './' . $r . substr($file, strlen(DIR_SYSTEM));
  1219. }
  1220. $file = str_replace('\\', '/', $file);
  1221. return $file;
  1222. }
  1223. /**
  1224. * 关闭缓冲区
  1225. *
  1226. * @param boolean 是否输出缓冲数据
  1227. * @return void
  1228. */
  1229. public static function close_buffers($flush = true)
  1230. {
  1231. if (ob_get_level() > Core::$buffer_level)
  1232. {
  1233. $close = ($flush === true) ? 'ob_end_flush' : 'ob_end_clean';
  1234. while (ob_get_level() > Core::$buffer_level)
  1235. {
  1236. $close();
  1237. }
  1238. Core::$buffer_level = ob_get_level();
  1239. }
  1240. }
  1241. /**
  1242. * 404,可直接将Exception对象传给$msg
  1243. *
  1244. * @param string/Exception $msg
  1245. */
  1246. public static function show_404($msg = null)
  1247. {
  1248. Core::close_buffers(false);
  1249. # 避免输出的CSS头试抛出页面无法显示
  1250. @header('Content-Type: text/html;charset=' . Core::config('core.charset'), true);
  1251. HttpIO::$status = 404;
  1252. HttpIO::send_headers();
  1253. if (null === $msg)
  1254. {
  1255. $msg = __('Page Not Found');
  1256. }
  1257. if (IS_DEBUG && class_exists('ErrException', false))
  1258. {
  1259. if ($msg instanceof Exception)
  1260. {
  1261. throw $msg;
  1262. }
  1263. else
  1264. {
  1265. throw new Exception($msg, 43);
  1266. }
  1267. }
  1268. if (IS_CLI)
  1269. {
  1270. echo $msg . CRLF;
  1271. exit();
  1272. }
  1273. try
  1274. {
  1275. $view = new View('error/404');
  1276. $view->message = $msg;
  1277. $view->render(true);
  1278. }
  1279. catch ( Exception $e )
  1280. {
  1281. list ( $REQUEST_URI ) = explode('?', $_SERVER['REQUEST_URI'], 2);
  1282. $REQUEST_URI = htmlspecialchars(rawurldecode($REQUEST_URI));
  1283. echo '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">' .
  1284. CRLF . '<html>' .
  1285. CRLF . '<head>' .
  1286. CRLF . '<title>404 Not Found</title>' .
  1287. CRLF . '</head>'.
  1288. CRLF . '<body>' .
  1289. CRLF . '<h1>Not Found</h1>' .
  1290. CRLF . '<p>The requested URL ' . $REQUEST_URI . ' was not found on this server.</p>' .
  1291. CRLF . '<hr />' .
  1292. CRLF . $_SERVER['SERVER_SIGNATURE'] .
  1293. CRLF . '</body>' .
  1294. CRLF . '</html>';
  1295. }
  1296. exit();
  1297. }
  1298. /**
  1299. * 系统错误,可直接将Exception对象传给$msg
  1300. * @param string/Exception $msg
  1301. */
  1302. public static function show_500($msg = null)
  1303. {
  1304. Core::close_buffers(false);
  1305. # 避免输出的CSS头试抛出页面无法显示
  1306. @header('Content-Type: text/html;charset=' . Core::config('core.charset'), true);
  1307. HttpIO::$status = 500;
  1308. HttpIO::send_headers();
  1309. if (null === $msg)
  1310. {
  1311. $msg = __('Internal Server Error');
  1312. }
  1313. if (IS_DEBUG && class_exists('ErrException', false))
  1314. {
  1315. if ($msg instanceof Exception)
  1316. {
  1317. throw $msg;
  1318. }
  1319. else
  1320. {
  1321. throw new Exception($msg, 0);
  1322. }
  1323. }
  1324. if (IS_CLI)
  1325. {
  1326. echo "\x1b[36m";
  1327. if ($msg instanceof Exception)
  1328. {
  1329. echo $msg->getMessage() . CRLF;
  1330. }
  1331. else
  1332. {
  1333. echo $msg . CRLF;
  1334. }
  1335. echo "\x1b[39m";
  1336. echo CRLF;
  1337. exit();
  1338. }
  1339. try
  1340. {
  1341. if ($msg instanceof Exception)
  1342. {
  1343. print_r($msg);exit;
  1344. $error = $msg->getMessage();
  1345. $trace_obj = $msg;
  1346. }
  1347. else
  1348. {
  1349. $error = $msg;
  1350. $trace_obj = new Exception($msg);
  1351. }
  1352. $error_config = Core::config('core.error500');
  1353. $view = new View('error/500');
  1354. if ($error_config && isset($error_config['close']) && $error_config['close']==true)
  1355. {
  1356. # 不记录
  1357. $view->error_saved = false;
  1358. }
  1359. else
  1360. {
  1361. $trace_array = array
  1362. (
  1363. 'project' => Core::$project,
  1364. 'uri' => HttpIO::$uri,
  1365. 'url' => HttpIO::PROTOCOL . $_SERVER['HTTP_HOST'] . $_SERVER["REQUEST_URI"],
  1366. 'post' => HttpIO::POST(null, HttpIO::PARAM_TYPE_OLDDATA),
  1367. 'get' => $_SERVER['QUERY_STRING'],
  1368. 'cookie' => HttpIO::COOKIE(null, HttpIO::PARAM_TYPE_OLDDATA),
  1369. 'client_ip' => HttpIO::IP,
  1370. 'user_agent' => HttpIO::USER_AGENT,
  1371. 'referrer' => HttpIO::REFERRER,
  1372. 'server_ip' => $_SERVER["SERVER_ADDR"],
  1373. 'trace' => $trace_obj->__toString(),
  1374. );
  1375. $date = @date('Y-m-d');
  1376. $no = strtoupper(substr(md5(serialize($trace_array)), 10, 10));
  1377. $error_no = $date.'-'.$no;
  1378. # 其它数据
  1379. $trace_array['server_name'] = (function_exists('php_uname')? php_uname('a') : 'unknown');
  1380. $trace_array['time'] = TIME;
  1381. $trace_array['use_time'] = microtime(1) - START_TIME;
  1382. $trace_array['trace'] = $trace_obj;
  1383. $trace_data = base64_encode(gzcompress(serialize($trace_array), 9));
  1384. unset($trace_array);
  1385. $view->error_saved = true;
  1386. # 记录错误日志
  1387. try
  1388. {
  1389. if (isset($error_config['save_type']) && $error_config['save_type'])
  1390. {
  1391. $save_type = $error_config['save_type'];
  1392. }
  1393. else
  1394. {
  1395. $save_type = 'file';
  1396. }
  1397. if ($save_type=='file')
  1398. {
  1399. # 文件模式
  1400. $write_mode = Core::config('core.file_write_mode');
  1401. if (preg_match('#^(db|cache)://([a-z0-9_]+)/([a-z0-9_]+)$#i', $write_mode , $m))
  1402. {
  1403. $save_type = $m[1];
  1404. $error_config['type_config'] = $m[2];
  1405. }
  1406. }
  1407. switch ($save_type)
  1408. {
  1409. case 'database':
  1410. $obj = $error_config['type_config']?new Database($error_config['type_config']) : new Database();
  1411. $data = array
  1412. (
  1413. 'time' => strtotime($date.' 00:00:00'),
  1414. 'no' => $no,
  1415. 'log' => $trace_data,
  1416. 'expire_time' => TIME + 7*86400,
  1417. );
  1418. $obj->insert('error500_log', $data);
  1419. break;
  1420. case 'cache':
  1421. $obj = $error_config['type_config']?new Cache($error_config['type_config']) : new Cache();
  1422. if (!$obj->get($error_no))
  1423. {
  1424. $obj->set($error_no, $trace_data, 7*86400);
  1425. }
  1426. break;
  1427. default:
  1428. $file = DIR_LOG .'error500'. DS . str_replace('-', DS, $date) . DS . $no . '.log';
  1429. if (!is_file($file))
  1430. {
  1431. File::create_file($file, $trace_data, null, null, $error_config['type_config']?$error_config['type_config']:'default');
  1432. }
  1433. break;
  1434. }
  1435. }
  1436. catch (Exception $e)
  1437. {
  1438. }
  1439. }
  1440. $view->error_no = $error_no;
  1441. $view->error = $error;
  1442. $view->render(true);
  1443. }
  1444. catch (Exception $e)
  1445. {
  1446. list ($REQUEST_URI) = explode('?', $_SERVER['REQUEST_URI'], 2);
  1447. $REQUEST_URI = htmlspecialchars(rawurldecode($REQUEST_URI));
  1448. echo '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">' .
  1449. CRLF . '<html>' .
  1450. CRLF . '<head>' .
  1451. CRLF . '<…

Large files files are truncated, but you can click here to view the full file