PageRenderTime 67ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/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
  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 . '<title>Internal Server Error</title>' .
  1452. CRLF . '</head>' .
  1453. CRLF . '<body>' .
  1454. CRLF . '<h1>Internal Server Error</h1>' .
  1455. CRLF . '<p>The requested URL ' . $REQUEST_URI . ' was error on this server.</p>' .
  1456. CRLF . '<hr />' .
  1457. CRLF . $_SERVER['SERVER_SIGNATURE'] .
  1458. CRLF . '</body>' .
  1459. CRLF . '</html>';
  1460. }
  1461. exit();
  1462. }
  1463. /**
  1464. * 返回一个用.表示的字符串的key对应数组的内容
  1465. *
  1466. * 例如
  1467. *
  1468. * $arr = array
  1469. * (
  1470. * 'a' => array
  1471. * (
  1472. * 'b' => 123,
  1473. * 'c' => array
  1474. * (
  1475. * 456,
  1476. * ),
  1477. * ),
  1478. * );
  1479. * Core::key_string($arr,'a.b'); //返回123
  1480. *
  1481. * Core::key_string($arr,'a');
  1482. * // 返回
  1483. * array
  1484. * (
  1485. * 'b' => 123,
  1486. * 'c' => array
  1487. * (
  1488. * 456,
  1489. * ),
  1490. * );
  1491. *
  1492. * Core::key_string($arr,'a.c.0'); //返回456
  1493. *
  1494. * Core::key_string($arr,'a.d'); //返回null
  1495. *
  1496. * @param array $arr
  1497. * @param string $key
  1498. * @return fixed
  1499. */
  1500. public static function key_string($arr, $key, $default = null)
  1501. {
  1502. if (!is_array($arr)) return $default;
  1503. $keyArr = explode('.', $key);
  1504. foreach ( $keyArr as $key )
  1505. {
  1506. if ( isset($arr[$key]) )
  1507. {
  1508. $arr = $arr[$key];
  1509. }
  1510. else
  1511. {
  1512. return $default;
  1513. }
  1514. }
  1515. return $arr;
  1516. }
  1517. /**
  1518. * 添加页面在关闭前执行的列队
  1519. * 将利用call_user_func或call_user_func_array回调
  1520. * 类似 register_shutdown_function
  1521. * @param array $function 方法名,可以是数组
  1522. * @param array $param_arr 参数,可空
  1523. */
  1524. public static function register_shutdown_function($function, $param_arr = null)
  1525. {
  1526. Core::$shutdown_function[] = array($function, $param_arr);
  1527. }
  1528. public static function shutdown_handler()
  1529. {
  1530. $error = error_get_last();
  1531. if ( $error )
  1532. {
  1533. static $run = null;
  1534. if ( $run === true ) return;
  1535. $run = true;
  1536. if ( ((E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR) & $error['type']) !== 0 )
  1537. {
  1538. $error['file'] = Core::debug_path($error['file']);
  1539. Core::show_500(var_export($error, true));
  1540. exit();
  1541. }
  1542. }
  1543. }
  1544. public static function exception_handler(Exception $e)
  1545. {
  1546. $code = $e->getCode();
  1547. if ( $code !== 8 )
  1548. {
  1549. Core::show_500($e);
  1550. exit();
  1551. }
  1552. }
  1553. public static function error_handler($code, $error, $file = null, $line = null)
  1554. {
  1555. if ( (error_reporting() & $code) !== 0 )
  1556. {
  1557. throw new ErrorException( $error, $code, 0, $file, $line );
  1558. }
  1559. return true;
  1560. }
  1561. /**
  1562. * 根据$objName返回一个实例化并静态存储的对象
  1563. *
  1564. * @param string $obj_name
  1565. * @param string $key
  1566. */
  1567. public static function factory($obj_name, $key = '')
  1568. {
  1569. if (!isset(Core::$instances[$obj_name][$key]))
  1570. {
  1571. Core::$instances[$obj_name][$key] = new $obj_name($key);
  1572. }
  1573. return Core::$instances[$obj_name][$key];
  1574. }
  1575. /**
  1576. * 释放对象以释放内存
  1577. *
  1578. * 通常在批处理后操作,可有效的释放getFactory静态缓存的对象
  1579. *
  1580. * @param string $obj_name 对象名称 不传的话则清除全部
  1581. * @param string $key 对象关键字 不传的话则清除$objName里的所有对象
  1582. */
  1583. public static function factory_release($obj_name = null, $key = null)
  1584. {
  1585. if (IS_CLI || IS_DEBUG)
  1586. {
  1587. $old_memory = memory_get_usage();
  1588. }
  1589. if (null===$obj_name)
  1590. {
  1591. Core::$instances = array();
  1592. }
  1593. elseif (isset(Core::$instances[$obj_name]))
  1594. {
  1595. if (null===$key)
  1596. {
  1597. unset(Core::$instances[$obj_name]);
  1598. }
  1599. else
  1600. {
  1601. unset(Core::$instances[$obj_name][$key]);
  1602. }
  1603. }
  1604. if (IS_CLI)
  1605. {
  1606. echo __('The release memory:') . ( memory_get_usage() - $old_memory ) . "\n";
  1607. }
  1608. else if (IS_DEBUG)
  1609. {
  1610. Core::debug()->info(__('The release memory:') . ( memory_get_usage() - $old_memory) );
  1611. }
  1612. }
  1613. /**
  1614. * 将项目切换回初始项目
  1615. *
  1616. * 当使用Core::change_project()设置切换过项目后,可使用此方法返回初始化时的项目
  1617. */
  1618. public static function reset_project()
  1619. {
  1620. if (defined('INITIAL_PROJECT_NAME') && INITIAL_PROJECT_NAME != Core::$project)
  1621. {
  1622. Core::change_project(INITIAL_PROJECT_NAME);
  1623. }
  1624. }
  1625. /**
  1626. * 切换到另外一个项目
  1627. *
  1628. * 切换其它项目后,相关的config,include_path等都将加载为设定项目的,但是已经加载的class等是不可能销毁的,所以需谨慎使用
  1629. *
  1630. * @param string $project
  1631. * @return boolean
  1632. * @throws Exception 失败则抛出异常(比如不存在指定的项目)
  1633. */
  1634. public static function change_project($project)
  1635. {
  1636. if (Core::$project==$project)
  1637. {
  1638. return true;
  1639. }
  1640. if ( !isset(Core::$core_config['projects'][$project] ) )
  1641. {
  1642. Core::show_500( __('not found the project: :project.', array(':project'=>$project) ) );
  1643. }
  1644. if ( !Core::$core_config['projects'][$project]['isuse'] )
  1645. {
  1646. Core::show_500( __('the project: :project is not open.', array(':project'=>$project) ) );
  1647. }
  1648. # 记录所有项目设置当切换回项目时使用此设置还原
  1649. static $all_prjects_setting = array();
  1650. if (Core::$project)
  1651. {
  1652. // 记录上一个项目设置
  1653. $all_prjects_setting[Core::$project] = array
  1654. (
  1655. 'config' => Core::$config,
  1656. 'include_path' => Core::$include_path,
  1657. 'file_list' => Core::$file_list,
  1658. 'project_dir' => Core::$project_dir,
  1659. 'base_url' => Core::$base_url,
  1660. );
  1661. }
  1662. # 原来的项目
  1663. $old_project = Core::$project;
  1664. if (isset($all_prjects_setting[$project]))
  1665. {
  1666. # 设为当前项目
  1667. Core::$project = $project;
  1668. # 还原配置
  1669. Core::$config = $all_prjects_setting[$project]['config'];
  1670. Core::$include_path = $all_prjects_setting[$project]['include_path'];
  1671. Core::$file_list = $all_prjects_setting[$project]['file_list'];
  1672. Core::$project_dir = $all_prjects_setting[$project]['project_dir'];
  1673. Core::$base_url = $all_prjects_setting[$project]['base_url'];
  1674. # 清除缓存数据
  1675. unset($all_prjects_setting[$project]);
  1676. }
  1677. else
  1678. {
  1679. $p_config = Core::$core_config['projects'][$project];
  1680. if (!isset($p_config['dir']) || !$p_config['dir'])
  1681. {
  1682. Core::show_500(__('the project ":project" dir is not defined.', array(':project'=>$project)));
  1683. }
  1684. # 设置include path
  1685. $project_dir = DIR_PROJECT . $project . DS;
  1686. if (!is_dir($project_dir))
  1687. {
  1688. Core::show_500(__('not found the project: :project.', array(':project' => $project)));
  1689. }
  1690. # 项目路径
  1691. $project_dir = realpath(DIR_PROJECT . $p_config['dir']);
  1692. if (!$project_dir || !is_dir($project_dir))
  1693. {
  1694. Core::show_500(__('the project dir :dir is not exist.', array(':dir'=>$p_config['dir'])));
  1695. }
  1696. # 设为当前项目
  1697. Core::$project = $project;
  1698. $project_dir .= DS;
  1699. Core::$project_dir = $project_dir;
  1700. # 处理base_url
  1701. if (isset($p_config['url']) && $p_config['url'])
  1702. {
  1703. $url = rtrim(current((array)$p_config['url']),'/');
  1704. }
  1705. else
  1706. {
  1707. $url = '/';
  1708. }
  1709. if (IS_ADMIN_MODE)
  1710. {
  1711. if (isset($p_config['url_admin']) && $p_config['url_admin'])
  1712. {
  1713. $current_url = current((array)$p_config['url_admin']);
  1714. if (false===strpos($current_url[0],'://'))
  1715. {
  1716. $url .= trim($current_url, '/');
  1717. $url = trim($url, '/') .'/';
  1718. }
  1719. }
  1720. }
  1721. if (IS_REST_MODE)
  1722. {
  1723. if (isset($p_config['url_rest']) && $p_config['url_rest'])
  1724. {
  1725. $current_url = current((array)$p_config['url_rest']);
  1726. if (false===strpos($current_url[0], '://'))
  1727. {
  1728. $url .= trim($current_url, '/');
  1729. $url = trim($url,'/') .'/';
  1730. }
  1731. }
  1732. }
  1733. Core::$base_url = $url;
  1734. # 重置$include_path
  1735. Core::$include_path['project'] = array
  1736. (
  1737. Core::$project => $project_dir
  1738. );
  1739. # 重新加载类库配置
  1740. Core::reload_all_libraries();
  1741. }
  1742. # 记录debug信息
  1743. if (IS_DEBUG)
  1744. {
  1745. Core::debug()->info($project, '程序已切换到了新项目');
  1746. }
  1747. # 回调callback
  1748. if (Core::$change_project_callback)
  1749. {
  1750. foreach (Core::$change_project_callback as $fun)
  1751. {
  1752. call_user_func($fun, $old_project, $project);
  1753. }
  1754. }
  1755. return true;
  1756. }
  1757. /**
  1758. * 增加change_project回调事件
  1759. *
  1760. * //将在每次执行 Core::change_project($new_project) 成功后执行 MyClass::myfun($old_project, $new_project) 方法,其中$old_project是原来的项目名
  1761. * Core::change_project_add_callback('MyClass::myfun');
  1762. *
  1763. * @param string|array $fun
  1764. */
  1765. public static function change_project_add_callback($fun)
  1766. {
  1767. Core::$change_project_callback[] = $fun;
  1768. if (count(Core::$change_project_callback)>1)
  1769. {
  1770. # 移除重复的项目
  1771. Core::$change_project_callback = array_unique(Core::$change_project_callback);
  1772. }
  1773. }
  1774. /**
  1775. * 移除import_library回调事件
  1776. *
  1777. * @param string|array $fun
  1778. */
  1779. public static function change_project_remove_callback($fun)
  1780. {
  1781. if (Core::$change_project_callback)
  1782. {
  1783. $new_arr = array();
  1784. foreach (Core::$change_project_callback as $item)
  1785. {
  1786. if ($item!==$fun)
  1787. {
  1788. $new_arr = $item;
  1789. }
  1790. }
  1791. Core::$change_project_callback = $new_arr;
  1792. }
  1793. }
  1794. /**
  1795. * 导入指定类库
  1796. *
  1797. * 支持多个,当一次导入多个时,从数组最后一个开始导入
  1798. *
  1799. * 导入的格式必须是类似 com.a.b 的形式,否则会抛出异常,例如: com.myqee.test
  1800. *
  1801. * Bootstrap::import_library('com.myqee.test');
  1802. * Bootstrap::import_library(array('com.myqee.test','com.myqee.cms'));
  1803. *
  1804. * @param string|array $library_name 指定类库 支持多个
  1805. * @return boolean
  1806. */
  1807. public static function import_library($library_name)
  1808. {
  1809. $library_name = (array)$library_name;
  1810. $status = parent::import_library($library_name);
  1811. # 回调callback
  1812. if ($status>0 && Core::$import_library_callback)
  1813. {
  1814. foreach (Core::$import_library_callback as $fun)
  1815. {
  1816. call_user_func($fun, $library_name);
  1817. }
  1818. }
  1819. return $status;
  1820. }
  1821. /**
  1822. * 增加import_library回调事件
  1823. *
  1824. * //将在每次执行 Core::import_library($library_name) 成功后执行 MyClass::myfun((array)$library_name) 方法
  1825. * Core::add_import_library_callback('MyClass::myfun');
  1826. *
  1827. * @param string|array $fun
  1828. */
  1829. public static function import_library_add_callback($fun)
  1830. {
  1831. Core::$import_library_callback[] = $fun;
  1832. if (count(Core::$import_library_callback)>1)
  1833. {
  1834. # 移除重复的项目
  1835. Core::$import_library_callback = array_unique(Core::$import_library_callback);
  1836. }
  1837. }
  1838. /**
  1839. * 移除import_library回调事件
  1840. *
  1841. * @param string|array $fun
  1842. */
  1843. public static function import_library_remove_callback($fun)
  1844. {
  1845. if (Core::$import_library_callback)
  1846. {
  1847. $new_arr = array();
  1848. foreach (Core::$import_library_callback as $item)
  1849. {
  1850. if ($item!==$fun)
  1851. {
  1852. $new_arr = $item;
  1853. }
  1854. }
  1855. Core::$import_library_callback = $new_arr;
  1856. }
  1857. }
  1858. /**
  1859. * 执行注册的关闭方法
  1860. */
  1861. protected static function run_shutdown_function()
  1862. {
  1863. static $run = null;
  1864. if ( null!==$run )
  1865. {
  1866. return true;
  1867. }
  1868. $run = true;
  1869. if (Core::$shutdown_function)
  1870. {
  1871. foreach (Core::$shutdown_function as $item)
  1872. {
  1873. try
  1874. {
  1875. call_user_func_array($item[0], (array)$item[1]);
  1876. }
  1877. catch ( Exception $e )
  1878. {
  1879. }
  1880. }
  1881. }
  1882. }
  1883. /**
  1884. * 特殊的合并项目配置
  1885. *
  1886. * 相当于一维数组之间相加,这里支持多维
  1887. *
  1888. * @param array $c1
  1889. * @param array $c2
  1890. * @return array
  1891. */
  1892. protected static function _merge_project_config($c1, $c2)
  1893. {
  1894. foreach ($c2 as $k=>$v)
  1895. {
  1896. if (!isset($c1[$k]))
  1897. {
  1898. $c1[$k] = $v;
  1899. }
  1900. elseif ( is_array($c1[$k]) && is_array($v) )
  1901. {
  1902. $c1[$k] = Core::_merge_project_config($c1[$k] , $v );
  1903. }
  1904. elseif (is_numeric($k) && is_array($c1[$k]))
  1905. {
  1906. $c1[$k][] = $v;
  1907. }
  1908. }
  1909. return $c1;
  1910. }
  1911. /**
  1912. * 关闭所有可能的外部链接,比如Database,Memcache等连接
  1913. */
  1914. public static function close_all_connect()
  1915. {
  1916. foreach ( Core::$close_connect_class_list as $class_name=>$fun )
  1917. {
  1918. try
  1919. {
  1920. call_user_func_array( array($class_name,$fun), array() );
  1921. }
  1922. catch (Exception $e)
  1923. {
  1924. Core::debug()->error('close_all_connect error:'.$e->getMessage());
  1925. }
  1926. }
  1927. }
  1928. /**
  1929. * 增加执行Core::close_all_connect()时会去关闭的类
  1930. *
  1931. * Core::add_close_connect_class('Database','close_all_connect');
  1932. * Core::add_close_connect_class('Cache_Driver_Memcache');
  1933. * Core::add_close_connect_class('TestClass','close');
  1934. * //当执行 Core::close_all_connect() 时会调用 Database::close_all_connect() 和 Cache_Driver_Memcache::close_all_connect() 和 TestClass::close() 方法
  1935. *
  1936. * @param string $class_name
  1937. * @param string $fun
  1938. */
  1939. public static function add_close_connect_class($class_name, $fun='close_all_connect')
  1940. {
  1941. Core::$close_connect_class_list[$class_name] = $fun;
  1942. }
  1943. /**
  1944. * 检查内部调用HASH是否有效
  1945. *
  1946. * @return boolean
  1947. */
  1948. protected static function check_system_request_allow()
  1949. {
  1950. $hash = $_SERVER['HTTP_X_MYQEE_SYSTEM_HASH']; // 请求验证HASH
  1951. $time = $_SERVER['HTTP_X_MYQEE_SYSTEM_TIME']; // 请求验证时间
  1952. $rstr = $_SERVER['HTTP_X_MYQEE_SYSTEM_RSTR']; // 请求随机字符串
  1953. $project = $_SERVER['HTTP_X_MYQEE_SYSTEM_PROJECT']; // 请求的项目
  1954. $path_info = $_SERVER['HTTP_X_MYQEE_SYSTEM_PATHINFO']; // 请求的path_info
  1955. $isadmin = $_SERVER['HTTP_X_MYQEE_SYSTEM_ISADMIN']; // 是否ADMIN
  1956. $isrest = $_SERVER['HTTP_X_MYQEE_SYSTEM_ISREST']; // 是否RESTFul请求
  1957. if (!$hash || !$time || !$rstr || !$project || !$path_info) return false;
  1958. // 请求时效检查
  1959. if (microtime(1) - $time > 600)
  1960. {
  1961. Core::log('system request timeout', 'system-request');
  1962. return false;
  1963. }
  1964. // 验证IP
  1965. if ('127.0.0.1'!=HttpIO::IP && HttpIO::IP != $_SERVER["SERVER_ADDR"])
  1966. {
  1967. $allow_ip = Core::config('core.system_exec_allow_ip');
  1968. if (is_array($allow_ip) && $allow_ip)
  1969. {
  1970. $allow = false;
  1971. foreach ($allow_ip as $ip)
  1972. {
  1973. if (HttpIO::IP == $ip)
  1974. {
  1975. $allow = true;
  1976. break;
  1977. }
  1978. if (strpos($allow_ip, '*'))
  1979. {
  1980. // 对IP进行匹配
  1981. if (preg_match('#^' . str_replace('\\*', '[^\.]+', preg_quote($allow_ip, '#')) . '$#', HttpIO::IP))
  1982. {
  1983. $allow = true;
  1984. break;
  1985. }
  1986. }
  1987. }
  1988. if (!$allow)
  1989. {
  1990. Core::log('system request not allow ip:' . HttpIO::IP, 'system-request');
  1991. return false;
  1992. }
  1993. }
  1994. }
  1995. $body = http_build_query(HttpIO::POST(null, HttpIO::PARAM_TYPE_OLDDATA));
  1996. // 系统调用密钥
  1997. $system_exec_pass = Core::config('system_exec_key');
  1998. $key = Core::config()->get('system_exec_key', 'system', true);
  1999. if (!$key || abs(TIME-$key['time'])>86400*10)
  2000. {
  2001. return false;
  2002. }
  2003. $other = $path_info .'_'. ($isadmin?1:0) .'_'. ($isrest?1:0) . $key['str'];
  2004. if ($system_exec_pass && strlen($system_exec_pass) >= 10)
  2005. {
  2006. // 如果有则使用系统调用密钥
  2007. $newhash = sha1($body . $time . $system_exec_pass . $rstr .'_'. $other);
  2008. }
  2009. else
  2010. {
  2011. // 没有,则用系统配置和数据库加密
  2012. $newhash = sha1($body . $time . serialize(Core::config('core')) . serialize(Core::config('database')) . $rstr .'_'. $other);
  2013. }
  2014. if ($newhash==$hash)
  2015. {
  2016. return true;
  2017. }
  2018. else
  2019. {
  2020. Core::log('system request hash error', 'system-request');
  2021. return false;
  2022. }
  2023. }
  2024. /**
  2025. * 获取asset文件MD5号
  2026. *
  2027. * @param string $file
  2028. * @return md5
  2029. */
  2030. public static function assets_hash($file)
  2031. {
  2032. //TODO 须加入文件版本号
  2033. return '';
  2034. }
  2035. /**
  2036. * 返回客户端IP数组列表
  2037. *
  2038. * 也可直接用 `HttpIO::IP` 来获取到当前单个IP
  2039. *
  2040. * @return array
  2041. */
  2042. public static function ip()
  2043. {
  2044. $ip = array();
  2045. if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
  2046. {
  2047. $ip = explode(',', str_replace(' ', '', $_SERVER['HTTP_X_FORWARDED_FOR']));
  2048. }
  2049. if(isset($_SERVER['HTTP_CLIENT_IP']))
  2050. {
  2051. $ip = array_merge($ip, explode(',', str_replace(' ', '', $_SERVER['HTTP_CLIENT_IP'])));
  2052. }
  2053. if (isset($_SERVER['REMOTE_ADDR']))
  2054. {
  2055. $ip = array_merge($ip, explode(',', str_replace(' ', '', $_SERVER['REMOTE_ADDR'])));
  2056. }
  2057. return $ip;
  2058. }
  2059. /**
  2060. * 系统调用内容输出函数(请勿自行执行)
  2061. */
  2062. public static function _output_body()
  2063. {
  2064. # 发送header数据
  2065. HttpIO::send_headers();
  2066. if (IS_DEBUG && isset($_REQUEST['debug']) && class_exists('Profiler', true))
  2067. {
  2068. # 调试打开时不缓存页面
  2069. HttpIO::set_cache_header(0);
  2070. }
  2071. # 执行注册的关闭方法
  2072. ob_start();
  2073. Core::run_shutdown_function();
  2074. $output = ob_get_clean();
  2075. # 在页面输出前关闭所有的连接
  2076. Core::close_all_connect();
  2077. # 输出内容
  2078. echo Core::$output , $output;
  2079. }
  2080. }
  2081. /**
  2082. * 无调试对象
  2083. *
  2084. * @author 呼吸二氧化碳 <jonwang@myqee.com>
  2085. * @category Core
  2086. * @package Classes
  2087. * @subpackage Core
  2088. * @copyright Copyright (c) 2008-2013 myqee.com
  2089. * @license http://www.myqee.com/license.html
  2090. */
  2091. class __NoDebug
  2092. {
  2093. public function __call($m, $v)
  2094. {
  2095. return $this;
  2096. }
  2097. public function log($i = null)
  2098. {
  2099. return $this;
  2100. }
  2101. public function info($i = null)
  2102. {
  2103. return $this;
  2104. }
  2105. public function error($i = null)
  2106. {
  2107. return $this;
  2108. }
  2109. public function group($i = null)
  2110. {
  2111. return $this;
  2112. }
  2113. public function groupEnd($i = null)
  2114. {
  2115. return $this;
  2116. }
  2117. public function table($Label = null, $Table = null)
  2118. {
  2119. return $this;
  2120. }
  2121. public function profiler($i = null)
  2122. {
  2123. return $this;
  2124. }
  2125. public function is_open()
  2126. {
  2127. return false;
  2128. }
  2129. }