PageRenderTime 92ms CodeModel.GetById 40ms app.highlight 40ms RepoModel.GetById 1ms 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

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

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

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