| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981 | <?php// +----------------------------------------------------------------------// | ThinkPHP [ WE CAN DO IT JUST THINK ]// +----------------------------------------------------------------------// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.// +----------------------------------------------------------------------// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )// +----------------------------------------------------------------------// | Author: liu21st <liu21st@gmail.com>// +----------------------------------------------------------------------namespace think;use think\exception\ClassNotFoundException;use think\exception\HttpResponseException;use think\route\Dispatch;/** * App 应用管理 */class App extends Container{    const VERSION = '5.1.38 LTS';    /**     * 当前模块路径     * @var string     */    protected $modulePath;    /**     * 应用调试模式     * @var bool     */    protected $appDebug = true;    /**     * 应用开始时间     * @var float     */    protected $beginTime;    /**     * 应用内存初始占用     * @var integer     */    protected $beginMem;    /**     * 应用类库命名空间     * @var string     */    protected $namespace = 'app';    /**     * 应用类库后缀     * @var bool     */    protected $suffix = false;    /**     * 严格路由检测     * @var bool     */    protected $routeMust;    /**     * 应用类库目录     * @var string     */    protected $appPath;    /**     * 框架目录     * @var string     */    protected $thinkPath;    /**     * 应用根目录     * @var string     */    protected $rootPath;    /**     * 运行时目录     * @var string     */    protected $runtimePath;    /**     * 配置目录     * @var string     */    protected $configPath;    /**     * 路由目录     * @var string     */    protected $routePath;    /**     * 配置后缀     * @var string     */    protected $configExt;    /**     * 应用调度实例     * @var Dispatch     */    protected $dispatch;    /**     * 绑定模块(控制器)     * @var string     */    protected $bindModule;    /**     * 初始化     * @var bool     */    protected $initialized = false;    public function __construct($appPath = '')    {        $this->thinkPath = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR;        $this->path($appPath);    }    /**     * 绑定模块或者控制器     * @access public     * @param  string $bind     * @return $this     */    public function bind($bind)    {        $this->bindModule = $bind;        return $this;    }    /**     * 设置应用类库目录     * @access public     * @param  string $path 路径     * @return $this     */    public function path($path)    {        $this->appPath = $path ? realpath($path) . DIRECTORY_SEPARATOR : $this->getAppPath();        return $this;    }    /**     * 初始化应用     * @access public     * @return void     */    public function initialize()    {        if ($this->initialized) {            return;        }        $this->initialized = true;        $this->beginTime   = microtime(true);        $this->beginMem    = memory_get_usage();        $this->rootPath    = dirname($this->appPath) . DIRECTORY_SEPARATOR;        $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;        $this->routePath   = $this->rootPath . 'route' . DIRECTORY_SEPARATOR;        $this->configPath  = $this->rootPath . 'config' . DIRECTORY_SEPARATOR;        static::setInstance($this);        $this->instance('app', $this);        // 加载环境变量配置文件        if (is_file($this->rootPath . '.env')) {            $this->env->load($this->rootPath . '.env');        }        $this->configExt = $this->env->get('config_ext', '.php');        // 加载惯例配置文件        $this->config->set(include $this->thinkPath . 'convention.php');        // 设置路径环境变量        $this->env->set([            'think_path'   => $this->thinkPath,            'root_path'    => $this->rootPath,            'app_path'     => $this->appPath,            'config_path'  => $this->configPath,            'route_path'   => $this->routePath,            'runtime_path' => $this->runtimePath,            'extend_path'  => $this->rootPath . 'extend' . DIRECTORY_SEPARATOR,            'vendor_path'  => $this->rootPath . 'vendor' . DIRECTORY_SEPARATOR,        ]);        $this->namespace = $this->env->get('app_namespace', $this->namespace);        $this->env->set('app_namespace', $this->namespace);        // 注册应用命名空间        Loader::addNamespace($this->namespace, $this->appPath);        // 初始化应用        $this->init();        // 开启类名后缀        $this->suffix = $this->config('app.class_suffix');        // 应用调试模式        $this->appDebug = $this->env->get('app_debug', $this->config('app.app_debug'));        $this->env->set('app_debug', $this->appDebug);        if (!$this->appDebug) {            ini_set('display_errors', 'Off');        } elseif (PHP_SAPI != 'cli') {            //重新申请一块比较大的buffer            if (ob_get_level() > 0) {                $output = ob_get_clean();            }            ob_start();            if (!empty($output)) {                echo $output;            }        }        // 注册异常处理类        if ($this->config('app.exception_handle')) {            Error::setExceptionHandler($this->config('app.exception_handle'));        }        // 注册根命名空间        if (!empty($this->config('app.root_namespace'))) {            Loader::addNamespace($this->config('app.root_namespace'));        }        // 加载composer autofile文件        Loader::loadComposerAutoloadFiles();        // 注册类库别名        Loader::addClassAlias($this->config->pull('alias'));        // 数据库配置初始化        Db::init($this->config->pull('database'));        // 设置系统时区        date_default_timezone_set($this->config('app.default_timezone'));        // 读取语言包        $this->loadLangPack();        // 路由初始化        $this->routeInit();    }    /**     * 初始化应用或模块     * @access public     * @param  string $module 模块名     * @return void     */    public function init($module = '')    {        // 定位模块目录        $module = $module ? $module . DIRECTORY_SEPARATOR : '';        $path   = $this->appPath . $module;        // 加载初始化文件        if (is_file($path . 'init.php')) {            include $path . 'init.php';        } elseif (is_file($this->runtimePath . $module . 'init.php')) {            include $this->runtimePath . $module . 'init.php';        } else {            // 加载行为扩展文件            if (is_file($path . 'tags.php')) {                $tags = include $path . 'tags.php';                if (is_array($tags)) {                    $this->hook->import($tags);                }            }            // 加载公共文件            if (is_file($path . 'common.php')) {                include_once $path . 'common.php';            }            if ('' == $module) {                // 加载系统助手函数                include $this->thinkPath . 'helper.php';            }            // 加载中间件            if (is_file($path . 'middleware.php')) {                $middleware = include $path . 'middleware.php';                if (is_array($middleware)) {                    $this->middleware->import($middleware);                }            }            // 注册服务的容器对象实例            if (is_file($path . 'provider.php')) {                $provider = include $path . 'provider.php';                if (is_array($provider)) {                    $this->bindTo($provider);                }            }            // 自动读取配置文件            if (is_dir($path . 'config')) {                $dir = $path . 'config' . DIRECTORY_SEPARATOR;            } elseif (is_dir($this->configPath . $module)) {                $dir = $this->configPath . $module;            }            $files = isset($dir) ? scandir($dir) : [];            foreach ($files as $file) {                if ('.' . pathinfo($file, PATHINFO_EXTENSION) === $this->configExt) {                    $this->config->load($dir . $file, pathinfo($file, PATHINFO_FILENAME));                }            }        }        $this->setModulePath($path);        if ($module) {            // 对容器中的对象实例进行配置更新            $this->containerConfigUpdate($module);        }    }    protected function containerConfigUpdate($module)    {        $config = $this->config->get();        // 注册异常处理类        if ($config['app']['exception_handle']) {            Error::setExceptionHandler($config['app']['exception_handle']);        }        Db::init($config['database']);        $this->middleware->setConfig($config['middleware']);        $this->route->setConfig($config['app']);        $this->request->init($config['app']);        $this->cookie->init($config['cookie']);        $this->view->init($config['template']);        $this->log->init($config['log']);        $this->session->setConfig($config['session']);        $this->debug->setConfig($config['trace']);        $this->cache->init($config['cache'], true);        // 加载当前模块语言包        $this->lang->load($this->appPath . $module . DIRECTORY_SEPARATOR . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php');        // 模块请求缓存检查        $this->checkRequestCache(            $config['app']['request_cache'],            $config['app']['request_cache_expire'],            $config['app']['request_cache_except']        );    }    /**     * 执行应用程序     * @access public     * @return Response     * @throws Exception     */    public function run()    {        try {            // 初始化应用            $this->initialize();            // 监听app_init            $this->hook->listen('app_init');            if ($this->bindModule) {                // 模块/控制器绑定                $this->route->bind($this->bindModule);            } elseif ($this->config('app.auto_bind_module')) {                // 入口自动绑定                $name = pathinfo($this->request->baseFile(), PATHINFO_FILENAME);                if ($name && 'index' != $name && is_dir($this->appPath . $name)) {                    $this->route->bind($name);                }            }            // 监听app_dispatch            $this->hook->listen('app_dispatch');            $dispatch = $this->dispatch;            if (empty($dispatch)) {                // 路由检测                $dispatch = $this->routeCheck()->init();            }            // 记录当前调度信息            $this->request->dispatch($dispatch);            // 记录路由和请求信息            if ($this->appDebug) {                $this->log('[ ROUTE ] ' . var_export($this->request->routeInfo(), true));                $this->log('[ HEADER ] ' . var_export($this->request->header(), true));                $this->log('[ PARAM ] ' . var_export($this->request->param(), true));            }            // 监听app_begin            $this->hook->listen('app_begin');            // 请求缓存检查            $this->checkRequestCache(                $this->config('request_cache'),                $this->config('request_cache_expire'),                $this->config('request_cache_except')            );            $data = null;        } catch (HttpResponseException $exception) {            $dispatch = null;            $data     = $exception->getResponse();        }        $this->middleware->add(function (Request $request, $next) use ($dispatch, $data) {            return is_null($data) ? $dispatch->run() : $data;        });        $response = $this->middleware->dispatch($this->request);        // 监听app_end        $this->hook->listen('app_end', $response);        return $response;    }    protected function getRouteCacheKey()    {        if ($this->config->get('route_check_cache_key')) {            $closure  = $this->config->get('route_check_cache_key');            $routeKey = $closure($this->request);        } else {            $routeKey = md5($this->request->baseUrl(true) . ':' . $this->request->method());        }        return $routeKey;    }    protected function loadLangPack()    {        // 读取默认语言        $this->lang->range($this->config('app.default_lang'));        if ($this->config('app.lang_switch_on')) {            // 开启多语言机制 检测当前语言            $this->lang->detect();        }        $this->request->setLangset($this->lang->range());        // 加载系统语言包        $this->lang->load([            $this->thinkPath . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php',            $this->appPath . 'lang' . DIRECTORY_SEPARATOR . $this->request->langset() . '.php',        ]);    }    /**     * 设置当前地址的请求缓存     * @access public     * @param  string $key 缓存标识,支持变量规则 ,例如 item/:name/:id     * @param  mixed  $expire 缓存有效期     * @param  array  $except 缓存排除     * @param  string $tag    缓存标签     * @return void     */    public function checkRequestCache($key, $expire = null, $except = [], $tag = null)    {        $cache = $this->request->cache($key, $expire, $except, $tag);        if ($cache) {            $this->setResponseCache($cache);        }    }    public function setResponseCache($cache)    {        list($key, $expire, $tag) = $cache;        if (strtotime($this->request->server('HTTP_IF_MODIFIED_SINCE')) + $expire > $this->request->server('REQUEST_TIME')) {            // 读取缓存            $response = Response::create()->code(304);            throw new HttpResponseException($response);        } elseif ($this->cache->has($key)) {            list($content, $header) = $this->cache->get($key);            $response = Response::create($content)->header($header);            throw new HttpResponseException($response);        }    }    /**     * 设置当前请求的调度信息     * @access public     * @param  Dispatch  $dispatch 调度信息     * @return $this     */    public function dispatch(Dispatch $dispatch)    {        $this->dispatch = $dispatch;        return $this;    }    /**     * 记录调试信息     * @access public     * @param  mixed  $msg  调试信息     * @param  string $type 信息类型     * @return void     */    public function log($msg, $type = 'info')    {        $this->appDebug && $this->log->record($msg, $type);    }    /**     * 获取配置参数 为空则获取所有配置     * @access public     * @param  string    $name 配置参数名(支持二级配置 .号分割)     * @return mixed     */    public function config($name = '')    {        return $this->config->get($name);    }    /**     * 路由初始化 导入路由定义规则     * @access public     * @return void     */    public function routeInit()    {        // 路由检测        $files = scandir($this->routePath);        foreach ($files as $file) {            if (strpos($file, '.php')) {                $filename = $this->routePath . $file;                // 导入路由配置                $rules = include $filename;                if (is_array($rules)) {                    $this->route->import($rules);                }            }        }        if ($this->route->config('route_annotation')) {            // 自动生成路由定义            if ($this->appDebug) {                $suffix = $this->route->config('controller_suffix') || $this->route->config('class_suffix');                $this->build->buildRoute($suffix);            }            $filename = $this->runtimePath . 'build_route.php';            if (is_file($filename)) {                include $filename;            }        }    }    /**     * URL路由检测(根据PATH_INFO)     * @access public     * @return Dispatch     */    public function routeCheck()    {        // 检测路由缓存        if (!$this->appDebug && $this->config->get('route_check_cache')) {            $routeKey = $this->getRouteCacheKey();            $option   = $this->config->get('route_cache_option');            if ($option && $this->cache->connect($option)->has($routeKey)) {                return $this->cache->connect($option)->get($routeKey);            } elseif ($this->cache->has($routeKey)) {                return $this->cache->get($routeKey);            }        }        // 获取应用调度信息        $path = $this->request->path();        // 是否强制路由模式        $must = !is_null($this->routeMust) ? $this->routeMust : $this->route->config('url_route_must');        // 路由检测 返回一个Dispatch对象        $dispatch = $this->route->check($path, $must);        if (!empty($routeKey)) {            try {                if ($option) {                    $this->cache->connect($option)->tag('route_cache')->set($routeKey, $dispatch);                } else {                    $this->cache->tag('route_cache')->set($routeKey, $dispatch);                }            } catch (\Exception $e) {                // 存在闭包的时候缓存无效            }        }        return $dispatch;    }    /**     * 设置应用的路由检测机制     * @access public     * @param  bool $must  是否强制检测路由     * @return $this     */    public function routeMust($must = false)    {        $this->routeMust = $must;        return $this;    }    /**     * 解析模块和类名     * @access protected     * @param  string $name         资源地址     * @param  string $layer        验证层名称     * @param  bool   $appendSuffix 是否添加类名后缀     * @return array     */    protected function parseModuleAndClass($name, $layer, $appendSuffix)    {        if (false !== strpos($name, '\\')) {            $class  = $name;            $module = $this->request->module();        } else {            if (strpos($name, '/')) {                list($module, $name) = explode('/', $name, 2);            } else {                $module = $this->request->module();            }            $class = $this->parseClass($module, $layer, $name, $appendSuffix);        }        return [$module, $class];    }    /**     * 实例化应用类库     * @access public     * @param  string $name         类名称     * @param  string $layer        业务层名称     * @param  bool   $appendSuffix 是否添加类名后缀     * @param  string $common       公共模块名     * @return object     * @throws ClassNotFoundException     */    public function create($name, $layer, $appendSuffix = false, $common = 'common')    {        $guid = $name . $layer;        if ($this->__isset($guid)) {            return $this->__get($guid);        }        list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix);        if (class_exists($class)) {            $object = $this->__get($class);        } else {            $class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);            if (class_exists($class)) {                $object = $this->__get($class);            } else {                throw new ClassNotFoundException('class not exists:' . $class, $class);            }        }        $this->__set($guid, $class);        return $object;    }    /**     * 实例化(分层)模型     * @access public     * @param  string $name         Model名称     * @param  string $layer        业务层名称     * @param  bool   $appendSuffix 是否添加类名后缀     * @param  string $common       公共模块名     * @return Model     * @throws ClassNotFoundException     */    public function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common')    {        return $this->create($name, $layer, $appendSuffix, $common);    }    /**     * 实例化(分层)控制器 格式:[模块名/]控制器名     * @access public     * @param  string $name              资源地址     * @param  string $layer             控制层名称     * @param  bool   $appendSuffix      是否添加类名后缀     * @param  string $empty             空控制器名称     * @return object     * @throws ClassNotFoundException     */    public function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '')    {        list($module, $class) = $this->parseModuleAndClass($name, $layer, $appendSuffix);        if (class_exists($class)) {            return $this->make($class, true);        } elseif ($empty && class_exists($emptyClass = $this->parseClass($module, $layer, $empty, $appendSuffix))) {            return $this->make($emptyClass, true);        }        throw new ClassNotFoundException('class not exists:' . $class, $class);    }    /**     * 实例化验证类 格式:[模块名/]验证器名     * @access public     * @param  string $name         资源地址     * @param  string $layer        验证层名称     * @param  bool   $appendSuffix 是否添加类名后缀     * @param  string $common       公共模块名     * @return Validate     * @throws ClassNotFoundException     */    public function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common')    {        $name = $name ?: $this->config('default_validate');        if (empty($name)) {            return new Validate;        }        return $this->create($name, $layer, $appendSuffix, $common);    }    /**     * 数据库初始化     * @access public     * @param  mixed         $config 数据库配置     * @param  bool|string   $name 连接标识 true 强制重新连接     * @return \think\db\Query     */    public function db($config = [], $name = false)    {        return Db::connect($config, $name);    }    /**     * 远程调用模块的操作方法 参数格式 [模块/控制器/]操作     * @access public     * @param  string       $url          调用地址     * @param  string|array $vars         调用参数 支持字符串和数组     * @param  string       $layer        要调用的控制层名称     * @param  bool         $appendSuffix 是否添加类名后缀     * @return mixed     * @throws ClassNotFoundException     */    public function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)    {        $info   = pathinfo($url);        $action = $info['basename'];        $module = '.' != $info['dirname'] ? $info['dirname'] : $this->request->controller();        $class  = $this->controller($module, $layer, $appendSuffix);        if (is_scalar($vars)) {            if (strpos($vars, '=')) {                parse_str($vars, $vars);            } else {                $vars = [$vars];            }        }        return $this->invokeMethod([$class, $action . $this->config('action_suffix')], $vars);    }    /**     * 解析应用类的类名     * @access public     * @param  string $module 模块名     * @param  string $layer  层名 controller model ...     * @param  string $name   类名     * @param  bool   $appendSuffix     * @return string     */    public function parseClass($module, $layer, $name, $appendSuffix = false)    {        $name  = str_replace(['/', '.'], '\\', $name);        $array = explode('\\', $name);        $class = Loader::parseName(array_pop($array), 1) . ($this->suffix || $appendSuffix ? ucfirst($layer) : '');        $path  = $array ? implode('\\', $array) . '\\' : '';        return $this->namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $path . $class;    }    /**     * 获取框架版本     * @access public     * @return string     */    public function version()    {        return static::VERSION;    }    /**     * 是否为调试模式     * @access public     * @return bool     */    public function isDebug()    {        return $this->appDebug;    }    /**     * 获取模块路径     * @access public     * @return string     */    public function getModulePath()    {        return $this->modulePath;    }    /**     * 设置模块路径     * @access public     * @param  string $path 路径     * @return void     */    public function setModulePath($path)    {        $this->modulePath = $path;        $this->env->set('module_path', $path);    }    /**     * 获取应用根目录     * @access public     * @return string     */    public function getRootPath()    {        return $this->rootPath;    }    /**     * 获取应用类库目录     * @access public     * @return string     */    public function getAppPath()    {        if (is_null($this->appPath)) {            $this->appPath = Loader::getRootPath() . 'application' . DIRECTORY_SEPARATOR;        }        return $this->appPath;    }    /**     * 获取应用运行时目录     * @access public     * @return string     */    public function getRuntimePath()    {        return $this->runtimePath;    }    /**     * 获取核心框架目录     * @access public     * @return string     */    public function getThinkPath()    {        return $this->thinkPath;    }    /**     * 获取路由目录     * @access public     * @return string     */    public function getRoutePath()    {        return $this->routePath;    }    /**     * 获取应用配置目录     * @access public     * @return string     */    public function getConfigPath()    {        return $this->configPath;    }    /**     * 获取配置后缀     * @access public     * @return string     */    public function getConfigExt()    {        return $this->configExt;    }    /**     * 获取应用类库命名空间     * @access public     * @return string     */    public function getNamespace()    {        return $this->namespace;    }    /**     * 设置应用类库命名空间     * @access public     * @param  string $namespace 命名空间名称     * @return $this     */    public function setNamespace($namespace)    {        $this->namespace = $namespace;        return $this;    }    /**     * 是否启用类库后缀     * @access public     * @return bool     */    public function getSuffix()    {        return $this->suffix;    }    /**     * 获取应用开启时间     * @access public     * @return float     */    public function getBeginTime()    {        return $this->beginTime;    }    /**     * 获取应用初始内存占用     * @access public     * @return integer     */    public function getBeginMem()    {        return $this->beginMem;    }}
 |