994 lines
27 KiB
PHP
994 lines
27 KiB
PHP
|
<?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.39 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();
|
|||
|
|
|||
|
// 针对swoole,如果worker_num大于1,则后面的进程会覆盖掉第一次的path数据(空),则会加载到默认的index控制器
|
|||
|
// 这里使用缓存的逻辑,如果为空则加载第一次的path数据,有则覆盖之
|
|||
|
// 该方案不是最佳的,待优化
|
|||
|
/*if (preg_match("/cli/i", php_sapi_name())) {
|
|||
|
if (!empty($path)) {
|
|||
|
$this->cache->tag('path_cache')->set('path', $path, 2);
|
|||
|
} else {
|
|||
|
$path = $this->cache->tag('path_cache')->get('path') ?: '';
|
|||
|
$this->cache->clear('path_cache');
|
|||
|
}
|
|||
|
}*/
|
|||
|
|
|||
|
// 是否强制路由模式
|
|||
|
$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;
|
|||
|
}
|
|||
|
|
|||
|
}
|