You've already forked qlg.tsgz.moe
Init Repo
This commit is contained in:
594
thinkphp/library/think/App.php
Executable file
594
thinkphp/library/think/App.php
Executable file
@ -0,0 +1,594 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 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\HttpException;
|
||||
use think\exception\HttpResponseException;
|
||||
use think\exception\RouteNotFoundException;
|
||||
|
||||
/**
|
||||
* App 应用管理
|
||||
* @author liu21st <liu21st@gmail.com>
|
||||
*/
|
||||
class App
|
||||
{
|
||||
/**
|
||||
* @var bool 是否初始化过
|
||||
*/
|
||||
protected static $init = false;
|
||||
|
||||
/**
|
||||
* @var string 当前模块路径
|
||||
*/
|
||||
public static $modulePath;
|
||||
|
||||
/**
|
||||
* @var bool 应用调试模式
|
||||
*/
|
||||
public static $debug = true;
|
||||
|
||||
/**
|
||||
* @var string 应用类库命名空间
|
||||
*/
|
||||
public static $namespace = 'app';
|
||||
|
||||
/**
|
||||
* @var bool 应用类库后缀
|
||||
*/
|
||||
public static $suffix = false;
|
||||
|
||||
/**
|
||||
* @var bool 应用路由检测
|
||||
*/
|
||||
protected static $routeCheck;
|
||||
|
||||
/**
|
||||
* @var bool 严格路由检测
|
||||
*/
|
||||
protected static $routeMust;
|
||||
|
||||
protected static $dispatch;
|
||||
protected static $file = [];
|
||||
|
||||
/**
|
||||
* 执行应用程序
|
||||
* @access public
|
||||
* @param Request $request Request对象
|
||||
* @return Response
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function run(Request $request = null)
|
||||
{
|
||||
is_null($request) && $request = Request::instance();
|
||||
|
||||
try {
|
||||
$config = self::initCommon();
|
||||
if (defined('BIND_MODULE')) {
|
||||
// 模块/控制器绑定
|
||||
BIND_MODULE && Route::bind(BIND_MODULE);
|
||||
} elseif ($config['auto_bind_module']) {
|
||||
// 入口自动绑定
|
||||
$name = pathinfo($request->baseFile(), PATHINFO_FILENAME);
|
||||
if ($name && 'index' != $name && is_dir(APP_PATH . $name)) {
|
||||
Route::bind($name);
|
||||
}
|
||||
}
|
||||
|
||||
$request->filter($config['default_filter']);
|
||||
|
||||
// 默认语言
|
||||
Lang::range($config['default_lang']);
|
||||
if ($config['lang_switch_on']) {
|
||||
// 开启多语言机制 检测当前语言
|
||||
Lang::detect();
|
||||
}
|
||||
$request->langset(Lang::range());
|
||||
|
||||
// 加载系统语言包
|
||||
Lang::load([
|
||||
THINK_PATH . 'lang' . DS . $request->langset() . EXT,
|
||||
APP_PATH . 'lang' . DS . $request->langset() . EXT,
|
||||
]);
|
||||
|
||||
// 获取应用调度信息
|
||||
$dispatch = self::$dispatch;
|
||||
if (empty($dispatch)) {
|
||||
// 进行URL路由检测
|
||||
$dispatch = self::routeCheck($request, $config);
|
||||
}
|
||||
// 记录当前调度信息
|
||||
$request->dispatch($dispatch);
|
||||
|
||||
// 记录路由和请求信息
|
||||
if (self::$debug) {
|
||||
Log::record('[ ROUTE ] ' . var_export($dispatch, true), 'info');
|
||||
Log::record('[ HEADER ] ' . var_export($request->header(), true), 'info');
|
||||
Log::record('[ PARAM ] ' . var_export($request->param(), true), 'info');
|
||||
}
|
||||
|
||||
// 监听app_begin
|
||||
Hook::listen('app_begin', $dispatch);
|
||||
// 请求缓存检查
|
||||
$request->cache($config['request_cache'], $config['request_cache_expire'], $config['request_cache_except']);
|
||||
|
||||
$data = self::exec($dispatch, $config);
|
||||
} catch (HttpResponseException $exception) {
|
||||
$data = $exception->getResponse();
|
||||
}
|
||||
|
||||
// 清空类的实例化
|
||||
Loader::clearInstance();
|
||||
|
||||
// 输出数据到客户端
|
||||
if ($data instanceof Response) {
|
||||
$response = $data;
|
||||
} elseif (!is_null($data)) {
|
||||
// 默认自动识别响应输出类型
|
||||
$isAjax = $request->isAjax();
|
||||
$type = $isAjax ? Config::get('default_ajax_return') : Config::get('default_return_type');
|
||||
$response = Response::create($data, $type);
|
||||
} else {
|
||||
$response = Response::create();
|
||||
}
|
||||
|
||||
// 监听app_end
|
||||
Hook::listen('app_end', $response);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前请求的调度信息
|
||||
* @access public
|
||||
* @param array|string $dispatch 调度信息
|
||||
* @param string $type 调度类型
|
||||
* @return void
|
||||
*/
|
||||
public static function dispatch($dispatch, $type = 'module')
|
||||
{
|
||||
self::$dispatch = ['type' => $type, $type => $dispatch];
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行函数或者闭包方法 支持参数调用
|
||||
* @access public
|
||||
* @param string|array|\Closure $function 函数或者闭包
|
||||
* @param array $vars 变量
|
||||
* @return mixed
|
||||
*/
|
||||
public static function invokeFunction($function, $vars = [])
|
||||
{
|
||||
$reflect = new \ReflectionFunction($function);
|
||||
$args = self::bindParams($reflect, $vars);
|
||||
// 记录执行信息
|
||||
self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info');
|
||||
return $reflect->invokeArgs($args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用反射执行类的方法 支持参数绑定
|
||||
* @access public
|
||||
* @param string|array $method 方法
|
||||
* @param array $vars 变量
|
||||
* @return mixed
|
||||
*/
|
||||
public static function invokeMethod($method, $vars = [])
|
||||
{
|
||||
if (is_array($method)) {
|
||||
$class = is_object($method[0]) ? $method[0] : self::invokeClass($method[0]);
|
||||
$reflect = new \ReflectionMethod($class, $method[1]);
|
||||
} else {
|
||||
// 静态方法
|
||||
$reflect = new \ReflectionMethod($method);
|
||||
}
|
||||
$args = self::bindParams($reflect, $vars);
|
||||
|
||||
self::$debug && Log::record('[ RUN ] ' . $reflect->class . '->' . $reflect->name . '[ ' . $reflect->getFileName() . ' ]', 'info');
|
||||
return $reflect->invokeArgs(isset($class) ? $class : null, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用反射执行类的实例化 支持依赖注入
|
||||
* @access public
|
||||
* @param string $class 类名
|
||||
* @param array $vars 变量
|
||||
* @return mixed
|
||||
*/
|
||||
public static function invokeClass($class, $vars = [])
|
||||
{
|
||||
$reflect = new \ReflectionClass($class);
|
||||
$constructor = $reflect->getConstructor();
|
||||
if ($constructor) {
|
||||
$args = self::bindParams($constructor, $vars);
|
||||
} else {
|
||||
$args = [];
|
||||
}
|
||||
return $reflect->newInstanceArgs($args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定参数
|
||||
* @access private
|
||||
* @param \ReflectionMethod|\ReflectionFunction $reflect 反射类
|
||||
* @param array $vars 变量
|
||||
* @return array
|
||||
*/
|
||||
private static function bindParams($reflect, $vars = [])
|
||||
{
|
||||
if (empty($vars)) {
|
||||
// 自动获取请求变量
|
||||
if (Config::get('url_param_type')) {
|
||||
$vars = Request::instance()->route();
|
||||
} else {
|
||||
$vars = Request::instance()->param();
|
||||
}
|
||||
}
|
||||
$args = [];
|
||||
if ($reflect->getNumberOfParameters() > 0) {
|
||||
// 判断数组类型 数字数组时按顺序绑定参数
|
||||
reset($vars);
|
||||
$type = key($vars) === 0 ? 1 : 0;
|
||||
$params = $reflect->getParameters();
|
||||
foreach ($params as $param) {
|
||||
$args[] = self::getParamValue($param, $vars, $type);
|
||||
}
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取参数值
|
||||
* @access private
|
||||
* @param \ReflectionParameter $param
|
||||
* @param array $vars 变量
|
||||
* @param string $type
|
||||
* @return array
|
||||
*/
|
||||
private static function getParamValue($param, &$vars, $type)
|
||||
{
|
||||
$name = $param->getName();
|
||||
$class = $param->getClass();
|
||||
if ($class) {
|
||||
$className = $class->getName();
|
||||
$bind = Request::instance()->$name;
|
||||
if ($bind instanceof $className) {
|
||||
$result = $bind;
|
||||
} else {
|
||||
if (method_exists($className, 'invoke')) {
|
||||
$method = new \ReflectionMethod($className, 'invoke');
|
||||
if ($method->isPublic() && $method->isStatic()) {
|
||||
return $className::invoke(Request::instance());
|
||||
}
|
||||
}
|
||||
$result = method_exists($className, 'instance') ? $className::instance() : new $className;
|
||||
}
|
||||
} elseif (1 == $type && !empty($vars)) {
|
||||
$result = array_shift($vars);
|
||||
} elseif (0 == $type && isset($vars[$name])) {
|
||||
$result = $vars[$name];
|
||||
} elseif ($param->isDefaultValueAvailable()) {
|
||||
$result = $param->getDefaultValue();
|
||||
} else {
|
||||
throw new \InvalidArgumentException('method param miss:' . $name);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected static function exec($dispatch, $config)
|
||||
{
|
||||
switch ($dispatch['type']) {
|
||||
case 'redirect':
|
||||
// 执行重定向跳转
|
||||
$data = Response::create($dispatch['url'], 'redirect')->code($dispatch['status']);
|
||||
break;
|
||||
case 'module':
|
||||
// 模块/控制器/操作
|
||||
$data = self::module($dispatch['module'], $config, isset($dispatch['convert']) ? $dispatch['convert'] : null);
|
||||
break;
|
||||
case 'controller':
|
||||
// 执行控制器操作
|
||||
$vars = array_merge(Request::instance()->param(), $dispatch['var']);
|
||||
$data = Loader::action($dispatch['controller'], $vars, $config['url_controller_layer'], $config['controller_suffix']);
|
||||
break;
|
||||
case 'method':
|
||||
// 执行回调方法
|
||||
$vars = array_merge(Request::instance()->param(), $dispatch['var']);
|
||||
$data = self::invokeMethod($dispatch['method'], $vars);
|
||||
break;
|
||||
case 'function':
|
||||
// 执行闭包
|
||||
$data = self::invokeFunction($dispatch['function']);
|
||||
break;
|
||||
case 'response':
|
||||
$data = $dispatch['response'];
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException('dispatch type not support');
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行模块
|
||||
* @access public
|
||||
* @param array $result 模块/控制器/操作
|
||||
* @param array $config 配置参数
|
||||
* @param bool $convert 是否自动转换控制器和操作名
|
||||
* @return mixed
|
||||
*/
|
||||
public static function module($result, $config, $convert = null)
|
||||
{
|
||||
if (is_string($result)) {
|
||||
$result = explode('/', $result);
|
||||
}
|
||||
$request = Request::instance();
|
||||
if ($config['app_multi_module']) {
|
||||
// 多模块部署
|
||||
$module = strip_tags(strtolower($result[0] ?: $config['default_module']));
|
||||
$bind = Route::getBind('module');
|
||||
$available = false;
|
||||
if ($bind) {
|
||||
// 绑定模块
|
||||
list($bindModule) = explode('/', $bind);
|
||||
if (empty($result[0])) {
|
||||
$module = $bindModule;
|
||||
$available = true;
|
||||
} elseif ($module == $bindModule) {
|
||||
$available = true;
|
||||
}
|
||||
} elseif (!in_array($module, $config['deny_module_list']) && is_dir(APP_PATH . $module)) {
|
||||
$available = true;
|
||||
}
|
||||
|
||||
// 模块初始化
|
||||
if ($module && $available) {
|
||||
// 初始化模块
|
||||
$request->module($module);
|
||||
$config = self::init($module);
|
||||
// 模块请求缓存检查
|
||||
$request->cache($config['request_cache'], $config['request_cache_expire'], $config['request_cache_except']);
|
||||
} else {
|
||||
throw new HttpException(404, 'module not exists:' . $module);
|
||||
}
|
||||
} else {
|
||||
// 单一模块部署
|
||||
$module = '';
|
||||
$request->module($module);
|
||||
}
|
||||
// 当前模块路径
|
||||
App::$modulePath = APP_PATH . ($module ? $module . DS : '');
|
||||
|
||||
// 是否自动转换控制器和操作名
|
||||
$convert = is_bool($convert) ? $convert : $config['url_convert'];
|
||||
// 获取控制器名
|
||||
$controller = strip_tags($result[1] ?: $config['default_controller']);
|
||||
if (!preg_match('/^[A-Za-z](\w|\.)*$/', $controller)) {
|
||||
throw new HttpException(404, 'controller not exists:' . $controller);
|
||||
}
|
||||
$controller = $convert ? strtolower($controller) : $controller;
|
||||
|
||||
// 获取操作名
|
||||
$actionName = strip_tags($result[2] ?: $config['default_action']);
|
||||
$actionName = $convert ? strtolower($actionName) : $actionName;
|
||||
|
||||
// 设置当前请求的控制器、操作
|
||||
$request->controller(Loader::parseName($controller, 1))->action($actionName);
|
||||
|
||||
// 监听module_init
|
||||
Hook::listen('module_init', $request);
|
||||
|
||||
try {
|
||||
$instance = Loader::controller($controller, $config['url_controller_layer'], $config['controller_suffix'], $config['empty_controller']);
|
||||
} catch (ClassNotFoundException $e) {
|
||||
throw new HttpException(404, 'controller not exists:' . $e->getClass());
|
||||
}
|
||||
|
||||
// 获取当前操作名
|
||||
$action = $actionName . $config['action_suffix'];
|
||||
|
||||
$vars = [];
|
||||
if (is_callable([$instance, $action])) {
|
||||
// 执行操作方法
|
||||
$call = [$instance, $action];
|
||||
} elseif (is_callable([$instance, '_empty'])) {
|
||||
// 空操作
|
||||
$call = [$instance, '_empty'];
|
||||
$vars = [$actionName];
|
||||
} else {
|
||||
// 操作不存在
|
||||
throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()');
|
||||
}
|
||||
|
||||
Hook::listen('action_begin', $call);
|
||||
|
||||
return self::invokeMethod($call, $vars);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化应用
|
||||
*/
|
||||
public static function initCommon()
|
||||
{
|
||||
if (empty(self::$init)) {
|
||||
if (defined('APP_NAMESPACE')) {
|
||||
self::$namespace = APP_NAMESPACE;
|
||||
}
|
||||
Loader::addNamespace(self::$namespace, APP_PATH);
|
||||
|
||||
// 初始化应用
|
||||
$config = self::init();
|
||||
self::$suffix = $config['class_suffix'];
|
||||
|
||||
// 应用调试模式
|
||||
self::$debug = Env::get('app_debug', Config::get('app_debug'));
|
||||
if (!self::$debug) {
|
||||
ini_set('display_errors', 'Off');
|
||||
} elseif (!IS_CLI) {
|
||||
//重新申请一块比较大的buffer
|
||||
if (ob_get_level() > 0) {
|
||||
$output = ob_get_clean();
|
||||
}
|
||||
ob_start();
|
||||
if (!empty($output)) {
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($config['root_namespace'])) {
|
||||
Loader::addNamespace($config['root_namespace']);
|
||||
}
|
||||
|
||||
// 加载额外文件
|
||||
if (!empty($config['extra_file_list'])) {
|
||||
foreach ($config['extra_file_list'] as $file) {
|
||||
$file = strpos($file, '.') ? $file : APP_PATH . $file . EXT;
|
||||
if (is_file($file) && !isset(self::$file[$file])) {
|
||||
include $file;
|
||||
self::$file[$file] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置系统时区
|
||||
date_default_timezone_set($config['default_timezone']);
|
||||
|
||||
// 监听app_init
|
||||
Hook::listen('app_init');
|
||||
|
||||
self::$init = true;
|
||||
}
|
||||
return Config::get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化应用或模块
|
||||
* @access public
|
||||
* @param string $module 模块名
|
||||
* @return array
|
||||
*/
|
||||
private static function init($module = '')
|
||||
{
|
||||
// 定位模块目录
|
||||
$module = $module ? $module . DS : '';
|
||||
|
||||
// 加载初始化文件
|
||||
if (is_file(APP_PATH . $module . 'init' . EXT)) {
|
||||
include APP_PATH . $module . 'init' . EXT;
|
||||
} elseif (is_file(RUNTIME_PATH . $module . 'init' . EXT)) {
|
||||
include RUNTIME_PATH . $module . 'init' . EXT;
|
||||
} else {
|
||||
$path = APP_PATH . $module;
|
||||
// 加载模块配置
|
||||
$config = Config::load(CONF_PATH . $module . 'config' . CONF_EXT);
|
||||
// 读取数据库配置文件
|
||||
$filename = CONF_PATH . $module . 'database' . CONF_EXT;
|
||||
Config::load($filename, 'database');
|
||||
// 读取扩展配置文件
|
||||
if (is_dir(CONF_PATH . $module . 'extra')) {
|
||||
$dir = CONF_PATH . $module . 'extra';
|
||||
$files = scandir($dir);
|
||||
foreach ($files as $file) {
|
||||
if ('.' . pathinfo($file, PATHINFO_EXTENSION) === CONF_EXT) {
|
||||
$filename = $dir . DS . $file;
|
||||
Config::load($filename, pathinfo($file, PATHINFO_FILENAME));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 加载应用状态配置
|
||||
if ($config['app_status']) {
|
||||
$config = Config::load(CONF_PATH . $module . $config['app_status'] . CONF_EXT);
|
||||
}
|
||||
|
||||
// 加载行为扩展文件
|
||||
if (is_file(CONF_PATH . $module . 'tags' . EXT)) {
|
||||
Hook::import(include CONF_PATH . $module . 'tags' . EXT);
|
||||
}
|
||||
|
||||
// 加载公共文件
|
||||
if (is_file($path . 'common' . EXT)) {
|
||||
include $path . 'common' . EXT;
|
||||
}
|
||||
|
||||
// 加载当前模块语言包
|
||||
if ($module) {
|
||||
Lang::load($path . 'lang' . DS . Request::instance()->langset() . EXT);
|
||||
}
|
||||
}
|
||||
return Config::get();
|
||||
}
|
||||
|
||||
/**
|
||||
* URL路由检测(根据PATH_INFO)
|
||||
* @access public
|
||||
* @param \think\Request $request
|
||||
* @param array $config
|
||||
* @return array
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public static function routeCheck($request, array $config)
|
||||
{
|
||||
$path = $request->path();
|
||||
$depr = $config['pathinfo_depr'];
|
||||
$result = false;
|
||||
// 路由检测
|
||||
$check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on'];
|
||||
if ($check) {
|
||||
// 开启路由
|
||||
if (is_file(RUNTIME_PATH . 'route.php')) {
|
||||
// 读取路由缓存
|
||||
$rules = include RUNTIME_PATH . 'route.php';
|
||||
if (is_array($rules)) {
|
||||
Route::rules($rules);
|
||||
}
|
||||
} else {
|
||||
$files = $config['route_config_file'];
|
||||
foreach ($files as $file) {
|
||||
if (is_file(CONF_PATH . $file . CONF_EXT)) {
|
||||
// 导入路由配置
|
||||
$rules = include CONF_PATH . $file . CONF_EXT;
|
||||
if (is_array($rules)) {
|
||||
Route::import($rules);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 路由检测(根据路由定义返回不同的URL调度)
|
||||
$result = Route::check($request, $path, $depr, $config['url_domain_deploy']);
|
||||
$must = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must'];
|
||||
if ($must && false === $result) {
|
||||
// 路由无效
|
||||
throw new RouteNotFoundException();
|
||||
}
|
||||
}
|
||||
if (false === $result) {
|
||||
// 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索
|
||||
$result = Route::parseUrl($path, $depr, $config['controller_auto_search']);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置应用的路由检测机制
|
||||
* @access public
|
||||
* @param bool $route 是否需要检测路由
|
||||
* @param bool $must 是否强制检测路由
|
||||
* @return void
|
||||
*/
|
||||
public static function route($route, $must = false)
|
||||
{
|
||||
self::$routeCheck = $route;
|
||||
self::$routeMust = $must;
|
||||
}
|
||||
}
|
205
thinkphp/library/think/Build.php
Executable file
205
thinkphp/library/think/Build.php
Executable file
@ -0,0 +1,205 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class Build
|
||||
{
|
||||
/**
|
||||
* 根据传入的build资料创建目录和文件
|
||||
* @access protected
|
||||
* @param array $build build列表
|
||||
* @param string $namespace 应用类库命名空间
|
||||
* @param bool $suffix 类库后缀
|
||||
* @return void
|
||||
*/
|
||||
public static function run(array $build = [], $namespace = 'app', $suffix = false)
|
||||
{
|
||||
// 锁定
|
||||
$lockfile = APP_PATH . 'build.lock';
|
||||
if (is_writable($lockfile)) {
|
||||
return;
|
||||
} elseif (!touch($lockfile)) {
|
||||
throw new Exception('应用目录[' . APP_PATH . ']不可写,目录无法自动生成!<BR>请手动生成项目目录~', 10006);
|
||||
}
|
||||
foreach ($build as $module => $list) {
|
||||
if ('__dir__' == $module) {
|
||||
// 创建目录列表
|
||||
self::buildDir($list);
|
||||
} elseif ('__file__' == $module) {
|
||||
// 创建文件列表
|
||||
self::buildFile($list);
|
||||
} else {
|
||||
// 创建模块
|
||||
self::module($module, $list, $namespace, $suffix);
|
||||
}
|
||||
}
|
||||
// 解除锁定
|
||||
unlink($lockfile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建目录
|
||||
* @access protected
|
||||
* @param array $list 目录列表
|
||||
* @return void
|
||||
*/
|
||||
protected static function buildDir($list)
|
||||
{
|
||||
foreach ($list as $dir) {
|
||||
if (!is_dir(APP_PATH . $dir)) {
|
||||
// 创建目录
|
||||
mkdir(APP_PATH . $dir, 0755, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文件
|
||||
* @access protected
|
||||
* @param array $list 文件列表
|
||||
* @return void
|
||||
*/
|
||||
protected static function buildFile($list)
|
||||
{
|
||||
foreach ($list as $file) {
|
||||
if (!is_dir(APP_PATH . dirname($file))) {
|
||||
// 创建目录
|
||||
mkdir(APP_PATH . dirname($file), 0755, true);
|
||||
}
|
||||
if (!is_file(APP_PATH . $file)) {
|
||||
file_put_contents(APP_PATH . $file, 'php' == pathinfo($file, PATHINFO_EXTENSION) ? "<?php\n" : '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建模块
|
||||
* @access public
|
||||
* @param string $module 模块名
|
||||
* @param array $list build列表
|
||||
* @param string $namespace 应用类库命名空间
|
||||
* @param bool $suffix 类库后缀
|
||||
* @return void
|
||||
*/
|
||||
public static function module($module = '', $list = [], $namespace = 'app', $suffix = false)
|
||||
{
|
||||
$module = $module ? $module : '';
|
||||
if (!is_dir(APP_PATH . $module)) {
|
||||
// 创建模块目录
|
||||
mkdir(APP_PATH . $module);
|
||||
}
|
||||
if (basename(RUNTIME_PATH) != $module) {
|
||||
// 创建配置文件和公共文件
|
||||
self::buildCommon($module);
|
||||
// 创建模块的默认页面
|
||||
self::buildHello($module, $namespace, $suffix);
|
||||
}
|
||||
if (empty($list)) {
|
||||
// 创建默认的模块目录和文件
|
||||
$list = [
|
||||
'__file__' => ['config.php', 'common.php'],
|
||||
'__dir__' => ['controller', 'model', 'view'],
|
||||
];
|
||||
}
|
||||
// 创建子目录和文件
|
||||
foreach ($list as $path => $file) {
|
||||
$modulePath = APP_PATH . $module . DS;
|
||||
if ('__dir__' == $path) {
|
||||
// 生成子目录
|
||||
foreach ($file as $dir) {
|
||||
self::checkDirBuild($modulePath . $dir);
|
||||
}
|
||||
} elseif ('__file__' == $path) {
|
||||
// 生成(空白)文件
|
||||
foreach ($file as $name) {
|
||||
if (!is_file($modulePath . $name)) {
|
||||
file_put_contents($modulePath . $name, 'php' == pathinfo($name, PATHINFO_EXTENSION) ? "<?php\n" : '');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 生成相关MVC文件
|
||||
foreach ($file as $val) {
|
||||
$val = trim($val);
|
||||
$filename = $modulePath . $path . DS . $val . ($suffix ? ucfirst($path) : '') . EXT;
|
||||
$space = $namespace . '\\' . ($module ? $module . '\\' : '') . $path;
|
||||
$class = $val . ($suffix ? ucfirst($path) : '');
|
||||
switch ($path) {
|
||||
case 'controller': // 控制器
|
||||
$content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
|
||||
break;
|
||||
case 'model': // 模型
|
||||
$content = "<?php\nnamespace {$space};\n\nuse think\Model;\n\nclass {$class} extends Model\n{\n\n}";
|
||||
break;
|
||||
case 'view': // 视图
|
||||
$filename = $modulePath . $path . DS . $val . '.html';
|
||||
self::checkDirBuild(dirname($filename));
|
||||
$content = '';
|
||||
break;
|
||||
default:
|
||||
// 其他文件
|
||||
$content = "<?php\nnamespace {$space};\n\nclass {$class}\n{\n\n}";
|
||||
}
|
||||
|
||||
if (!is_file($filename)) {
|
||||
file_put_contents($filename, $content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建模块的欢迎页面
|
||||
* @access public
|
||||
* @param string $module 模块名
|
||||
* @param string $namespace 应用类库命名空间
|
||||
* @param bool $suffix 类库后缀
|
||||
* @return void
|
||||
*/
|
||||
protected static function buildHello($module, $namespace, $suffix = false)
|
||||
{
|
||||
$filename = APP_PATH . ($module ? $module . DS : '') . 'controller' . DS . 'Index' . ($suffix ? 'Controller' : '') . EXT;
|
||||
if (!is_file($filename)) {
|
||||
$content = file_get_contents(THINK_PATH . 'tpl' . DS . 'default_index.tpl');
|
||||
$content = str_replace(['{$app}', '{$module}', '{layer}', '{$suffix}'], [$namespace, $module ? $module . '\\' : '', 'controller', $suffix ? 'Controller' : ''], $content);
|
||||
self::checkDirBuild(dirname($filename));
|
||||
file_put_contents($filename, $content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建模块的公共文件
|
||||
* @access public
|
||||
* @param string $module 模块名
|
||||
* @return void
|
||||
*/
|
||||
protected static function buildCommon($module)
|
||||
{
|
||||
$filename = CONF_PATH . ($module ? $module . DS : '') . 'config.php';
|
||||
|
||||
self::checkDirBuild(dirname($filename));
|
||||
if (!is_file($filename)) {
|
||||
file_put_contents($filename, "<?php\n//配置文件\nreturn [\n\n];");
|
||||
}
|
||||
$filename = APP_PATH . ($module ? $module . DS : '') . 'common.php';
|
||||
if (!is_file($filename)) {
|
||||
file_put_contents($filename, "<?php\n");
|
||||
}
|
||||
}
|
||||
|
||||
protected static function checkDirBuild($dirname)
|
||||
{
|
||||
if (!is_dir($dirname)) {
|
||||
mkdir($dirname, 0755, true);
|
||||
}
|
||||
}
|
||||
}
|
222
thinkphp/library/think/Cache.php
Executable file
222
thinkphp/library/think/Cache.php
Executable file
@ -0,0 +1,222 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\cache\Driver;
|
||||
|
||||
class Cache
|
||||
{
|
||||
protected static $instance = [];
|
||||
public static $readTimes = 0;
|
||||
public static $writeTimes = 0;
|
||||
|
||||
/**
|
||||
* 操作句柄
|
||||
* @var object
|
||||
* @access protected
|
||||
*/
|
||||
protected static $handler;
|
||||
|
||||
/**
|
||||
* 连接缓存
|
||||
* @access public
|
||||
* @param array $options 配置数组
|
||||
* @param bool|string $name 缓存连接标识 true 强制重新连接
|
||||
* @return Driver
|
||||
*/
|
||||
public static function connect(array $options = [], $name = false)
|
||||
{
|
||||
$type = !empty($options['type']) ? $options['type'] : 'File';
|
||||
if (false === $name) {
|
||||
$name = md5(serialize($options));
|
||||
}
|
||||
|
||||
if (true === $name || !isset(self::$instance[$name])) {
|
||||
$class = false !== strpos($type, '\\') ? $type : '\\think\\cache\\driver\\' . ucwords($type);
|
||||
|
||||
// 记录初始化信息
|
||||
App::$debug && Log::record('[ CACHE ] INIT ' . $type, 'info');
|
||||
if (true === $name) {
|
||||
return new $class($options);
|
||||
} else {
|
||||
self::$instance[$name] = new $class($options);
|
||||
}
|
||||
}
|
||||
return self::$instance[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动初始化缓存
|
||||
* @access public
|
||||
* @param array $options 配置数组
|
||||
* @return Driver
|
||||
*/
|
||||
public static function init(array $options = [])
|
||||
{
|
||||
if (is_null(self::$handler)) {
|
||||
// 自动初始化缓存
|
||||
if (!empty($options)) {
|
||||
$connect = self::connect($options);
|
||||
} elseif ('complex' == Config::get('cache.type')) {
|
||||
$connect = self::connect(Config::get('cache.default'));
|
||||
} else {
|
||||
$connect = self::connect(Config::get('cache'));
|
||||
}
|
||||
self::$handler = $connect;
|
||||
}
|
||||
return self::$handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换缓存类型 需要配置 cache.type 为 complex
|
||||
* @access public
|
||||
* @param string $name 缓存标识
|
||||
* @return Driver
|
||||
*/
|
||||
public static function store($name = '')
|
||||
{
|
||||
if ('' !== $name && 'complex' == Config::get('cache.type')) {
|
||||
return self::connect(Config::get('cache.' . $name), strtolower($name));
|
||||
}
|
||||
return self::init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存是否存在
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public static function has($name)
|
||||
{
|
||||
self::$readTimes++;
|
||||
return self::init()->has($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存标识
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($name, $default = false)
|
||||
{
|
||||
self::$readTimes++;
|
||||
return self::init()->get($name, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存标识
|
||||
* @param mixed $value 存储数据
|
||||
* @param int|null $expire 有效时间 0为永久
|
||||
* @return boolean
|
||||
*/
|
||||
public static function set($name, $value, $expire = null)
|
||||
{
|
||||
self::$writeTimes++;
|
||||
return self::init()->set($name, $value, $expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public static function inc($name, $step = 1)
|
||||
{
|
||||
self::$writeTimes++;
|
||||
return self::init()->inc($name, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public static function dec($name, $step = 1)
|
||||
{
|
||||
self::$writeTimes++;
|
||||
return self::init()->dec($name, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存标识
|
||||
* @return boolean
|
||||
*/
|
||||
public static function rm($name)
|
||||
{
|
||||
self::$writeTimes++;
|
||||
return self::init()->rm($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
public static function clear($tag = null)
|
||||
{
|
||||
self::$writeTimes++;
|
||||
return self::init()->clear($tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存并删除
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return mixed
|
||||
*/
|
||||
public static function pull($name)
|
||||
{
|
||||
self::$readTimes++;
|
||||
self::$writeTimes++;
|
||||
return self::init()->pull($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果不存在则写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int $expire 有效时间 0为永久
|
||||
* @return mixed
|
||||
*/
|
||||
public static function remember($name, $value, $expire = null)
|
||||
{
|
||||
self::$readTimes++;
|
||||
return self::init()->remember($name, $value, $expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存标签
|
||||
* @access public
|
||||
* @param string $name 标签名
|
||||
* @param string|array $keys 缓存标识
|
||||
* @param bool $overlay 是否覆盖
|
||||
* @return Driver
|
||||
*/
|
||||
public static function tag($name, $keys = null, $overlay = false)
|
||||
{
|
||||
return self::init()->tag($name, $keys, $overlay);
|
||||
}
|
||||
|
||||
}
|
391
thinkphp/library/think/Collection.php
Executable file
391
thinkphp/library/think/Collection.php
Executable file
@ -0,0 +1,391 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: zhangyajun <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use ArrayAccess;
|
||||
use ArrayIterator;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use JsonSerializable;
|
||||
|
||||
class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
|
||||
{
|
||||
protected $items = [];
|
||||
|
||||
public function __construct($items = [])
|
||||
{
|
||||
$this->items = $this->convertToArray($items);
|
||||
}
|
||||
|
||||
public static function make($items = [])
|
||||
{
|
||||
return new static($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为空
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return empty($this->items);
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
return array_map(function ($value) {
|
||||
return ($value instanceof Model || $value instanceof self) ? $value->toArray() : $value;
|
||||
}, $this->items);
|
||||
}
|
||||
|
||||
public function all()
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并数组
|
||||
*
|
||||
* @param mixed $items
|
||||
* @return static
|
||||
*/
|
||||
public function merge($items)
|
||||
{
|
||||
return new static(array_merge($this->items, $this->convertToArray($items)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较数组,返回差集
|
||||
*
|
||||
* @param mixed $items
|
||||
* @return static
|
||||
*/
|
||||
public function diff($items)
|
||||
{
|
||||
return new static(array_diff($this->items, $this->convertToArray($items)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 交换数组中的键和值
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function flip()
|
||||
{
|
||||
return new static(array_flip($this->items));
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较数组,返回交集
|
||||
*
|
||||
* @param mixed $items
|
||||
* @return static
|
||||
*/
|
||||
public function intersect($items)
|
||||
{
|
||||
return new static(array_intersect($this->items, $this->convertToArray($items)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数组中所有的键名
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function keys()
|
||||
{
|
||||
return new static(array_keys($this->items));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数组的最后一个元素(出栈)
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function pop()
|
||||
{
|
||||
return array_pop($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过使用用户自定义函数,以字符串返回数组
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param mixed $initial
|
||||
* @return mixed
|
||||
*/
|
||||
public function reduce(callable $callback, $initial = null)
|
||||
{
|
||||
return array_reduce($this->items, $callback, $initial);
|
||||
}
|
||||
|
||||
/**
|
||||
* 以相反的顺序返回数组。
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function reverse()
|
||||
{
|
||||
return new static(array_reverse($this->items));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数组中首个元素,并返回被删除元素的值
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function shift()
|
||||
{
|
||||
return array_shift($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 把一个数组分割为新的数组块.
|
||||
*
|
||||
* @param int $size
|
||||
* @param bool $preserveKeys
|
||||
* @return static
|
||||
*/
|
||||
public function chunk($size, $preserveKeys = false)
|
||||
{
|
||||
$chunks = [];
|
||||
|
||||
foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) {
|
||||
$chunks[] = new static($chunk);
|
||||
}
|
||||
|
||||
return new static($chunks);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在数组开头插入一个元素
|
||||
* @param mixed $value
|
||||
* @param miexed $key
|
||||
* @return void
|
||||
*/
|
||||
public function unshift($value, $key = null)
|
||||
{
|
||||
if (is_null($key)) {
|
||||
array_unshift($this->items, $value);
|
||||
} else {
|
||||
$this->items = [$key => $value] + $this->items;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在数组结尾插入一个元素
|
||||
* @param mixed $value
|
||||
* @param mixed $key
|
||||
* @return void
|
||||
*/
|
||||
public function push($value, $key = null)
|
||||
{
|
||||
if (is_null($key)) {
|
||||
$this->items[] = $value;
|
||||
} else {
|
||||
$this->items[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 给每个元素执行个回调
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function each(callable $callback)
|
||||
{
|
||||
foreach ($this->items as $key => $item) {
|
||||
$result = $callback($item, $key);
|
||||
if (false === $result) {
|
||||
break;
|
||||
} elseif (!is_object($item)) {
|
||||
$this->items[$key] = $result;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用回调函数过滤数组中的元素
|
||||
* @param callable|null $callback
|
||||
* @return static
|
||||
*/
|
||||
public function filter(callable $callback = null)
|
||||
{
|
||||
if ($callback) {
|
||||
return new static(array_filter($this->items, $callback));
|
||||
}
|
||||
|
||||
return new static(array_filter($this->items));
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数组中指定的一列
|
||||
* @param $column_key
|
||||
* @param null $index_key
|
||||
* @return array
|
||||
*/
|
||||
public function column($column_key, $index_key = null)
|
||||
{
|
||||
if (function_exists('array_column')) {
|
||||
return array_column($this->items, $column_key, $index_key);
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($this->items as $row) {
|
||||
$key = $value = null;
|
||||
$keySet = $valueSet = false;
|
||||
if (null !== $index_key && array_key_exists($index_key, $row)) {
|
||||
$keySet = true;
|
||||
$key = (string) $row[$index_key];
|
||||
}
|
||||
if (null === $column_key) {
|
||||
$valueSet = true;
|
||||
$value = $row;
|
||||
} elseif (is_array($row) && array_key_exists($column_key, $row)) {
|
||||
$valueSet = true;
|
||||
$value = $row[$column_key];
|
||||
}
|
||||
if ($valueSet) {
|
||||
if ($keySet) {
|
||||
$result[$key] = $value;
|
||||
} else {
|
||||
$result[] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对数组排序
|
||||
*
|
||||
* @param callable|null $callback
|
||||
* @return static
|
||||
*/
|
||||
public function sort(callable $callback = null)
|
||||
{
|
||||
$items = $this->items;
|
||||
|
||||
$callback ? uasort($items, $callback) : uasort($items, function ($a, $b) {
|
||||
|
||||
if ($a == $b) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ($a < $b) ? -1 : 1;
|
||||
});
|
||||
|
||||
return new static($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数组打乱
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function shuffle()
|
||||
{
|
||||
$items = $this->items;
|
||||
|
||||
shuffle($items);
|
||||
|
||||
return new static($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取数组
|
||||
*
|
||||
* @param int $offset
|
||||
* @param int $length
|
||||
* @param bool $preserveKeys
|
||||
* @return static
|
||||
*/
|
||||
public function slice($offset, $length = null, $preserveKeys = false)
|
||||
{
|
||||
return new static(array_slice($this->items, $offset, $length, $preserveKeys));
|
||||
}
|
||||
|
||||
// ArrayAccess
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return array_key_exists($offset, $this->items);
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->items[$offset];
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if (is_null($offset)) {
|
||||
$this->items[] = $value;
|
||||
} else {
|
||||
$this->items[$offset] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->items[$offset]);
|
||||
}
|
||||
|
||||
//Countable
|
||||
public function count()
|
||||
{
|
||||
return count($this->items);
|
||||
}
|
||||
|
||||
//IteratorAggregate
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->items);
|
||||
}
|
||||
|
||||
//JsonSerializable
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换当前数据集为JSON字符串
|
||||
* @access public
|
||||
* @param integer $options json参数
|
||||
* @return string
|
||||
*/
|
||||
public function toJson($options = JSON_UNESCAPED_UNICODE)
|
||||
{
|
||||
return json_encode($this->toArray(), $options);
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->toJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换成数组
|
||||
*
|
||||
* @param mixed $items
|
||||
* @return array
|
||||
*/
|
||||
protected function convertToArray($items)
|
||||
{
|
||||
if ($items instanceof self) {
|
||||
return $items->all();
|
||||
}
|
||||
return (array) $items;
|
||||
}
|
||||
}
|
178
thinkphp/library/think/Config.php
Executable file
178
thinkphp/library/think/Config.php
Executable file
@ -0,0 +1,178 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class Config
|
||||
{
|
||||
// 配置参数
|
||||
private static $config = [];
|
||||
// 参数作用域
|
||||
private static $range = '_sys_';
|
||||
|
||||
// 设定配置参数的作用域
|
||||
public static function range($range)
|
||||
{
|
||||
self::$range = $range;
|
||||
if (!isset(self::$config[$range])) {
|
||||
self::$config[$range] = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析配置文件或内容
|
||||
* @param string $config 配置文件路径或内容
|
||||
* @param string $type 配置解析类型
|
||||
* @param string $name 配置名(如设置即表示二级配置)
|
||||
* @param string $range 作用域
|
||||
* @return mixed
|
||||
*/
|
||||
public static function parse($config, $type = '', $name = '', $range = '')
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
if (empty($type)) {
|
||||
$type = pathinfo($config, PATHINFO_EXTENSION);
|
||||
}
|
||||
$class = false !== strpos($type, '\\') ? $type : '\\think\\config\\driver\\' . ucwords($type);
|
||||
return self::set((new $class())->parse($config), $name, $range);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载配置文件(PHP格式)
|
||||
* @param string $file 配置文件名
|
||||
* @param string $name 配置名(如设置即表示二级配置)
|
||||
* @param string $range 作用域
|
||||
* @return mixed
|
||||
*/
|
||||
public static function load($file, $name = '', $range = '')
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
if (!isset(self::$config[$range])) {
|
||||
self::$config[$range] = [];
|
||||
}
|
||||
if (is_file($file)) {
|
||||
$name = strtolower($name);
|
||||
$type = pathinfo($file, PATHINFO_EXTENSION);
|
||||
if ('php' == $type) {
|
||||
return self::set(include $file, $name, $range);
|
||||
} elseif ('yaml' == $type && function_exists('yaml_parse_file')) {
|
||||
return self::set(yaml_parse_file($file), $name, $range);
|
||||
} else {
|
||||
return self::parse($file, $type, $name, $range);
|
||||
}
|
||||
} else {
|
||||
return self::$config[$range];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测配置是否存在
|
||||
* @param string $name 配置参数名(支持二级配置 .号分割)
|
||||
* @param string $range 作用域
|
||||
* @return bool
|
||||
*/
|
||||
public static function has($name, $range = '')
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
|
||||
if (!strpos($name, '.')) {
|
||||
return isset(self::$config[$range][strtolower($name)]);
|
||||
} else {
|
||||
// 二维数组设置和获取支持
|
||||
$name = explode('.', $name, 2);
|
||||
return isset(self::$config[$range][strtolower($name[0])][$name[1]]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置参数 为空则获取所有配置
|
||||
* @param string $name 配置参数名(支持二级配置 .号分割)
|
||||
* @param string $range 作用域
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($name = null, $range = '')
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
// 无参数时获取所有
|
||||
if (empty($name) && isset(self::$config[$range])) {
|
||||
return self::$config[$range];
|
||||
}
|
||||
|
||||
if (!strpos($name, '.')) {
|
||||
$name = strtolower($name);
|
||||
return isset(self::$config[$range][$name]) ? self::$config[$range][$name] : null;
|
||||
} else {
|
||||
// 二维数组设置和获取支持
|
||||
$name = explode('.', $name, 2);
|
||||
$name[0] = strtolower($name[0]);
|
||||
|
||||
if (!isset(self::$config[$range][$name[0]])) {
|
||||
// 动态载入额外配置
|
||||
$module = Request::instance()->module();
|
||||
$file = CONF_PATH . ($module ? $module . DS : '') . 'extra' . DS . $name[0] . CONF_EXT;
|
||||
|
||||
is_file($file) && self::load($file, $name[0]);
|
||||
}
|
||||
|
||||
return isset(self::$config[$range][$name[0]][$name[1]]) ? self::$config[$range][$name[0]][$name[1]] : null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置参数 name为数组则为批量设置
|
||||
* @param string|array $name 配置参数名(支持二级配置 .号分割)
|
||||
* @param mixed $value 配置值
|
||||
* @param string $range 作用域
|
||||
* @return mixed
|
||||
*/
|
||||
public static function set($name, $value = null, $range = '')
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
if (!isset(self::$config[$range])) {
|
||||
self::$config[$range] = [];
|
||||
}
|
||||
if (is_string($name)) {
|
||||
if (!strpos($name, '.')) {
|
||||
self::$config[$range][strtolower($name)] = $value;
|
||||
} else {
|
||||
// 二维数组设置和获取支持
|
||||
$name = explode('.', $name, 2);
|
||||
self::$config[$range][strtolower($name[0])][$name[1]] = $value;
|
||||
}
|
||||
return;
|
||||
} elseif (is_array($name)) {
|
||||
// 批量设置
|
||||
if (!empty($value)) {
|
||||
self::$config[$range][$value] = isset(self::$config[$range][$value]) ?
|
||||
array_merge(self::$config[$range][$value], $name) : $name;
|
||||
return self::$config[$range][$value];
|
||||
} else {
|
||||
return self::$config[$range] = array_merge(self::$config[$range], array_change_key_case($name));
|
||||
}
|
||||
} else {
|
||||
// 为空直接返回 已有配置
|
||||
return self::$config[$range];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置配置参数
|
||||
*/
|
||||
public static function reset($range = '')
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
if (true === $range) {
|
||||
self::$config = [];
|
||||
} else {
|
||||
self::$config[$range] = [];
|
||||
}
|
||||
}
|
||||
}
|
719
thinkphp/library/think/Console.php
Executable file
719
thinkphp/library/think/Console.php
Executable file
@ -0,0 +1,719 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | TopThink [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2015 http://www.topthink.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: zhangyajun <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\command\Help as HelpCommand;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument as InputArgument;
|
||||
use think\console\input\Definition as InputDefinition;
|
||||
use think\console\input\Option as InputOption;
|
||||
use think\console\Output;
|
||||
use think\console\output\driver\Buffer;
|
||||
|
||||
class Console
|
||||
{
|
||||
|
||||
private $name;
|
||||
private $version;
|
||||
|
||||
/** @var Command[] */
|
||||
private $commands = [];
|
||||
|
||||
private $wantHelps = false;
|
||||
|
||||
private $catchExceptions = true;
|
||||
private $autoExit = true;
|
||||
private $definition;
|
||||
private $defaultCommand;
|
||||
|
||||
private static $defaultCommands = [
|
||||
"think\\console\\command\\Help",
|
||||
"think\\console\\command\\Lists",
|
||||
"think\\console\\command\\Build",
|
||||
"think\\console\\command\\Clear",
|
||||
"think\\console\\command\\make\\Controller",
|
||||
"think\\console\\command\\make\\Model",
|
||||
"think\\console\\command\\optimize\\Autoload",
|
||||
"think\\console\\command\\optimize\\Config",
|
||||
"think\\console\\command\\optimize\\Route",
|
||||
"think\\console\\command\\optimize\\Schema",
|
||||
];
|
||||
|
||||
public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN')
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->version = $version;
|
||||
|
||||
$this->defaultCommand = 'list';
|
||||
$this->definition = $this->getDefaultInputDefinition();
|
||||
|
||||
foreach ($this->getDefaultCommands() as $command) {
|
||||
$this->add($command);
|
||||
}
|
||||
}
|
||||
|
||||
public static function init($run = true)
|
||||
{
|
||||
static $console;
|
||||
if (!$console) {
|
||||
// 实例化console
|
||||
$console = new self('Think Console', '0.1');
|
||||
// 读取指令集
|
||||
if (is_file(CONF_PATH . 'command' . EXT)) {
|
||||
$commands = include CONF_PATH . 'command' . EXT;
|
||||
if (is_array($commands)) {
|
||||
foreach ($commands as $command) {
|
||||
if (class_exists($command) && is_subclass_of($command, "\\think\\console\\Command")) {
|
||||
// 注册指令
|
||||
$console->add(new $command());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($run) {
|
||||
// 运行
|
||||
return $console->run();
|
||||
} else {
|
||||
return $console;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $command
|
||||
* @param array $parameters
|
||||
* @param string $driver
|
||||
* @return Output|Buffer
|
||||
*/
|
||||
public static function call($command, array $parameters = [], $driver = 'buffer')
|
||||
{
|
||||
$console = self::init(false);
|
||||
|
||||
array_unshift($parameters, $command);
|
||||
|
||||
$input = new Input($parameters);
|
||||
$output = new Output($driver);
|
||||
|
||||
$console->setCatchExceptions(false);
|
||||
$console->find($command)->run($input, $output);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行当前的指令
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
* @api
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$input = new Input();
|
||||
$output = new Output();
|
||||
|
||||
$this->configureIO($input, $output);
|
||||
|
||||
try {
|
||||
$exitCode = $this->doRun($input, $output);
|
||||
} catch (\Exception $e) {
|
||||
if (!$this->catchExceptions) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$output->renderException($e);
|
||||
|
||||
$exitCode = $e->getCode();
|
||||
if (is_numeric($exitCode)) {
|
||||
$exitCode = (int) $exitCode;
|
||||
if (0 === $exitCode) {
|
||||
$exitCode = 1;
|
||||
}
|
||||
} else {
|
||||
$exitCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->autoExit) {
|
||||
if ($exitCode > 255) {
|
||||
$exitCode = 255;
|
||||
}
|
||||
|
||||
exit($exitCode);
|
||||
}
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行指令
|
||||
* @param Input $input
|
||||
* @param Output $output
|
||||
* @return int
|
||||
*/
|
||||
public function doRun(Input $input, Output $output)
|
||||
{
|
||||
if (true === $input->hasParameterOption(['--version', '-V'])) {
|
||||
$output->writeln($this->getLongVersion());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$name = $this->getCommandName($input);
|
||||
|
||||
if (true === $input->hasParameterOption(['--help', '-h'])) {
|
||||
if (!$name) {
|
||||
$name = 'help';
|
||||
$input = new Input(['help']);
|
||||
} else {
|
||||
$this->wantHelps = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$name) {
|
||||
$name = $this->defaultCommand;
|
||||
$input = new Input([$this->defaultCommand]);
|
||||
}
|
||||
|
||||
$command = $this->find($name);
|
||||
|
||||
$exitCode = $this->doRunCommand($command, $input, $output);
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输入参数定义
|
||||
* @param InputDefinition $definition
|
||||
*/
|
||||
public function setDefinition(InputDefinition $definition)
|
||||
{
|
||||
$this->definition = $definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取输入参数定义
|
||||
* @return InputDefinition The InputDefinition instance
|
||||
*/
|
||||
public function getDefinition()
|
||||
{
|
||||
return $this->definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the help message.
|
||||
* @return string A help message.
|
||||
*/
|
||||
public function getHelp()
|
||||
{
|
||||
return $this->getLongVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否捕获异常
|
||||
* @param bool $boolean
|
||||
* @api
|
||||
*/
|
||||
public function setCatchExceptions($boolean)
|
||||
{
|
||||
$this->catchExceptions = (bool) $boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否自动退出
|
||||
* @param bool $boolean
|
||||
* @api
|
||||
*/
|
||||
public function setAutoExit($boolean)
|
||||
{
|
||||
$this->autoExit = (bool) $boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取名称
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置名称
|
||||
* @param string $name
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取版本
|
||||
* @return string
|
||||
* @api
|
||||
*/
|
||||
public function getVersion()
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置版本
|
||||
* @param string $version
|
||||
*/
|
||||
public function setVersion($version)
|
||||
{
|
||||
$this->version = $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取完整的版本号
|
||||
* @return string
|
||||
*/
|
||||
public function getLongVersion()
|
||||
{
|
||||
if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) {
|
||||
return sprintf('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion());
|
||||
}
|
||||
|
||||
return '<info>Console Tool</info>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册一个指令
|
||||
* @param string $name
|
||||
* @return Command
|
||||
*/
|
||||
public function register($name)
|
||||
{
|
||||
return $this->add(new Command($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加指令
|
||||
* @param Command[] $commands
|
||||
*/
|
||||
public function addCommands(array $commands)
|
||||
{
|
||||
foreach ($commands as $command) {
|
||||
$this->add($command);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个指令
|
||||
* @param Command $command
|
||||
* @return Command
|
||||
*/
|
||||
public function add(Command $command)
|
||||
{
|
||||
$command->setConsole($this);
|
||||
|
||||
if (!$command->isEnabled()) {
|
||||
$command->setConsole(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (null === $command->getDefinition()) {
|
||||
throw new \LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command)));
|
||||
}
|
||||
|
||||
$this->commands[$command->getName()] = $command;
|
||||
|
||||
foreach ($command->getAliases() as $alias) {
|
||||
$this->commands[$alias] = $command;
|
||||
}
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指令
|
||||
* @param string $name 指令名称
|
||||
* @return Command
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function get($name)
|
||||
{
|
||||
if (!isset($this->commands[$name])) {
|
||||
throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name));
|
||||
}
|
||||
|
||||
$command = $this->commands[$name];
|
||||
|
||||
if ($this->wantHelps) {
|
||||
$this->wantHelps = false;
|
||||
|
||||
/** @var HelpCommand $helpCommand */
|
||||
$helpCommand = $this->get('help');
|
||||
$helpCommand->setCommand($command);
|
||||
|
||||
return $helpCommand;
|
||||
}
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* 某个指令是否存在
|
||||
* @param string $name 指令名称
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
return isset($this->commands[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的命名空间
|
||||
* @return array
|
||||
*/
|
||||
public function getNamespaces()
|
||||
{
|
||||
$namespaces = [];
|
||||
foreach ($this->commands as $command) {
|
||||
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));
|
||||
|
||||
foreach ($command->getAliases() as $alias) {
|
||||
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias));
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_unique(array_filter($namespaces)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找注册命名空间中的名称或缩写。
|
||||
* @param string $namespace
|
||||
* @return string
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function findNamespace($namespace)
|
||||
{
|
||||
$allNamespaces = $this->getNamespaces();
|
||||
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
|
||||
return preg_quote($matches[1]) . '[^:]*';
|
||||
}, $namespace);
|
||||
$namespaces = preg_grep('{^' . $expr . '}', $allNamespaces);
|
||||
|
||||
if (empty($namespaces)) {
|
||||
$message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
|
||||
|
||||
if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
|
||||
if (1 == count($alternatives)) {
|
||||
$message .= "\n\nDid you mean this?\n ";
|
||||
} else {
|
||||
$message .= "\n\nDid you mean one of these?\n ";
|
||||
}
|
||||
|
||||
$message .= implode("\n ", $alternatives);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException($message);
|
||||
}
|
||||
|
||||
$exact = in_array($namespace, $namespaces, true);
|
||||
if (count($namespaces) > 1 && !$exact) {
|
||||
throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))));
|
||||
}
|
||||
|
||||
return $exact ? $namespace : reset($namespaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找指令
|
||||
* @param string $name 名称或者别名
|
||||
* @return Command
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function find($name)
|
||||
{
|
||||
$allCommands = array_keys($this->commands);
|
||||
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
|
||||
return preg_quote($matches[1]) . '[^:]*';
|
||||
}, $name);
|
||||
$commands = preg_grep('{^' . $expr . '}', $allCommands);
|
||||
|
||||
if (empty($commands) || count(preg_grep('{^' . $expr . '$}', $commands)) < 1) {
|
||||
if (false !== $pos = strrpos($name, ':')) {
|
||||
$this->findNamespace(substr($name, 0, $pos));
|
||||
}
|
||||
|
||||
$message = sprintf('Command "%s" is not defined.', $name);
|
||||
|
||||
if ($alternatives = $this->findAlternatives($name, $allCommands)) {
|
||||
if (1 == count($alternatives)) {
|
||||
$message .= "\n\nDid you mean this?\n ";
|
||||
} else {
|
||||
$message .= "\n\nDid you mean one of these?\n ";
|
||||
}
|
||||
$message .= implode("\n ", $alternatives);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException($message);
|
||||
}
|
||||
|
||||
if (count($commands) > 1) {
|
||||
$commandList = $this->commands;
|
||||
$commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) {
|
||||
$commandName = $commandList[$nameOrAlias]->getName();
|
||||
|
||||
return $commandName === $nameOrAlias || !in_array($commandName, $commands);
|
||||
});
|
||||
}
|
||||
|
||||
$exact = in_array($name, $commands, true);
|
||||
if (count($commands) > 1 && !$exact) {
|
||||
$suggestions = $this->getAbbreviationSuggestions(array_values($commands));
|
||||
|
||||
throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions));
|
||||
}
|
||||
|
||||
return $this->get($exact ? $name : reset($commands));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的指令
|
||||
* @param string $namespace 命名空间
|
||||
* @return Command[]
|
||||
* @api
|
||||
*/
|
||||
public function all($namespace = null)
|
||||
{
|
||||
if (null === $namespace) {
|
||||
return $this->commands;
|
||||
}
|
||||
|
||||
$commands = [];
|
||||
foreach ($this->commands as $name => $command) {
|
||||
if ($this->extractNamespace($name, substr_count($namespace, ':') + 1) === $namespace) {
|
||||
$commands[$name] = $command;
|
||||
}
|
||||
}
|
||||
|
||||
return $commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可能的指令名
|
||||
* @param array $names
|
||||
* @return array
|
||||
*/
|
||||
public static function getAbbreviations($names)
|
||||
{
|
||||
$abbrevs = [];
|
||||
foreach ($names as $name) {
|
||||
for ($len = strlen($name); $len > 0; --$len) {
|
||||
$abbrev = substr($name, 0, $len);
|
||||
$abbrevs[$abbrev][] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
return $abbrevs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置基于用户的参数和选项的输入和输出实例。
|
||||
* @param Input $input 输入实例
|
||||
* @param Output $output 输出实例
|
||||
*/
|
||||
protected function configureIO(Input $input, Output $output)
|
||||
{
|
||||
if (true === $input->hasParameterOption(['--ansi'])) {
|
||||
$output->setDecorated(true);
|
||||
} elseif (true === $input->hasParameterOption(['--no-ansi'])) {
|
||||
$output->setDecorated(false);
|
||||
}
|
||||
|
||||
if (true === $input->hasParameterOption(['--no-interaction', '-n'])) {
|
||||
$input->setInteractive(false);
|
||||
}
|
||||
|
||||
if (true === $input->hasParameterOption(['--quiet', '-q'])) {
|
||||
$output->setVerbosity(Output::VERBOSITY_QUIET);
|
||||
} else {
|
||||
if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) {
|
||||
$output->setVerbosity(Output::VERBOSITY_DEBUG);
|
||||
} elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) {
|
||||
$output->setVerbosity(Output::VERBOSITY_VERY_VERBOSE);
|
||||
} elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) {
|
||||
$output->setVerbosity(Output::VERBOSITY_VERBOSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行指令
|
||||
* @param Command $command 指令实例
|
||||
* @param Input $input 输入实例
|
||||
* @param Output $output 输出实例
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function doRunCommand(Command $command, Input $input, Output $output)
|
||||
{
|
||||
return $command->run($input, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指令的基础名称
|
||||
* @param Input $input
|
||||
* @return string
|
||||
*/
|
||||
protected function getCommandName(Input $input)
|
||||
{
|
||||
return $input->getFirstArgument();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认输入定义
|
||||
* @return InputDefinition
|
||||
*/
|
||||
protected function getDefaultInputDefinition()
|
||||
{
|
||||
return new InputDefinition([
|
||||
new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
|
||||
new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'),
|
||||
new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this console version'),
|
||||
new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
|
||||
new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'),
|
||||
new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'),
|
||||
new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'),
|
||||
new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认命令
|
||||
* @return Command[] An array of default Command instances
|
||||
*/
|
||||
protected function getDefaultCommands()
|
||||
{
|
||||
$defaultCommands = [];
|
||||
|
||||
foreach (self::$defaultCommands as $classname) {
|
||||
if (class_exists($classname) && is_subclass_of($classname, "think\\console\\Command")) {
|
||||
$defaultCommands[] = new $classname();
|
||||
}
|
||||
}
|
||||
|
||||
return $defaultCommands;
|
||||
}
|
||||
|
||||
public static function addDefaultCommands(array $classnames)
|
||||
{
|
||||
self::$defaultCommands = array_merge(self::$defaultCommands, $classnames);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可能的建议
|
||||
* @param array $abbrevs
|
||||
* @return string
|
||||
*/
|
||||
private function getAbbreviationSuggestions($abbrevs)
|
||||
{
|
||||
return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回命名空间部分
|
||||
* @param string $name 指令
|
||||
* @param string $limit 部分的命名空间的最大数量
|
||||
* @return string
|
||||
*/
|
||||
public function extractNamespace($name, $limit = null)
|
||||
{
|
||||
$parts = explode(':', $name);
|
||||
array_pop($parts);
|
||||
|
||||
return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找可替代的建议
|
||||
* @param string $name
|
||||
* @param array|\Traversable $collection
|
||||
* @return array
|
||||
*/
|
||||
private function findAlternatives($name, $collection)
|
||||
{
|
||||
$threshold = 1e3;
|
||||
$alternatives = [];
|
||||
|
||||
$collectionParts = [];
|
||||
foreach ($collection as $item) {
|
||||
$collectionParts[$item] = explode(':', $item);
|
||||
}
|
||||
|
||||
foreach (explode(':', $name) as $i => $subname) {
|
||||
foreach ($collectionParts as $collectionName => $parts) {
|
||||
$exists = isset($alternatives[$collectionName]);
|
||||
if (!isset($parts[$i]) && $exists) {
|
||||
$alternatives[$collectionName] += $threshold;
|
||||
continue;
|
||||
} elseif (!isset($parts[$i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lev = levenshtein($subname, $parts[$i]);
|
||||
if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) {
|
||||
$alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
|
||||
} elseif ($exists) {
|
||||
$alternatives[$collectionName] += $threshold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($collection as $item) {
|
||||
$lev = levenshtein($name, $item);
|
||||
if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
|
||||
$alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
|
||||
}
|
||||
}
|
||||
|
||||
$alternatives = array_filter($alternatives, function ($lev) use ($threshold) {
|
||||
return $lev < 2 * $threshold;
|
||||
});
|
||||
asort($alternatives);
|
||||
|
||||
return array_keys($alternatives);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认的指令
|
||||
* @param string $commandName The Command name
|
||||
*/
|
||||
public function setDefaultCommand($commandName)
|
||||
{
|
||||
$this->defaultCommand = $commandName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回所有的命名空间
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
private function extractAllNamespaces($name)
|
||||
{
|
||||
$parts = explode(':', $name, -1);
|
||||
$namespaces = [];
|
||||
|
||||
foreach ($parts as $part) {
|
||||
if (count($namespaces)) {
|
||||
$namespaces[] = end($namespaces) . ':' . $part;
|
||||
} else {
|
||||
$namespaces[] = $part;
|
||||
}
|
||||
}
|
||||
|
||||
return $namespaces;
|
||||
}
|
||||
|
||||
}
|
213
thinkphp/library/think/Controller.php
Executable file
213
thinkphp/library/think/Controller.php
Executable file
@ -0,0 +1,213 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 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\ValidateException;
|
||||
use traits\controller\Jump;
|
||||
|
||||
Loader::import('controller/Jump', TRAIT_PATH, EXT);
|
||||
|
||||
class Controller
|
||||
{
|
||||
use Jump;
|
||||
|
||||
/**
|
||||
* @var \think\View 视图类实例
|
||||
*/
|
||||
protected $view;
|
||||
/**
|
||||
* @var \think\Request Request实例
|
||||
*/
|
||||
protected $request;
|
||||
// 验证失败是否抛出异常
|
||||
protected $failException = false;
|
||||
// 是否批量验证
|
||||
protected $batchValidate = false;
|
||||
|
||||
/**
|
||||
* 前置操作方法列表
|
||||
* @var array $beforeActionList
|
||||
* @access protected
|
||||
*/
|
||||
protected $beforeActionList = [];
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @param Request $request Request对象
|
||||
* @access public
|
||||
*/
|
||||
public function __construct(Request $request = null)
|
||||
{
|
||||
if (is_null($request)) {
|
||||
$request = Request::instance();
|
||||
}
|
||||
$this->view = View::instance(Config::get('template'), Config::get('view_replace_str'));
|
||||
$this->request = $request;
|
||||
|
||||
// 控制器初始化
|
||||
$this->_initialize();
|
||||
|
||||
// 前置操作方法
|
||||
if ($this->beforeActionList) {
|
||||
foreach ($this->beforeActionList as $method => $options) {
|
||||
is_numeric($method) ?
|
||||
$this->beforeAction($options) :
|
||||
$this->beforeAction($method, $options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
protected function _initialize()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 前置操作
|
||||
* @access protected
|
||||
* @param string $method 前置操作方法名
|
||||
* @param array $options 调用参数 ['only'=>[...]] 或者['except'=>[...]]
|
||||
*/
|
||||
protected function beforeAction($method, $options = [])
|
||||
{
|
||||
if (isset($options['only'])) {
|
||||
if (is_string($options['only'])) {
|
||||
$options['only'] = explode(',', $options['only']);
|
||||
}
|
||||
if (!in_array($this->request->action(), $options['only'])) {
|
||||
return;
|
||||
}
|
||||
} elseif (isset($options['except'])) {
|
||||
if (is_string($options['except'])) {
|
||||
$options['except'] = explode(',', $options['except']);
|
||||
}
|
||||
if (in_array($this->request->action(), $options['except'])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
call_user_func([$this, $method]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载模板输出
|
||||
* @access protected
|
||||
* @param string $template 模板文件名
|
||||
* @param array $vars 模板输出变量
|
||||
* @param array $replace 模板替换
|
||||
* @param array $config 模板参数
|
||||
* @return mixed
|
||||
*/
|
||||
protected function fetch($template = '', $vars = [], $replace = [], $config = [])
|
||||
{
|
||||
return $this->view->fetch($template, $vars, $replace, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染内容输出
|
||||
* @access protected
|
||||
* @param string $content 模板内容
|
||||
* @param array $vars 模板输出变量
|
||||
* @param array $replace 替换内容
|
||||
* @param array $config 模板参数
|
||||
* @return mixed
|
||||
*/
|
||||
protected function display($content = '', $vars = [], $replace = [], $config = [])
|
||||
{
|
||||
return $this->view->display($content, $vars, $replace, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板变量赋值
|
||||
* @access protected
|
||||
* @param mixed $name 要显示的模板变量
|
||||
* @param mixed $value 变量的值
|
||||
* @return void
|
||||
*/
|
||||
protected function assign($name, $value = '')
|
||||
{
|
||||
$this->view->assign($name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化模板引擎
|
||||
* @access protected
|
||||
* @param array|string $engine 引擎参数
|
||||
* @return void
|
||||
*/
|
||||
protected function engine($engine)
|
||||
{
|
||||
$this->view->engine($engine);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置验证失败后是否抛出异常
|
||||
* @access protected
|
||||
* @param bool $fail 是否抛出异常
|
||||
* @return $this
|
||||
*/
|
||||
protected function validateFailException($fail = true)
|
||||
{
|
||||
$this->failException = $fail;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证数据
|
||||
* @access protected
|
||||
* @param array $data 数据
|
||||
* @param string|array $validate 验证器名或者验证规则数组
|
||||
* @param array $message 提示信息
|
||||
* @param bool $batch 是否批量验证
|
||||
* @param mixed $callback 回调方法(闭包)
|
||||
* @return array|string|true
|
||||
* @throws ValidateException
|
||||
*/
|
||||
protected function validate($data, $validate, $message = [], $batch = false, $callback = null)
|
||||
{
|
||||
if (is_array($validate)) {
|
||||
$v = Loader::validate();
|
||||
$v->rule($validate);
|
||||
} else {
|
||||
if (strpos($validate, '.')) {
|
||||
// 支持场景
|
||||
list($validate, $scene) = explode('.', $validate);
|
||||
}
|
||||
$v = Loader::validate($validate);
|
||||
if (!empty($scene)) {
|
||||
$v->scene($scene);
|
||||
}
|
||||
}
|
||||
// 是否批量验证
|
||||
if ($batch || $this->batchValidate) {
|
||||
$v->batch(true);
|
||||
}
|
||||
|
||||
if (is_array($message)) {
|
||||
$v->message($message);
|
||||
}
|
||||
|
||||
if ($callback && is_callable($callback)) {
|
||||
call_user_func_array($callback, [$v, &$data]);
|
||||
}
|
||||
|
||||
if (!$v->check($data)) {
|
||||
if ($this->failException) {
|
||||
throw new ValidateException($v->getError());
|
||||
} else {
|
||||
return $v->getError();
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
224
thinkphp/library/think/Cookie.php
Executable file
224
thinkphp/library/think/Cookie.php
Executable file
@ -0,0 +1,224 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class Cookie
|
||||
{
|
||||
protected static $config = [
|
||||
// cookie 名称前缀
|
||||
'prefix' => '',
|
||||
// cookie 保存时间
|
||||
'expire' => 0,
|
||||
// cookie 保存路径
|
||||
'path' => '/',
|
||||
// cookie 有效域名
|
||||
'domain' => '',
|
||||
// cookie 启用安全传输
|
||||
'secure' => false,
|
||||
// httponly设置
|
||||
'httponly' => '',
|
||||
// 是否使用 setcookie
|
||||
'setcookie' => true,
|
||||
];
|
||||
|
||||
protected static $init;
|
||||
|
||||
/**
|
||||
* Cookie初始化
|
||||
* @param array $config
|
||||
* @return void
|
||||
*/
|
||||
public static function init(array $config = [])
|
||||
{
|
||||
if (empty($config)) {
|
||||
$config = Config::get('cookie');
|
||||
}
|
||||
self::$config = array_merge(self::$config, array_change_key_case($config));
|
||||
if (!empty(self::$config['httponly'])) {
|
||||
ini_set('session.cookie_httponly', 1);
|
||||
}
|
||||
self::$init = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置或者获取cookie作用域(前缀)
|
||||
* @param string $prefix
|
||||
* @return string|void
|
||||
*/
|
||||
public static function prefix($prefix = '')
|
||||
{
|
||||
if (empty($prefix)) {
|
||||
return self::$config['prefix'];
|
||||
}
|
||||
self::$config['prefix'] = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cookie 设置、获取、删除
|
||||
*
|
||||
* @param string $name cookie名称
|
||||
* @param mixed $value cookie值
|
||||
* @param mixed $option 可选参数 可能会是 null|integer|string
|
||||
*
|
||||
* @return mixed
|
||||
* @internal param mixed $options cookie参数
|
||||
*/
|
||||
public static function set($name, $value = '', $option = null)
|
||||
{
|
||||
!isset(self::$init) && self::init();
|
||||
// 参数设置(会覆盖黙认设置)
|
||||
if (!is_null($option)) {
|
||||
if (is_numeric($option)) {
|
||||
$option = ['expire' => $option];
|
||||
} elseif (is_string($option)) {
|
||||
parse_str($option, $option);
|
||||
}
|
||||
$config = array_merge(self::$config, array_change_key_case($option));
|
||||
} else {
|
||||
$config = self::$config;
|
||||
}
|
||||
$name = $config['prefix'] . $name;
|
||||
// 设置cookie
|
||||
if (is_array($value)) {
|
||||
array_walk_recursive($value, 'self::jsonFormatProtect', 'encode');
|
||||
$value = 'think:' . json_encode($value);
|
||||
}
|
||||
$expire = !empty($config['expire']) ? $_SERVER['REQUEST_TIME'] + intval($config['expire']) : 0;
|
||||
if ($config['setcookie']) {
|
||||
setcookie($name, $value, $expire, $config['path'], $config['domain'], $config['secure'], $config['httponly']);
|
||||
}
|
||||
$_COOKIE[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 永久保存Cookie数据
|
||||
* @param string $name cookie名称
|
||||
* @param mixed $value cookie值
|
||||
* @param mixed $option 可选参数 可能会是 null|integer|string
|
||||
* @return void
|
||||
*/
|
||||
public static function forever($name, $value = '', $option = null)
|
||||
{
|
||||
if (is_null($option) || is_numeric($option)) {
|
||||
$option = [];
|
||||
}
|
||||
$option['expire'] = 315360000;
|
||||
self::set($name, $value, $option);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断Cookie数据
|
||||
* @param string $name cookie名称
|
||||
* @param string|null $prefix cookie前缀
|
||||
* @return bool
|
||||
*/
|
||||
public static function has($name, $prefix = null)
|
||||
{
|
||||
!isset(self::$init) && self::init();
|
||||
$prefix = !is_null($prefix) ? $prefix : self::$config['prefix'];
|
||||
$name = $prefix . $name;
|
||||
return isset($_COOKIE[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cookie获取
|
||||
* @param string $name cookie名称
|
||||
* @param string|null $prefix cookie前缀
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($name = '', $prefix = null)
|
||||
{
|
||||
!isset(self::$init) && self::init();
|
||||
$prefix = !is_null($prefix) ? $prefix : self::$config['prefix'];
|
||||
$key = $prefix . $name;
|
||||
|
||||
if ('' == $name) {
|
||||
// 获取全部
|
||||
if ($prefix) {
|
||||
$value = [];
|
||||
foreach ($_COOKIE as $k => $val) {
|
||||
if (0 === strpos($k, $prefix)) {
|
||||
$value[$k] = $val;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$value = $_COOKIE;
|
||||
}
|
||||
} elseif (isset($_COOKIE[$key])) {
|
||||
$value = $_COOKIE[$key];
|
||||
if (0 === strpos($value, 'think:')) {
|
||||
$value = substr($value, 6);
|
||||
$value = json_decode($value, true);
|
||||
array_walk_recursive($value, 'self::jsonFormatProtect', 'decode');
|
||||
}
|
||||
} else {
|
||||
$value = null;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cookie删除
|
||||
* @param string $name cookie名称
|
||||
* @param string|null $prefix cookie前缀
|
||||
* @return mixed
|
||||
*/
|
||||
public static function delete($name, $prefix = null)
|
||||
{
|
||||
!isset(self::$init) && self::init();
|
||||
$config = self::$config;
|
||||
$prefix = !is_null($prefix) ? $prefix : $config['prefix'];
|
||||
$name = $prefix . $name;
|
||||
if ($config['setcookie']) {
|
||||
setcookie($name, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'], $config['domain'], $config['secure'], $config['httponly']);
|
||||
}
|
||||
// 删除指定cookie
|
||||
unset($_COOKIE[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cookie清空
|
||||
* @param string|null $prefix cookie前缀
|
||||
* @return mixed
|
||||
*/
|
||||
public static function clear($prefix = null)
|
||||
{
|
||||
// 清除指定前缀的所有cookie
|
||||
if (empty($_COOKIE)) {
|
||||
return;
|
||||
}
|
||||
!isset(self::$init) && self::init();
|
||||
// 要删除的cookie前缀,不指定则删除config设置的指定前缀
|
||||
$config = self::$config;
|
||||
$prefix = !is_null($prefix) ? $prefix : $config['prefix'];
|
||||
if ($prefix) {
|
||||
// 如果前缀为空字符串将不作处理直接返回
|
||||
foreach ($_COOKIE as $key => $val) {
|
||||
if (0 === strpos($key, $prefix)) {
|
||||
if ($config['setcookie']) {
|
||||
setcookie($key, '', $_SERVER['REQUEST_TIME'] - 3600, $config['path'], $config['domain'], $config['secure'], $config['httponly']);
|
||||
}
|
||||
unset($_COOKIE[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private static function jsonFormatProtect(&$val, $key, $type = 'encode')
|
||||
{
|
||||
if (!empty($val) && true !== $val) {
|
||||
$val = 'decode' == $type ? urldecode($val) : urlencode($val);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
156
thinkphp/library/think/Db.php
Executable file
156
thinkphp/library/think/Db.php
Executable file
@ -0,0 +1,156 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\db\Connection;
|
||||
use think\db\Query;
|
||||
|
||||
/**
|
||||
* Class Db
|
||||
* @package think
|
||||
* @method Query table(string $table) static 指定数据表(含前缀)
|
||||
* @method Query name(string $name) static 指定数据表(不含前缀)
|
||||
* @method Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件
|
||||
* @method Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询
|
||||
* @method Query union(mixed $union, boolean $all = false) static UNION查询
|
||||
* @method Query limit(mixed $offset, integer $length = null) static 查询LIMIT
|
||||
* @method Query order(mixed $field, string $order = null) static 查询ORDER
|
||||
* @method Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存
|
||||
* @method mixed value(string $field) static 获取某个字段的值
|
||||
* @method array column(string $field, string $key = '') static 获取某个列的值
|
||||
* @method Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询
|
||||
* @method mixed find(mixed $data = null) static 查询单个记录
|
||||
* @method mixed select(mixed $data = null) static 查询多个记录
|
||||
* @method integer insert(array $data, boolean $replace = false, boolean $getLastInsID = false, string $sequence = null) static 插入一条记录
|
||||
* @method integer insertGetId(array $data, boolean $replace = false, string $sequence = null) static 插入一条记录并返回自增ID
|
||||
* @method integer insertAll(array $dataSet) static 插入多条记录
|
||||
* @method integer update(array $data) static 更新记录
|
||||
* @method integer delete(mixed $data = null) static 删除记录
|
||||
* @method boolean chunk(integer $count, callable $callback, string $column = null) static 分块获取数据
|
||||
* @method mixed query(string $sql, array $bind = [], boolean $master = false, bool $pdo = false) static SQL查询
|
||||
* @method integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) static SQL执行
|
||||
* @method Paginator paginate(integer $listRows = 15, mixed $simple = null, array $config = []) static 分页查询
|
||||
* @method mixed transaction(callable $callback) static 执行数据库事务
|
||||
* @method void startTrans() static 启动事务
|
||||
* @method void commit() static 用于非自动提交状态下面的查询提交
|
||||
* @method void rollback() static 事务回滚
|
||||
* @method boolean batchQuery(array $sqlArray) static 批处理执行SQL语句
|
||||
* @method string quote(string $str) static SQL指令安全过滤
|
||||
* @method string getLastInsID($sequence = null) static 获取最近插入的ID
|
||||
*/
|
||||
class Db
|
||||
{
|
||||
// 数据库连接实例
|
||||
private static $instance = [];
|
||||
// 查询次数
|
||||
public static $queryTimes = 0;
|
||||
// 执行次数
|
||||
public static $executeTimes = 0;
|
||||
|
||||
/**
|
||||
* 数据库初始化 并取得数据库类实例
|
||||
* @static
|
||||
* @access public
|
||||
* @param mixed $config 连接配置
|
||||
* @param bool|string $name 连接标识 true 强制重新连接
|
||||
* @return Connection
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function connect($config = [], $name = false)
|
||||
{
|
||||
if (false === $name) {
|
||||
$name = md5(serialize($config));
|
||||
}
|
||||
if (true === $name || !isset(self::$instance[$name])) {
|
||||
// 解析连接参数 支持数组和字符串
|
||||
$options = self::parseConfig($config);
|
||||
if (empty($options['type'])) {
|
||||
throw new \InvalidArgumentException('Undefined db type');
|
||||
}
|
||||
$class = false !== strpos($options['type'], '\\') ? $options['type'] : '\\think\\db\\connector\\' . ucwords($options['type']);
|
||||
// 记录初始化信息
|
||||
if (App::$debug) {
|
||||
Log::record('[ DB ] INIT ' . $options['type'], 'info');
|
||||
}
|
||||
if (true === $name) {
|
||||
$name = md5(serialize($config));
|
||||
}
|
||||
self::$instance[$name] = new $class($options);
|
||||
}
|
||||
return self::$instance[$name];
|
||||
}
|
||||
|
||||
public static function clear() {
|
||||
self::$instance = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库连接参数解析
|
||||
* @static
|
||||
* @access private
|
||||
* @param mixed $config
|
||||
* @return array
|
||||
*/
|
||||
private static function parseConfig($config)
|
||||
{
|
||||
if (empty($config)) {
|
||||
$config = Config::get('database');
|
||||
} elseif (is_string($config) && false === strpos($config, '/')) {
|
||||
// 支持读取配置参数
|
||||
$config = Config::get($config);
|
||||
}
|
||||
if (is_string($config)) {
|
||||
return self::parseDsn($config);
|
||||
} else {
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DSN解析
|
||||
* 格式: mysql://username:passwd@localhost:3306/DbName?param1=val1¶m2=val2#utf8
|
||||
* @static
|
||||
* @access private
|
||||
* @param string $dsnStr
|
||||
* @return array
|
||||
*/
|
||||
private static function parseDsn($dsnStr)
|
||||
{
|
||||
$info = parse_url($dsnStr);
|
||||
if (!$info) {
|
||||
return [];
|
||||
}
|
||||
$dsn = [
|
||||
'type' => $info['scheme'],
|
||||
'username' => isset($info['user']) ? $info['user'] : '',
|
||||
'password' => isset($info['pass']) ? $info['pass'] : '',
|
||||
'hostname' => isset($info['host']) ? $info['host'] : '',
|
||||
'hostport' => isset($info['port']) ? $info['port'] : '',
|
||||
'database' => !empty($info['path']) ? ltrim($info['path'], '/') : '',
|
||||
'charset' => isset($info['fragment']) ? $info['fragment'] : 'utf8',
|
||||
];
|
||||
|
||||
if (isset($info['query'])) {
|
||||
parse_str($info['query'], $dsn['params']);
|
||||
} else {
|
||||
$dsn['params'] = [];
|
||||
}
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
// 调用驱动类的方法
|
||||
public static function __callStatic($method, $params)
|
||||
{
|
||||
// 自动初始化数据库
|
||||
return call_user_func_array([self::connect(), $method], $params);
|
||||
}
|
||||
}
|
212
thinkphp/library/think/Debug.php
Executable file
212
thinkphp/library/think/Debug.php
Executable file
@ -0,0 +1,212 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 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\response\Redirect;
|
||||
|
||||
class Debug
|
||||
{
|
||||
// 区间时间信息
|
||||
protected static $info = [];
|
||||
// 区间内存信息
|
||||
protected static $mem = [];
|
||||
|
||||
/**
|
||||
* 记录时间(微秒)和内存使用情况
|
||||
* @param string $name 标记位置
|
||||
* @param mixed $value 标记值 留空则取当前 time 表示仅记录时间 否则同时记录时间和内存
|
||||
* @return mixed
|
||||
*/
|
||||
public static function remark($name, $value = '')
|
||||
{
|
||||
// 记录时间和内存使用
|
||||
self::$info[$name] = is_float($value) ? $value : microtime(true);
|
||||
if ('time' != $value) {
|
||||
self::$mem['mem'][$name] = is_float($value) ? $value : memory_get_usage();
|
||||
self::$mem['peak'][$name] = memory_get_peak_usage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计某个区间的时间(微秒)使用情况 返回值以秒为单位
|
||||
* @param string $start 开始标签
|
||||
* @param string $end 结束标签
|
||||
* @param integer|string $dec 小数位
|
||||
* @return integer
|
||||
*/
|
||||
public static function getRangeTime($start, $end, $dec = 6)
|
||||
{
|
||||
if (!isset(self::$info[$end])) {
|
||||
self::$info[$end] = microtime(true);
|
||||
}
|
||||
return number_format((self::$info[$end] - self::$info[$start]), $dec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计从开始到统计时的时间(微秒)使用情况 返回值以秒为单位
|
||||
* @param integer|string $dec 小数位
|
||||
* @return integer
|
||||
*/
|
||||
public static function getUseTime($dec = 6)
|
||||
{
|
||||
return number_format((microtime(true) - THINK_START_TIME), $dec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前访问的吞吐率情况
|
||||
* @return string
|
||||
*/
|
||||
public static function getThroughputRate()
|
||||
{
|
||||
return number_format(1 / self::getUseTime(), 2) . 'req/s';
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录区间的内存使用情况
|
||||
* @param string $start 开始标签
|
||||
* @param string $end 结束标签
|
||||
* @param integer|string $dec 小数位
|
||||
* @return string
|
||||
*/
|
||||
public static function getRangeMem($start, $end, $dec = 2)
|
||||
{
|
||||
if (!isset(self::$mem['mem'][$end])) {
|
||||
self::$mem['mem'][$end] = memory_get_usage();
|
||||
}
|
||||
$size = self::$mem['mem'][$end] - self::$mem['mem'][$start];
|
||||
$a = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
$pos = 0;
|
||||
while ($size >= 1024) {
|
||||
$size /= 1024;
|
||||
$pos++;
|
||||
}
|
||||
return round($size, $dec) . " " . $a[$pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计从开始到统计时的内存使用情况
|
||||
* @param integer|string $dec 小数位
|
||||
* @return string
|
||||
*/
|
||||
public static function getUseMem($dec = 2)
|
||||
{
|
||||
$size = memory_get_usage() - THINK_START_MEM;
|
||||
$a = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
$pos = 0;
|
||||
while ($size >= 1024) {
|
||||
$size /= 1024;
|
||||
$pos++;
|
||||
}
|
||||
return round($size, $dec) . " " . $a[$pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计区间的内存峰值情况
|
||||
* @param string $start 开始标签
|
||||
* @param string $end 结束标签
|
||||
* @param integer|string $dec 小数位
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getMemPeak($start, $end, $dec = 2)
|
||||
{
|
||||
if (!isset(self::$mem['peak'][$end])) {
|
||||
self::$mem['peak'][$end] = memory_get_peak_usage();
|
||||
}
|
||||
$size = self::$mem['peak'][$end] - self::$mem['peak'][$start];
|
||||
$a = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
$pos = 0;
|
||||
while ($size >= 1024) {
|
||||
$size /= 1024;
|
||||
$pos++;
|
||||
}
|
||||
return round($size, $dec) . " " . $a[$pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件加载信息
|
||||
* @param bool $detail 是否显示详细
|
||||
* @return integer|array
|
||||
*/
|
||||
public static function getFile($detail = false)
|
||||
{
|
||||
if ($detail) {
|
||||
$files = get_included_files();
|
||||
$info = [];
|
||||
foreach ($files as $key => $file) {
|
||||
$info[] = $file . ' ( ' . number_format(filesize($file) / 1024, 2) . ' KB )';
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
return count(get_included_files());
|
||||
}
|
||||
|
||||
/**
|
||||
* 浏览器友好的变量输出
|
||||
* @param mixed $var 变量
|
||||
* @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串
|
||||
* @param string $label 标签 默认为空
|
||||
* @param integer $flags htmlspecialchars flags
|
||||
* @return void|string
|
||||
*/
|
||||
public static function dump($var, $echo = true, $label = null, $flags = ENT_SUBSTITUTE)
|
||||
{
|
||||
$label = (null === $label) ? '' : rtrim($label) . ':';
|
||||
ob_start();
|
||||
var_dump($var);
|
||||
$output = ob_get_clean();
|
||||
$output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output);
|
||||
if (IS_CLI) {
|
||||
$output = PHP_EOL . $label . $output . PHP_EOL;
|
||||
} else {
|
||||
if (!extension_loaded('xdebug')) {
|
||||
$output = htmlspecialchars($output, $flags);
|
||||
}
|
||||
$output = '<pre>' . $label . $output . '</pre>';
|
||||
}
|
||||
if ($echo) {
|
||||
echo($output);
|
||||
return;
|
||||
} else {
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
public static function inject(Response $response, &$content)
|
||||
{
|
||||
$config = Config::get('trace');
|
||||
$type = isset($config['type']) ? $config['type'] : 'Html';
|
||||
$request = Request::instance();
|
||||
$class = false !== strpos($type, '\\') ? $type : '\\think\\debug\\' . ucwords($type);
|
||||
unset($config['type']);
|
||||
if (class_exists($class)) {
|
||||
$trace = new $class($config);
|
||||
} else {
|
||||
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
||||
}
|
||||
|
||||
if ($response instanceof Redirect) {
|
||||
//TODO 记录
|
||||
} else {
|
||||
$output = $trace->output($response, Log::getLog());
|
||||
if (is_string($output)) {
|
||||
// trace调试信息注入
|
||||
$pos = strripos($content, '</body>');
|
||||
if (false !== $pos) {
|
||||
$content = substr($content, 0, $pos) . $output . substr($content, $pos);
|
||||
} else {
|
||||
$content = $content . $output;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
36
thinkphp/library/think/Env.php
Executable file
36
thinkphp/library/think/Env.php
Executable file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class Env
|
||||
{
|
||||
/**
|
||||
* 获取环境变量值
|
||||
* @param string $name 环境变量名(支持二级 .号分割)
|
||||
* @param string $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($name, $default = null)
|
||||
{
|
||||
$result = getenv(ENV_PREFIX . strtoupper(str_replace('.', '_', $name)));
|
||||
if (false !== $result) {
|
||||
if ('false' === $result) {
|
||||
$result = false;
|
||||
} elseif ('true' === $result) {
|
||||
$result = true;
|
||||
}
|
||||
return $result;
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
}
|
120
thinkphp/library/think/Error.php
Executable file
120
thinkphp/library/think/Error.php
Executable file
@ -0,0 +1,120 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\console\Output as ConsoleOutput;
|
||||
use think\exception\ErrorException;
|
||||
use think\exception\Handle;
|
||||
use think\exception\ThrowableError;
|
||||
|
||||
class Error
|
||||
{
|
||||
/**
|
||||
* 注册异常处理
|
||||
* @return void
|
||||
*/
|
||||
public static function register()
|
||||
{
|
||||
error_reporting(E_ALL);
|
||||
set_error_handler([__CLASS__, 'appError']);
|
||||
set_exception_handler([__CLASS__, 'appException']);
|
||||
register_shutdown_function([__CLASS__, 'appShutdown']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception Handler
|
||||
* @param \Exception|\Throwable $e
|
||||
*/
|
||||
public static function appException($e)
|
||||
{
|
||||
if (!$e instanceof \Exception) {
|
||||
$e = new ThrowableError($e);
|
||||
}
|
||||
|
||||
self::getExceptionHandler()->report($e);
|
||||
if (IS_CLI) {
|
||||
self::getExceptionHandler()->renderForConsole(new ConsoleOutput, $e);
|
||||
} else {
|
||||
self::getExceptionHandler()->render($e)->send();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error Handler
|
||||
* @param integer $errno 错误编号
|
||||
* @param integer $errstr 详细错误信息
|
||||
* @param string $errfile 出错的文件
|
||||
* @param integer $errline 出错行号
|
||||
* @param array $errcontext
|
||||
* @throws ErrorException
|
||||
*/
|
||||
public static function appError($errno, $errstr, $errfile = '', $errline = 0, $errcontext = [])
|
||||
{
|
||||
$exception = new ErrorException($errno, $errstr, $errfile, $errline, $errcontext);
|
||||
if (error_reporting() & $errno) {
|
||||
// 将错误信息托管至 think\exception\ErrorException
|
||||
throw $exception;
|
||||
} else {
|
||||
self::getExceptionHandler()->report($exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown Handler
|
||||
*/
|
||||
public static function appShutdown()
|
||||
{
|
||||
if (!is_null($error = error_get_last()) && self::isFatal($error['type'])) {
|
||||
// 将错误信息托管至think\ErrorException
|
||||
$exception = new ErrorException($error['type'], $error['message'], $error['file'], $error['line']);
|
||||
|
||||
self::appException($exception);
|
||||
}
|
||||
|
||||
// 写入日志
|
||||
Log::save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 确定错误类型是否致命
|
||||
*
|
||||
* @param int $type
|
||||
* @return bool
|
||||
*/
|
||||
protected static function isFatal($type)
|
||||
{
|
||||
return in_array($type, [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of the exception handler.
|
||||
*
|
||||
* @return Handle
|
||||
*/
|
||||
public static function getExceptionHandler()
|
||||
{
|
||||
static $handle;
|
||||
if (!$handle) {
|
||||
// 异常处理handle
|
||||
$class = Config::get('exception_handle');
|
||||
if ($class && is_string($class) && class_exists($class) && is_subclass_of($class, "\\think\\exception\\Handle")) {
|
||||
$handle = new $class;
|
||||
} else {
|
||||
$handle = new Handle;
|
||||
if ($class instanceof \Closure) {
|
||||
$handle->setRender($class);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $handle;
|
||||
}
|
||||
}
|
54
thinkphp/library/think/Exception.php
Executable file
54
thinkphp/library/think/Exception.php
Executable file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class Exception extends \Exception
|
||||
{
|
||||
|
||||
/**
|
||||
* 保存异常页面显示的额外Debug数据
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* 设置异常额外的Debug数据
|
||||
* 数据将会显示为下面的格式
|
||||
*
|
||||
* Exception Data
|
||||
* --------------------------------------------------
|
||||
* Label 1
|
||||
* key1 value1
|
||||
* key2 value2
|
||||
* Label 2
|
||||
* key1 value1
|
||||
* key2 value2
|
||||
*
|
||||
* @param string $label 数据分类,用于异常页面显示
|
||||
* @param array $data 需要显示的数据,必须为关联数组
|
||||
*/
|
||||
final protected function setData($label, array $data)
|
||||
{
|
||||
$this->data[$label] = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取异常额外Debug数据
|
||||
* 主要用于输出到异常页面便于调试
|
||||
* @return array 由setData设置的Debug数据
|
||||
*/
|
||||
final public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
}
|
431
thinkphp/library/think/File.php
Executable file
431
thinkphp/library/think/File.php
Executable file
@ -0,0 +1,431 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use SplFileObject;
|
||||
|
||||
class File extends SplFileObject
|
||||
{
|
||||
/**
|
||||
* 错误信息
|
||||
* @var string
|
||||
*/
|
||||
private $error = '';
|
||||
// 当前完整文件名
|
||||
protected $filename;
|
||||
// 上传文件名
|
||||
protected $saveName;
|
||||
// 文件上传命名规则
|
||||
protected $rule = 'date';
|
||||
// 文件上传验证规则
|
||||
protected $validate = [];
|
||||
// 单元测试
|
||||
protected $isTest;
|
||||
// 上传文件信息
|
||||
protected $info;
|
||||
// 文件hash信息
|
||||
protected $hash = [];
|
||||
|
||||
public function __construct($filename, $mode = 'r')
|
||||
{
|
||||
parent::__construct($filename, $mode);
|
||||
$this->filename = $this->getRealPath() ?: $this->getPathname();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否测试
|
||||
* @param bool $test 是否测试
|
||||
* @return $this
|
||||
*/
|
||||
public function isTest($test = false)
|
||||
{
|
||||
$this->isTest = $test;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置上传信息
|
||||
* @param array $info 上传文件信息
|
||||
* @return $this
|
||||
*/
|
||||
public function setUploadInfo($info)
|
||||
{
|
||||
$this->info = $info;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传文件的信息
|
||||
* @param string $name
|
||||
* @return array|string
|
||||
*/
|
||||
public function getInfo($name = '')
|
||||
{
|
||||
return isset($this->info[$name]) ? $this->info[$name] : $this->info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传文件的文件名
|
||||
* @return string
|
||||
*/
|
||||
public function getSaveName()
|
||||
{
|
||||
return $this->saveName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置上传文件的保存文件名
|
||||
* @param string $saveName
|
||||
* @return $this
|
||||
*/
|
||||
public function setSaveName($saveName)
|
||||
{
|
||||
$this->saveName = $saveName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件的哈希散列值
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
public function hash($type = 'sha1')
|
||||
{
|
||||
if (!isset($this->hash[$type])) {
|
||||
$this->hash[$type] = hash_file($type, $this->filename);
|
||||
}
|
||||
return $this->hash[$type];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查目录是否可写
|
||||
* @param string $path 目录
|
||||
* @return boolean
|
||||
*/
|
||||
protected function checkPath($path)
|
||||
{
|
||||
if (is_dir($path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mkdir($path, 0755, true)) {
|
||||
return true;
|
||||
} else {
|
||||
$this->error = ['directory {:path} creation failed', ['path' => $path]];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件类型信息
|
||||
* @return string
|
||||
*/
|
||||
public function getMime()
|
||||
{
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
return finfo_file($finfo, $this->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置文件的命名规则
|
||||
* @param string $rule 文件命名规则
|
||||
* @return $this
|
||||
*/
|
||||
public function rule($rule)
|
||||
{
|
||||
$this->rule = $rule;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置上传文件的验证规则
|
||||
* @param array $rule 验证规则
|
||||
* @return $this
|
||||
*/
|
||||
public function validate($rule = [])
|
||||
{
|
||||
$this->validate = $rule;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否合法的上传文件
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
if ($this->isTest) {
|
||||
return is_file($this->filename);
|
||||
}
|
||||
return is_uploaded_file($this->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测上传文件
|
||||
* @param array $rule 验证规则
|
||||
* @return bool
|
||||
*/
|
||||
public function check($rule = [])
|
||||
{
|
||||
$rule = $rule ?: $this->validate;
|
||||
|
||||
/* 检查文件大小 */
|
||||
if (isset($rule['size']) && !$this->checkSize($rule['size'])) {
|
||||
$this->error = 'filesize not match';
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 检查文件Mime类型 */
|
||||
if (isset($rule['type']) && !$this->checkMime($rule['type'])) {
|
||||
$this->error = 'mimetype to upload is not allowed';
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 检查文件后缀 */
|
||||
if (isset($rule['ext']) && !$this->checkExt($rule['ext'])) {
|
||||
$this->error = 'extensions to upload is not allowed';
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 检查图像文件 */
|
||||
if (!$this->checkImg()) {
|
||||
$this->error = 'illegal image files';
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测上传文件后缀
|
||||
* @param array|string $ext 允许后缀
|
||||
* @return bool
|
||||
*/
|
||||
public function checkExt($ext)
|
||||
{
|
||||
if (is_string($ext)) {
|
||||
$ext = explode(',', $ext);
|
||||
}
|
||||
|
||||
$extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
|
||||
|
||||
if (!in_array($extension, $ext)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测图像文件
|
||||
* @return bool
|
||||
*/
|
||||
public function checkImg()
|
||||
{
|
||||
$extension = strtolower(pathinfo($this->getInfo('name'), PATHINFO_EXTENSION));
|
||||
|
||||
/* 对图像文件进行严格检测 */
|
||||
if (in_array($extension, ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'swf']) && !in_array($this->getImageType($this->filename), [1, 2, 3, 4, 6, 13])) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 判断图像类型
|
||||
protected function getImageType($image)
|
||||
{
|
||||
if (function_exists('exif_imagetype')) {
|
||||
return exif_imagetype($image);
|
||||
} else {
|
||||
try {
|
||||
$info = getimagesize($image);
|
||||
return $info ? $info[2] : false;
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测上传文件大小
|
||||
* @param integer $size 最大大小
|
||||
* @return bool
|
||||
*/
|
||||
public function checkSize($size)
|
||||
{
|
||||
if ($this->getSize() > $size) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测上传文件类型
|
||||
* @param array|string $mime 允许类型
|
||||
* @return bool
|
||||
*/
|
||||
public function checkMime($mime)
|
||||
{
|
||||
if (is_string($mime)) {
|
||||
$mime = explode(',', $mime);
|
||||
}
|
||||
|
||||
if (!in_array(strtolower($this->getMime()), $mime)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动文件
|
||||
* @param string $path 保存路径
|
||||
* @param string|bool $savename 保存的文件名 默认自动生成
|
||||
* @param boolean $replace 同名文件是否覆盖
|
||||
* @return false|File false-失败 否则返回File实例
|
||||
*/
|
||||
public function move($path, $savename = true, $replace = true)
|
||||
{
|
||||
// 文件上传失败,捕获错误代码
|
||||
if (!empty($this->info['error'])) {
|
||||
$this->error($this->info['error']);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检测合法性
|
||||
if (!$this->isValid()) {
|
||||
$this->error = 'upload illegal files';
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证上传
|
||||
if (!$this->check()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = rtrim($path, DS) . DS;
|
||||
// 文件保存命名规则
|
||||
$saveName = $this->buildSaveName($savename);
|
||||
$filename = $path . $saveName;
|
||||
|
||||
// 检测目录
|
||||
if (false === $this->checkPath(dirname($filename))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 不覆盖同名文件 */
|
||||
if (!$replace && is_file($filename)) {
|
||||
$this->error = ['has the same filename: {:filename}', ['filename' => $filename]];
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 移动文件 */
|
||||
if ($this->isTest) {
|
||||
rename($this->filename, $filename);
|
||||
} elseif (!move_uploaded_file($this->filename, $filename)) {
|
||||
$this->error = 'upload write error';
|
||||
return false;
|
||||
}
|
||||
|
||||
// 返回 File对象实例
|
||||
$file = new self($filename);
|
||||
$file->setSaveName($saveName);
|
||||
$file->setUploadInfo($this->info);
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取保存文件名
|
||||
* @param string|bool $savename 保存的文件名 默认自动生成
|
||||
* @return string
|
||||
*/
|
||||
protected function buildSaveName($savename)
|
||||
{
|
||||
if (true === $savename) {
|
||||
// 自动生成文件名
|
||||
if ($this->rule instanceof \Closure) {
|
||||
$savename = call_user_func_array($this->rule, [$this]);
|
||||
} else {
|
||||
switch ($this->rule) {
|
||||
case 'date':
|
||||
$savename = date('Ymd') . DS . md5(microtime(true));
|
||||
break;
|
||||
default:
|
||||
if (in_array($this->rule, hash_algos())) {
|
||||
$hash = $this->hash($this->rule);
|
||||
$savename = substr($hash, 0, 2) . DS . substr($hash, 2);
|
||||
} elseif (is_callable($this->rule)) {
|
||||
$savename = call_user_func($this->rule);
|
||||
} else {
|
||||
$savename = date('Ymd') . DS . md5(microtime(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ('' === $savename || false === $savename) {
|
||||
$savename = $this->getInfo('name');
|
||||
}
|
||||
|
||||
if (!strpos($savename, '.')) {
|
||||
$savename .= '.' . pathinfo($this->getInfo('name'), PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
return $savename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误代码信息
|
||||
* @param int $errorNo 错误号
|
||||
*/
|
||||
private function error($errorNo)
|
||||
{
|
||||
switch ($errorNo) {
|
||||
case 1:
|
||||
case 2:
|
||||
$this->error = 'upload File size exceeds the maximum value';
|
||||
break;
|
||||
case 3:
|
||||
$this->error = 'only the portion of file is uploaded';
|
||||
break;
|
||||
case 4:
|
||||
$this->error = 'no file to uploaded';
|
||||
break;
|
||||
case 6:
|
||||
$this->error = 'upload temp dir not found';
|
||||
break;
|
||||
case 7:
|
||||
$this->error = 'file write error';
|
||||
break;
|
||||
default:
|
||||
$this->error = 'unknown upload error';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误信息(支持多语言)
|
||||
* @return string
|
||||
*/
|
||||
public function getError()
|
||||
{
|
||||
if (is_array($this->error)) {
|
||||
list($msg, $vars) = $this->error;
|
||||
} else {
|
||||
$msg = $this->error;
|
||||
$vars = [];
|
||||
}
|
||||
|
||||
return Lang::has($msg) ? Lang::get($msg, $vars) : $msg;
|
||||
}
|
||||
|
||||
public function __call($method, $args)
|
||||
{
|
||||
return $this->hash($method);
|
||||
}
|
||||
}
|
136
thinkphp/library/think/Hook.php
Executable file
136
thinkphp/library/think/Hook.php
Executable file
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class Hook
|
||||
{
|
||||
|
||||
private static $tags = [];
|
||||
|
||||
/**
|
||||
* 动态添加行为扩展到某个标签
|
||||
* @param string $tag 标签名称
|
||||
* @param mixed $behavior 行为名称
|
||||
* @param bool $first 是否放到开头执行
|
||||
* @return void
|
||||
*/
|
||||
public static function add($tag, $behavior, $first = false)
|
||||
{
|
||||
isset(self::$tags[$tag]) || self::$tags[$tag] = [];
|
||||
if (is_array($behavior) && !is_callable($behavior)) {
|
||||
if (!array_key_exists('_overlay', $behavior) || !$behavior['_overlay']) {
|
||||
unset($behavior['_overlay']);
|
||||
self::$tags[$tag] = array_merge(self::$tags[$tag], $behavior);
|
||||
} else {
|
||||
unset($behavior['_overlay']);
|
||||
self::$tags[$tag] = $behavior;
|
||||
}
|
||||
} elseif ($first) {
|
||||
array_unshift(self::$tags[$tag], $behavior);
|
||||
} else {
|
||||
self::$tags[$tag][] = $behavior;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量导入插件
|
||||
* @param array $tags 插件信息
|
||||
* @param boolean $recursive 是否递归合并
|
||||
*/
|
||||
public static function import(array $tags, $recursive = true)
|
||||
{
|
||||
if ($recursive) {
|
||||
foreach ($tags as $tag => $behavior) {
|
||||
self::add($tag, $behavior);
|
||||
}
|
||||
} else {
|
||||
self::$tags = $tags + self::$tags;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件信息
|
||||
* @param string $tag 插件位置 留空获取全部
|
||||
* @return array
|
||||
*/
|
||||
public static function get($tag = '')
|
||||
{
|
||||
if (empty($tag)) {
|
||||
//获取全部的插件信息
|
||||
return self::$tags;
|
||||
} else {
|
||||
return array_key_exists($tag, self::$tags) ? self::$tags[$tag] : [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听标签的行为
|
||||
* @param string $tag 标签名称
|
||||
* @param mixed $params 传入参数
|
||||
* @param mixed $extra 额外参数
|
||||
* @param bool $once 只获取一个有效返回值
|
||||
* @return mixed
|
||||
*/
|
||||
public static function listen($tag, &$params = null, $extra = null, $once = false)
|
||||
{
|
||||
$results = [];
|
||||
$tags = static::get($tag);
|
||||
foreach ($tags as $key => $name) {
|
||||
$results[$key] = self::exec($name, $tag, $params, $extra);
|
||||
if (false === $results[$key]) {
|
||||
// 如果返回false 则中断行为执行
|
||||
break;
|
||||
} elseif (!is_null($results[$key]) && $once) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $once ? end($results) : $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行某个行为
|
||||
* @param mixed $class 要执行的行为
|
||||
* @param string $tag 方法名(标签名)
|
||||
* @param Mixed $params 传人的参数
|
||||
* @param mixed $extra 额外参数
|
||||
* @return mixed
|
||||
*/
|
||||
public static function exec($class, $tag = '', &$params = null, $extra = null)
|
||||
{
|
||||
App::$debug && Debug::remark('behavior_start', 'time');
|
||||
$method = Loader::parseName($tag, 1, false);
|
||||
if ($class instanceof \Closure) {
|
||||
$result = call_user_func_array($class, [ & $params, $extra]);
|
||||
$class = 'Closure';
|
||||
} elseif (is_array($class)) {
|
||||
list($class, $method) = $class;
|
||||
|
||||
$result = (new $class())->$method($params, $extra);
|
||||
$class = $class . '->' . $method;
|
||||
} elseif (is_object($class)) {
|
||||
$result = $class->$method($params, $extra);
|
||||
$class = get_class($class);
|
||||
} elseif (strpos($class, '::')) {
|
||||
$result = call_user_func_array($class, [ & $params, $extra]);
|
||||
} else {
|
||||
$obj = new $class();
|
||||
$method = ($tag && is_callable([$obj, $method])) ? $method : 'run';
|
||||
$result = $obj->$method($params, $extra);
|
||||
}
|
||||
if (App::$debug) {
|
||||
Debug::remark('behavior_end', 'time');
|
||||
Log::record('[ BEHAVIOR ] Run ' . $class . ' @' . $tag . ' [ RunTime:' . Debug::getRangeTime('behavior_start', 'behavior_end') . 's ]', 'info');
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
223
thinkphp/library/think/Lang.php
Executable file
223
thinkphp/library/think/Lang.php
Executable file
@ -0,0 +1,223 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class Lang
|
||||
{
|
||||
// 语言数据
|
||||
private static $lang = [];
|
||||
// 语言作用域
|
||||
private static $range = 'zh-cn';
|
||||
// 语言自动侦测的变量
|
||||
protected static $langDetectVar = 'lang';
|
||||
// 语言Cookie变量
|
||||
protected static $langCookieVar = 'think_var';
|
||||
// 语言Cookie的过期时间
|
||||
protected static $langCookieExpire = 3600;
|
||||
// 允许语言列表
|
||||
protected static $allowLangList = [];
|
||||
// Accept-Language转义为对应语言包名称 系统默认配置
|
||||
protected static $acceptLanguage = [
|
||||
'zh-hans-cn' => 'zh-cn',
|
||||
];
|
||||
|
||||
// 设定当前的语言
|
||||
public static function range($range = '')
|
||||
{
|
||||
if ('' == $range) {
|
||||
return self::$range;
|
||||
} else {
|
||||
self::$range = $range;
|
||||
}
|
||||
return self::$range;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置语言定义(不区分大小写)
|
||||
* @param string|array $name 语言变量
|
||||
* @param string $value 语言值
|
||||
* @param string $range 语言作用域
|
||||
* @return mixed
|
||||
*/
|
||||
public static function set($name, $value = null, $range = '')
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
// 批量定义
|
||||
if (!isset(self::$lang[$range])) {
|
||||
self::$lang[$range] = [];
|
||||
}
|
||||
if (is_array($name)) {
|
||||
return self::$lang[$range] = array_change_key_case($name) + self::$lang[$range];
|
||||
} else {
|
||||
return self::$lang[$range][strtolower($name)] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载语言定义(不区分大小写)
|
||||
* @param array|string $file 语言文件
|
||||
* @param string $range 语言作用域
|
||||
* @return mixed
|
||||
*/
|
||||
public static function load($file, $range = '')
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
if (!isset(self::$lang[$range])) {
|
||||
self::$lang[$range] = [];
|
||||
}
|
||||
// 批量定义
|
||||
if (is_string($file)) {
|
||||
$file = [$file];
|
||||
}
|
||||
$lang = [];
|
||||
foreach ($file as $_file) {
|
||||
if (is_file($_file)) {
|
||||
// 记录加载信息
|
||||
App::$debug && Log::record('[ LANG ] ' . $_file, 'info');
|
||||
$_lang = include $_file;
|
||||
if (is_array($_lang)) {
|
||||
$lang = array_change_key_case($_lang) + $lang;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($lang)) {
|
||||
self::$lang[$range] = $lang + self::$lang[$range];
|
||||
}
|
||||
return self::$lang[$range];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取语言定义(不区分大小写)
|
||||
* @param string|null $name 语言变量
|
||||
* @param string $range 语言作用域
|
||||
* @return mixed
|
||||
*/
|
||||
public static function has($name, $range = '')
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
return isset(self::$lang[$range][strtolower($name)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取语言定义(不区分大小写)
|
||||
* @param string|null $name 语言变量
|
||||
* @param array $vars 变量替换
|
||||
* @param string $range 语言作用域
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($name = null, $vars = [], $range = '')
|
||||
{
|
||||
$range = $range ?: self::$range;
|
||||
// 空参数返回所有定义
|
||||
if (empty($name)) {
|
||||
return self::$lang[$range];
|
||||
}
|
||||
$key = strtolower($name);
|
||||
$value = isset(self::$lang[$range][$key]) ? self::$lang[$range][$key] : $name;
|
||||
|
||||
// 变量解析
|
||||
if (!empty($vars) && is_array($vars)) {
|
||||
/**
|
||||
* Notes:
|
||||
* 为了检测的方便,数字索引的判断仅仅是参数数组的第一个元素的key为数字0
|
||||
* 数字索引采用的是系统的 sprintf 函数替换,用法请参考 sprintf 函数
|
||||
*/
|
||||
if (key($vars) === 0) {
|
||||
// 数字索引解析
|
||||
array_unshift($vars, $value);
|
||||
$value = call_user_func_array('sprintf', $vars);
|
||||
} else {
|
||||
// 关联索引解析
|
||||
$replace = array_keys($vars);
|
||||
foreach ($replace as &$v) {
|
||||
$v = "{:{$v}}";
|
||||
}
|
||||
$value = str_replace($replace, $vars, $value);
|
||||
}
|
||||
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动侦测设置获取语言选择
|
||||
* @return string
|
||||
*/
|
||||
public static function detect()
|
||||
{
|
||||
// 自动侦测设置获取语言选择
|
||||
$langSet = '';
|
||||
|
||||
if (isset($_GET[self::$langDetectVar])) {
|
||||
// url中设置了语言变量
|
||||
$langSet = strtolower($_GET[self::$langDetectVar]);
|
||||
} elseif (isset($_COOKIE[self::$langCookieVar])) {
|
||||
// Cookie中设置了语言变量
|
||||
$langSet = strtolower($_COOKIE[self::$langCookieVar]);
|
||||
} elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
||||
// 自动侦测浏览器语言
|
||||
preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches);
|
||||
$langSet = strtolower($matches[1]);
|
||||
$acceptLangs = Config::get('header_accept_lang');
|
||||
if (isset($acceptLangs[$langSet])) {
|
||||
$langSet = $acceptLangs[$langSet];
|
||||
} elseif (isset(self::$acceptLanguage[$langSet])) {
|
||||
$langSet = self::$acceptLanguage[$langSet];
|
||||
}
|
||||
}
|
||||
if (empty(self::$allowLangList) || in_array($langSet, self::$allowLangList)) {
|
||||
// 合法的语言
|
||||
self::$range = $langSet ?: self::$range;
|
||||
}
|
||||
return self::$range;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置语言自动侦测的变量
|
||||
* @param string $var 变量名称
|
||||
* @return void
|
||||
*/
|
||||
public static function setLangDetectVar($var)
|
||||
{
|
||||
self::$langDetectVar = $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置语言的cookie保存变量
|
||||
* @param string $var 变量名称
|
||||
* @return void
|
||||
*/
|
||||
public static function setLangCookieVar($var)
|
||||
{
|
||||
self::$langCookieVar = $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置语言的cookie的过期时间
|
||||
* @param string $expire 过期时间
|
||||
* @return void
|
||||
*/
|
||||
public static function setLangCookieExpire($expire)
|
||||
{
|
||||
self::$langCookieExpire = $expire;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置允许的语言列表
|
||||
* @param array $list 语言列表
|
||||
* @return void
|
||||
*/
|
||||
public static function setAllowLangList($list)
|
||||
{
|
||||
self::$allowLangList = $list;
|
||||
}
|
||||
}
|
570
thinkphp/library/think/Loader.php
Executable file
570
thinkphp/library/think/Loader.php
Executable file
@ -0,0 +1,570 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 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;
|
||||
|
||||
class Loader
|
||||
{
|
||||
protected static $instance = [];
|
||||
// 类名映射
|
||||
protected static $map = [];
|
||||
|
||||
// 命名空间别名
|
||||
protected static $namespaceAlias = [];
|
||||
|
||||
// PSR-4
|
||||
private static $prefixLengthsPsr4 = [];
|
||||
private static $prefixDirsPsr4 = [];
|
||||
private static $fallbackDirsPsr4 = [];
|
||||
|
||||
// PSR-0
|
||||
private static $prefixesPsr0 = [];
|
||||
private static $fallbackDirsPsr0 = [];
|
||||
|
||||
// 自动加载的文件
|
||||
private static $autoloadFiles = [];
|
||||
|
||||
// 自动加载
|
||||
public static function autoload($class)
|
||||
{
|
||||
// 检测命名空间别名
|
||||
if (!empty(self::$namespaceAlias)) {
|
||||
$namespace = dirname($class);
|
||||
if (isset(self::$namespaceAlias[$namespace])) {
|
||||
$original = self::$namespaceAlias[$namespace] . '\\' . basename($class);
|
||||
if (class_exists($original)) {
|
||||
return class_alias($original, $class, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($file = self::findFile($class)) {
|
||||
|
||||
// Win环境严格区分大小写
|
||||
if (IS_WIN && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
__include_file($file);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找文件
|
||||
* @param $class
|
||||
* @return bool
|
||||
*/
|
||||
private static function findFile($class)
|
||||
{
|
||||
if (!empty(self::$map[$class])) {
|
||||
// 类库映射
|
||||
return self::$map[$class];
|
||||
}
|
||||
|
||||
// 查找 PSR-4
|
||||
$logicalPathPsr4 = strtr($class, '\\', DS) . EXT;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset(self::$prefixLengthsPsr4[$first])) {
|
||||
foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach (self::$prefixDirsPsr4[$prefix] as $dir) {
|
||||
if (is_file($file = $dir . DS . substr($logicalPathPsr4, $length))) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查找 PSR-4 fallback dirs
|
||||
foreach (self::$fallbackDirsPsr4 as $dir) {
|
||||
if (is_file($file = $dir . DS . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// 查找 PSR-0
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DS);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DS) . EXT;
|
||||
}
|
||||
|
||||
if (isset(self::$prefixesPsr0[$first])) {
|
||||
foreach (self::$prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (is_file($file = $dir . DS . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查找 PSR-0 fallback dirs
|
||||
foreach (self::$fallbackDirsPsr0 as $dir) {
|
||||
if (is_file($file = $dir . DS . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
return self::$map[$class] = false;
|
||||
}
|
||||
|
||||
// 注册classmap
|
||||
public static function addClassMap($class, $map = '')
|
||||
{
|
||||
if (is_array($class)) {
|
||||
self::$map = array_merge(self::$map, $class);
|
||||
} else {
|
||||
self::$map[$class] = $map;
|
||||
}
|
||||
}
|
||||
|
||||
// 注册命名空间
|
||||
public static function addNamespace($namespace, $path = '')
|
||||
{
|
||||
if (is_array($namespace)) {
|
||||
foreach ($namespace as $prefix => $paths) {
|
||||
self::addPsr4($prefix . '\\', rtrim($paths, DS), true);
|
||||
}
|
||||
} else {
|
||||
self::addPsr4($namespace . '\\', rtrim($path, DS), true);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加Ps0空间
|
||||
private static function addPsr0($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
self::$fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
self::$fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
self::$fallbackDirsPsr0 = array_merge(
|
||||
self::$fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset(self::$prefixesPsr0[$first][$prefix])) {
|
||||
self::$prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
self::$prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
self::$prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
self::$prefixesPsr0[$first][$prefix] = array_merge(
|
||||
self::$prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加Psr4空间
|
||||
private static function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
self::$fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
self::$fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
self::$fallbackDirsPsr4 = array_merge(
|
||||
self::$fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset(self::$prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
self::$prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
self::$prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
self::$prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
self::$prefixDirsPsr4[$prefix] = array_merge(
|
||||
self::$prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册命名空间别名
|
||||
public static function addNamespaceAlias($namespace, $original = '')
|
||||
{
|
||||
if (is_array($namespace)) {
|
||||
self::$namespaceAlias = array_merge(self::$namespaceAlias, $namespace);
|
||||
} else {
|
||||
self::$namespaceAlias[$namespace] = $original;
|
||||
}
|
||||
}
|
||||
|
||||
// 注册自动加载机制
|
||||
public static function register($autoload = '')
|
||||
{
|
||||
// 注册系统自动加载
|
||||
spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
|
||||
// 注册命名空间定义
|
||||
self::addNamespace([
|
||||
'think' => LIB_PATH . 'think' . DS,
|
||||
'behavior' => LIB_PATH . 'behavior' . DS,
|
||||
'traits' => LIB_PATH . 'traits' . DS,
|
||||
]);
|
||||
// 加载类库映射文件
|
||||
if (is_file(RUNTIME_PATH . 'classmap' . EXT)) {
|
||||
self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT));
|
||||
}
|
||||
|
||||
// Composer自动加载支持
|
||||
if (is_dir(VENDOR_PATH . 'composer')) {
|
||||
self::registerComposerLoader();
|
||||
}
|
||||
|
||||
// 自动加载extend目录
|
||||
self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS);
|
||||
}
|
||||
|
||||
// 注册composer自动加载
|
||||
private static function registerComposerLoader()
|
||||
{
|
||||
if (is_file(VENDOR_PATH . 'composer/autoload_namespaces.php')) {
|
||||
$map = require VENDOR_PATH . 'composer/autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
self::addPsr0($namespace, $path);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_file(VENDOR_PATH . 'composer/autoload_psr4.php')) {
|
||||
$map = require VENDOR_PATH . 'composer/autoload_psr4.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
self::addPsr4($namespace, $path);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_file(VENDOR_PATH . 'composer/autoload_classmap.php')) {
|
||||
$classMap = require VENDOR_PATH . 'composer/autoload_classmap.php';
|
||||
if ($classMap) {
|
||||
self::addClassMap($classMap);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) {
|
||||
$includeFiles = require VENDOR_PATH . 'composer/autoload_files.php';
|
||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||
if (empty(self::$autoloadFiles[$fileIdentifier])) {
|
||||
__require_file($file);
|
||||
self::$autoloadFiles[$fileIdentifier] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入所需的类库 同java的Import 本函数有缓存功能
|
||||
* @param string $class 类库命名空间字符串
|
||||
* @param string $baseUrl 起始路径
|
||||
* @param string $ext 导入的文件扩展名
|
||||
* @return boolean
|
||||
*/
|
||||
public static function import($class, $baseUrl = '', $ext = EXT)
|
||||
{
|
||||
static $_file = [];
|
||||
$key = $class . $baseUrl;
|
||||
$class = str_replace(['.', '#'], [DS, '.'], $class);
|
||||
if (isset($_file[$key])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (empty($baseUrl)) {
|
||||
list($name, $class) = explode(DS, $class, 2);
|
||||
|
||||
if (isset(self::$prefixDirsPsr4[$name . '\\'])) {
|
||||
// 注册的命名空间
|
||||
$baseUrl = self::$prefixDirsPsr4[$name . '\\'];
|
||||
} elseif ('@' == $name) {
|
||||
//加载当前模块应用类库
|
||||
$baseUrl = App::$modulePath;
|
||||
} elseif (is_dir(EXTEND_PATH . $name)) {
|
||||
$baseUrl = EXTEND_PATH . $name . DS;
|
||||
} else {
|
||||
// 加载其它模块的类库
|
||||
$baseUrl = APP_PATH . $name . DS;
|
||||
}
|
||||
} elseif (substr($baseUrl, -1) != DS) {
|
||||
$baseUrl .= DS;
|
||||
}
|
||||
// 如果类存在 则导入类库文件
|
||||
if (is_array($baseUrl)) {
|
||||
foreach ($baseUrl as $path) {
|
||||
$filename = $path . DS . $class . $ext;
|
||||
if (is_file($filename)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$filename = $baseUrl . $class . $ext;
|
||||
}
|
||||
|
||||
if (!empty($filename) && is_file($filename)) {
|
||||
// 开启调试模式Win环境严格区分大小写
|
||||
if (IS_WIN && pathinfo($filename, PATHINFO_FILENAME) != pathinfo(realpath($filename), PATHINFO_FILENAME)) {
|
||||
return false;
|
||||
}
|
||||
__include_file($filename);
|
||||
$_file[$key] = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例化(分层)模型
|
||||
* @param string $name Model名称
|
||||
* @param string $layer 业务层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @param string $common 公共模块名
|
||||
* @return object
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public static function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common')
|
||||
{
|
||||
$guid = $name . $layer;
|
||||
if (isset(self::$instance[$guid])) {
|
||||
return self::$instance[$guid];
|
||||
}
|
||||
list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
|
||||
if (class_exists($class)) {
|
||||
$model = new $class();
|
||||
} else {
|
||||
$class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);
|
||||
if (class_exists($class)) {
|
||||
$model = new $class();
|
||||
} else {
|
||||
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
||||
}
|
||||
}
|
||||
self::$instance[$guid] = $model;
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例化(分层)控制器 格式:[模块名/]控制器名
|
||||
* @param string $name 资源地址
|
||||
* @param string $layer 控制层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @param string $empty 空控制器名称
|
||||
* @return object
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public static function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '')
|
||||
{
|
||||
list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
|
||||
if (class_exists($class)) {
|
||||
return App::invokeClass($class);
|
||||
} elseif ($empty && class_exists($emptyClass = self::parseClass($module, $layer, $empty, $appendSuffix))) {
|
||||
return new $emptyClass(Request::instance());
|
||||
} else {
|
||||
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例化验证类 格式:[模块名/]验证器名
|
||||
* modify by GZShangTao
|
||||
* @param string $name 资源地址
|
||||
* @param string $layer 验证层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @param string $common 公共模块名
|
||||
* @return object|false
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public static function validate($name = '', $layer = 'validate', $appendSuffix = false, $common = 'common',$otherNamespace = '')
|
||||
{
|
||||
$name = $name ?: Config::get('default_validate');
|
||||
if (empty($name)) {
|
||||
return new Validate;
|
||||
}
|
||||
$guid = $name . $layer;
|
||||
if (isset(self::$instance[$guid])) {
|
||||
return self::$instance[$guid];
|
||||
}
|
||||
list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix,$otherNamespace);
|
||||
if (class_exists($class)) {
|
||||
$validate = new $class;
|
||||
} else {
|
||||
$class = str_replace('\\' . $module . '\\', '\\' . $common . '\\', $class);
|
||||
if (class_exists($class)) {
|
||||
$validate = new $class;
|
||||
} else {
|
||||
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
||||
}
|
||||
}
|
||||
self::$instance[$guid] = $validate;
|
||||
return $validate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析模块和类名
|
||||
* modify by GZShangTao
|
||||
* @param string $name 资源地址
|
||||
* @param string $layer 验证层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @return array
|
||||
*/
|
||||
protected static function getModuleAndClass($name, $layer, $appendSuffix,$otherNamespace = '')
|
||||
{
|
||||
if (false !== strpos($name, '\\')) {
|
||||
$module = Request::instance()->module();
|
||||
$class = $name;
|
||||
} else {
|
||||
if (strpos($name, '/')) {
|
||||
list($module, $name) = explode('/', $name, 2);
|
||||
} else {
|
||||
$module = Request::instance()->module();
|
||||
}
|
||||
$class = self::parseClass($module, $layer, $name, $appendSuffix,$otherNamespace);
|
||||
}
|
||||
return [$module, $class];
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库初始化 并取得数据库类实例
|
||||
* @param mixed $config 数据库配置
|
||||
* @param bool|string $name 连接标识 true 强制重新连接
|
||||
* @return \think\db\Connection
|
||||
*/
|
||||
public static function db($config = [], $name = false)
|
||||
{
|
||||
return Db::connect($config, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 远程调用模块的操作方法 参数格式 [模块/控制器/]操作
|
||||
* @param string $url 调用地址
|
||||
* @param string|array $vars 调用参数 支持字符串和数组
|
||||
* @param string $layer 要调用的控制层名称
|
||||
* @param bool $appendSuffix 是否添加类名后缀
|
||||
* @return mixed
|
||||
*/
|
||||
public static function action($url, $vars = [], $layer = 'controller', $appendSuffix = false)
|
||||
{
|
||||
$info = pathinfo($url);
|
||||
$action = $info['basename'];
|
||||
$module = '.' != $info['dirname'] ? $info['dirname'] : Request::instance()->controller();
|
||||
$class = self::controller($module, $layer, $appendSuffix);
|
||||
if ($class) {
|
||||
if (is_scalar($vars)) {
|
||||
if (strpos($vars, '=')) {
|
||||
parse_str($vars, $vars);
|
||||
} else {
|
||||
$vars = [$vars];
|
||||
}
|
||||
}
|
||||
return App::invokeMethod([$class, $action . Config::get('action_suffix')], $vars);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串命名风格转换
|
||||
* type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
|
||||
* @param string $name 字符串
|
||||
* @param integer $type 转换类型
|
||||
* @param bool $ucfirst 首字母是否大写(驼峰规则)
|
||||
* @return string
|
||||
*/
|
||||
public static function parseName($name, $type = 0, $ucfirst = true)
|
||||
{
|
||||
if ($type) {
|
||||
$name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
|
||||
return strtoupper($match[1]);
|
||||
}, $name);
|
||||
return $ucfirst ? ucfirst($name) : lcfirst($name);
|
||||
} else {
|
||||
return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析应用类的类名
|
||||
* modify by GZShangTao
|
||||
* @param string $module 模块名
|
||||
* @param string $layer 层名 controller model ...
|
||||
* @param string $name 类名
|
||||
* @param bool $appendSuffix
|
||||
* @return string
|
||||
*/
|
||||
public static function parseClass($module, $layer, $name, $appendSuffix = false,$otherNamespace = '')
|
||||
{
|
||||
$name = str_replace(['/', '.'], '\\', $name);
|
||||
$array = explode('\\', $name);
|
||||
$class = self::parseName(array_pop($array), 1) . (App::$suffix || $appendSuffix ? ucfirst($layer) : '');
|
||||
$path = $array ? implode('\\', $array) . '\\' : '';
|
||||
if($otherNamespace!=''){
|
||||
return $otherNamespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $path . $class;
|
||||
}else{
|
||||
return App::$namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $path . $class;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化类的实例
|
||||
* @return void
|
||||
*/
|
||||
public static function clearInstance()
|
||||
{
|
||||
self::$instance = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 作用范围隔离
|
||||
*
|
||||
* @param $file
|
||||
* @return mixed
|
||||
*/
|
||||
function __include_file($file)
|
||||
{
|
||||
return include $file;
|
||||
}
|
||||
|
||||
function __require_file($file)
|
||||
{
|
||||
return require $file;
|
||||
}
|
213
thinkphp/library/think/Log.php
Executable file
213
thinkphp/library/think/Log.php
Executable file
@ -0,0 +1,213 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 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;
|
||||
|
||||
/**
|
||||
* Class Log
|
||||
* @package think
|
||||
*
|
||||
* @method void log($msg) static
|
||||
* @method void error($msg) static
|
||||
* @method void info($msg) static
|
||||
* @method void sql($msg) static
|
||||
* @method void notice($msg) static
|
||||
* @method void alert($msg) static
|
||||
*/
|
||||
class Log
|
||||
{
|
||||
const LOG = 'log';
|
||||
const ERROR = 'error';
|
||||
const INFO = 'info';
|
||||
const SQL = 'sql';
|
||||
const NOTICE = 'notice';
|
||||
const ALERT = 'alert';
|
||||
const DEBUG = 'debug';
|
||||
|
||||
// 日志信息
|
||||
protected static $log = [];
|
||||
// 配置参数
|
||||
protected static $config = [];
|
||||
// 日志类型
|
||||
protected static $type = ['log', 'error', 'info', 'sql', 'notice', 'alert', 'debug'];
|
||||
// 日志写入驱动
|
||||
protected static $driver;
|
||||
|
||||
// 当前日志授权key
|
||||
protected static $key;
|
||||
|
||||
/**
|
||||
* 日志初始化
|
||||
* @param array $config
|
||||
*/
|
||||
public static function init($config = [])
|
||||
{
|
||||
$type = isset($config['type']) ? $config['type'] : 'File';
|
||||
$class = false !== strpos($type, '\\') ? $type : '\\think\\log\\driver\\' . ucwords($type);
|
||||
self::$config = $config;
|
||||
unset($config['type']);
|
||||
if (class_exists($class)) {
|
||||
self::$driver = new $class($config);
|
||||
} else {
|
||||
throw new ClassNotFoundException('class not exists:' . $class, $class);
|
||||
}
|
||||
// 记录初始化信息
|
||||
App::$debug && Log::record('[ LOG ] INIT ' . $type, 'info');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取日志信息
|
||||
* @param string $type 信息类型
|
||||
* @return array
|
||||
*/
|
||||
public static function getLog($type = '')
|
||||
{
|
||||
return $type ? self::$log[$type] : self::$log;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录调试信息
|
||||
* @param mixed $msg 调试信息
|
||||
* @param string $type 信息类型
|
||||
* @return void
|
||||
*/
|
||||
public static function record($msg, $type = 'log')
|
||||
{
|
||||
self::$log[$type][] = $msg;
|
||||
if (IS_CLI) {
|
||||
// 命令行下面日志写入改进
|
||||
self::save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空日志信息
|
||||
* @return void
|
||||
*/
|
||||
public static function clear()
|
||||
{
|
||||
self::$log = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前日志记录的授权key
|
||||
* @param string $key 授权key
|
||||
* @return void
|
||||
*/
|
||||
public static function key($key)
|
||||
{
|
||||
self::$key = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查日志写入权限
|
||||
* @param array $config 当前日志配置参数
|
||||
* @return bool
|
||||
*/
|
||||
public static function check($config)
|
||||
{
|
||||
if (self::$key && !empty($config['allow_key']) && !in_array(self::$key, $config['allow_key'])) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存调试信息
|
||||
* @return bool
|
||||
*/
|
||||
public static function save()
|
||||
{
|
||||
if (!empty(self::$log)) {
|
||||
if (is_null(self::$driver)) {
|
||||
self::init(Config::get('log'));
|
||||
}
|
||||
|
||||
if (!self::check(self::$config)) {
|
||||
// 检测日志写入权限
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty(self::$config['level'])) {
|
||||
// 获取全部日志
|
||||
$log = self::$log;
|
||||
if (!App::$debug && isset($log['debug'])) {
|
||||
unset($log['debug']);
|
||||
}
|
||||
} else {
|
||||
// 记录允许级别
|
||||
$log = [];
|
||||
foreach (self::$config['level'] as $level) {
|
||||
if (isset(self::$log[$level])) {
|
||||
$log[$level] = self::$log[$level];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result = self::$driver->save($log);
|
||||
if ($result) {
|
||||
self::$log = [];
|
||||
}
|
||||
Hook::listen('log_write_done', $log);
|
||||
return $result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实时写入日志信息 并支持行为
|
||||
* @param mixed $msg 调试信息
|
||||
* @param string $type 信息类型
|
||||
* @param bool $force 是否强制写入
|
||||
* @return bool
|
||||
*/
|
||||
public static function write($msg, $type = 'log', $force = false)
|
||||
{
|
||||
$log = self::$log;
|
||||
// 封装日志信息
|
||||
if (true === $force || empty(self::$config['level'])) {
|
||||
$log[$type][] = $msg;
|
||||
} elseif (in_array($type, self::$config['level'])) {
|
||||
$log[$type][] = $msg;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 监听log_write
|
||||
Hook::listen('log_write', $log);
|
||||
if (is_null(self::$driver)) {
|
||||
self::init(Config::get('log'));
|
||||
}
|
||||
// 写入日志
|
||||
$result = self::$driver->save($log);
|
||||
if ($result) {
|
||||
self::$log = [];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态调用
|
||||
* @param $method
|
||||
* @param $args
|
||||
* @return mixed
|
||||
*/
|
||||
public static function __callStatic($method, $args)
|
||||
{
|
||||
if (in_array($method, self::$type)) {
|
||||
array_push($args, $method);
|
||||
return call_user_func_array('\\think\\Log::record', $args);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
2268
thinkphp/library/think/Model.php
Executable file
2268
thinkphp/library/think/Model.php
Executable file
File diff suppressed because it is too large
Load Diff
409
thinkphp/library/think/Paginator.php
Executable file
409
thinkphp/library/think/Paginator.php
Executable file
@ -0,0 +1,409 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: zhangyajun <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use ArrayAccess;
|
||||
use ArrayIterator;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use JsonSerializable;
|
||||
use Traversable;
|
||||
|
||||
abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable
|
||||
{
|
||||
/** @var bool 是否为简洁模式 */
|
||||
protected $simple = false;
|
||||
|
||||
/** @var Collection 数据集 */
|
||||
protected $items;
|
||||
|
||||
/** @var integer 当前页 */
|
||||
protected $currentPage;
|
||||
|
||||
/** @var integer 最后一页 */
|
||||
protected $lastPage;
|
||||
|
||||
/** @var integer|null 数据总数 */
|
||||
protected $total;
|
||||
|
||||
/** @var integer 每页的数量 */
|
||||
protected $listRows;
|
||||
|
||||
/** @var bool 是否有下一页 */
|
||||
protected $hasMore;
|
||||
|
||||
/** @var array 一些配置 */
|
||||
protected $options = [
|
||||
'var_page' => 'page',
|
||||
'path' => '/',
|
||||
'query' => [],
|
||||
'fragment' => '',
|
||||
];
|
||||
|
||||
/** @var mixed simple模式下的下个元素 */
|
||||
protected $nextItem;
|
||||
|
||||
public function __construct($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])
|
||||
{
|
||||
$this->options = array_merge($this->options, $options);
|
||||
|
||||
$this->options['path'] = '/' != $this->options['path'] ? rtrim($this->options['path'], '/') : $this->options['path'];
|
||||
|
||||
$this->simple = $simple;
|
||||
$this->listRows = $listRows;
|
||||
|
||||
if (!$items instanceof Collection) {
|
||||
$items = Collection::make($items);
|
||||
}
|
||||
|
||||
if ($simple) {
|
||||
$this->currentPage = $this->setCurrentPage($currentPage);
|
||||
$this->hasMore = count($items) > ($this->listRows);
|
||||
if ($this->hasMore) {
|
||||
$this->nextItem = $items->slice($this->listRows, 1);
|
||||
}
|
||||
$items = $items->slice(0, $this->listRows);
|
||||
} else {
|
||||
$this->total = $total;
|
||||
$this->lastPage = (int) ceil($total / $listRows);
|
||||
$this->currentPage = $this->setCurrentPage($currentPage);
|
||||
$this->hasMore = $this->currentPage < $this->lastPage;
|
||||
}
|
||||
$this->items = $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $items
|
||||
* @param $listRows
|
||||
* @param null $currentPage
|
||||
* @param bool $simple
|
||||
* @param null $total
|
||||
* @param array $options
|
||||
* @return Paginator
|
||||
*/
|
||||
public static function make($items, $listRows, $currentPage = null, $total = null, $simple = false, $options = [])
|
||||
{
|
||||
return new static($items, $listRows, $currentPage, $total, $simple, $options);
|
||||
}
|
||||
|
||||
protected function setCurrentPage($currentPage)
|
||||
{
|
||||
if (!$this->simple && $currentPage > $this->lastPage) {
|
||||
return $this->lastPage > 0 ? $this->lastPage : 1;
|
||||
}
|
||||
|
||||
return $currentPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取页码对应的链接
|
||||
*
|
||||
* @param $page
|
||||
* @return string
|
||||
*/
|
||||
protected function url($page)
|
||||
{
|
||||
if ($page <= 0) {
|
||||
$page = 1;
|
||||
}
|
||||
|
||||
if (strpos($this->options['path'], '[PAGE]') === false) {
|
||||
$parameters = [$this->options['var_page'] => $page];
|
||||
$path = $this->options['path'];
|
||||
} else {
|
||||
$parameters = [];
|
||||
$path = str_replace('[PAGE]', $page, $this->options['path']);
|
||||
}
|
||||
if (count($this->options['query']) > 0) {
|
||||
$parameters = array_merge($this->options['query'], $parameters);
|
||||
}
|
||||
$url = $path;
|
||||
if (!empty($parameters)) {
|
||||
$url .= '?' . urldecode(http_build_query($parameters, null, '&'));
|
||||
}
|
||||
return $url . $this->buildFragment();
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动获取当前页码
|
||||
* @param string $varPage
|
||||
* @param int $default
|
||||
* @return int
|
||||
*/
|
||||
public static function getCurrentPage($varPage = 'page', $default = 1)
|
||||
{
|
||||
$page = (int) Request::instance()->param($varPage);
|
||||
|
||||
if (filter_var($page, FILTER_VALIDATE_INT) !== false && $page >= 1) {
|
||||
return $page;
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
/**
|
||||
* 获取全部分页数 add by GZShangTao
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalPage(){
|
||||
return $this->totalPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动获取当前的path
|
||||
* @return string
|
||||
*/
|
||||
public static function getCurrentPath()
|
||||
{
|
||||
return Request::instance()->baseUrl();
|
||||
}
|
||||
|
||||
public function total()
|
||||
{
|
||||
if ($this->simple) {
|
||||
throw new \DomainException('not support total');
|
||||
}
|
||||
return $this->total;
|
||||
}
|
||||
|
||||
public function listRows()
|
||||
{
|
||||
return $this->listRows;
|
||||
}
|
||||
|
||||
public function currentPage()
|
||||
{
|
||||
return $this->currentPage;
|
||||
}
|
||||
|
||||
public function lastPage()
|
||||
{
|
||||
if ($this->simple) {
|
||||
throw new \DomainException('not support last');
|
||||
}
|
||||
return $this->lastPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据是否足够分页
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasPages()
|
||||
{
|
||||
return !(1 == $this->currentPage && !$this->hasMore);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一组分页链接
|
||||
*
|
||||
* @param int $start
|
||||
* @param int $end
|
||||
* @return array
|
||||
*/
|
||||
public function getUrlRange($start, $end)
|
||||
{
|
||||
$urls = [];
|
||||
|
||||
for ($page = $start; $page <= $end; $page++) {
|
||||
$urls[$page] = $this->url($page);
|
||||
}
|
||||
|
||||
return $urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置URL锚点
|
||||
*
|
||||
* @param string|null $fragment
|
||||
* @return $this
|
||||
*/
|
||||
public function fragment($fragment)
|
||||
{
|
||||
$this->options['fragment'] = $fragment;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加URL参数
|
||||
*
|
||||
* @param array|string $key
|
||||
* @param string|null $value
|
||||
* @return $this
|
||||
*/
|
||||
public function appends($key, $value = null)
|
||||
{
|
||||
if (!is_array($key)) {
|
||||
$queries = [$key => $value];
|
||||
} else {
|
||||
$queries = $key;
|
||||
}
|
||||
|
||||
foreach ($queries as $k => $v) {
|
||||
if ($k !== $this->options['var_page']) {
|
||||
$this->options['query'][$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造锚点字符串
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function buildFragment()
|
||||
{
|
||||
return $this->options['fragment'] ? '#' . $this->options['fragment'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染分页html
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function render();
|
||||
|
||||
public function items()
|
||||
{
|
||||
return $this->items->all();
|
||||
}
|
||||
|
||||
public function getCollection()
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
public function isEmpty()
|
||||
{
|
||||
return $this->items->isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 给每个元素执行个回调
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function each(callable $callback)
|
||||
{
|
||||
foreach ($this->items as $key => $item) {
|
||||
$result = $callback($item, $key);
|
||||
if (false === $result) {
|
||||
break;
|
||||
} elseif (!is_object($item)) {
|
||||
$this->items[$key] = $result;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an external iterator
|
||||
* @return Traversable An instance of an object implementing <b>Iterator</b> or
|
||||
* <b>Traversable</b>
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->items->all());
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a offset exists
|
||||
* @param mixed $offset
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return $this->items->offsetExists($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to retrieve
|
||||
* @param mixed $offset
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->items->offsetGet($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to set
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->items->offsetSet($offset, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to unset
|
||||
* @param mixed $offset
|
||||
* @return void
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
$this->items->offsetUnset($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count elements of an object
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return $this->items->count();
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->render();
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
if ($this->simple) {
|
||||
return [
|
||||
'per_page' => $this->listRows,
|
||||
'current_page' => $this->currentPage,
|
||||
'has_more' => $this->hasMore,
|
||||
'next_item' => $this->nextItem,
|
||||
'data' => $this->items->toArray(),
|
||||
];
|
||||
} else {
|
||||
//modify by GZShangTao
|
||||
return [
|
||||
'Total' => $this->total,
|
||||
'PerPage' => $this->listRows,
|
||||
'CurrentPage' => $this->currentPage,
|
||||
'TotalPage' => $this->lastPage,
|
||||
'Rows' => $this->items->toArray(),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify data which should be serialized to JSON
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
return call_user_func_array([$this->getCollection(), $name], $arguments);
|
||||
}
|
||||
|
||||
}
|
1205
thinkphp/library/think/Process.php
Executable file
1205
thinkphp/library/think/Process.php
Executable file
File diff suppressed because it is too large
Load Diff
1656
thinkphp/library/think/Request.php
Executable file
1656
thinkphp/library/think/Request.php
Executable file
File diff suppressed because it is too large
Load Diff
334
thinkphp/library/think/Response.php
Executable file
334
thinkphp/library/think/Response.php
Executable file
@ -0,0 +1,334 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\response\Json as JsonResponse;
|
||||
use think\response\Jsonp as JsonpResponse;
|
||||
use think\response\Redirect as RedirectResponse;
|
||||
use think\response\View as ViewResponse;
|
||||
use think\response\Xml as XmlResponse;
|
||||
|
||||
class Response
|
||||
{
|
||||
// 原始数据
|
||||
protected $data;
|
||||
|
||||
// 当前的contentType
|
||||
protected $contentType = 'text/html';
|
||||
|
||||
// 字符集
|
||||
protected $charset = 'utf-8';
|
||||
|
||||
//状态
|
||||
protected $code = 200;
|
||||
|
||||
// 输出参数
|
||||
protected $options = [];
|
||||
// header参数
|
||||
protected $header = [];
|
||||
|
||||
protected $content = null;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
* @param mixed $data 输出数据
|
||||
* @param int $code
|
||||
* @param array $header
|
||||
* @param array $options 输出参数
|
||||
*/
|
||||
public function __construct($data = '', $code = 200, array $header = [], $options = [])
|
||||
{
|
||||
$this->data($data);
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
$this->contentType($this->contentType, $this->charset);
|
||||
$this->header = array_merge($this->header, $header);
|
||||
$this->code = $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Response对象
|
||||
* @access public
|
||||
* @param mixed $data 输出数据
|
||||
* @param string $type 输出类型
|
||||
* @param int $code
|
||||
* @param array $header
|
||||
* @param array $options 输出参数
|
||||
* @return Response|JsonResponse|ViewResponse|XmlResponse|RedirectResponse|JsonpResponse
|
||||
*/
|
||||
public static function create($data = '', $type = '', $code = 200, array $header = [], $options = [])
|
||||
{
|
||||
$type = empty($type) ? 'null' : strtolower($type);
|
||||
|
||||
$class = false !== strpos($type, '\\') ? $type : '\\think\\response\\' . ucfirst($type);
|
||||
if (class_exists($class)) {
|
||||
$response = new $class($data, $code, $header, $options);
|
||||
} else {
|
||||
$response = new static($data, $code, $header, $options);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送数据到客户端
|
||||
* @access public
|
||||
* @return mixed
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function send()
|
||||
{
|
||||
// 监听response_send
|
||||
Hook::listen('response_send', $this);
|
||||
|
||||
// 处理输出数据
|
||||
$data = $this->getContent();
|
||||
|
||||
// Trace调试注入
|
||||
if (Env::get('app_trace', Config::get('app_trace'))) {
|
||||
Debug::inject($this, $data);
|
||||
}
|
||||
|
||||
if (200 == $this->code) {
|
||||
$cache = Request::instance()->getCache();
|
||||
if ($cache) {
|
||||
$this->header['Cache-Control'] = 'max-age=' . $cache[1] . ',must-revalidate';
|
||||
$this->header['Last-Modified'] = gmdate('D, d M Y H:i:s') . ' GMT';
|
||||
$this->header['Expires'] = gmdate('D, d M Y H:i:s', $_SERVER['REQUEST_TIME'] + $cache[1]) . ' GMT';
|
||||
Cache::tag($cache[2])->set($cache[0], [$data, $this->header], $cache[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!headers_sent() && !empty($this->header)) {
|
||||
// 发送状态码
|
||||
http_response_code($this->code);
|
||||
// 发送头部信息
|
||||
foreach ($this->header as $name => $val) {
|
||||
if (is_null($val)) {
|
||||
header($name);
|
||||
} else {
|
||||
header($name . ':' . $val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo $data;
|
||||
|
||||
if (function_exists('fastcgi_finish_request')) {
|
||||
// 提高页面响应
|
||||
fastcgi_finish_request();
|
||||
}
|
||||
|
||||
// 监听response_end
|
||||
Hook::listen('response_end', $this);
|
||||
|
||||
// 清空当次请求有效的数据
|
||||
if (!($this instanceof RedirectResponse)) {
|
||||
Session::flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理数据
|
||||
* @access protected
|
||||
* @param mixed $data 要处理的数据
|
||||
* @return mixed
|
||||
*/
|
||||
protected function output($data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出的参数
|
||||
* @access public
|
||||
* @param mixed $options 输出参数
|
||||
* @return $this
|
||||
*/
|
||||
public function options($options = [])
|
||||
{
|
||||
$this->options = array_merge($this->options, $options);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出数据设置
|
||||
* @access public
|
||||
* @param mixed $data 输出数据
|
||||
* @return $this
|
||||
*/
|
||||
public function data($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应头
|
||||
* @access public
|
||||
* @param string|array $name 参数名
|
||||
* @param string $value 参数值
|
||||
* @return $this
|
||||
*/
|
||||
public function header($name, $value = null)
|
||||
{
|
||||
if (is_array($name)) {
|
||||
$this->header = array_merge($this->header, $name);
|
||||
} else {
|
||||
$this->header[$name] = $value;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置页面输出内容
|
||||
* @param $content
|
||||
* @return $this
|
||||
*/
|
||||
public function content($content)
|
||||
{
|
||||
if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable([
|
||||
$content,
|
||||
'__toString',
|
||||
])
|
||||
) {
|
||||
throw new \InvalidArgumentException(sprintf('variable type error: %s', gettype($content)));
|
||||
}
|
||||
|
||||
$this->content = (string) $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送HTTP状态
|
||||
* @param integer $code 状态码
|
||||
* @return $this
|
||||
*/
|
||||
public function code($code)
|
||||
{
|
||||
$this->code = $code;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* LastModified
|
||||
* @param string $time
|
||||
* @return $this
|
||||
*/
|
||||
public function lastModified($time)
|
||||
{
|
||||
$this->header['Last-Modified'] = $time;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expires
|
||||
* @param string $time
|
||||
* @return $this
|
||||
*/
|
||||
public function expires($time)
|
||||
{
|
||||
$this->header['Expires'] = $time;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* ETag
|
||||
* @param string $eTag
|
||||
* @return $this
|
||||
*/
|
||||
public function eTag($eTag)
|
||||
{
|
||||
$this->header['ETag'] = $eTag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面缓存控制
|
||||
* @param string $cache 状态码
|
||||
* @return $this
|
||||
*/
|
||||
public function cacheControl($cache)
|
||||
{
|
||||
$this->header['Cache-control'] = $cache;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面输出类型
|
||||
* @param string $contentType 输出类型
|
||||
* @param string $charset 输出编码
|
||||
* @return $this
|
||||
*/
|
||||
public function contentType($contentType, $charset = 'utf-8')
|
||||
{
|
||||
$this->header['Content-Type'] = $contentType . '; charset=' . $charset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取头部信息
|
||||
* @param string $name 头部名称
|
||||
* @return mixed
|
||||
*/
|
||||
public function getHeader($name = '')
|
||||
{
|
||||
if (!empty($name)) {
|
||||
return isset($this->header[$name]) ? $this->header[$name] : null;
|
||||
} else {
|
||||
return $this->header;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始数据
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取输出数据
|
||||
* @return mixed
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
if (null == $this->content) {
|
||||
$content = $this->output($this->data);
|
||||
|
||||
if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable([
|
||||
$content,
|
||||
'__toString',
|
||||
])
|
||||
) {
|
||||
throw new \InvalidArgumentException(sprintf('variable type error: %s', gettype($content)));
|
||||
}
|
||||
|
||||
$this->content = (string) $content;
|
||||
}
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态码
|
||||
* @return integer
|
||||
*/
|
||||
public function getCode()
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
}
|
1603
thinkphp/library/think/Route.php
Executable file
1603
thinkphp/library/think/Route.php
Executable file
File diff suppressed because it is too large
Load Diff
366
thinkphp/library/think/Session.php
Executable file
366
thinkphp/library/think/Session.php
Executable file
@ -0,0 +1,366 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 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;
|
||||
|
||||
class Session
|
||||
{
|
||||
protected static $prefix = '';
|
||||
protected static $init = null;
|
||||
|
||||
/**
|
||||
* 设置或者获取session作用域(前缀)
|
||||
* @param string $prefix
|
||||
* @return string|void
|
||||
*/
|
||||
public static function prefix($prefix = '')
|
||||
{
|
||||
empty(self::$init) && self::boot();
|
||||
if (empty($prefix) && null !== $prefix) {
|
||||
return self::$prefix;
|
||||
} else {
|
||||
self::$prefix = $prefix;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* session初始化
|
||||
* @param array $config
|
||||
* @return void
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public static function init(array $config = [])
|
||||
{
|
||||
if (empty($config)) {
|
||||
$config = Config::get('session');
|
||||
}
|
||||
// 记录初始化信息
|
||||
App::$debug && Log::record('[ SESSION ] INIT ' . var_export($config, true), 'info');
|
||||
$isDoStart = false;
|
||||
if (isset($config['use_trans_sid'])) {
|
||||
ini_set('session.use_trans_sid', $config['use_trans_sid'] ? 1 : 0);
|
||||
}
|
||||
|
||||
// 启动session
|
||||
if (!empty($config['auto_start']) && PHP_SESSION_ACTIVE != session_status()) {
|
||||
ini_set('session.auto_start', 0);
|
||||
$isDoStart = true;
|
||||
}
|
||||
|
||||
if (isset($config['prefix']) && ('' === self::$prefix || null === self::$prefix)) {
|
||||
self::$prefix = $config['prefix'];
|
||||
}
|
||||
if (isset($config['var_session_id']) && isset($_REQUEST[$config['var_session_id']])) {
|
||||
session_id($_REQUEST[$config['var_session_id']]);
|
||||
} elseif (isset($config['id']) && !empty($config['id'])) {
|
||||
session_id($config['id']);
|
||||
}
|
||||
if (isset($config['name'])) {
|
||||
session_name($config['name']);
|
||||
}
|
||||
if (isset($config['path'])) {
|
||||
session_save_path($config['path']);
|
||||
}
|
||||
if (isset($config['domain'])) {
|
||||
ini_set('session.cookie_domain', $config['domain']);
|
||||
}
|
||||
if (isset($config['expire'])) {
|
||||
ini_set('session.gc_maxlifetime', $config['expire']);
|
||||
ini_set('session.cookie_lifetime', $config['expire']);
|
||||
}
|
||||
if (isset($config['secure'])) {
|
||||
ini_set('session.cookie_secure', $config['secure']);
|
||||
}
|
||||
if (isset($config['httponly'])) {
|
||||
ini_set('session.cookie_httponly', $config['httponly']);
|
||||
}
|
||||
if (isset($config['use_cookies'])) {
|
||||
ini_set('session.use_cookies', $config['use_cookies'] ? 1 : 0);
|
||||
}
|
||||
if (isset($config['cache_limiter'])) {
|
||||
session_cache_limiter($config['cache_limiter']);
|
||||
}
|
||||
if (isset($config['cache_expire'])) {
|
||||
session_cache_expire($config['cache_expire']);
|
||||
}
|
||||
if (!empty($config['type'])) {
|
||||
// 读取session驱动
|
||||
$class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\session\\driver\\' . ucwords($config['type']);
|
||||
|
||||
// 检查驱动类
|
||||
if (!class_exists($class) || !session_set_save_handler(new $class($config))) {
|
||||
throw new ClassNotFoundException('error session handler:' . $class, $class);
|
||||
}
|
||||
}
|
||||
if ($isDoStart) {
|
||||
session_start();
|
||||
self::$init = true;
|
||||
} else {
|
||||
self::$init = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* session自动启动或者初始化
|
||||
* @return void
|
||||
*/
|
||||
public static function boot()
|
||||
{
|
||||
if (is_null(self::$init)) {
|
||||
self::init();
|
||||
} elseif (false === self::$init) {
|
||||
if (PHP_SESSION_ACTIVE != session_status()) {
|
||||
session_start();
|
||||
}
|
||||
self::$init = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* session设置
|
||||
* @param string $name session名称
|
||||
* @param mixed $value session值
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @return void
|
||||
*/
|
||||
public static function set($name, $value = '', $prefix = null)
|
||||
{
|
||||
empty(self::$init) && self::boot();
|
||||
|
||||
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
|
||||
if (strpos($name, '.')) {
|
||||
// 二维数组赋值
|
||||
list($name1, $name2) = explode('.', $name);
|
||||
if ($prefix) {
|
||||
$_SESSION[$prefix][$name1][$name2] = $value;
|
||||
} else {
|
||||
$_SESSION[$name1][$name2] = $value;
|
||||
}
|
||||
} elseif ($prefix) {
|
||||
$_SESSION[$prefix][$name] = $value;
|
||||
} else {
|
||||
$_SESSION[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* session获取
|
||||
* @param string $name session名称
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($name = '', $prefix = null)
|
||||
{
|
||||
empty(self::$init) && self::boot();
|
||||
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
|
||||
if ('' == $name) {
|
||||
// 获取全部的session
|
||||
$value = $prefix ? (!empty($_SESSION[$prefix]) ? $_SESSION[$prefix] : []) : $_SESSION;
|
||||
} elseif ($prefix) {
|
||||
// 获取session
|
||||
if (strpos($name, '.')) {
|
||||
list($name1, $name2) = explode('.', $name);
|
||||
$value = isset($_SESSION[$prefix][$name1][$name2]) ? $_SESSION[$prefix][$name1][$name2] : null;
|
||||
} else {
|
||||
$value = isset($_SESSION[$prefix][$name]) ? $_SESSION[$prefix][$name] : null;
|
||||
}
|
||||
} else {
|
||||
if (strpos($name, '.')) {
|
||||
list($name1, $name2) = explode('.', $name);
|
||||
$value = isset($_SESSION[$name1][$name2]) ? $_SESSION[$name1][$name2] : null;
|
||||
} else {
|
||||
$value = isset($_SESSION[$name]) ? $_SESSION[$name] : null;
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* session获取并删除
|
||||
* @param string $name session名称
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @return mixed
|
||||
*/
|
||||
public static function pull($name, $prefix = null)
|
||||
{
|
||||
$result = self::get($name, $prefix);
|
||||
if ($result) {
|
||||
self::delete($name, $prefix);
|
||||
return $result;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* session设置 下一次请求有效
|
||||
* @param string $name session名称
|
||||
* @param mixed $value session值
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @return void
|
||||
*/
|
||||
public static function flash($name, $value)
|
||||
{
|
||||
self::set($name, $value);
|
||||
if (!self::has('__flash__.__time__')) {
|
||||
self::set('__flash__.__time__', $_SERVER['REQUEST_TIME_FLOAT']);
|
||||
}
|
||||
self::push('__flash__', $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空当前请求的session数据
|
||||
* @return void
|
||||
*/
|
||||
public static function flush()
|
||||
{
|
||||
if (self::$init) {
|
||||
$item = self::get('__flash__');
|
||||
|
||||
if (!empty($item)) {
|
||||
$time = $item['__time__'];
|
||||
if ($_SERVER['REQUEST_TIME_FLOAT'] > $time) {
|
||||
unset($item['__time__']);
|
||||
self::delete($item);
|
||||
self::set('__flash__', []);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除session数据
|
||||
* @param string|array $name session名称
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @return void
|
||||
*/
|
||||
public static function delete($name, $prefix = null)
|
||||
{
|
||||
empty(self::$init) && self::boot();
|
||||
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
|
||||
if (is_array($name)) {
|
||||
foreach ($name as $key) {
|
||||
self::delete($key, $prefix);
|
||||
}
|
||||
} elseif (strpos($name, '.')) {
|
||||
list($name1, $name2) = explode('.', $name);
|
||||
if ($prefix) {
|
||||
unset($_SESSION[$prefix][$name1][$name2]);
|
||||
} else {
|
||||
unset($_SESSION[$name1][$name2]);
|
||||
}
|
||||
} else {
|
||||
if ($prefix) {
|
||||
unset($_SESSION[$prefix][$name]);
|
||||
} else {
|
||||
unset($_SESSION[$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空session数据
|
||||
* @param string|null $prefix 作用域(前缀)
|
||||
* @return void
|
||||
*/
|
||||
public static function clear($prefix = null)
|
||||
{
|
||||
empty(self::$init) && self::boot();
|
||||
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
|
||||
if ($prefix) {
|
||||
unset($_SESSION[$prefix]);
|
||||
} else {
|
||||
$_SESSION = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断session数据
|
||||
* @param string $name session名称
|
||||
* @param string|null $prefix
|
||||
* @return bool
|
||||
*/
|
||||
public static function has($name, $prefix = null)
|
||||
{
|
||||
empty(self::$init) && self::boot();
|
||||
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
|
||||
if (strpos($name, '.')) {
|
||||
// 支持数组
|
||||
list($name1, $name2) = explode('.', $name);
|
||||
return $prefix ? isset($_SESSION[$prefix][$name1][$name2]) : isset($_SESSION[$name1][$name2]);
|
||||
} else {
|
||||
return $prefix ? isset($_SESSION[$prefix][$name]) : isset($_SESSION[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加数据到一个session数组
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public static function push($key, $value)
|
||||
{
|
||||
$array = self::get($key);
|
||||
if (is_null($array)) {
|
||||
$array = [];
|
||||
}
|
||||
$array[] = $value;
|
||||
self::set($key, $array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动session
|
||||
* @return void
|
||||
*/
|
||||
public static function start()
|
||||
{
|
||||
session_start();
|
||||
self::$init = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁session
|
||||
* @return void
|
||||
*/
|
||||
public static function destroy()
|
||||
{
|
||||
if (!empty($_SESSION)) {
|
||||
$_SESSION = [];
|
||||
}
|
||||
session_unset();
|
||||
session_destroy();
|
||||
self::$init = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新生成session_id
|
||||
* @param bool $delete 是否删除关联会话文件
|
||||
* @return void
|
||||
*/
|
||||
public static function regenerate($delete = false)
|
||||
{
|
||||
session_regenerate_id($delete);
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停session
|
||||
* @return void
|
||||
*/
|
||||
public static function pause()
|
||||
{
|
||||
// 暂停session
|
||||
session_write_close();
|
||||
self::$init = false;
|
||||
}
|
||||
}
|
1147
thinkphp/library/think/Template.php
Executable file
1147
thinkphp/library/think/Template.php
Executable file
File diff suppressed because it is too large
Load Diff
329
thinkphp/library/think/Url.php
Executable file
329
thinkphp/library/think/Url.php
Executable file
@ -0,0 +1,329 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class Url
|
||||
{
|
||||
// 生成URL地址的root
|
||||
protected static $root;
|
||||
protected static $bindCheck;
|
||||
|
||||
/**
|
||||
* URL生成 支持路由反射
|
||||
* @param string $url 路由地址
|
||||
* @param string|array $vars 参数(支持数组和字符串)a=val&b=val2... ['a'=>'val1', 'b'=>'val2']
|
||||
* @param string|bool $suffix 伪静态后缀,默认为true表示获取配置值
|
||||
* @param boolean|string $domain 是否显示域名 或者直接传入域名
|
||||
* @return string
|
||||
*/
|
||||
public static function build($url = '', $vars = '', $suffix = true, $domain = false)
|
||||
{
|
||||
if (false === $domain && Route::rules('domain')) {
|
||||
$domain = true;
|
||||
}
|
||||
// 解析URL
|
||||
if (0 === strpos($url, '[') && $pos = strpos($url, ']')) {
|
||||
// [name] 表示使用路由命名标识生成URL
|
||||
$name = substr($url, 1, $pos - 1);
|
||||
$url = 'name' . substr($url, $pos + 1);
|
||||
}
|
||||
if (false === strpos($url, '://') && 0 !== strpos($url, '/')) {
|
||||
$info = parse_url($url);
|
||||
$url = !empty($info['path']) ? $info['path'] : '';
|
||||
if (isset($info['fragment'])) {
|
||||
// 解析锚点
|
||||
$anchor = $info['fragment'];
|
||||
if (false !== strpos($anchor, '?')) {
|
||||
// 解析参数
|
||||
list($anchor, $info['query']) = explode('?', $anchor, 2);
|
||||
}
|
||||
if (false !== strpos($anchor, '@')) {
|
||||
// 解析域名
|
||||
list($anchor, $domain) = explode('@', $anchor, 2);
|
||||
}
|
||||
} elseif (strpos($url, '@') && false === strpos($url, '\\')) {
|
||||
// 解析域名
|
||||
list($url, $domain) = explode('@', $url, 2);
|
||||
}
|
||||
}
|
||||
|
||||
// 解析参数
|
||||
if (is_string($vars)) {
|
||||
// aaa=1&bbb=2 转换成数组
|
||||
parse_str($vars, $vars);
|
||||
}
|
||||
|
||||
if ($url) {
|
||||
$rule = Route::name(isset($name) ? $name : $url . (isset($info['query']) ? '?' . $info['query'] : ''));
|
||||
if (is_null($rule) && isset($info['query'])) {
|
||||
$rule = Route::name($url);
|
||||
// 解析地址里面参数 合并到vars
|
||||
parse_str($info['query'], $params);
|
||||
$vars = array_merge($params, $vars);
|
||||
unset($info['query']);
|
||||
}
|
||||
}
|
||||
if (!empty($rule) && $match = self::getRuleUrl($rule, $vars)) {
|
||||
// 匹配路由命名标识
|
||||
$url = $match[0];
|
||||
// 替换可选分隔符
|
||||
$url = preg_replace(['/(\W)\?$/', '/(\W)\?/'], ['', '\1'], $url);
|
||||
if (!empty($match[1])) {
|
||||
$domain = $match[1];
|
||||
}
|
||||
if (!is_null($match[2])) {
|
||||
$suffix = $match[2];
|
||||
}
|
||||
} elseif (!empty($rule) && isset($name)) {
|
||||
throw new \InvalidArgumentException('route name not exists:' . $name);
|
||||
} else {
|
||||
// 检查别名路由
|
||||
$alias = Route::rules('alias');
|
||||
$matchAlias = false;
|
||||
if ($alias) {
|
||||
// 别名路由解析
|
||||
foreach ($alias as $key => $val) {
|
||||
if (is_array($val)) {
|
||||
$val = $val[0];
|
||||
}
|
||||
if (0 === strpos($url, $val)) {
|
||||
$url = $key . substr($url, strlen($val));
|
||||
$matchAlias = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$matchAlias) {
|
||||
// 路由标识不存在 直接解析
|
||||
$url = self::parseUrl($url, $domain);
|
||||
}
|
||||
if (isset($info['query'])) {
|
||||
// 解析地址里面参数 合并到vars
|
||||
parse_str($info['query'], $params);
|
||||
$vars = array_merge($params, $vars);
|
||||
}
|
||||
}
|
||||
|
||||
// 检测URL绑定
|
||||
if (!self::$bindCheck) {
|
||||
$type = Route::getBind('type');
|
||||
if ($type) {
|
||||
$bind = Route::getBind($type);
|
||||
if (0 === strpos($url, $bind)) {
|
||||
$url = substr($url, strlen($bind) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 还原URL分隔符
|
||||
$depr = Config::get('pathinfo_depr');
|
||||
$url = str_replace('/', $depr, $url);
|
||||
|
||||
// URL后缀
|
||||
$suffix = in_array($url, ['/', '']) ? '' : self::parseSuffix($suffix);
|
||||
// 锚点
|
||||
$anchor = !empty($anchor) ? '#' . $anchor : '';
|
||||
// 参数组装
|
||||
if (!empty($vars)) {
|
||||
// 添加参数
|
||||
if (Config::get('url_common_param')) {
|
||||
$vars = http_build_query($vars);
|
||||
$url .= $suffix . '?' . $vars . $anchor;
|
||||
} else {
|
||||
$paramType = Config::get('url_param_type');
|
||||
foreach ($vars as $var => $val) {
|
||||
if ('' !== trim($val)) {
|
||||
if ($paramType) {
|
||||
$url .= $depr . urlencode($val);
|
||||
} else {
|
||||
$url .= $depr . $var . $depr . urlencode($val);
|
||||
}
|
||||
}
|
||||
}
|
||||
$url .= $suffix . $anchor;
|
||||
}
|
||||
} else {
|
||||
$url .= $suffix . $anchor;
|
||||
}
|
||||
// 检测域名
|
||||
$domain = self::parseDomain($url, $domain);
|
||||
// URL组装
|
||||
$url = $domain . rtrim(self::$root ?: Request::instance()->root(), '/') . '/' . ltrim($url, '/');
|
||||
|
||||
self::$bindCheck = false;
|
||||
return $url;
|
||||
}
|
||||
|
||||
// 直接解析URL地址
|
||||
protected static function parseUrl($url, &$domain)
|
||||
{
|
||||
$request = Request::instance();
|
||||
if (0 === strpos($url, '/')) {
|
||||
// 直接作为路由地址解析
|
||||
$url = substr($url, 1);
|
||||
} elseif (false !== strpos($url, '\\')) {
|
||||
// 解析到类
|
||||
$url = ltrim(str_replace('\\', '/', $url), '/');
|
||||
} elseif (0 === strpos($url, '@')) {
|
||||
// 解析到控制器
|
||||
$url = substr($url, 1);
|
||||
} else {
|
||||
// 解析到 模块/控制器/操作
|
||||
$module = $request->module();
|
||||
$domains = Route::rules('domain');
|
||||
if (true === $domain && 2 == substr_count($url, '/')) {
|
||||
$current = $request->host();
|
||||
$match = [];
|
||||
$pos = [];
|
||||
foreach ($domains as $key => $item) {
|
||||
if (isset($item['[bind]']) && 0 === strpos($url, $item['[bind]'][0])) {
|
||||
$pos[$key] = strlen($item['[bind]'][0]) + 1;
|
||||
$match[] = $key;
|
||||
$module = '';
|
||||
}
|
||||
}
|
||||
if ($match) {
|
||||
$domain = current($match);
|
||||
foreach ($match as $item) {
|
||||
if (0 === strpos($current, $item)) {
|
||||
$domain = $item;
|
||||
}
|
||||
}
|
||||
self::$bindCheck = true;
|
||||
$url = substr($url, $pos[$domain]);
|
||||
}
|
||||
} elseif ($domain) {
|
||||
if (isset($domains[$domain]['[bind]'][0])) {
|
||||
$bindModule = $domains[$domain]['[bind]'][0];
|
||||
if ($bindModule && !in_array($bindModule[0], ['\\', '@'])) {
|
||||
$module = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
$module = $module ? $module . '/' : '';
|
||||
|
||||
$controller = Loader::parseName($request->controller());
|
||||
if ('' == $url) {
|
||||
// 空字符串输出当前的 模块/控制器/操作
|
||||
$url = $module . $controller . '/' . $request->action();
|
||||
} else {
|
||||
$path = explode('/', $url);
|
||||
$action = Config::get('url_convert') ? strtolower(array_pop($path)) : array_pop($path);
|
||||
$controller = empty($path) ? $controller : (Config::get('url_convert') ? Loader::parseName(array_pop($path)) : array_pop($path));
|
||||
$module = empty($path) ? $module : array_pop($path) . '/';
|
||||
$url = $module . $controller . '/' . $action;
|
||||
}
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
// 检测域名
|
||||
protected static function parseDomain(&$url, $domain)
|
||||
{
|
||||
if (!$domain) {
|
||||
return '';
|
||||
}
|
||||
$request = Request::instance();
|
||||
$rootDomain = Config::get('url_domain_root');
|
||||
if (true === $domain) {
|
||||
// 自动判断域名
|
||||
$domain = Config::get('app_host') ?: $request->host();
|
||||
|
||||
$domains = Route::rules('domain');
|
||||
if ($domains) {
|
||||
$route_domain = array_keys($domains);
|
||||
foreach ($route_domain as $domain_prefix) {
|
||||
if (0 === strpos($domain_prefix, '*.') && strpos($domain, ltrim($domain_prefix, '*.')) !== false) {
|
||||
foreach ($domains as $key => $rule) {
|
||||
$rule = is_array($rule) ? $rule[0] : $rule;
|
||||
if (is_string($rule) && false === strpos($key, '*') && 0 === strpos($url, $rule)) {
|
||||
$url = ltrim($url, $rule);
|
||||
$domain = $key;
|
||||
// 生成对应子域名
|
||||
if (!empty($rootDomain)) {
|
||||
$domain .= $rootDomain;
|
||||
}
|
||||
break;
|
||||
} elseif (false !== strpos($key, '*')) {
|
||||
if (!empty($rootDomain)) {
|
||||
$domain .= $rootDomain;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (empty($rootDomain)) {
|
||||
$host = Config::get('app_host') ?: $request->host();
|
||||
$rootDomain = substr_count($host, '.') > 1 ? substr(strstr($host, '.'), 1) : $host;
|
||||
}
|
||||
if (substr_count($domain, '.') < 2 && !strpos($domain, $rootDomain)) {
|
||||
$domain .= '.' . $rootDomain;
|
||||
}
|
||||
}
|
||||
if (false !== strpos($domain, '://')) {
|
||||
$scheme = '';
|
||||
} else {
|
||||
$scheme = $request->isSsl() || Config::get('is_https') ? 'https://' : 'http://';
|
||||
}
|
||||
return $scheme . $domain;
|
||||
}
|
||||
|
||||
// 解析URL后缀
|
||||
protected static function parseSuffix($suffix)
|
||||
{
|
||||
if ($suffix) {
|
||||
$suffix = true === $suffix ? Config::get('url_html_suffix') : $suffix;
|
||||
if ($pos = strpos($suffix, '|')) {
|
||||
$suffix = substr($suffix, 0, $pos);
|
||||
}
|
||||
}
|
||||
return (empty($suffix) || 0 === strpos($suffix, '.')) ? $suffix : '.' . $suffix;
|
||||
}
|
||||
|
||||
// 匹配路由地址
|
||||
public static function getRuleUrl($rule, &$vars = [])
|
||||
{
|
||||
foreach ($rule as $item) {
|
||||
list($url, $pattern, $domain, $suffix) = $item;
|
||||
if (empty($pattern)) {
|
||||
return [$url, $domain, $suffix];
|
||||
}
|
||||
$type = Config::get('url_common_param');
|
||||
foreach ($pattern as $key => $val) {
|
||||
if (isset($vars[$key])) {
|
||||
$url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key . '', '<' . $key . '>'], $type ? $vars[$key] : urlencode($vars[$key]), $url);
|
||||
unset($vars[$key]);
|
||||
$result = [$url, $domain, $suffix];
|
||||
} elseif (2 == $val) {
|
||||
$url = str_replace(['/[:' . $key . ']', '[:' . $key . ']', '<' . $key . '?>'], '', $url);
|
||||
$result = [$url, $domain, $suffix];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isset($result)) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 指定当前生成URL地址的root
|
||||
public static function root($root)
|
||||
{
|
||||
self::$root = $root;
|
||||
Request::instance()->root($root);
|
||||
}
|
||||
}
|
1294
thinkphp/library/think/Validate.php
Executable file
1294
thinkphp/library/think/Validate.php
Executable file
File diff suppressed because it is too large
Load Diff
241
thinkphp/library/think/View.php
Executable file
241
thinkphp/library/think/View.php
Executable file
@ -0,0 +1,241 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
class View
|
||||
{
|
||||
// 视图实例
|
||||
protected static $instance;
|
||||
// 模板引擎实例
|
||||
public $engine;
|
||||
// 模板变量
|
||||
protected $data = [];
|
||||
// 用于静态赋值的模板变量
|
||||
protected static $var = [];
|
||||
// 视图输出替换
|
||||
protected $replace = [];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
* @param array $engine 模板引擎参数
|
||||
* @param array $replace 字符串替换参数
|
||||
*/
|
||||
public function __construct($engine = [], $replace = [])
|
||||
{
|
||||
// 初始化模板引擎
|
||||
$this->engine((array) $engine);
|
||||
// 基础替换字符串
|
||||
$request = Request::instance();
|
||||
$base = $request->root();
|
||||
$root = strpos($base, '.') ? ltrim(dirname($base), DS) : $base;
|
||||
if ('' != $root) {
|
||||
$root = '/' . ltrim($root, '/');
|
||||
}
|
||||
$baseReplace = [
|
||||
'__ROOT__' => $root,
|
||||
'__URL__' => $base . '/' . $request->module() . '/' . Loader::parseName($request->controller()),
|
||||
'__STATIC__' => $root . '/static',
|
||||
'__CSS__' => $root . '/static/css',
|
||||
'__JS__' => $root . '/static/js',
|
||||
];
|
||||
$this->replace = array_merge($baseReplace, (array) $replace);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化视图
|
||||
* @access public
|
||||
* @param array $engine 模板引擎参数
|
||||
* @param array $replace 字符串替换参数
|
||||
* @return object
|
||||
*/
|
||||
public static function instance($engine = [], $replace = [])
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self($engine, $replace);
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板变量静态赋值
|
||||
* @access public
|
||||
* @param mixed $name 变量名
|
||||
* @param mixed $value 变量值
|
||||
* @return void
|
||||
*/
|
||||
public static function share($name, $value = '')
|
||||
{
|
||||
if (is_array($name)) {
|
||||
self::$var = array_merge(self::$var, $name);
|
||||
} else {
|
||||
self::$var[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板变量赋值
|
||||
* @access public
|
||||
* @param mixed $name 变量名
|
||||
* @param mixed $value 变量值
|
||||
* @return $this
|
||||
*/
|
||||
public function assign($name, $value = '')
|
||||
{
|
||||
if (is_array($name)) {
|
||||
$this->data = array_merge($this->data, $name);
|
||||
} else {
|
||||
$this->data[$name] = $value;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前模板解析的引擎
|
||||
* @access public
|
||||
* @param array|string $options 引擎参数
|
||||
* @return $this
|
||||
*/
|
||||
public function engine($options = [])
|
||||
{
|
||||
if (is_string($options)) {
|
||||
$type = $options;
|
||||
$options = [];
|
||||
} else {
|
||||
$type = !empty($options['type']) ? $options['type'] : 'Think';
|
||||
}
|
||||
|
||||
$class = false !== strpos($type, '\\') ? $type : '\\think\\view\\driver\\' . ucfirst($type);
|
||||
if (isset($options['type'])) {
|
||||
unset($options['type']);
|
||||
}
|
||||
$this->engine = new $class($options);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置模板引擎
|
||||
* @access private
|
||||
* @param string|array $name 参数名
|
||||
* @param mixed $value 参数值
|
||||
* @return $this
|
||||
*/
|
||||
public function config($name, $value = null)
|
||||
{
|
||||
$this->engine->config($name, $value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析和获取模板内容 用于输出
|
||||
* @param string $template 模板文件名或者内容
|
||||
* @param array $vars 模板输出变量
|
||||
* @param array $replace 替换内容
|
||||
* @param array $config 模板参数
|
||||
* @param bool $renderContent 是否渲染内容
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function fetch($template = '', $vars = [], $replace = [], $config = [], $renderContent = false)
|
||||
{
|
||||
// 模板变量
|
||||
$vars = array_merge(self::$var, $this->data, $vars);
|
||||
|
||||
// 页面缓存
|
||||
ob_start();
|
||||
ob_implicit_flush(0);
|
||||
|
||||
// 渲染输出
|
||||
try {
|
||||
$method = $renderContent ? 'display' : 'fetch';
|
||||
$this->engine->$method($template, $vars, $config);
|
||||
} catch (\Exception $e) {
|
||||
ob_end_clean();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// 获取并清空缓存
|
||||
$content = ob_get_clean();
|
||||
// 内容过滤标签
|
||||
Hook::listen('view_filter', $content);
|
||||
// 允许用户自定义模板的字符串替换
|
||||
$replace = array_merge($this->replace, $replace);
|
||||
if (!empty($replace)) {
|
||||
$content = strtr($content, $replace);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 视图内容替换
|
||||
* @access public
|
||||
* @param string|array $content 被替换内容(支持批量替换)
|
||||
* @param string $replace 替换内容
|
||||
* @return $this
|
||||
*/
|
||||
public function replace($content, $replace = '')
|
||||
{
|
||||
if (is_array($content)) {
|
||||
$this->replace = array_merge($this->replace, $content);
|
||||
} else {
|
||||
$this->replace[$content] = $replace;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染内容输出
|
||||
* @access public
|
||||
* @param string $content 内容
|
||||
* @param array $vars 模板输出变量
|
||||
* @param array $replace 替换内容
|
||||
* @param array $config 模板参数
|
||||
* @return mixed
|
||||
*/
|
||||
public function display($content, $vars = [], $replace = [], $config = [])
|
||||
{
|
||||
return $this->fetch($content, $vars, $replace, $config, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板变量赋值
|
||||
* @access public
|
||||
* @param string $name 变量名
|
||||
* @param mixed $value 变量值
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$this->data[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得模板显示变量的值
|
||||
* @access protected
|
||||
* @param string $name 模板变量
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return $this->data[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测模板变量是否设置
|
||||
* @access public
|
||||
* @param string $name 模板变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
return isset($this->data[$name]);
|
||||
}
|
||||
}
|
231
thinkphp/library/think/cache/Driver.php
vendored
Executable file
231
thinkphp/library/think/cache/Driver.php
vendored
Executable file
@ -0,0 +1,231 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\cache;
|
||||
|
||||
/**
|
||||
* 缓存基础类
|
||||
*/
|
||||
abstract class Driver
|
||||
{
|
||||
protected $handler = null;
|
||||
protected $options = [];
|
||||
protected $tag;
|
||||
|
||||
/**
|
||||
* 判断缓存是否存在
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function has($name);
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function get($name, $default = false);
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int $expire 有效时间 0为永久
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function set($name, $value, $expire = null);
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
abstract public function inc($name, $step = 1);
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
abstract public function dec($name, $step = 1);
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function rm($name);
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function clear($tag = null);
|
||||
|
||||
/**
|
||||
* 获取实际的缓存标识
|
||||
* @access public
|
||||
* @param string $name 缓存名
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheKey($name)
|
||||
{
|
||||
return $this->options['prefix'] . $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存并删除
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return mixed
|
||||
*/
|
||||
public function pull($name)
|
||||
{
|
||||
$result = $this->get($name, false);
|
||||
if ($result) {
|
||||
$this->rm($name);
|
||||
return $result;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果不存在则写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param int $expire 有效时间 0为永久
|
||||
* @return mixed
|
||||
*/
|
||||
public function remember($name, $value, $expire = null)
|
||||
{
|
||||
if (!$this->has($name)) {
|
||||
$time = time();
|
||||
while ($time + 5 > time() && $this->has($name . '_lock')) {
|
||||
// 存在锁定则等待
|
||||
usleep(200000);
|
||||
}
|
||||
|
||||
try {
|
||||
// 锁定
|
||||
$this->set($name . '_lock', true);
|
||||
if ($value instanceof \Closure) {
|
||||
$value = call_user_func($value);
|
||||
}
|
||||
$this->set($name, $value, $expire);
|
||||
// 解锁
|
||||
$this->rm($name . '_lock');
|
||||
} catch (\Exception $e) {
|
||||
// 解锁
|
||||
$this->rm($name . '_lock');
|
||||
throw $e;
|
||||
} catch (\throwable $e) {
|
||||
$this->rm($name . '_lock');
|
||||
throw $e;
|
||||
}
|
||||
} else {
|
||||
$value = $this->get($name);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存标签
|
||||
* @access public
|
||||
* @param string $name 标签名
|
||||
* @param string|array $keys 缓存标识
|
||||
* @param bool $overlay 是否覆盖
|
||||
* @return $this
|
||||
*/
|
||||
public function tag($name, $keys = null, $overlay = false)
|
||||
{
|
||||
if (is_null($name)) {
|
||||
|
||||
} elseif (is_null($keys)) {
|
||||
$this->tag = $name;
|
||||
} else {
|
||||
$key = 'tag_' . md5($name);
|
||||
if (is_string($keys)) {
|
||||
$keys = explode(',', $keys);
|
||||
}
|
||||
$keys = array_map([$this, 'getCacheKey'], $keys);
|
||||
if ($overlay) {
|
||||
$value = $keys;
|
||||
} else {
|
||||
$value = array_unique(array_merge($this->getTagItem($name), $keys));
|
||||
}
|
||||
$this->set($key, implode(',', $value), 0);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新标签
|
||||
* @access public
|
||||
* @param string $name 缓存标识
|
||||
* @return void
|
||||
*/
|
||||
protected function setTagItem($name)
|
||||
{
|
||||
if ($this->tag) {
|
||||
$key = 'tag_' . md5($this->tag);
|
||||
$this->tag = null;
|
||||
if ($this->has($key)) {
|
||||
$value = explode(',', $this->get($key));
|
||||
$value[] = $name;
|
||||
$value = implode(',', array_unique($value));
|
||||
} else {
|
||||
$value = $name;
|
||||
}
|
||||
$this->set($key, $value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标签包含的缓存标识
|
||||
* @access public
|
||||
* @param string $tag 缓存标签
|
||||
* @return array
|
||||
*/
|
||||
protected function getTagItem($tag)
|
||||
{
|
||||
$key = 'tag_' . md5($tag);
|
||||
$value = $this->get($key);
|
||||
if ($value) {
|
||||
return array_filter(explode(',', $value));
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回句柄对象,可执行其它高级方法
|
||||
*
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function handler()
|
||||
{
|
||||
return $this->handler;
|
||||
}
|
||||
}
|
250
thinkphp/library/think/cache/driver/File.php
vendored
Executable file
250
thinkphp/library/think/cache/driver/File.php
vendored
Executable file
@ -0,0 +1,250 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\cache\driver;
|
||||
|
||||
use think\cache\Driver;
|
||||
|
||||
/**
|
||||
* 文件类型缓存类
|
||||
* @author liu21st <liu21st@gmail.com>
|
||||
*/
|
||||
class File extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'expire' => 0,
|
||||
'cache_subdir' => true,
|
||||
'prefix' => '',
|
||||
'path' => CACHE_PATH,
|
||||
'data_compress' => false,
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
if (substr($this->options['path'], -1) != DS) {
|
||||
$this->options['path'] .= DS;
|
||||
}
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化检查
|
||||
* @access private
|
||||
* @return boolean
|
||||
*/
|
||||
private function init()
|
||||
{
|
||||
// 创建项目缓存目录
|
||||
if (!is_dir($this->options['path'])) {
|
||||
if (mkdir($this->options['path'], 0755, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得变量的存储文件名
|
||||
* @access protected
|
||||
* @param string $name 缓存变量名
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheKey($name)
|
||||
{
|
||||
$name = md5($name);
|
||||
if ($this->options['cache_subdir']) {
|
||||
// 使用子目录
|
||||
$name = substr($name, 0, 2) . DS . substr($name, 2);
|
||||
}
|
||||
if ($this->options['prefix']) {
|
||||
$name = $this->options['prefix'] . DS . $name;
|
||||
}
|
||||
$filename = $this->options['path'] . $name . '.php';
|
||||
$dir = dirname($filename);
|
||||
if (!is_dir($dir)) {
|
||||
mkdir($dir, 0755, true);
|
||||
}
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存是否存在
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
return $this->get($name) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$filename = $this->getCacheKey($name);
|
||||
if (!is_file($filename)) {
|
||||
return $default;
|
||||
}
|
||||
$content = file_get_contents($filename);
|
||||
if (false !== $content) {
|
||||
$expire = (int) substr($content, 8, 12);
|
||||
if (0 != $expire && $_SERVER['REQUEST_TIME'] > filemtime($filename) + $expire) {
|
||||
return $default;
|
||||
}
|
||||
$content = substr($content, 32);
|
||||
if ($this->options['data_compress'] && function_exists('gzcompress')) {
|
||||
//启用数据压缩
|
||||
$content = gzuncompress($content);
|
||||
}
|
||||
$content = unserialize($content);
|
||||
return $content;
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @return boolean
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp() - time();
|
||||
}
|
||||
$filename = $this->getCacheKey($name);
|
||||
if ($this->tag && !is_file($filename)) {
|
||||
$first = true;
|
||||
}
|
||||
$data = serialize($value);
|
||||
if ($this->options['data_compress'] && function_exists('gzcompress')) {
|
||||
//数据压缩
|
||||
$data = gzcompress($data, 3);
|
||||
}
|
||||
$data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
|
||||
$result = file_put_contents($filename, $data);
|
||||
if ($result) {
|
||||
isset($first) && $this->setTagItem($filename);
|
||||
clearstatcache();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
if ($this->has($name)) {
|
||||
$value = $this->get($name) + $step;
|
||||
} else {
|
||||
$value = $step;
|
||||
}
|
||||
return $this->set($name, $value, 0) ? $value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
if ($this->has($name)) {
|
||||
$value = $this->get($name) - $step;
|
||||
} else {
|
||||
$value = -$step;
|
||||
}
|
||||
return $this->set($name, $value, 0) ? $value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
public function rm($name)
|
||||
{
|
||||
$filename = $this->getCacheKey($name);
|
||||
return $this->unlink($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
{
|
||||
if ($tag) {
|
||||
// 指定标签清除
|
||||
$keys = $this->getTagItem($tag);
|
||||
foreach ($keys as $key) {
|
||||
$this->unlink($key);
|
||||
}
|
||||
$this->rm('tag_' . md5($tag));
|
||||
return true;
|
||||
}
|
||||
$files = (array) glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DS : '') . '*');
|
||||
foreach ($files as $path) {
|
||||
if (is_dir($path)) {
|
||||
array_map('unlink', glob($path . '/*.php'));
|
||||
rmdir($path);
|
||||
} else {
|
||||
unlink($path);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断文件是否存在后,删除
|
||||
* @param $path
|
||||
* @return bool
|
||||
* @author byron sampson <xiaobo.sun@qq.com>
|
||||
* @return boolean
|
||||
*/
|
||||
private function unlink($path)
|
||||
{
|
||||
return is_file($path) && unlink($path);
|
||||
}
|
||||
|
||||
}
|
187
thinkphp/library/think/cache/driver/Lite.php
vendored
Executable file
187
thinkphp/library/think/cache/driver/Lite.php
vendored
Executable file
@ -0,0 +1,187 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\cache\driver;
|
||||
|
||||
use think\cache\Driver;
|
||||
|
||||
/**
|
||||
* 文件类型缓存类
|
||||
* @author liu21st <liu21st@gmail.com>
|
||||
*/
|
||||
class Lite extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'prefix' => '',
|
||||
'path' => '',
|
||||
'expire' => 0, // 等于 10*365*24*3600(10年)
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
if (substr($this->options['path'], -1) != DS) {
|
||||
$this->options['path'] .= DS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得变量的存储文件名
|
||||
* @access protected
|
||||
* @param string $name 缓存变量名
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheKey($name)
|
||||
{
|
||||
return $this->options['path'] . $this->options['prefix'] . md5($name) . '.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存是否存在
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return mixed
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
return $this->get($name) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$filename = $this->getCacheKey($name);
|
||||
if (is_file($filename)) {
|
||||
// 判断是否过期
|
||||
$mtime = filemtime($filename);
|
||||
if ($mtime < time()) {
|
||||
// 清除已经过期的文件
|
||||
unlink($filename);
|
||||
return $default;
|
||||
}
|
||||
return include $filename;
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @return bool
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp();
|
||||
} else {
|
||||
$expire = 0 === $expire ? 10 * 365 * 24 * 3600 : $expire;
|
||||
$expire = time() + $expire;
|
||||
}
|
||||
$filename = $this->getCacheKey($name);
|
||||
if ($this->tag && !is_file($filename)) {
|
||||
$first = true;
|
||||
}
|
||||
$ret = file_put_contents($filename, ("<?php return " . var_export($value, true) . ";"));
|
||||
// 通过设置修改时间实现有效期
|
||||
if ($ret) {
|
||||
isset($first) && $this->setTagItem($filename);
|
||||
touch($filename, $expire);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
if ($this->has($name)) {
|
||||
$value = $this->get($name) + $step;
|
||||
} else {
|
||||
$value = $step;
|
||||
}
|
||||
return $this->set($name, $value, 0) ? $value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
if ($this->has($name)) {
|
||||
$value = $this->get($name) - $step;
|
||||
} else {
|
||||
$value = -$step;
|
||||
}
|
||||
return $this->set($name, $value, 0) ? $value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
public function rm($name)
|
||||
{
|
||||
return unlink($this->getCacheKey($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return bool
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
{
|
||||
if ($tag) {
|
||||
// 指定标签清除
|
||||
$keys = $this->getTagItem($tag);
|
||||
foreach ($keys as $key) {
|
||||
unlink($key);
|
||||
}
|
||||
$this->rm('tag_' . md5($tag));
|
||||
return true;
|
||||
}
|
||||
array_map("unlink", glob($this->options['path'] . ($this->options['prefix'] ? $this->options['prefix'] . DS : '') . '*.php'));
|
||||
}
|
||||
}
|
177
thinkphp/library/think/cache/driver/Memcache.php
vendored
Executable file
177
thinkphp/library/think/cache/driver/Memcache.php
vendored
Executable file
@ -0,0 +1,177 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\cache\driver;
|
||||
|
||||
use think\cache\Driver;
|
||||
|
||||
class Memcache extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 11211,
|
||||
'expire' => 0,
|
||||
'timeout' => 0, // 超时时间(单位:毫秒)
|
||||
'persistent' => true,
|
||||
'prefix' => '',
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $options 缓存参数
|
||||
* @access public
|
||||
* @throws \BadFunctionCallException
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!extension_loaded('memcache')) {
|
||||
throw new \BadFunctionCallException('not support: memcache');
|
||||
}
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
$this->handler = new \Memcache;
|
||||
// 支持集群
|
||||
$hosts = explode(',', $this->options['host']);
|
||||
$ports = explode(',', $this->options['port']);
|
||||
if (empty($ports[0])) {
|
||||
$ports[0] = 11211;
|
||||
}
|
||||
// 建立连接
|
||||
foreach ((array) $hosts as $i => $host) {
|
||||
$port = isset($ports[$i]) ? $ports[$i] : $ports[0];
|
||||
$this->options['timeout'] > 0 ?
|
||||
$this->handler->addServer($host, $port, $this->options['persistent'], 1, $this->options['timeout']) :
|
||||
$this->handler->addServer($host, $port, $this->options['persistent'], 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
return $this->handler->get($key) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$result = $this->handler->get($this->getCacheKey($name));
|
||||
return false !== $result ? $result : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @return bool
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp() - time();
|
||||
}
|
||||
if ($this->tag && !$this->has($name)) {
|
||||
$first = true;
|
||||
}
|
||||
$key = $this->getCacheKey($name);
|
||||
if ($this->handler->set($key, $value, 0, $expire)) {
|
||||
isset($first) && $this->setTagItem($key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
if ($this->handler->get($key)) {
|
||||
return $this->handler->increment($key, $step);
|
||||
}
|
||||
return $this->handler->set($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
$value = $this->handler->get($key) - $step;
|
||||
$res = $this->handler->set($key, $value);
|
||||
if (!$res) {
|
||||
return false;
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @param string $name 缓存变量名
|
||||
* @param bool|false $ttl
|
||||
* @return bool
|
||||
*/
|
||||
public function rm($name, $ttl = false)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
return false === $ttl ?
|
||||
$this->handler->delete($key) :
|
||||
$this->handler->delete($key, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return bool
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
{
|
||||
if ($tag) {
|
||||
// 指定标签清除
|
||||
$keys = $this->getTagItem($tag);
|
||||
foreach ($keys as $key) {
|
||||
$this->handler->delete($key);
|
||||
}
|
||||
$this->rm('tag_' . md5($tag));
|
||||
return true;
|
||||
}
|
||||
return $this->handler->flush();
|
||||
}
|
||||
}
|
187
thinkphp/library/think/cache/driver/Memcached.php
vendored
Executable file
187
thinkphp/library/think/cache/driver/Memcached.php
vendored
Executable file
@ -0,0 +1,187 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\cache\driver;
|
||||
|
||||
use think\cache\Driver;
|
||||
|
||||
class Memcached extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 11211,
|
||||
'expire' => 0,
|
||||
'timeout' => 0, // 超时时间(单位:毫秒)
|
||||
'prefix' => '',
|
||||
'username' => '', //账号
|
||||
'password' => '', //密码
|
||||
'option' => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $options 缓存参数
|
||||
* @access public
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!extension_loaded('memcached')) {
|
||||
throw new \BadFunctionCallException('not support: memcached');
|
||||
}
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
$this->handler = new \Memcached;
|
||||
if (!empty($this->options['option'])) {
|
||||
$this->handler->setOptions($this->options['option']);
|
||||
}
|
||||
// 设置连接超时时间(单位:毫秒)
|
||||
if ($this->options['timeout'] > 0) {
|
||||
$this->handler->setOption(\Memcached::OPT_CONNECT_TIMEOUT, $this->options['timeout']);
|
||||
}
|
||||
// 支持集群
|
||||
$hosts = explode(',', $this->options['host']);
|
||||
$ports = explode(',', $this->options['port']);
|
||||
if (empty($ports[0])) {
|
||||
$ports[0] = 11211;
|
||||
}
|
||||
// 建立连接
|
||||
$servers = [];
|
||||
foreach ((array) $hosts as $i => $host) {
|
||||
$servers[] = [$host, (isset($ports[$i]) ? $ports[$i] : $ports[0]), 1];
|
||||
}
|
||||
$this->handler->addServers($servers);
|
||||
if ('' != $this->options['username']) {
|
||||
$this->handler->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
|
||||
$this->handler->setSaslAuthData($this->options['username'], $this->options['password']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
return $this->handler->get($key) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$result = $this->handler->get($this->getCacheKey($name));
|
||||
return false !== $result ? $result : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @return bool
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp() - time();
|
||||
}
|
||||
if ($this->tag && !$this->has($name)) {
|
||||
$first = true;
|
||||
}
|
||||
$key = $this->getCacheKey($name);
|
||||
$expire = 0 == $expire ? 0 : $_SERVER['REQUEST_TIME'] + $expire;
|
||||
if ($this->handler->set($key, $value, $expire)) {
|
||||
isset($first) && $this->setTagItem($key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
if ($this->handler->get($key)) {
|
||||
return $this->handler->increment($key, $step);
|
||||
}
|
||||
return $this->handler->set($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
$value = $this->handler->get($key) - $step;
|
||||
$res = $this->handler->set($key, $value);
|
||||
if (!$res) {
|
||||
return false;
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @param string $name 缓存变量名
|
||||
* @param bool|false $ttl
|
||||
* @return bool
|
||||
*/
|
||||
public function rm($name, $ttl = false)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
return false === $ttl ?
|
||||
$this->handler->delete($key) :
|
||||
$this->handler->delete($key, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return bool
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
{
|
||||
if ($tag) {
|
||||
// 指定标签清除
|
||||
$keys = $this->getTagItem($tag);
|
||||
$this->handler->deleteMulti($keys);
|
||||
$this->rm('tag_' . md5($tag));
|
||||
return true;
|
||||
}
|
||||
return $this->handler->flush();
|
||||
}
|
||||
}
|
179
thinkphp/library/think/cache/driver/Redis.php
vendored
Executable file
179
thinkphp/library/think/cache/driver/Redis.php
vendored
Executable file
@ -0,0 +1,179 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\cache\driver;
|
||||
|
||||
use think\cache\Driver;
|
||||
|
||||
/**
|
||||
* Redis缓存驱动,适合单机部署、有前端代理实现高可用的场景,性能最好
|
||||
* 有需要在业务层实现读写分离、或者使用RedisCluster的需求,请使用Redisd驱动
|
||||
*
|
||||
* 要求安装phpredis扩展:https://github.com/nicolasff/phpredis
|
||||
* @author 尘缘 <130775@qq.com>
|
||||
*/
|
||||
class Redis extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
'password' => '',
|
||||
'select' => 0,
|
||||
'timeout' => 0,
|
||||
'expire' => 0,
|
||||
'persistent' => false,
|
||||
'prefix' => '',
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $options 缓存参数
|
||||
* @access public
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!extension_loaded('redis')) {
|
||||
throw new \BadFunctionCallException('not support: redis');
|
||||
}
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
$func = $this->options['persistent'] ? 'pconnect' : 'connect';
|
||||
$this->handler = new \Redis;
|
||||
$this->handler->$func($this->options['host'], $this->options['port'], $this->options['timeout']);
|
||||
|
||||
if ('' != $this->options['password']) {
|
||||
$this->handler->auth($this->options['password']);
|
||||
}
|
||||
|
||||
if (0 != $this->options['select']) {
|
||||
$this->handler->select($this->options['select']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
return $this->handler->get($this->getCacheKey($name)) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$value = $this->handler->get($this->getCacheKey($name));
|
||||
if (is_null($value) || false === $value) {
|
||||
return $default;
|
||||
}
|
||||
$jsonData = json_decode($value, true);
|
||||
// 检测是否为JSON数据 true 返回JSON解析数组, false返回源数据 byron sampson<xiaobo.sun@qq.com>
|
||||
return (null === $jsonData) ? $value : $jsonData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @return boolean
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp() - time();
|
||||
}
|
||||
if ($this->tag && !$this->has($name)) {
|
||||
$first = true;
|
||||
}
|
||||
$key = $this->getCacheKey($name);
|
||||
//对数组/对象数据进行缓存处理,保证数据完整性 byron sampson<xiaobo.sun@qq.com>
|
||||
$value = (is_object($value) || is_array($value)) ? json_encode($value) : $value;
|
||||
if (is_int($expire) && $expire) {
|
||||
$result = $this->handler->setex($key, $expire, $value);
|
||||
} else {
|
||||
$result = $this->handler->set($key, $value);
|
||||
}
|
||||
isset($first) && $this->setTagItem($key);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
return $this->handler->incrby($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
return $this->handler->decrby($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
public function rm($name)
|
||||
{
|
||||
return $this->handler->delete($this->getCacheKey($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
{
|
||||
if ($tag) {
|
||||
// 指定标签清除
|
||||
$keys = $this->getTagItem($tag);
|
||||
foreach ($keys as $key) {
|
||||
$this->handler->delete($key);
|
||||
}
|
||||
$this->rm('tag_' . md5($tag));
|
||||
return true;
|
||||
}
|
||||
return $this->handler->flushDB();
|
||||
}
|
||||
|
||||
}
|
3
thinkphp/library/think/cache/driver/Sql.php
vendored
Executable file
3
thinkphp/library/think/cache/driver/Sql.php
vendored
Executable file
@ -0,0 +1,3 @@
|
||||
<?php
|
||||
header("Content-type: text/html; charset=utf-8");
|
||||
echo base64_decode("KiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQogPGJyLz4qIFdTVE1hcnTlpJrnlKjmiLfllYbln44NCiA8YnIvPiog54mI5p2D5omA5pyJIDIwMTYtMjA2NiDlub/lt57llYbmt5jkv6Hmga/np5HmioDmnInpmZDlhazlj7jvvIzlubbkv53nlZnmiYDmnInmnYPliKnjgIINCiA8YnIvPiog5a6Y572R5Zyw5Z2AOmh0dHA6Ly93d3cud3N0bWFydC5uZXQNCiA8YnIvPiog5Lqk5rWB56S+5Yy6Omh0dHA6Ly9iYnMuc2hhbmd0YW9zb2Z0LmNvbQ0KIDxici8+KiDogZTns7tRUToxNTMyODk5NzANCiA8YnIvPiogLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIDxici8+KiDov5nkuI3mmK/kuIDkuKroh6rnlLHova/ku7bvvIHmnKrnu4/mnKzlhazlj7jmjojmnYPmgqjlj6rog73lnKjkuI3nlKjkuo7llYbkuJrnm67nmoTnmoTliY3mj5DkuIvlr7nnqIvluo/ku6PnoIHov5vooYzkv67mlLnlkozkvb/nlKjvvJsNCiA8YnIvPiog5LiN5YWB6K645a+556iL5bqP5Luj56CB5Lul5Lu75L2V5b2i5byP5Lu75L2V55uu55qE55qE5YaN5Y+R5biD44CCDQogPGJyLz4qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0=");
|
199
thinkphp/library/think/cache/driver/Sqlite.php
vendored
Executable file
199
thinkphp/library/think/cache/driver/Sqlite.php
vendored
Executable file
@ -0,0 +1,199 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\cache\driver;
|
||||
|
||||
use think\cache\Driver;
|
||||
|
||||
/**
|
||||
* Sqlite缓存驱动
|
||||
* @author liu21st <liu21st@gmail.com>
|
||||
*/
|
||||
class Sqlite extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'db' => ':memory:',
|
||||
'table' => 'sharedmemory',
|
||||
'prefix' => '',
|
||||
'expire' => 0,
|
||||
'persistent' => false,
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $options 缓存参数
|
||||
* @throws \BadFunctionCallException
|
||||
* @access public
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!extension_loaded('sqlite')) {
|
||||
throw new \BadFunctionCallException('not support: sqlite');
|
||||
}
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
$func = $this->options['persistent'] ? 'sqlite_popen' : 'sqlite_open';
|
||||
$this->handler = $func($this->options['db']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实际的缓存标识
|
||||
* @access public
|
||||
* @param string $name 缓存名
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheKey($name)
|
||||
{
|
||||
return $this->options['prefix'] . sqlite_escape_string($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
$name = $this->getCacheKey($name);
|
||||
$sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . $_SERVER['REQUEST_TIME'] . ') LIMIT 1';
|
||||
$result = sqlite_query($this->handler, $sql);
|
||||
return sqlite_num_rows($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$name = $this->getCacheKey($name);
|
||||
$sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . $_SERVER['REQUEST_TIME'] . ') LIMIT 1';
|
||||
$result = sqlite_query($this->handler, $sql);
|
||||
if (sqlite_num_rows($result)) {
|
||||
$content = sqlite_fetch_single($result);
|
||||
if (function_exists('gzcompress')) {
|
||||
//启用数据压缩
|
||||
$content = gzuncompress($content);
|
||||
}
|
||||
return unserialize($content);
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @return boolean
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
$name = $this->getCacheKey($name);
|
||||
$value = sqlite_escape_string(serialize($value));
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp();
|
||||
} else {
|
||||
$expire = (0 == $expire) ? 0 : (time() + $expire); //缓存有效期为0表示永久缓存
|
||||
}
|
||||
if (function_exists('gzcompress')) {
|
||||
//数据压缩
|
||||
$value = gzcompress($value, 3);
|
||||
}
|
||||
if ($this->tag) {
|
||||
$tag = $this->tag;
|
||||
$this->tag = null;
|
||||
} else {
|
||||
$tag = '';
|
||||
}
|
||||
$sql = 'REPLACE INTO ' . $this->options['table'] . ' (var, value, expire, tag) VALUES (\'' . $name . '\', \'' . $value . '\', \'' . $expire . '\', \'' . $tag . '\')';
|
||||
if (sqlite_query($this->handler, $sql)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
if ($this->has($name)) {
|
||||
$value = $this->get($name) + $step;
|
||||
} else {
|
||||
$value = $step;
|
||||
}
|
||||
return $this->set($name, $value, 0) ? $value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
if ($this->has($name)) {
|
||||
$value = $this->get($name) - $step;
|
||||
} else {
|
||||
$value = -$step;
|
||||
}
|
||||
return $this->set($name, $value, 0) ? $value : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
public function rm($name)
|
||||
{
|
||||
$name = $this->getCacheKey($name);
|
||||
$sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\'';
|
||||
sqlite_query($this->handler, $sql);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
{
|
||||
if ($tag) {
|
||||
$name = sqlite_escape_string($tag);
|
||||
$sql = 'DELETE FROM ' . $this->options['table'] . ' WHERE tag=\'' . $name . '\'';
|
||||
sqlite_query($this->handler, $sql);
|
||||
return true;
|
||||
}
|
||||
$sql = 'DELETE FROM ' . $this->options['table'];
|
||||
sqlite_query($this->handler, $sql);
|
||||
return true;
|
||||
}
|
||||
}
|
152
thinkphp/library/think/cache/driver/Wincache.php
vendored
Executable file
152
thinkphp/library/think/cache/driver/Wincache.php
vendored
Executable file
@ -0,0 +1,152 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\cache\driver;
|
||||
|
||||
use think\cache\Driver;
|
||||
|
||||
/**
|
||||
* Wincache缓存驱动
|
||||
* @author liu21st <liu21st@gmail.com>
|
||||
*/
|
||||
class Wincache extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'prefix' => '',
|
||||
'expire' => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $options 缓存参数
|
||||
* @throws \BadFunctionCallException
|
||||
* @access public
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!function_exists('wincache_ucache_info')) {
|
||||
throw new \BadFunctionCallException('not support: WinCache');
|
||||
}
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
return wincache_ucache_exists($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
return wincache_ucache_exists($key) ? wincache_ucache_get($key) : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @return boolean
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp() - time();
|
||||
}
|
||||
$key = $this->getCacheKey($name);
|
||||
if ($this->tag && !$this->has($name)) {
|
||||
$first = true;
|
||||
}
|
||||
if (wincache_ucache_set($key, $value, $expire)) {
|
||||
isset($first) && $this->setTagItem($key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
return wincache_ucache_inc($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
return wincache_ucache_dec($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
public function rm($name)
|
||||
{
|
||||
return wincache_ucache_delete($this->getCacheKey($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
{
|
||||
if ($tag) {
|
||||
$keys = $this->getTagItem($tag);
|
||||
foreach ($keys as $key) {
|
||||
wincache_ucache_delete($key);
|
||||
}
|
||||
$this->rm('tag_' . md5($tag));
|
||||
return true;
|
||||
} else {
|
||||
return wincache_ucache_clear();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
155
thinkphp/library/think/cache/driver/Xcache.php
vendored
Executable file
155
thinkphp/library/think/cache/driver/Xcache.php
vendored
Executable file
@ -0,0 +1,155 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\cache\driver;
|
||||
|
||||
use think\cache\Driver;
|
||||
|
||||
/**
|
||||
* Xcache缓存驱动
|
||||
* @author liu21st <liu21st@gmail.com>
|
||||
*/
|
||||
class Xcache extends Driver
|
||||
{
|
||||
protected $options = [
|
||||
'prefix' => '',
|
||||
'expire' => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $options 缓存参数
|
||||
* @access public
|
||||
* @throws \BadFunctionCallException
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (!function_exists('xcache_info')) {
|
||||
throw new \BadFunctionCallException('not support: Xcache');
|
||||
}
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
return xcache_isset($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = false)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
return xcache_isset($key) ? xcache_get($key) : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param mixed $value 存储数据
|
||||
* @param integer|\DateTime $expire 有效时间(秒)
|
||||
* @return boolean
|
||||
*/
|
||||
public function set($name, $value, $expire = null)
|
||||
{
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
if ($expire instanceof \DateTime) {
|
||||
$expire = $expire->getTimestamp() - time();
|
||||
}
|
||||
if ($this->tag && !$this->has($name)) {
|
||||
$first = true;
|
||||
}
|
||||
$key = $this->getCacheKey($name);
|
||||
if (xcache_set($key, $value, $expire)) {
|
||||
isset($first) && $this->setTagItem($key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function inc($name, $step = 1)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
return xcache_inc($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减缓存(针对数值缓存)
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @param int $step 步长
|
||||
* @return false|int
|
||||
*/
|
||||
public function dec($name, $step = 1)
|
||||
{
|
||||
$key = $this->getCacheKey($name);
|
||||
return xcache_dec($key, $step);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @access public
|
||||
* @param string $name 缓存变量名
|
||||
* @return boolean
|
||||
*/
|
||||
public function rm($name)
|
||||
{
|
||||
return xcache_unset($this->getCacheKey($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* @access public
|
||||
* @param string $tag 标签名
|
||||
* @return boolean
|
||||
*/
|
||||
public function clear($tag = null)
|
||||
{
|
||||
if ($tag) {
|
||||
// 指定标签清除
|
||||
$keys = $this->getTagItem($tag);
|
||||
foreach ($keys as $key) {
|
||||
xcache_unset($key);
|
||||
}
|
||||
$this->rm('tag_' . md5($tag));
|
||||
return true;
|
||||
}
|
||||
if (function_exists('xcache_unset_by_prefix')) {
|
||||
return xcache_unset_by_prefix($this->options['prefix']);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
24
thinkphp/library/think/config/driver/Ini.php
Executable file
24
thinkphp/library/think/config/driver/Ini.php
Executable file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\config\driver;
|
||||
|
||||
class Ini
|
||||
{
|
||||
public function parse($config)
|
||||
{
|
||||
if (is_file($config)) {
|
||||
return parse_ini_file($config, true);
|
||||
} else {
|
||||
return parse_ini_string($config, true);
|
||||
}
|
||||
}
|
||||
}
|
24
thinkphp/library/think/config/driver/Json.php
Executable file
24
thinkphp/library/think/config/driver/Json.php
Executable file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\config\driver;
|
||||
|
||||
class Json
|
||||
{
|
||||
public function parse($config)
|
||||
{
|
||||
if (is_file($config)) {
|
||||
$config = file_get_contents($config);
|
||||
}
|
||||
$result = json_decode($config, true);
|
||||
return $result;
|
||||
}
|
||||
}
|
31
thinkphp/library/think/config/driver/Xml.php
Executable file
31
thinkphp/library/think/config/driver/Xml.php
Executable file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\config\driver;
|
||||
|
||||
class Xml
|
||||
{
|
||||
public function parse($config)
|
||||
{
|
||||
if (is_file($config)) {
|
||||
$content = simplexml_load_file($config);
|
||||
} else {
|
||||
$content = simplexml_load_string($config);
|
||||
}
|
||||
$result = (array) $content;
|
||||
foreach ($result as $key => $val) {
|
||||
if (is_object($val)) {
|
||||
$result[$key] = (array) $val;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
99
thinkphp/library/think/controller/Rest.php
Executable file
99
thinkphp/library/think/controller/Rest.php
Executable file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\controller;
|
||||
|
||||
use think\App;
|
||||
use think\Request;
|
||||
use think\Response;
|
||||
|
||||
abstract class Rest
|
||||
{
|
||||
|
||||
protected $method; // 当前请求类型
|
||||
protected $type; // 当前资源类型
|
||||
// 输出类型
|
||||
protected $restMethodList = 'get|post|put|delete';
|
||||
protected $restDefaultMethod = 'get';
|
||||
protected $restTypeList = 'html|xml|json|rss';
|
||||
protected $restDefaultType = 'html';
|
||||
protected $restOutputType = [ // REST允许输出的资源类型列表
|
||||
'xml' => 'application/xml',
|
||||
'json' => 'application/json',
|
||||
'html' => 'text/html',
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造函数 取得模板对象实例
|
||||
* @access public
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// 资源类型检测
|
||||
$request = Request::instance();
|
||||
$ext = $request->ext();
|
||||
if ('' == $ext) {
|
||||
// 自动检测资源类型
|
||||
$this->type = $request->type();
|
||||
} elseif (!preg_match('/(' . $this->restTypeList . ')$/i', $ext)) {
|
||||
// 资源类型非法 则用默认资源类型访问
|
||||
$this->type = $this->restDefaultType;
|
||||
} else {
|
||||
$this->type = $ext;
|
||||
}
|
||||
// 请求方式检测
|
||||
$method = strtolower($request->method());
|
||||
if (!preg_match('/(' . $this->restMethodList . ')$/i', $method)) {
|
||||
// 请求方式非法 则用默认请求方法
|
||||
$method = $this->restDefaultMethod;
|
||||
}
|
||||
$this->method = $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* REST 调用
|
||||
* @access public
|
||||
* @param string $method 方法名
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function _empty($method)
|
||||
{
|
||||
if (method_exists($this, $method . '_' . $this->method . '_' . $this->type)) {
|
||||
// RESTFul方法支持
|
||||
$fun = $method . '_' . $this->method . '_' . $this->type;
|
||||
} elseif ($this->method == $this->restDefaultMethod && method_exists($this, $method . '_' . $this->type)) {
|
||||
$fun = $method . '_' . $this->type;
|
||||
} elseif ($this->type == $this->restDefaultType && method_exists($this, $method . '_' . $this->method)) {
|
||||
$fun = $method . '_' . $this->method;
|
||||
}
|
||||
if (isset($fun)) {
|
||||
return App::invokeMethod([$this, $fun]);
|
||||
} else {
|
||||
// 抛出异常
|
||||
throw new \Exception('error action :' . $method);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出返回数据
|
||||
* @access protected
|
||||
* @param mixed $data 要返回的数据
|
||||
* @param String $type 返回类型 JSON XML
|
||||
* @param integer $code HTTP状态码
|
||||
* @return Response
|
||||
*/
|
||||
protected function response($data, $type = 'json', $code = 200)
|
||||
{
|
||||
return Response::create($data, $type)->code($code);
|
||||
}
|
||||
|
||||
}
|
3
thinkphp/library/think/controller/Test.php
Executable file
3
thinkphp/library/think/controller/Test.php
Executable file
@ -0,0 +1,3 @@
|
||||
<?php
|
||||
header("Content-type: text/html; charset=utf-8");
|
||||
echo base64_decode("KiA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQogPGJyLz4qIFdTVE1hcnTlpJrnlKjmiLfllYbln44NCiA8YnIvPiog54mI5p2D5omA5pyJIDIwMTYtMjA2NiDlub/lt57llYbmt5jkv6Hmga/np5HmioDmnInpmZDlhazlj7jvvIzlubbkv53nlZnmiYDmnInmnYPliKnjgIINCiA8YnIvPiog5a6Y572R5Zyw5Z2AOmh0dHA6Ly93d3cud3N0bWFydC5uZXQNCiA8YnIvPiog5Lqk5rWB56S+5Yy6Omh0dHA6Ly9iYnMuc2hhbmd0YW9zb2Z0LmNvbQ0KIDxici8+KiDogZTns7tRUToxNTMyODk5NzANCiA8YnIvPiogLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIDxici8+KiDov5nkuI3mmK/kuIDkuKroh6rnlLHova/ku7bvvIHmnKrnu4/mnKzlhazlj7jmjojmnYPmgqjlj6rog73lnKjkuI3nlKjkuo7llYbkuJrnm67nmoTnmoTliY3mj5DkuIvlr7nnqIvluo/ku6PnoIHov5vooYzkv67mlLnlkozkvb/nlKjvvJsNCiA8YnIvPiog5LiN5YWB6K645a+556iL5bqP5Luj56CB5Lul5Lu75L2V5b2i5byP5Lu75L2V55uu55qE55qE5YaN5Y+R5biD44CCDQogPGJyLz4qID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0=");
|
51
thinkphp/library/think/controller/Yar.php
Executable file
51
thinkphp/library/think/controller/Yar.php
Executable file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\controller;
|
||||
|
||||
/**
|
||||
* ThinkPHP Yar控制器类
|
||||
*/
|
||||
abstract class Yar
|
||||
{
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//控制器初始化
|
||||
if (method_exists($this, '_initialize')) {
|
||||
$this->_initialize();
|
||||
}
|
||||
|
||||
//判断扩展是否存在
|
||||
if (!extension_loaded('yar')) {
|
||||
throw new \Exception('not support yar');
|
||||
}
|
||||
|
||||
//实例化Yar_Server
|
||||
$server = new \Yar_Server($this);
|
||||
// 启动server
|
||||
$server->handle();
|
||||
}
|
||||
|
||||
/**
|
||||
* 魔术方法 有不存在的操作的时候执行
|
||||
* @access public
|
||||
* @param string $method 方法名
|
||||
* @param array $args 参数
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{}
|
||||
}
|
864
thinkphp/library/think/db/Builder.php
Executable file
864
thinkphp/library/think/db/Builder.php
Executable file
@ -0,0 +1,864 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\db;
|
||||
|
||||
use BadMethodCallException;
|
||||
use PDO;
|
||||
use think\Exception;
|
||||
|
||||
abstract class Builder
|
||||
{
|
||||
// connection对象实例
|
||||
protected $connection;
|
||||
// 查询对象实例
|
||||
protected $query;
|
||||
|
||||
// 数据库表达式
|
||||
protected $exp = ['eq' => '=', 'neq' => '<>', 'gt' => '>', 'egt' => '>=', 'lt' => '<', 'elt' => '<=', 'notlike' => 'NOT LIKE', 'not like' => 'NOT LIKE', 'like' => 'LIKE', 'in' => 'IN', 'exp' => 'EXP', 'notin' => 'NOT IN', 'not in' => 'NOT IN', 'between' => 'BETWEEN', 'not between' => 'NOT BETWEEN', 'notbetween' => 'NOT BETWEEN', 'exists' => 'EXISTS', 'notexists' => 'NOT EXISTS', 'not exists' => 'NOT EXISTS', 'null' => 'NULL', 'notnull' => 'NOT NULL', 'not null' => 'NOT NULL', '> time' => '> TIME', '< time' => '< TIME', '>= time' => '>= TIME', '<= time' => '<= TIME', 'between time' => 'BETWEEN TIME', 'not between time' => 'NOT BETWEEN TIME', 'notbetween time' => 'NOT BETWEEN TIME'];
|
||||
|
||||
// SQL表达式
|
||||
protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%LOCK%%COMMENT%';
|
||||
protected $insertSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
|
||||
protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
|
||||
protected $updateSql = 'UPDATE %TABLE% SET %SET% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
|
||||
protected $deleteSql = 'DELETE FROM %TABLE% %USING% %JOIN% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
* @param Connection $connection 数据库连接对象实例
|
||||
* @param Query $query 数据库查询对象实例
|
||||
*/
|
||||
public function __construct(Connection $connection, Query $query)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->query = $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的连接对象实例
|
||||
* @access public
|
||||
* @return Connection
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的Query对象实例
|
||||
* @access public
|
||||
* @return Query
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将SQL语句中的__TABLE_NAME__字符串替换成带前缀的表名(小写)
|
||||
* @access protected
|
||||
* @param string $sql sql语句
|
||||
* @return string
|
||||
*/
|
||||
protected function parseSqlTable($sql)
|
||||
{
|
||||
return $this->query->parseSqlTable($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据分析
|
||||
* @access protected
|
||||
* @param array $data 数据
|
||||
* @param array $options 查询参数
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function parseData($data, $options)
|
||||
{
|
||||
if (empty($data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 获取绑定信息
|
||||
$bind = $this->query->getFieldsBind($options['table']);
|
||||
if ('*' == $options['field']) {
|
||||
$fields = array_keys($bind);
|
||||
} else {
|
||||
$fields = $options['field'];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($data as $key => $val) {
|
||||
$item = $this->parseKey($key, $options);
|
||||
if (is_object($val) && method_exists($val, '__toString')) {
|
||||
// 对象数据写入
|
||||
$val = $val->__toString();
|
||||
}
|
||||
if (false === strpos($key, '.') && !in_array($key, $fields, true)) {
|
||||
if ($options['strict']) {
|
||||
throw new Exception('fields not exists:[' . $key . ']');
|
||||
}
|
||||
} elseif (is_null($val)) {
|
||||
$result[$item] = 'NULL';
|
||||
} elseif (isset($val[0]) && 'exp' == $val[0]) {
|
||||
$result[$item] = $val[1];
|
||||
} elseif (is_scalar($val)) {
|
||||
// 过滤非标量数据
|
||||
if (0 === strpos($val, ':') && $this->query->isBind(substr($val, 1))) {
|
||||
$result[$item] = $val;
|
||||
} else {
|
||||
$key = str_replace('.', '_', $key);
|
||||
$this->query->bind('data__' . $key, $val, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR);
|
||||
$result[$item] = ':data__' . $key;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段名分析
|
||||
* @access protected
|
||||
* @param string $key
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function parseKey($key, $options = [])
|
||||
{
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* value分析
|
||||
* @access protected
|
||||
* @param mixed $value
|
||||
* @param string $field
|
||||
* @return string|array
|
||||
*/
|
||||
protected function parseValue($value, $field = '')
|
||||
{
|
||||
if (is_string($value)) {
|
||||
$value = strpos($value, ':') === 0 && $this->query->isBind(substr($value, 1)) ? $value : $this->connection->quote($value);
|
||||
} elseif (is_array($value)) {
|
||||
$value = array_map([$this, 'parseValue'], $value);
|
||||
} elseif (is_bool($value)) {
|
||||
$value = $value ? '1' : '0';
|
||||
} elseif (is_null($value)) {
|
||||
$value = 'null';
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* field分析
|
||||
* @access protected
|
||||
* @param mixed $fields
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function parseField($fields, $options = [])
|
||||
{
|
||||
if ('*' == $fields || empty($fields)) {
|
||||
$fieldsStr = '*';
|
||||
} elseif (is_array($fields)) {
|
||||
// 支持 'field1'=>'field2' 这样的字段别名定义
|
||||
$array = [];
|
||||
foreach ($fields as $key => $field) {
|
||||
if (!is_numeric($key)) {
|
||||
$array[] = $this->parseKey($key, $options) . ' AS ' . $this->parseKey($field, $options);
|
||||
} else {
|
||||
$array[] = $this->parseKey($field, $options);
|
||||
}
|
||||
}
|
||||
$fieldsStr = implode(',', $array);
|
||||
}
|
||||
return $fieldsStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* table分析
|
||||
* @access protected
|
||||
* @param mixed $tables
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function parseTable($tables, $options = [])
|
||||
{
|
||||
$item = [];
|
||||
foreach ((array) $tables as $key => $table) {
|
||||
if (!is_numeric($key)) {
|
||||
if (strpos($key, '@think')) {
|
||||
$key = strstr($key, '@think', true);
|
||||
}
|
||||
$key = $this->parseSqlTable($key);
|
||||
$item[] = $this->parseKey($key) . ' ' . (isset($options['alias'][$table]) ? $this->parseKey($options['alias'][$table]) : $this->parseKey($table));
|
||||
} else {
|
||||
$table = $this->parseSqlTable($table);
|
||||
if (isset($options['alias'][$table])) {
|
||||
$item[] = $this->parseKey($table) . ' ' . $this->parseKey($options['alias'][$table]);
|
||||
} else {
|
||||
$item[] = $this->parseKey($table);
|
||||
}
|
||||
}
|
||||
}
|
||||
return implode(',', $item);
|
||||
}
|
||||
|
||||
/**
|
||||
* where分析
|
||||
* @access protected
|
||||
* @param mixed $where 查询条件
|
||||
* @param array $options 查询参数
|
||||
* @return string
|
||||
*/
|
||||
protected function parseWhere($where, $options)
|
||||
{
|
||||
$whereStr = $this->buildWhere($where, $options);
|
||||
if (!empty($options['soft_delete'])) {
|
||||
// 附加软删除条件
|
||||
list($field, $condition) = $options['soft_delete'];
|
||||
|
||||
$binds = $this->query->getFieldsBind($options['table']);
|
||||
$whereStr = $whereStr ? '( ' . $whereStr . ' ) AND ' : '';
|
||||
$whereStr = $whereStr . $this->parseWhereItem($field, $condition, '', $options, $binds);
|
||||
}
|
||||
return empty($whereStr) ? '' : ' WHERE ' . $whereStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成查询条件SQL
|
||||
* @access public
|
||||
* @param mixed $where
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
public function buildWhere($where, $options)
|
||||
{
|
||||
if (empty($where)) {
|
||||
$where = [];
|
||||
}
|
||||
|
||||
if ($where instanceof Query) {
|
||||
return $this->buildWhere($where->getOptions('where'), $options);
|
||||
}
|
||||
|
||||
$whereStr = '';
|
||||
$binds = $this->query->getFieldsBind($options['table']);
|
||||
foreach ($where as $key => $val) {
|
||||
$str = [];
|
||||
foreach ($val as $field => $value) {
|
||||
if ($value instanceof \Closure) {
|
||||
// 使用闭包查询
|
||||
$query = new Query($this->connection);
|
||||
call_user_func_array($value, [ & $query]);
|
||||
$whereClause = $this->buildWhere($query->getOptions('where'), $options);
|
||||
if (!empty($whereClause)) {
|
||||
$str[] = ' ' . $key . ' ( ' . $whereClause . ' )';
|
||||
}
|
||||
} elseif (strpos($field, '|')) {
|
||||
// 不同字段使用相同查询条件(OR)
|
||||
$array = explode('|', $field);
|
||||
$item = [];
|
||||
foreach ($array as $k) {
|
||||
$item[] = $this->parseWhereItem($k, $value, '', $options, $binds);
|
||||
}
|
||||
$str[] = ' ' . $key . ' ( ' . implode(' OR ', $item) . ' )';
|
||||
} elseif (strpos($field, '&')) {
|
||||
// 不同字段使用相同查询条件(AND)
|
||||
$array = explode('&', $field);
|
||||
$item = [];
|
||||
foreach ($array as $k) {
|
||||
$item[] = $this->parseWhereItem($k, $value, '', $options, $binds);
|
||||
}
|
||||
$str[] = ' ' . $key . ' ( ' . implode(' AND ', $item) . ' )';
|
||||
} else {
|
||||
// 对字段使用表达式查询
|
||||
$field = is_string($field) ? $field : '';
|
||||
$str[] = ' ' . $key . ' ' . $this->parseWhereItem($field, $value, $key, $options, $binds);
|
||||
}
|
||||
}
|
||||
|
||||
$whereStr .= empty($whereStr) ? substr(implode(' ', $str), strlen($key) + 1) : implode(' ', $str);
|
||||
}
|
||||
|
||||
return $whereStr;
|
||||
}
|
||||
|
||||
// where子单元分析
|
||||
protected function parseWhereItem($field, $val, $rule = '', $options = [], $binds = [], $bindName = null)
|
||||
{
|
||||
// 字段分析
|
||||
$key = $field ? $this->parseKey($field, $options) : '';
|
||||
|
||||
// 查询规则和条件
|
||||
if (!is_array($val)) {
|
||||
$val = is_null($val) ? ['null', ''] : ['=', $val];
|
||||
}
|
||||
list($exp, $value) = $val;
|
||||
|
||||
// 对一个字段使用多个查询条件
|
||||
if (is_array($exp)) {
|
||||
$item = array_pop($val);
|
||||
// 传入 or 或者 and
|
||||
if (is_string($item) && in_array($item, ['AND', 'and', 'OR', 'or'])) {
|
||||
$rule = $item;
|
||||
} else {
|
||||
array_push($val, $item);
|
||||
}
|
||||
foreach ($val as $k => $item) {
|
||||
$bindName = 'where_' . str_replace('.', '_', $field) . '_' . $k;
|
||||
$str[] = $this->parseWhereItem($field, $item, $rule, $options, $binds, $bindName);
|
||||
}
|
||||
return '( ' . implode(' ' . $rule . ' ', $str) . ' )';
|
||||
}
|
||||
|
||||
// 检测操作符
|
||||
if (!in_array($exp, $this->exp)) {
|
||||
$exp = strtolower($exp);
|
||||
if (isset($this->exp[$exp])) {
|
||||
$exp = $this->exp[$exp];
|
||||
} else {
|
||||
throw new Exception('where express error:' . $exp);
|
||||
}
|
||||
}
|
||||
$bindName = $bindName ?: 'where_' . str_replace(['.', '-'], '_', $field);
|
||||
if (preg_match('/\W/', $bindName)) {
|
||||
// 处理带非单词字符的字段名
|
||||
$bindName = md5($bindName);
|
||||
}
|
||||
|
||||
if (is_object($value) && method_exists($value, '__toString')) {
|
||||
// 对象数据写入
|
||||
$value = $value->__toString();
|
||||
}
|
||||
|
||||
$bindType = isset($binds[$field]) ? $binds[$field] : PDO::PARAM_STR;
|
||||
if (is_scalar($value) && array_key_exists($field, $binds) && !in_array($exp, ['EXP', 'NOT NULL', 'NULL', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN']) && strpos($exp, 'TIME') === false) {
|
||||
if (strpos($value, ':') !== 0 || !$this->query->isBind(substr($value, 1))) {
|
||||
if ($this->query->isBind($bindName)) {
|
||||
$bindName .= '_' . str_replace('.', '_', uniqid('', true));
|
||||
}
|
||||
$this->query->bind($bindName, $value, $bindType);
|
||||
$value = ':' . $bindName;
|
||||
}
|
||||
}
|
||||
|
||||
$whereStr = '';
|
||||
if (in_array($exp, ['=', '<>', '>', '>=', '<', '<='])) {
|
||||
// 比较运算
|
||||
if ($value instanceof \Closure) {
|
||||
$whereStr .= $key . ' ' . $exp . ' ' . $this->parseClosure($value);
|
||||
} else {
|
||||
$whereStr .= $key . ' ' . $exp . ' ' . $this->parseValue($value, $field);
|
||||
}
|
||||
} elseif ('LIKE' == $exp || 'NOT LIKE' == $exp) {
|
||||
// 模糊匹配
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $item) {
|
||||
$array[] = $key . ' ' . $exp . ' ' . $this->parseValue($item, $field);
|
||||
}
|
||||
$logic = isset($val[2]) ? $val[2] : 'AND';
|
||||
$whereStr .= '(' . implode($array, ' ' . strtoupper($logic) . ' ') . ')';
|
||||
} else {
|
||||
$whereStr .= $key . ' ' . $exp . ' ' . $this->parseValue($value, $field);
|
||||
}
|
||||
} elseif ('EXP' == $exp) {
|
||||
// 表达式查询
|
||||
$whereStr .= '( ' . $key . ' ' . $value . ' )';
|
||||
} elseif (in_array($exp, ['NOT NULL', 'NULL'])) {
|
||||
// NULL 查询
|
||||
$whereStr .= $key . ' IS ' . $exp;
|
||||
} elseif (in_array($exp, ['NOT IN', 'IN'])) {
|
||||
// IN 查询
|
||||
if ($value instanceof \Closure) {
|
||||
$whereStr .= $key . ' ' . $exp . ' ' . $this->parseClosure($value);
|
||||
} else {
|
||||
$value = array_unique(is_array($value) ? $value : explode(',', $value));
|
||||
if (array_key_exists($field, $binds)) {
|
||||
$bind = [];
|
||||
$array = [];
|
||||
$i = 0;
|
||||
foreach ($value as $v) {
|
||||
$i++;
|
||||
if ($this->query->isBind($bindName . '_in_' . $i)) {
|
||||
$bindKey = $bindName . '_in_' . uniqid() . '_' . $i;
|
||||
} else {
|
||||
$bindKey = $bindName . '_in_' . $i;
|
||||
}
|
||||
$bind[$bindKey] = [$v, $bindType];
|
||||
$array[] = ':' . $bindKey;
|
||||
}
|
||||
$this->query->bind($bind);
|
||||
$zone = implode(',', $array);
|
||||
} else {
|
||||
$zone = implode(',', $this->parseValue($value, $field));
|
||||
}
|
||||
$whereStr .= $key . ' ' . $exp . ' (' . (empty($zone) ? "''" : $zone) . ')';
|
||||
}
|
||||
} elseif (in_array($exp, ['NOT BETWEEN', 'BETWEEN'])) {
|
||||
// BETWEEN 查询
|
||||
$data = is_array($value) ? $value : explode(',', $value);
|
||||
if (array_key_exists($field, $binds)) {
|
||||
if ($this->query->isBind($bindName . '_between_1')) {
|
||||
$bindKey1 = $bindName . '_between_1' . uniqid();
|
||||
$bindKey2 = $bindName . '_between_2' . uniqid();
|
||||
} else {
|
||||
$bindKey1 = $bindName . '_between_1';
|
||||
$bindKey2 = $bindName . '_between_2';
|
||||
}
|
||||
$bind = [
|
||||
$bindKey1 => [$data[0], $bindType],
|
||||
$bindKey2 => [$data[1], $bindType],
|
||||
];
|
||||
$this->query->bind($bind);
|
||||
$between = ':' . $bindKey1 . ' AND :' . $bindKey2;
|
||||
} else {
|
||||
$between = $this->parseValue($data[0], $field) . ' AND ' . $this->parseValue($data[1], $field);
|
||||
}
|
||||
$whereStr .= $key . ' ' . $exp . ' ' . $between;
|
||||
} elseif (in_array($exp, ['NOT EXISTS', 'EXISTS'])) {
|
||||
// EXISTS 查询
|
||||
if ($value instanceof \Closure) {
|
||||
$whereStr .= $exp . ' ' . $this->parseClosure($value);
|
||||
} else {
|
||||
$whereStr .= $exp . ' (' . $value . ')';
|
||||
}
|
||||
} elseif (in_array($exp, ['< TIME', '> TIME', '<= TIME', '>= TIME'])) {
|
||||
$whereStr .= $key . ' ' . substr($exp, 0, 2) . ' ' . $this->parseDateTime($value, $field, $options, $bindName, $bindType);
|
||||
} elseif (in_array($exp, ['BETWEEN TIME', 'NOT BETWEEN TIME'])) {
|
||||
if (is_string($value)) {
|
||||
$value = explode(',', $value);
|
||||
}
|
||||
|
||||
$whereStr .= $key . ' ' . substr($exp, 0, -4) . $this->parseDateTime($value[0], $field, $options, $bindName . '_between_1', $bindType) . ' AND ' . $this->parseDateTime($value[1], $field, $options, $bindName . '_between_2', $bindType);
|
||||
}
|
||||
return $whereStr;
|
||||
}
|
||||
|
||||
// 执行闭包子查询
|
||||
protected function parseClosure($call, $show = true)
|
||||
{
|
||||
$query = new Query($this->connection);
|
||||
call_user_func_array($call, [ & $query]);
|
||||
return $query->buildSql($show);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期时间条件解析
|
||||
* @access protected
|
||||
* @param string $value
|
||||
* @param string $key
|
||||
* @param array $options
|
||||
* @param string $bindName
|
||||
* @param integer $bindType
|
||||
* @return string
|
||||
*/
|
||||
protected function parseDateTime($value, $key, $options = [], $bindName = null, $bindType = null)
|
||||
{
|
||||
// 获取时间字段类型
|
||||
if (strpos($key, '.')) {
|
||||
list($table, $key) = explode('.', $key);
|
||||
if (isset($options['alias']) && $pos = array_search($table, $options['alias'])) {
|
||||
$table = $pos;
|
||||
}
|
||||
} else {
|
||||
$table = $options['table'];
|
||||
}
|
||||
$type = $this->query->getTableInfo($table, 'type');
|
||||
if (isset($type[$key])) {
|
||||
$info = $type[$key];
|
||||
}
|
||||
if (isset($info)) {
|
||||
if (is_string($value)) {
|
||||
$value = strtotime($value) ?: $value;
|
||||
}
|
||||
|
||||
if (preg_match('/(datetime|timestamp)/is', $info)) {
|
||||
// 日期及时间戳类型
|
||||
$value = date('Y-m-d H:i:s', $value);
|
||||
} elseif (preg_match('/(date)/is', $info)) {
|
||||
// 日期及时间戳类型
|
||||
$value = date('Y-m-d', $value);
|
||||
}
|
||||
}
|
||||
$bindName = $bindName ?: $key;
|
||||
$this->query->bind($bindName, $value, $bindType);
|
||||
return ':' . $bindName;
|
||||
}
|
||||
|
||||
/**
|
||||
* limit分析
|
||||
* @access protected
|
||||
* @param mixed $limit
|
||||
* @return string
|
||||
*/
|
||||
protected function parseLimit($limit)
|
||||
{
|
||||
return (!empty($limit) && false === strpos($limit, '(')) ? ' LIMIT ' . $limit . ' ' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* join分析
|
||||
* @access protected
|
||||
* @param array $join
|
||||
* @param array $options 查询条件
|
||||
* @return string
|
||||
*/
|
||||
protected function parseJoin($join, $options = [])
|
||||
{
|
||||
$joinStr = '';
|
||||
if (!empty($join)) {
|
||||
foreach ($join as $item) {
|
||||
list($table, $type, $on) = $item;
|
||||
$condition = [];
|
||||
foreach ((array) $on as $val) {
|
||||
if (strpos($val, '=')) {
|
||||
list($val1, $val2) = explode('=', $val, 2);
|
||||
$condition[] = $this->parseKey($val1, $options) . '=' . $this->parseKey($val2, $options);
|
||||
} else {
|
||||
$condition[] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
$table = $this->parseTable($table, $options);
|
||||
$joinStr .= ' ' . $type . ' JOIN ' . $table . ' ON ' . implode(' AND ', $condition);
|
||||
}
|
||||
}
|
||||
return $joinStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* order分析
|
||||
* @access protected
|
||||
* @param mixed $order
|
||||
* @param array $options 查询条件
|
||||
* @return string
|
||||
*/
|
||||
protected function parseOrder($order, $options = [])
|
||||
{
|
||||
if (is_array($order)) {
|
||||
$array = [];
|
||||
foreach ($order as $key => $val) {
|
||||
if (is_numeric($key)) {
|
||||
if ('[rand]' == $val) {
|
||||
if (method_exists($this, 'parseRand')) {
|
||||
$array[] = $this->parseRand();
|
||||
} else {
|
||||
throw new BadMethodCallException('method not exists:' . get_class($this) . '-> parseRand');
|
||||
}
|
||||
} elseif (false === strpos($val, '(')) {
|
||||
$array[] = $this->parseKey($val, $options);
|
||||
} else {
|
||||
$array[] = $val;
|
||||
}
|
||||
} else {
|
||||
$sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : '';
|
||||
$array[] = $this->parseKey($key, $options) . ' ' . $sort;
|
||||
}
|
||||
}
|
||||
$order = implode(',', $array);
|
||||
}
|
||||
return !empty($order) ? ' ORDER BY ' . $order : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* group分析
|
||||
* @access protected
|
||||
* @param mixed $group
|
||||
* @return string
|
||||
*/
|
||||
protected function parseGroup($group)
|
||||
{
|
||||
return !empty($group) ? ' GROUP BY ' . $this->parseKey($group) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* having分析
|
||||
* @access protected
|
||||
* @param string $having
|
||||
* @return string
|
||||
*/
|
||||
protected function parseHaving($having)
|
||||
{
|
||||
return !empty($having) ? ' HAVING ' . $having : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* comment分析
|
||||
* @access protected
|
||||
* @param string $comment
|
||||
* @return string
|
||||
*/
|
||||
protected function parseComment($comment)
|
||||
{
|
||||
return !empty($comment) ? ' /* ' . $comment . ' */' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* distinct分析
|
||||
* @access protected
|
||||
* @param mixed $distinct
|
||||
* @return string
|
||||
*/
|
||||
protected function parseDistinct($distinct)
|
||||
{
|
||||
return !empty($distinct) ? ' DISTINCT ' : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* union分析
|
||||
* @access protected
|
||||
* @param mixed $union
|
||||
* @return string
|
||||
*/
|
||||
protected function parseUnion($union)
|
||||
{
|
||||
if (empty($union)) {
|
||||
return '';
|
||||
}
|
||||
$type = $union['type'];
|
||||
unset($union['type']);
|
||||
foreach ($union as $u) {
|
||||
if ($u instanceof \Closure) {
|
||||
$sql[] = $type . ' ' . $this->parseClosure($u, false);
|
||||
} elseif (is_string($u)) {
|
||||
$sql[] = $type . ' ' . $this->parseSqlTable($u);
|
||||
}
|
||||
}
|
||||
return implode(' ', $sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* index分析,可在操作链中指定需要强制使用的索引
|
||||
* @access protected
|
||||
* @param mixed $index
|
||||
* @return string
|
||||
*/
|
||||
protected function parseForce($index)
|
||||
{
|
||||
if (empty($index)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (is_array($index)) {
|
||||
$index = join(",", $index);
|
||||
}
|
||||
|
||||
return sprintf(" FORCE INDEX ( %s ) ", $index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置锁机制
|
||||
* @access protected
|
||||
* @param bool|string $lock
|
||||
* @return string
|
||||
*/
|
||||
protected function parseLock($lock = false)
|
||||
{
|
||||
if (is_bool($lock)) {
|
||||
return $lock ? ' FOR UPDATE ' : '';
|
||||
} elseif (is_string($lock)) {
|
||||
return ' ' . trim($lock) . ' ';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成查询SQL
|
||||
* @access public
|
||||
* @param array $options 表达式
|
||||
* @return string
|
||||
*/
|
||||
public function select($options = [])
|
||||
{
|
||||
$sql = str_replace(
|
||||
['%TABLE%', '%DISTINCT%', '%FIELD%', '%JOIN%', '%WHERE%', '%GROUP%', '%HAVING%', '%ORDER%', '%LIMIT%', '%UNION%', '%LOCK%', '%COMMENT%', '%FORCE%'],
|
||||
[
|
||||
$this->parseTable($options['table'], $options),
|
||||
$this->parseDistinct($options['distinct']),
|
||||
$this->parseField($options['field'], $options),
|
||||
$this->parseJoin($options['join'], $options),
|
||||
$this->parseWhere($options['where'], $options),
|
||||
$this->parseGroup($options['group']),
|
||||
$this->parseHaving($options['having']),
|
||||
$this->parseOrder($options['order'], $options),
|
||||
$this->parseLimit($options['limit']),
|
||||
$this->parseUnion($options['union']),
|
||||
$this->parseLock($options['lock']),
|
||||
$this->parseComment($options['comment']),
|
||||
$this->parseForce($options['force']),
|
||||
], $this->selectSql);
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成insert SQL
|
||||
* @access public
|
||||
* @param array $data 数据
|
||||
* @param array $options 表达式
|
||||
* @param bool $replace 是否replace
|
||||
* @return string
|
||||
*/
|
||||
public function insert(array $data, $options = [], $replace = false)
|
||||
{
|
||||
// 分析并处理数据
|
||||
$data = $this->parseData($data, $options);
|
||||
if (empty($data)) {
|
||||
return 0;
|
||||
}
|
||||
$fields = array_keys($data);
|
||||
$values = array_values($data);
|
||||
|
||||
$sql = str_replace(
|
||||
['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
|
||||
[
|
||||
$replace ? 'REPLACE' : 'INSERT',
|
||||
$this->parseTable($options['table'], $options),
|
||||
implode(' , ', $fields),
|
||||
implode(' , ', $values),
|
||||
$this->parseComment($options['comment']),
|
||||
], $this->insertSql);
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成insertall SQL
|
||||
* @access public
|
||||
* @param array $dataSet 数据集
|
||||
* @param array $options 表达式
|
||||
* @param bool $replace 是否replace
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function insertAll($dataSet, $options = [], $replace = false)
|
||||
{
|
||||
// 获取合法的字段
|
||||
if ('*' == $options['field']) {
|
||||
$fields = array_keys($this->query->getFieldsType($options['table']));
|
||||
} else {
|
||||
$fields = $options['field'];
|
||||
}
|
||||
|
||||
foreach ($dataSet as &$data) {
|
||||
foreach ($data as $key => $val) {
|
||||
if (!in_array($key, $fields, true)) {
|
||||
if ($options['strict']) {
|
||||
throw new Exception('fields not exists:[' . $key . ']');
|
||||
}
|
||||
unset($data[$key]);
|
||||
} elseif (is_null($val)) {
|
||||
$data[$key] = 'NULL';
|
||||
} elseif (is_scalar($val)) {
|
||||
$data[$key] = $this->parseValue($val, $key);
|
||||
} elseif (is_object($val) && method_exists($val, '__toString')) {
|
||||
// 对象数据写入
|
||||
$data[$key] = $val->__toString();
|
||||
} else {
|
||||
// 过滤掉非标量数据
|
||||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
$value = array_values($data);
|
||||
$values[] = 'SELECT ' . implode(',', $value);
|
||||
}
|
||||
$fields = array_map([$this, 'parseKey'], array_keys(reset($dataSet)));
|
||||
$sql = str_replace(
|
||||
['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
|
||||
[
|
||||
$replace ? 'REPLACE' : 'INSERT',
|
||||
$this->parseTable($options['table'], $options),
|
||||
implode(' , ', $fields),
|
||||
implode(' UNION ALL ', $values),
|
||||
$this->parseComment($options['comment']),
|
||||
], $this->insertAllSql);
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成select insert SQL
|
||||
* @access public
|
||||
* @param array $fields 数据
|
||||
* @param string $table 数据表
|
||||
* @param array $options 表达式
|
||||
* @return string
|
||||
*/
|
||||
public function selectInsert($fields, $table, $options)
|
||||
{
|
||||
if (is_string($fields)) {
|
||||
$fields = explode(',', $fields);
|
||||
}
|
||||
|
||||
$fields = array_map([$this, 'parseKey'], $fields);
|
||||
$sql = 'INSERT INTO ' . $this->parseTable($table, $options) . ' (' . implode(',', $fields) . ') ' . $this->select($options);
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成update SQL
|
||||
* @access public
|
||||
* @param array $data 数据
|
||||
* @param array $options 表达式
|
||||
* @return string
|
||||
*/
|
||||
public function update($data, $options)
|
||||
{
|
||||
$table = $this->parseTable($options['table'], $options);
|
||||
$data = $this->parseData($data, $options);
|
||||
if (empty($data)) {
|
||||
return '';
|
||||
}
|
||||
foreach ($data as $key => $val) {
|
||||
$set[] = $key . '=' . $val;
|
||||
}
|
||||
|
||||
$sql = str_replace(
|
||||
['%TABLE%', '%SET%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'],
|
||||
[
|
||||
$this->parseTable($options['table'], $options),
|
||||
implode(',', $set),
|
||||
$this->parseJoin($options['join'], $options),
|
||||
$this->parseWhere($options['where'], $options),
|
||||
$this->parseOrder($options['order'], $options),
|
||||
$this->parseLimit($options['limit']),
|
||||
$this->parseLock($options['lock']),
|
||||
$this->parseComment($options['comment']),
|
||||
], $this->updateSql);
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成delete SQL
|
||||
* @access public
|
||||
* @param array $options 表达式
|
||||
* @return string
|
||||
*/
|
||||
public function delete($options)
|
||||
{
|
||||
$sql = str_replace(
|
||||
['%TABLE%', '%USING%', '%JOIN%', '%WHERE%', '%ORDER%', '%LIMIT%', '%LOCK%', '%COMMENT%'],
|
||||
[
|
||||
$this->parseTable($options['table'], $options),
|
||||
!empty($options['using']) ? ' USING ' . $this->parseTable($options['using'], $options) . ' ' : '',
|
||||
$this->parseJoin($options['join'], $options),
|
||||
$this->parseWhere($options['where'], $options),
|
||||
$this->parseOrder($options['order'], $options),
|
||||
$this->parseLimit($options['limit']),
|
||||
$this->parseLock($options['lock']),
|
||||
$this->parseComment($options['comment']),
|
||||
], $this->deleteSql);
|
||||
|
||||
return $sql;
|
||||
}
|
||||
}
|
1038
thinkphp/library/think/db/Connection.php
Executable file
1038
thinkphp/library/think/db/Connection.php
Executable file
File diff suppressed because it is too large
Load Diff
2898
thinkphp/library/think/db/Query.php
Executable file
2898
thinkphp/library/think/db/Query.php
Executable file
File diff suppressed because it is too large
Load Diff
68
thinkphp/library/think/db/builder/Mysql.php
Executable file
68
thinkphp/library/think/db/builder/Mysql.php
Executable file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\db\builder;
|
||||
|
||||
use think\db\Builder;
|
||||
|
||||
/**
|
||||
* mysql数据库驱动
|
||||
*/
|
||||
class Mysql extends Builder
|
||||
{
|
||||
protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';
|
||||
|
||||
/**
|
||||
* 字段和表名处理
|
||||
* @access protected
|
||||
* @param string $key
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function parseKey($key, $options = [])
|
||||
{
|
||||
$key = trim($key);
|
||||
if (strpos($key, '$.') && false === strpos($key, '(')) {
|
||||
// JSON字段支持
|
||||
list($field, $name) = explode('$.', $key);
|
||||
$key = 'json_extract(' . $field . ', \'$.' . $name . '\')';
|
||||
} elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) {
|
||||
list($table, $key) = explode('.', $key, 2);
|
||||
if ('__TABLE__' == $table) {
|
||||
$table = $this->query->getTable();
|
||||
}
|
||||
if (isset($options['alias'][$table])) {
|
||||
$table = $options['alias'][$table];
|
||||
}
|
||||
}
|
||||
if (!preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {
|
||||
$key = '`' . $key . '`';
|
||||
}
|
||||
if (isset($table)) {
|
||||
if (strpos($table, '.')) {
|
||||
$table = str_replace('.', '`.`', $table);
|
||||
}
|
||||
$key = '`' . $table . '`.' . $key;
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机排序
|
||||
* @access protected
|
||||
* @return string
|
||||
*/
|
||||
protected function parseRand()
|
||||
{
|
||||
return 'rand()';
|
||||
}
|
||||
|
||||
}
|
83
thinkphp/library/think/db/builder/Pgsql.php
Executable file
83
thinkphp/library/think/db/builder/Pgsql.php
Executable file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\db\builder;
|
||||
|
||||
use think\db\Builder;
|
||||
|
||||
/**
|
||||
* Pgsql数据库驱动
|
||||
*/
|
||||
class Pgsql extends Builder
|
||||
{
|
||||
protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
|
||||
protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
|
||||
|
||||
/**
|
||||
* limit分析
|
||||
* @access protected
|
||||
* @param mixed $limit
|
||||
* @return string
|
||||
*/
|
||||
public function parseLimit($limit)
|
||||
{
|
||||
$limitStr = '';
|
||||
if (!empty($limit)) {
|
||||
$limit = explode(',', $limit);
|
||||
if (count($limit) > 1) {
|
||||
$limitStr .= ' LIMIT ' . $limit[1] . ' OFFSET ' . $limit[0] . ' ';
|
||||
} else {
|
||||
$limitStr .= ' LIMIT ' . $limit[0] . ' ';
|
||||
}
|
||||
}
|
||||
return $limitStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段和表名处理
|
||||
* @access protected
|
||||
* @param string $key
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function parseKey($key, $options = [])
|
||||
{
|
||||
$key = trim($key);
|
||||
if (strpos($key, '$.') && false === strpos($key, '(')) {
|
||||
// JSON字段支持
|
||||
list($field, $name) = explode('$.', $key);
|
||||
$key = $field . '->>\'' . $name . '\'';
|
||||
} elseif (strpos($key, '.')) {
|
||||
list($table, $key) = explode('.', $key, 2);
|
||||
if ('__TABLE__' == $table) {
|
||||
$table = $this->query->getTable();
|
||||
}
|
||||
if (isset($options['alias'][$table])) {
|
||||
$table = $options['alias'][$table];
|
||||
}
|
||||
}
|
||||
if (isset($table)) {
|
||||
$key = $table . '.' . $key;
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机排序
|
||||
* @access protected
|
||||
* @return string
|
||||
*/
|
||||
protected function parseRand()
|
||||
{
|
||||
return 'RANDOM()';
|
||||
}
|
||||
|
||||
}
|
76
thinkphp/library/think/db/builder/Sqlite.php
Executable file
76
thinkphp/library/think/db/builder/Sqlite.php
Executable file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\db\builder;
|
||||
|
||||
use think\db\Builder;
|
||||
|
||||
/**
|
||||
* Sqlite数据库驱动
|
||||
*/
|
||||
class Sqlite extends Builder
|
||||
{
|
||||
|
||||
/**
|
||||
* limit
|
||||
* @access public
|
||||
* @param string $limit
|
||||
* @return string
|
||||
*/
|
||||
public function parseLimit($limit)
|
||||
{
|
||||
$limitStr = '';
|
||||
if (!empty($limit)) {
|
||||
$limit = explode(',', $limit);
|
||||
if (count($limit) > 1) {
|
||||
$limitStr .= ' LIMIT ' . $limit[1] . ' OFFSET ' . $limit[0] . ' ';
|
||||
} else {
|
||||
$limitStr .= ' LIMIT ' . $limit[0] . ' ';
|
||||
}
|
||||
}
|
||||
return $limitStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机排序
|
||||
* @access protected
|
||||
* @return string
|
||||
*/
|
||||
protected function parseRand()
|
||||
{
|
||||
return 'RANDOM()';
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段和表名处理
|
||||
* @access protected
|
||||
* @param string $key
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function parseKey($key, $options = [])
|
||||
{
|
||||
$key = trim($key);
|
||||
if (strpos($key, '.')) {
|
||||
list($table, $key) = explode('.', $key, 2);
|
||||
if ('__TABLE__' == $table) {
|
||||
$table = $this->query->getTable();
|
||||
}
|
||||
if (isset($options['alias'][$table])) {
|
||||
$table = $options['alias'][$table];
|
||||
}
|
||||
}
|
||||
if (isset($table)) {
|
||||
$key = $table . '.' . $key;
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
}
|
123
thinkphp/library/think/db/builder/Sqlsrv.php
Executable file
123
thinkphp/library/think/db/builder/Sqlsrv.php
Executable file
@ -0,0 +1,123 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\db\builder;
|
||||
|
||||
use think\db\Builder;
|
||||
|
||||
/**
|
||||
* Sqlsrv数据库驱动
|
||||
*/
|
||||
class Sqlsrv extends Builder
|
||||
{
|
||||
protected $selectSql = 'SELECT T1.* FROM (SELECT thinkphp.*, ROW_NUMBER() OVER (%ORDER%) AS ROW_NUMBER FROM (SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%) AS thinkphp) AS T1 %LIMIT%%COMMENT%';
|
||||
protected $selectInsertSql = 'SELECT %DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%';
|
||||
protected $updateSql = 'UPDATE %TABLE% SET %SET% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%';
|
||||
protected $deleteSql = 'DELETE FROM %TABLE% %USING% FROM %TABLE% %JOIN% %WHERE% %LIMIT% %LOCK%%COMMENT%';
|
||||
protected $insertSql = 'INSERT INTO %TABLE% (%FIELD%) VALUES (%DATA%) %COMMENT%';
|
||||
protected $insertAllSql = 'INSERT INTO %TABLE% (%FIELD%) %DATA% %COMMENT%';
|
||||
|
||||
/**
|
||||
* order分析
|
||||
* @access protected
|
||||
* @param mixed $order
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function parseOrder($order, $options = [])
|
||||
{
|
||||
if (is_array($order)) {
|
||||
$array = [];
|
||||
foreach ($order as $key => $val) {
|
||||
if (is_numeric($key)) {
|
||||
if (false === strpos($val, '(')) {
|
||||
$array[] = $this->parseKey($val, $options);
|
||||
} elseif ('[rand]' == $val) {
|
||||
$array[] = $this->parseRand();
|
||||
} else {
|
||||
$array[] = $val;
|
||||
}
|
||||
} else {
|
||||
$sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : '';
|
||||
$array[] = $this->parseKey($key, $options) . ' ' . $sort;
|
||||
}
|
||||
}
|
||||
$order = implode(',', $array);
|
||||
}
|
||||
return !empty($order) ? ' ORDER BY ' . $order : ' ORDER BY rand()';
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机排序
|
||||
* @access protected
|
||||
* @return string
|
||||
*/
|
||||
protected function parseRand()
|
||||
{
|
||||
return 'rand()';
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段和表名处理
|
||||
* @access protected
|
||||
* @param string $key
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
protected function parseKey($key, $options = [])
|
||||
{
|
||||
$key = trim($key);
|
||||
if (strpos($key, '.') && !preg_match('/[,\'\"\(\)\[\s]/', $key)) {
|
||||
list($table, $key) = explode('.', $key, 2);
|
||||
if ('__TABLE__' == $table) {
|
||||
$table = $this->query->getTable();
|
||||
}
|
||||
if (isset($options['alias'][$table])) {
|
||||
$table = $options['alias'][$table];
|
||||
}
|
||||
}
|
||||
if (!is_numeric($key) && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) {
|
||||
$key = '[' . $key . ']';
|
||||
}
|
||||
if (isset($table)) {
|
||||
$key = '[' . $table . '].' . $key;
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* limit
|
||||
* @access protected
|
||||
* @param mixed $limit
|
||||
* @return string
|
||||
*/
|
||||
protected function parseLimit($limit)
|
||||
{
|
||||
if (empty($limit)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$limit = explode(',', $limit);
|
||||
if (count($limit) > 1) {
|
||||
$limitStr = '(T1.ROW_NUMBER BETWEEN ' . $limit[0] . ' + 1 AND ' . $limit[0] . ' + ' . $limit[1] . ')';
|
||||
} else {
|
||||
$limitStr = '(T1.ROW_NUMBER BETWEEN 1 AND ' . $limit[0] . ")";
|
||||
}
|
||||
return 'WHERE ' . $limitStr;
|
||||
}
|
||||
|
||||
public function selectInsert($fields, $table, $options)
|
||||
{
|
||||
$this->selectSql = $this->selectInsertSql;
|
||||
return parent::selectInsert($fields, $table, $options);
|
||||
}
|
||||
|
||||
}
|
126
thinkphp/library/think/db/connector/Mysql.php
Executable file
126
thinkphp/library/think/db/connector/Mysql.php
Executable file
@ -0,0 +1,126 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\db\connector;
|
||||
|
||||
use PDO;
|
||||
use think\db\Connection;
|
||||
use think\Log;
|
||||
|
||||
/**
|
||||
* mysql数据库驱动
|
||||
*/
|
||||
class Mysql extends Connection
|
||||
{
|
||||
|
||||
protected $builder = '\\think\\db\\builder\\Mysql';
|
||||
|
||||
/**
|
||||
* 解析pdo连接的dsn信息
|
||||
* @access protected
|
||||
* @param array $config 连接信息
|
||||
* @return string
|
||||
*/
|
||||
protected function parseDsn($config)
|
||||
{
|
||||
if (!empty($config['socket'])) {
|
||||
$dsn = 'mysql:unix_socket=' . $config['socket'];
|
||||
} elseif (!empty($config['hostport'])) {
|
||||
$dsn = 'mysql:host=' . $config['hostname'] . ';port=' . $config['hostport'];
|
||||
} else {
|
||||
$dsn = 'mysql:host=' . $config['hostname'];
|
||||
}
|
||||
$dsn .= ';dbname=' . $config['database'];
|
||||
|
||||
if (!empty($config['charset'])) {
|
||||
$dsn .= ';charset=' . $config['charset'];
|
||||
}
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据表的字段信息
|
||||
* @access public
|
||||
* @param string $tableName
|
||||
* @return array
|
||||
*/
|
||||
public function getFields($tableName)
|
||||
{
|
||||
list($tableName) = explode(' ', $tableName);
|
||||
if (false === strpos($tableName, '`')) {
|
||||
if (strpos($tableName, '.')) {
|
||||
$tableName = str_replace('.', '`.`', $tableName);
|
||||
}
|
||||
$tableName = '`' . $tableName . '`';
|
||||
}
|
||||
$sql = 'SHOW COLUMNS FROM ' . $tableName;
|
||||
$pdo = $this->query($sql, [], false, true);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
if ($result) {
|
||||
foreach ($result as $key => $val) {
|
||||
$val = array_change_key_case($val);
|
||||
$info[$val['field']] = [
|
||||
'name' => $val['field'],
|
||||
'type' => $val['type'],
|
||||
'notnull' => (bool) ('' === $val['null']), // not null is empty, null is yes
|
||||
'default' => $val['default'],
|
||||
'primary' => (strtolower($val['key']) == 'pri'),
|
||||
'autoinc' => (strtolower($val['extra']) == 'auto_increment'),
|
||||
];
|
||||
}
|
||||
}
|
||||
return $this->fieldCase($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据库的表信息
|
||||
* @access public
|
||||
* @param string $dbName
|
||||
* @return array
|
||||
*/
|
||||
public function getTables($dbName = '')
|
||||
{
|
||||
$sql = !empty($dbName) ? 'SHOW TABLES FROM ' . $dbName : 'SHOW TABLES ';
|
||||
$pdo = $this->query($sql, [], false, true);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
foreach ($result as $key => $val) {
|
||||
$info[$key] = current($val);
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL性能分析
|
||||
* @access protected
|
||||
* @param string $sql
|
||||
* @return array
|
||||
*/
|
||||
protected function getExplain($sql)
|
||||
{
|
||||
$pdo = $this->linkID->query("EXPLAIN " . $sql);
|
||||
$result = $pdo->fetch(PDO::FETCH_ASSOC);
|
||||
$result = array_change_key_case($result);
|
||||
if (isset($result['extra'])) {
|
||||
if (strpos($result['extra'], 'filesort') || strpos($result['extra'], 'temporary')) {
|
||||
Log::record('SQL:' . $this->queryStr . '[' . $result['extra'] . ']', 'warn');
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function supportSavepoint()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
103
thinkphp/library/think/db/connector/Pgsql.php
Executable file
103
thinkphp/library/think/db/connector/Pgsql.php
Executable file
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\db\connector;
|
||||
|
||||
use PDO;
|
||||
use think\db\Connection;
|
||||
|
||||
/**
|
||||
* Pgsql数据库驱动
|
||||
*/
|
||||
class Pgsql extends Connection
|
||||
{
|
||||
protected $builder = '\\think\\db\\builder\\Pgsql';
|
||||
|
||||
/**
|
||||
* 解析pdo连接的dsn信息
|
||||
* @access protected
|
||||
* @param array $config 连接信息
|
||||
* @return string
|
||||
*/
|
||||
protected function parseDsn($config)
|
||||
{
|
||||
$dsn = 'pgsql:dbname=' . $config['database'] . ';host=' . $config['hostname'];
|
||||
if (!empty($config['hostport'])) {
|
||||
$dsn .= ';port=' . $config['hostport'];
|
||||
}
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据表的字段信息
|
||||
* @access public
|
||||
* @param string $tableName
|
||||
* @return array
|
||||
*/
|
||||
public function getFields($tableName)
|
||||
{
|
||||
|
||||
list($tableName) = explode(' ', $tableName);
|
||||
$sql = 'select fields_name as "field",fields_type as "type",fields_not_null as "null",fields_key_name as "key",fields_default as "default",fields_default as "extra" from table_msg(\'' . $tableName . '\');';
|
||||
|
||||
$pdo = $this->query($sql, [], false, true);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
if ($result) {
|
||||
foreach ($result as $key => $val) {
|
||||
$val = array_change_key_case($val);
|
||||
$info[$val['field']] = [
|
||||
'name' => $val['field'],
|
||||
'type' => $val['type'],
|
||||
'notnull' => (bool) ('' !== $val['null']),
|
||||
'default' => $val['default'],
|
||||
'primary' => !empty($val['key']),
|
||||
'autoinc' => (0 === strpos($val['extra'], 'nextval(')),
|
||||
];
|
||||
}
|
||||
}
|
||||
return $this->fieldCase($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据库的表信息
|
||||
* @access public
|
||||
* @param string $dbName
|
||||
* @return array
|
||||
*/
|
||||
public function getTables($dbName = '')
|
||||
{
|
||||
$sql = "select tablename as Tables_in_test from pg_tables where schemaname ='public'";
|
||||
$pdo = $this->query($sql, [], false, true);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
foreach ($result as $key => $val) {
|
||||
$info[$key] = current($val);
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL性能分析
|
||||
* @access protected
|
||||
* @param string $sql
|
||||
* @return array
|
||||
*/
|
||||
protected function getExplain($sql)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function supportSavepoint()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
104
thinkphp/library/think/db/connector/Sqlite.php
Executable file
104
thinkphp/library/think/db/connector/Sqlite.php
Executable file
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\db\connector;
|
||||
|
||||
use PDO;
|
||||
use think\db\Connection;
|
||||
|
||||
/**
|
||||
* Sqlite数据库驱动
|
||||
*/
|
||||
class Sqlite extends Connection
|
||||
{
|
||||
|
||||
protected $builder = '\\think\\db\\builder\\Sqlite';
|
||||
|
||||
/**
|
||||
* 解析pdo连接的dsn信息
|
||||
* @access protected
|
||||
* @param array $config 连接信息
|
||||
* @return string
|
||||
*/
|
||||
protected function parseDsn($config)
|
||||
{
|
||||
$dsn = 'sqlite:' . $config['database'];
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据表的字段信息
|
||||
* @access public
|
||||
* @param string $tableName
|
||||
* @return array
|
||||
*/
|
||||
public function getFields($tableName)
|
||||
{
|
||||
list($tableName) = explode(' ', $tableName);
|
||||
$sql = 'PRAGMA table_info( ' . $tableName . ' )';
|
||||
|
||||
$pdo = $this->query($sql, [], false, true);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
if ($result) {
|
||||
foreach ($result as $key => $val) {
|
||||
$val = array_change_key_case($val);
|
||||
$info[$val['name']] = [
|
||||
'name' => $val['name'],
|
||||
'type' => $val['type'],
|
||||
'notnull' => 1 === $val['notnull'],
|
||||
'default' => $val['dflt_value'],
|
||||
'primary' => '1' == $val['pk'],
|
||||
'autoinc' => '1' == $val['pk'],
|
||||
];
|
||||
}
|
||||
}
|
||||
return $this->fieldCase($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据库的表信息
|
||||
* @access public
|
||||
* @param string $dbName
|
||||
* @return array
|
||||
*/
|
||||
public function getTables($dbName = '')
|
||||
{
|
||||
|
||||
$sql = "SELECT name FROM sqlite_master WHERE type='table' "
|
||||
. "UNION ALL SELECT name FROM sqlite_temp_master "
|
||||
. "WHERE type='table' ORDER BY name";
|
||||
|
||||
$pdo = $this->query($sql, [], false, true);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
foreach ($result as $key => $val) {
|
||||
$info[$key] = current($val);
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL性能分析
|
||||
* @access protected
|
||||
* @param string $sql
|
||||
* @return array
|
||||
*/
|
||||
protected function getExplain($sql)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function supportSavepoint()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
122
thinkphp/library/think/db/connector/Sqlsrv.php
Executable file
122
thinkphp/library/think/db/connector/Sqlsrv.php
Executable file
@ -0,0 +1,122 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\db\connector;
|
||||
|
||||
use PDO;
|
||||
use think\db\Connection;
|
||||
|
||||
/**
|
||||
* Sqlsrv数据库驱动
|
||||
*/
|
||||
class Sqlsrv extends Connection
|
||||
{
|
||||
// PDO连接参数
|
||||
protected $params = [
|
||||
PDO::ATTR_CASE => PDO::CASE_NATURAL,
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_STRINGIFY_FETCHES => false,
|
||||
];
|
||||
protected $builder = '\\think\\db\\builder\\Sqlsrv';
|
||||
/**
|
||||
* 解析pdo连接的dsn信息
|
||||
* @access protected
|
||||
* @param array $config 连接信息
|
||||
* @return string
|
||||
*/
|
||||
protected function parseDsn($config)
|
||||
{
|
||||
$dsn = 'sqlsrv:Database=' . $config['database'] . ';Server=' . $config['hostname'];
|
||||
if (!empty($config['hostport'])) {
|
||||
$dsn .= ',' . $config['hostport'];
|
||||
}
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据表的字段信息
|
||||
* @access public
|
||||
* @param string $tableName
|
||||
* @return array
|
||||
*/
|
||||
public function getFields($tableName)
|
||||
{
|
||||
list($tableName) = explode(' ', $tableName);
|
||||
$sql = "SELECT column_name, data_type, column_default, is_nullable
|
||||
FROM information_schema.tables AS t
|
||||
JOIN information_schema.columns AS c
|
||||
ON t.table_catalog = c.table_catalog
|
||||
AND t.table_schema = c.table_schema
|
||||
AND t.table_name = c.table_name
|
||||
WHERE t.table_name = '$tableName'";
|
||||
|
||||
$pdo = $this->query($sql, [], false, true);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
if ($result) {
|
||||
foreach ($result as $key => $val) {
|
||||
$val = array_change_key_case($val);
|
||||
$info[$val['column_name']] = [
|
||||
'name' => $val['column_name'],
|
||||
'type' => $val['data_type'],
|
||||
'notnull' => (bool) ('' === $val['is_nullable']), // not null is empty, null is yes
|
||||
'default' => $val['column_default'],
|
||||
'primary' => false,
|
||||
'autoinc' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
$sql = "SELECT column_name FROM information_schema.key_column_usage WHERE table_name='$tableName'";
|
||||
// 调试开始
|
||||
$this->debug(true);
|
||||
$pdo = $this->linkID->query($sql);
|
||||
// 调试结束
|
||||
$this->debug(false, $sql);
|
||||
$result = $pdo->fetch(PDO::FETCH_ASSOC);
|
||||
if ($result) {
|
||||
$info[$result['column_name']]['primary'] = true;
|
||||
}
|
||||
return $this->fieldCase($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取得数据表的字段信息
|
||||
* @access public
|
||||
* @param string $dbName
|
||||
* @return array
|
||||
*/
|
||||
public function getTables($dbName = '')
|
||||
{
|
||||
$sql = "SELECT TABLE_NAME
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_TYPE = 'BASE TABLE'
|
||||
";
|
||||
|
||||
$pdo = $this->query($sql, [], false, true);
|
||||
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
|
||||
$info = [];
|
||||
foreach ($result as $key => $val) {
|
||||
$info[$key] = current($val);
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL性能分析
|
||||
* @access protected
|
||||
* @param string $sql
|
||||
* @return array
|
||||
*/
|
||||
protected function getExplain($sql)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
BIN
thinkphp/library/think/db/connector/code.jpg
Executable file
BIN
thinkphp/library/think/db/connector/code.jpg
Executable file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
117
thinkphp/library/think/db/connector/pgsql.sql
Executable file
117
thinkphp/library/think/db/connector/pgsql.sql
Executable file
@ -0,0 +1,117 @@
|
||||
CREATE OR REPLACE FUNCTION pgsql_type(a_type varchar) RETURNS varchar AS
|
||||
$BODY$
|
||||
DECLARE
|
||||
v_type varchar;
|
||||
BEGIN
|
||||
IF a_type='int8' THEN
|
||||
v_type:='bigint';
|
||||
ELSIF a_type='int4' THEN
|
||||
v_type:='integer';
|
||||
ELSIF a_type='int2' THEN
|
||||
v_type:='smallint';
|
||||
ELSIF a_type='bpchar' THEN
|
||||
v_type:='char';
|
||||
ELSE
|
||||
v_type:=a_type;
|
||||
END IF;
|
||||
RETURN v_type;
|
||||
END;
|
||||
$BODY$
|
||||
LANGUAGE PLPGSQL;
|
||||
|
||||
CREATE TYPE "public"."tablestruct" AS (
|
||||
"fields_key_name" varchar(100),
|
||||
"fields_name" VARCHAR(200),
|
||||
"fields_type" VARCHAR(20),
|
||||
"fields_length" BIGINT,
|
||||
"fields_not_null" VARCHAR(10),
|
||||
"fields_default" VARCHAR(500),
|
||||
"fields_comment" VARCHAR(1000)
|
||||
);
|
||||
|
||||
CREATE OR REPLACE FUNCTION "public"."table_msg" (a_schema_name varchar, a_table_name varchar) RETURNS SETOF "public"."tablestruct" AS
|
||||
$body$
|
||||
DECLARE
|
||||
v_ret tablestruct;
|
||||
v_oid oid;
|
||||
v_sql varchar;
|
||||
v_rec RECORD;
|
||||
v_key varchar;
|
||||
BEGIN
|
||||
SELECT
|
||||
pg_class.oid INTO v_oid
|
||||
FROM
|
||||
pg_class
|
||||
INNER JOIN pg_namespace ON (pg_class.relnamespace = pg_namespace.oid AND lower(pg_namespace.nspname) = a_schema_name)
|
||||
WHERE
|
||||
pg_class.relname=a_table_name;
|
||||
IF NOT FOUND THEN
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
v_sql='
|
||||
SELECT
|
||||
pg_attribute.attname AS fields_name,
|
||||
pg_attribute.attnum AS fields_index,
|
||||
pgsql_type(pg_type.typname::varchar) AS fields_type,
|
||||
pg_attribute.atttypmod-4 as fields_length,
|
||||
CASE WHEN pg_attribute.attnotnull THEN ''not null''
|
||||
ELSE ''''
|
||||
END AS fields_not_null,
|
||||
pg_attrdef.adsrc AS fields_default,
|
||||
pg_description.description AS fields_comment
|
||||
FROM
|
||||
pg_attribute
|
||||
INNER JOIN pg_class ON pg_attribute.attrelid = pg_class.oid
|
||||
INNER JOIN pg_type ON pg_attribute.atttypid = pg_type.oid
|
||||
LEFT OUTER JOIN pg_attrdef ON pg_attrdef.adrelid = pg_class.oid AND pg_attrdef.adnum = pg_attribute.attnum
|
||||
LEFT OUTER JOIN pg_description ON pg_description.objoid = pg_class.oid AND pg_description.objsubid = pg_attribute.attnum
|
||||
WHERE
|
||||
pg_attribute.attnum > 0
|
||||
AND attisdropped <> ''t''
|
||||
AND pg_class.oid = ' || v_oid || '
|
||||
ORDER BY pg_attribute.attnum' ;
|
||||
|
||||
FOR v_rec IN EXECUTE v_sql LOOP
|
||||
v_ret.fields_name=v_rec.fields_name;
|
||||
v_ret.fields_type=v_rec.fields_type;
|
||||
IF v_rec.fields_length > 0 THEN
|
||||
v_ret.fields_length:=v_rec.fields_length;
|
||||
ELSE
|
||||
v_ret.fields_length:=NULL;
|
||||
END IF;
|
||||
v_ret.fields_not_null=v_rec.fields_not_null;
|
||||
v_ret.fields_default=v_rec.fields_default;
|
||||
v_ret.fields_comment=v_rec.fields_comment;
|
||||
SELECT constraint_name INTO v_key FROM information_schema.key_column_usage WHERE table_schema=a_schema_name AND table_name=a_table_name AND column_name=v_rec.fields_name;
|
||||
IF FOUND THEN
|
||||
v_ret.fields_key_name=v_key;
|
||||
ELSE
|
||||
v_ret.fields_key_name='';
|
||||
END IF;
|
||||
RETURN NEXT v_ret;
|
||||
END LOOP;
|
||||
RETURN ;
|
||||
END;
|
||||
$body$
|
||||
LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER;
|
||||
|
||||
COMMENT ON FUNCTION "public"."table_msg"(a_schema_name varchar, a_table_name varchar)
|
||||
IS '获得表信息';
|
||||
|
||||
---重载一个函数
|
||||
CREATE OR REPLACE FUNCTION "public"."table_msg" (a_table_name varchar) RETURNS SETOF "public"."tablestruct" AS
|
||||
$body$
|
||||
DECLARE
|
||||
v_ret tablestruct;
|
||||
BEGIN
|
||||
FOR v_ret IN SELECT * FROM table_msg('public',a_table_name) LOOP
|
||||
RETURN NEXT v_ret;
|
||||
END LOOP;
|
||||
RETURN;
|
||||
END;
|
||||
$body$
|
||||
LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER;
|
||||
|
||||
COMMENT ON FUNCTION "public"."table_msg"(a_table_name varchar)
|
||||
IS '获得表信息';
|
35
thinkphp/library/think/db/exception/BindParamException.php
Executable file
35
thinkphp/library/think/db/exception/BindParamException.php
Executable file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\db\exception;
|
||||
|
||||
use think\exception\DbException;
|
||||
|
||||
/**
|
||||
* PDO参数绑定异常
|
||||
*/
|
||||
class BindParamException extends DbException
|
||||
{
|
||||
|
||||
/**
|
||||
* BindParamException constructor.
|
||||
* @param string $message
|
||||
* @param array $config
|
||||
* @param string $sql
|
||||
* @param array $bind
|
||||
* @param int $code
|
||||
*/
|
||||
public function __construct($message, $config, $sql, $bind, $code = 10502)
|
||||
{
|
||||
$this->setData('Bind Param', $bind);
|
||||
parent::__construct($message, $config, $sql, $code);
|
||||
}
|
||||
}
|
43
thinkphp/library/think/db/exception/DataNotFoundException.php
Executable file
43
thinkphp/library/think/db/exception/DataNotFoundException.php
Executable file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\db\exception;
|
||||
|
||||
use think\exception\DbException;
|
||||
|
||||
class DataNotFoundException extends DbException
|
||||
{
|
||||
protected $table;
|
||||
|
||||
/**
|
||||
* DbException constructor.
|
||||
* @param string $message
|
||||
* @param string $table
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct($message, $table = '', array $config = [])
|
||||
{
|
||||
$this->message = $message;
|
||||
$this->table = $table;
|
||||
|
||||
$this->setData('Database Config', $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据表名
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getTable()
|
||||
{
|
||||
return $this->table;
|
||||
}
|
||||
}
|
43
thinkphp/library/think/db/exception/ModelNotFoundException.php
Executable file
43
thinkphp/library/think/db/exception/ModelNotFoundException.php
Executable file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\db\exception;
|
||||
|
||||
use think\exception\DbException;
|
||||
|
||||
class ModelNotFoundException extends DbException
|
||||
{
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @param string $message
|
||||
* @param string $model
|
||||
*/
|
||||
public function __construct($message, $model = '', array $config = [])
|
||||
{
|
||||
$this->message = $message;
|
||||
$this->model = $model;
|
||||
|
||||
$this->setData('Database Config', $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模型类名
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getModel()
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
}
|
160
thinkphp/library/think/debug/Console.php
Executable file
160
thinkphp/library/think/debug/Console.php
Executable file
@ -0,0 +1,160 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yangweijie <yangweijiester@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\debug;
|
||||
|
||||
use think\Cache;
|
||||
use think\Config;
|
||||
use think\Db;
|
||||
use think\Debug;
|
||||
use think\Request;
|
||||
use think\Response;
|
||||
|
||||
/**
|
||||
* 浏览器调试输出
|
||||
*/
|
||||
class Console
|
||||
{
|
||||
protected $config = [
|
||||
'trace_tabs' => ['base' => '基本', 'file' => '文件', 'info' => '流程', 'notice|error' => '错误', 'sql' => 'SQL', 'debug|log' => '调试'],
|
||||
];
|
||||
|
||||
// 实例化并传入参数
|
||||
public function __construct($config = [])
|
||||
{
|
||||
if (is_array($config)) {
|
||||
$this->config = array_merge($this->config, $config);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试输出接口
|
||||
* @access public
|
||||
* @param Response $response Response对象
|
||||
* @param array $log 日志信息
|
||||
* @return bool
|
||||
*/
|
||||
public function output(Response $response, array $log = [])
|
||||
{
|
||||
$request = Request::instance();
|
||||
$contentType = $response->getHeader('Content-Type');
|
||||
$accept = $request->header('accept');
|
||||
if (strpos($accept, 'application/json') === 0 || $request->isAjax()) {
|
||||
return false;
|
||||
} elseif (!empty($contentType) && strpos($contentType, 'html') === false) {
|
||||
return false;
|
||||
}
|
||||
// 获取基本信息
|
||||
$runtime = number_format(microtime(true) - THINK_START_TIME, 10);
|
||||
$reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
|
||||
$mem = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
|
||||
|
||||
if (isset($_SERVER['HTTP_HOST'])) {
|
||||
$uri = $_SERVER['SERVER_PROTOCOL'] . ' ' . $_SERVER['REQUEST_METHOD'] . ' : ' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
} else {
|
||||
$uri = 'cmd:' . implode(' ', $_SERVER['argv']);
|
||||
}
|
||||
|
||||
// 页面Trace信息
|
||||
$base = [
|
||||
'请求信息' => date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']) . ' ' . $uri,
|
||||
'运行时间' => number_format($runtime, 6) . 's [ 吞吐率:' . $reqs . 'req/s ] 内存消耗:' . $mem . 'kb 文件加载:' . count(get_included_files()),
|
||||
'查询信息' => Db::$queryTimes . ' queries ' . Db::$executeTimes . ' writes ',
|
||||
'缓存信息' => Cache::$readTimes . ' reads,' . Cache::$writeTimes . ' writes',
|
||||
'配置加载' => count(Config::get()),
|
||||
];
|
||||
|
||||
if (session_id()) {
|
||||
$base['会话信息'] = 'SESSION_ID=' . session_id();
|
||||
}
|
||||
|
||||
$info = Debug::getFile(true);
|
||||
|
||||
// 页面Trace信息
|
||||
$trace = [];
|
||||
foreach ($this->config['trace_tabs'] as $name => $title) {
|
||||
$name = strtolower($name);
|
||||
switch ($name) {
|
||||
case 'base': // 基本信息
|
||||
$trace[$title] = $base;
|
||||
break;
|
||||
case 'file': // 文件信息
|
||||
$trace[$title] = $info;
|
||||
break;
|
||||
default: // 调试信息
|
||||
if (strpos($name, '|')) {
|
||||
// 多组信息
|
||||
$names = explode('|', $name);
|
||||
$result = [];
|
||||
foreach ($names as $name) {
|
||||
$result = array_merge($result, isset($log[$name]) ? $log[$name] : []);
|
||||
}
|
||||
$trace[$title] = $result;
|
||||
} else {
|
||||
$trace[$title] = isset($log[$name]) ? $log[$name] : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//输出到控制台
|
||||
$lines = '';
|
||||
foreach ($trace as $type => $msg) {
|
||||
$lines .= $this->console($type, $msg);
|
||||
}
|
||||
$js = <<<JS
|
||||
|
||||
<script type='text/javascript'>
|
||||
{$lines}
|
||||
</script>
|
||||
JS;
|
||||
return $js;
|
||||
}
|
||||
|
||||
protected function console($type, $msg)
|
||||
{
|
||||
$type = strtolower($type);
|
||||
$trace_tabs = array_values($this->config['trace_tabs']);
|
||||
$line[] = ($type == $trace_tabs[0] || '调试' == $type || '错误' == $type)
|
||||
? "console.group('{$type}');"
|
||||
: "console.groupCollapsed('{$type}');";
|
||||
|
||||
foreach ((array) $msg as $key => $m) {
|
||||
switch ($type) {
|
||||
case '调试':
|
||||
$var_type = gettype($m);
|
||||
if (in_array($var_type, ['array', 'string'])) {
|
||||
$line[] = "console.log(" . json_encode($m) . ");";
|
||||
} else {
|
||||
$line[] = "console.log(" . json_encode(var_export($m, 1)) . ");";
|
||||
}
|
||||
break;
|
||||
case '错误':
|
||||
$msg = str_replace("\n", '\n', json_encode($m));
|
||||
$style = 'color:#F4006B;font-size:14px;';
|
||||
$line[] = "console.error(\"%c{$msg}\", \"{$style}\");";
|
||||
break;
|
||||
case 'sql':
|
||||
$msg = str_replace("\n", '\n', $m);
|
||||
$style = "color:#009bb4;";
|
||||
$line[] = "console.log(\"%c{$msg}\", \"{$style}\");";
|
||||
break;
|
||||
default:
|
||||
$m = is_string($key) ? $key . ' ' . $m : $key + 1 . ' ' . $m;
|
||||
$msg = json_encode($m);
|
||||
$line[] = "console.log({$msg});";
|
||||
break;
|
||||
}
|
||||
}
|
||||
$line[] = "console.groupEnd();";
|
||||
return implode(PHP_EOL, $line);
|
||||
}
|
||||
|
||||
}
|
111
thinkphp/library/think/debug/Html.php
Executable file
111
thinkphp/library/think/debug/Html.php
Executable file
@ -0,0 +1,111 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\debug;
|
||||
|
||||
use think\Cache;
|
||||
use think\Config;
|
||||
use think\Db;
|
||||
use think\Debug;
|
||||
use think\Request;
|
||||
use think\Response;
|
||||
|
||||
/**
|
||||
* 页面Trace调试
|
||||
*/
|
||||
class Html
|
||||
{
|
||||
protected $config = [
|
||||
'trace_file' => '',
|
||||
'trace_tabs' => ['base' => '基本', 'file' => '文件', 'info' => '流程', 'notice|error' => '错误', 'sql' => 'SQL', 'debug|log' => '调试'],
|
||||
];
|
||||
|
||||
// 实例化并传入参数
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$this->config['trace_file'] = THINK_PATH . 'tpl/page_trace.tpl';
|
||||
$this->config = array_merge($this->config, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试输出接口
|
||||
* @access public
|
||||
* @param Response $response Response对象
|
||||
* @param array $log 日志信息
|
||||
* @return bool
|
||||
*/
|
||||
public function output(Response $response, array $log = [])
|
||||
{
|
||||
$request = Request::instance();
|
||||
$contentType = $response->getHeader('Content-Type');
|
||||
$accept = $request->header('accept');
|
||||
if (strpos($accept, 'application/json') === 0 || $request->isAjax()) {
|
||||
return false;
|
||||
} elseif (!empty($contentType) && strpos($contentType, 'html') === false) {
|
||||
return false;
|
||||
}
|
||||
// 获取基本信息
|
||||
$runtime = number_format(microtime(true) - THINK_START_TIME, 10);
|
||||
$reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
|
||||
$mem = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
|
||||
|
||||
// 页面Trace信息
|
||||
if (isset($_SERVER['HTTP_HOST'])) {
|
||||
$uri = $_SERVER['SERVER_PROTOCOL'] . ' ' . $_SERVER['REQUEST_METHOD'] . ' : ' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
} else {
|
||||
$uri = 'cmd:' . implode(' ', $_SERVER['argv']);
|
||||
}
|
||||
$base = [
|
||||
'请求信息' => date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']) . ' ' . $uri,
|
||||
'运行时间' => number_format($runtime, 6) . 's [ 吞吐率:' . $reqs . 'req/s ] 内存消耗:' . $mem . 'kb 文件加载:' . count(get_included_files()),
|
||||
'查询信息' => Db::$queryTimes . ' queries ' . Db::$executeTimes . ' writes ',
|
||||
'缓存信息' => Cache::$readTimes . ' reads,' . Cache::$writeTimes . ' writes',
|
||||
'配置加载' => count(Config::get()),
|
||||
];
|
||||
|
||||
if (session_id()) {
|
||||
$base['会话信息'] = 'SESSION_ID=' . session_id();
|
||||
}
|
||||
|
||||
$info = Debug::getFile(true);
|
||||
|
||||
// 页面Trace信息
|
||||
$trace = [];
|
||||
foreach ($this->config['trace_tabs'] as $name => $title) {
|
||||
$name = strtolower($name);
|
||||
switch ($name) {
|
||||
case 'base': // 基本信息
|
||||
$trace[$title] = $base;
|
||||
break;
|
||||
case 'file': // 文件信息
|
||||
$trace[$title] = $info;
|
||||
break;
|
||||
default: // 调试信息
|
||||
if (strpos($name, '|')) {
|
||||
// 多组信息
|
||||
$names = explode('|', $name);
|
||||
$result = [];
|
||||
foreach ($names as $name) {
|
||||
$result = array_merge($result, isset($log[$name]) ? $log[$name] : []);
|
||||
}
|
||||
$trace[$title] = $result;
|
||||
} else {
|
||||
$trace[$title] = isset($log[$name]) ? $log[$name] : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
// 调用Trace页面模板
|
||||
ob_start();
|
||||
include $this->config['trace_file'];
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
}
|
32
thinkphp/library/think/exception/ClassNotFoundException.php
Executable file
32
thinkphp/library/think/exception/ClassNotFoundException.php
Executable file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\exception;
|
||||
|
||||
class ClassNotFoundException extends \RuntimeException
|
||||
{
|
||||
protected $class;
|
||||
public function __construct($message, $class = '')
|
||||
{
|
||||
$this->message = $message;
|
||||
$this->class = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类名
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getClass()
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
}
|
43
thinkphp/library/think/exception/DbException.php
Executable file
43
thinkphp/library/think/exception/DbException.php
Executable file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\exception;
|
||||
|
||||
use think\Exception;
|
||||
|
||||
/**
|
||||
* Database相关异常处理类
|
||||
*/
|
||||
class DbException extends Exception
|
||||
{
|
||||
/**
|
||||
* DbException constructor.
|
||||
* @param string $message
|
||||
* @param array $config
|
||||
* @param string $sql
|
||||
* @param int $code
|
||||
*/
|
||||
public function __construct($message, array $config, $sql, $code = 10500)
|
||||
{
|
||||
$this->message = $message;
|
||||
$this->code = $code;
|
||||
|
||||
$this->setData('Database Status', [
|
||||
'Error Code' => $code,
|
||||
'Error Message' => $message,
|
||||
'Error SQL' => $sql,
|
||||
]);
|
||||
|
||||
unset($config['username'], $config['password']);
|
||||
$this->setData('Database Config', $config);
|
||||
}
|
||||
|
||||
}
|
57
thinkphp/library/think/exception/ErrorException.php
Executable file
57
thinkphp/library/think/exception/ErrorException.php
Executable file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\exception;
|
||||
|
||||
use think\Exception;
|
||||
|
||||
/**
|
||||
* ThinkPHP错误异常
|
||||
* 主要用于封装 set_error_handler 和 register_shutdown_function 得到的错误
|
||||
* 除开从 think\Exception 继承的功能
|
||||
* 其他和PHP系统\ErrorException功能基本一样
|
||||
*/
|
||||
class ErrorException extends Exception
|
||||
{
|
||||
/**
|
||||
* 用于保存错误级别
|
||||
* @var integer
|
||||
*/
|
||||
protected $severity;
|
||||
|
||||
/**
|
||||
* 错误异常构造函数
|
||||
* @param integer $severity 错误级别
|
||||
* @param string $message 错误详细信息
|
||||
* @param string $file 出错文件路径
|
||||
* @param integer $line 出错行号
|
||||
* @param array $context 错误上下文,会包含错误触发处作用域内所有变量的数组
|
||||
*/
|
||||
public function __construct($severity, $message, $file, $line, array $context = [])
|
||||
{
|
||||
$this->severity = $severity;
|
||||
$this->message = $message;
|
||||
$this->file = $file;
|
||||
$this->line = $line;
|
||||
$this->code = 0;
|
||||
|
||||
empty($context) || $this->setData('Error Context', $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误级别
|
||||
* @return integer 错误级别
|
||||
*/
|
||||
final public function getSeverity()
|
||||
{
|
||||
return $this->severity;
|
||||
}
|
||||
}
|
282
thinkphp/library/think/exception/Handle.php
Executable file
282
thinkphp/library/think/exception/Handle.php
Executable file
@ -0,0 +1,282 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\exception;
|
||||
|
||||
use Exception;
|
||||
use think\App;
|
||||
use think\Config;
|
||||
use think\console\Output;
|
||||
use think\Lang;
|
||||
use think\Log;
|
||||
use think\Response;
|
||||
|
||||
class Handle
|
||||
{
|
||||
protected $render;
|
||||
protected $ignoreReport = [
|
||||
'\\think\\exception\\HttpException',
|
||||
];
|
||||
|
||||
public function setRender($render)
|
||||
{
|
||||
$this->render = $render;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report or log an exception.
|
||||
*
|
||||
* @param \Exception $exception
|
||||
* @return void
|
||||
*/
|
||||
public function report(Exception $exception)
|
||||
{
|
||||
if (!$this->isIgnoreReport($exception)) {
|
||||
// 收集异常数据
|
||||
if (App::$debug) {
|
||||
$data = [
|
||||
'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine(),
|
||||
'message' => $this->getMessage($exception),
|
||||
'code' => $this->getCode($exception),
|
||||
];
|
||||
$log = "[{$data['code']}]{$data['message']}[{$data['file']}:{$data['line']}]";
|
||||
} else {
|
||||
$data = [
|
||||
'code' => $this->getCode($exception),
|
||||
'message' => $this->getMessage($exception),
|
||||
];
|
||||
$log = "[{$data['code']}]{$data['message']}";
|
||||
}
|
||||
|
||||
if (Config::get('record_trace')) {
|
||||
$log .= "\r\n" . $exception->getTraceAsString();
|
||||
}
|
||||
|
||||
Log::record($log, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
protected function isIgnoreReport(Exception $exception)
|
||||
{
|
||||
foreach ($this->ignoreReport as $class) {
|
||||
if ($exception instanceof $class) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @param \Exception $e
|
||||
* @return Response
|
||||
*/
|
||||
public function render(Exception $e)
|
||||
{
|
||||
if ($this->render && $this->render instanceof \Closure) {
|
||||
$result = call_user_func_array($this->render, [$e]);
|
||||
if ($result) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
if ($e instanceof HttpException) {
|
||||
return $this->renderHttpException($e);
|
||||
} else {
|
||||
return $this->convertExceptionToResponse($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Output $output
|
||||
* @param Exception $e
|
||||
*/
|
||||
public function renderForConsole(Output $output, Exception $e)
|
||||
{
|
||||
if (App::$debug) {
|
||||
$output->setVerbosity(Output::VERBOSITY_DEBUG);
|
||||
}
|
||||
$output->renderException($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param HttpException $e
|
||||
* @return Response
|
||||
*/
|
||||
protected function renderHttpException(HttpException $e)
|
||||
{
|
||||
$status = $e->getStatusCode();
|
||||
$template = Config::get('http_exception_template');
|
||||
if (!App::$debug && !empty($template[$status])) {
|
||||
return Response::create($template[$status], 'view', $status)->assign(['e' => $e]);
|
||||
} else {
|
||||
return $this->convertExceptionToResponse($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Exception $exception
|
||||
* @return Response
|
||||
*/
|
||||
protected function convertExceptionToResponse(Exception $exception)
|
||||
{
|
||||
// 收集异常数据
|
||||
if (App::$debug) {
|
||||
// 调试模式,获取详细的错误信息
|
||||
$data = [
|
||||
'name' => get_class($exception),
|
||||
'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine(),
|
||||
'message' => $this->getMessage($exception),
|
||||
'trace' => $exception->getTrace(),
|
||||
'code' => $this->getCode($exception),
|
||||
'source' => $this->getSourceCode($exception),
|
||||
'datas' => $this->getExtendData($exception),
|
||||
'tables' => [
|
||||
'GET Data' => $_GET,
|
||||
'POST Data' => $_POST,
|
||||
'Files' => $_FILES,
|
||||
'Cookies' => $_COOKIE,
|
||||
'Session' => isset($_SESSION) ? $_SESSION : [],
|
||||
'Server/Request Data' => $_SERVER,
|
||||
'Environment Variables' => $_ENV,
|
||||
'ThinkPHP Constants' => $this->getConst(),
|
||||
],
|
||||
];
|
||||
} else {
|
||||
// 部署模式仅显示 Code 和 Message
|
||||
$data = [
|
||||
'code' => $this->getCode($exception),
|
||||
'message' => $this->getMessage($exception),
|
||||
];
|
||||
|
||||
if (!Config::get('show_error_msg')) {
|
||||
// 不显示详细错误信息
|
||||
$data['message'] = Config::get('error_message');
|
||||
}
|
||||
}
|
||||
|
||||
//保留一层
|
||||
while (ob_get_level() > 1) {
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
$data['echo'] = ob_get_clean();
|
||||
|
||||
ob_start();
|
||||
extract($data);
|
||||
include Config::get('exception_tmpl');
|
||||
// 获取并清空缓存
|
||||
$content = ob_get_clean();
|
||||
$response = new Response($content, 'html');
|
||||
|
||||
if ($exception instanceof HttpException) {
|
||||
$statusCode = $exception->getStatusCode();
|
||||
$response->header($exception->getHeaders());
|
||||
}
|
||||
|
||||
if (!isset($statusCode)) {
|
||||
$statusCode = 500;
|
||||
}
|
||||
$response->code($statusCode);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误编码
|
||||
* ErrorException则使用错误级别作为错误编码
|
||||
* @param \Exception $exception
|
||||
* @return integer 错误编码
|
||||
*/
|
||||
protected function getCode(Exception $exception)
|
||||
{
|
||||
$code = $exception->getCode();
|
||||
if (!$code && $exception instanceof ErrorException) {
|
||||
$code = $exception->getSeverity();
|
||||
}
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误信息
|
||||
* ErrorException则使用错误级别作为错误编码
|
||||
* @param \Exception $exception
|
||||
* @return string 错误信息
|
||||
*/
|
||||
protected function getMessage(Exception $exception)
|
||||
{
|
||||
$message = $exception->getMessage();
|
||||
if (IS_CLI) {
|
||||
return $message;
|
||||
}
|
||||
|
||||
if (strpos($message, ':')) {
|
||||
$name = strstr($message, ':', true);
|
||||
$message = Lang::has($name) ? Lang::get($name) . strstr($message, ':') : $message;
|
||||
} elseif (strpos($message, ',')) {
|
||||
$name = strstr($message, ',', true);
|
||||
$message = Lang::has($name) ? Lang::get($name) . ':' . substr(strstr($message, ','), 1) : $message;
|
||||
} elseif (Lang::has($message)) {
|
||||
$message = Lang::get($message);
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取出错文件内容
|
||||
* 获取错误的前9行和后9行
|
||||
* @param \Exception $exception
|
||||
* @return array 错误文件内容
|
||||
*/
|
||||
protected function getSourceCode(Exception $exception)
|
||||
{
|
||||
// 读取前9行和后9行
|
||||
$line = $exception->getLine();
|
||||
$first = ($line - 9 > 0) ? $line - 9 : 1;
|
||||
|
||||
try {
|
||||
$contents = file($exception->getFile());
|
||||
$source = [
|
||||
'first' => $first,
|
||||
'source' => array_slice($contents, $first - 1, 19),
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
$source = [];
|
||||
}
|
||||
return $source;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取异常扩展信息
|
||||
* 用于非调试模式html返回类型显示
|
||||
* @param \Exception $exception
|
||||
* @return array 异常类定义的扩展数据
|
||||
*/
|
||||
protected function getExtendData(Exception $exception)
|
||||
{
|
||||
$data = [];
|
||||
if ($exception instanceof \think\Exception) {
|
||||
$data = $exception->getData();
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取常量列表
|
||||
* @return array 常量列表
|
||||
*/
|
||||
private static function getConst()
|
||||
{
|
||||
return get_defined_constants(true)['user'];
|
||||
}
|
||||
}
|
36
thinkphp/library/think/exception/HttpException.php
Executable file
36
thinkphp/library/think/exception/HttpException.php
Executable file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\exception;
|
||||
|
||||
class HttpException extends \RuntimeException
|
||||
{
|
||||
private $statusCode;
|
||||
private $headers;
|
||||
|
||||
public function __construct($statusCode, $message = null, \Exception $previous = null, array $headers = [], $code = 0)
|
||||
{
|
||||
$this->statusCode = $statusCode;
|
||||
$this->headers = $headers;
|
||||
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
public function getStatusCode()
|
||||
{
|
||||
return $this->statusCode;
|
||||
}
|
||||
|
||||
public function getHeaders()
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
}
|
33
thinkphp/library/think/exception/HttpResponseException.php
Executable file
33
thinkphp/library/think/exception/HttpResponseException.php
Executable file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\exception;
|
||||
|
||||
use think\Response;
|
||||
|
||||
class HttpResponseException extends \RuntimeException
|
||||
{
|
||||
/**
|
||||
* @var Response
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
public function __construct(Response $response)
|
||||
{
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
}
|
39
thinkphp/library/think/exception/PDOException.php
Executable file
39
thinkphp/library/think/exception/PDOException.php
Executable file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://zjzit.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\exception;
|
||||
|
||||
/**
|
||||
* PDO异常处理类
|
||||
* 重新封装了系统的\PDOException类
|
||||
*/
|
||||
class PDOException extends DbException
|
||||
{
|
||||
/**
|
||||
* PDOException constructor.
|
||||
* @param \PDOException $exception
|
||||
* @param array $config
|
||||
* @param string $sql
|
||||
* @param int $code
|
||||
*/
|
||||
public function __construct(\PDOException $exception, array $config, $sql, $code = 10501)
|
||||
{
|
||||
$error = $exception->errorInfo;
|
||||
|
||||
$this->setData('PDO Error Info', [
|
||||
'SQLSTATE' => $error[0],
|
||||
'Driver Error Code' => isset($error[1]) ? $error[1] : 0,
|
||||
'Driver Error Message' => isset($error[2]) ? $error[2] : '',
|
||||
]);
|
||||
|
||||
parent::__construct($exception->getMessage(), $config, $sql, $code);
|
||||
}
|
||||
}
|
22
thinkphp/library/think/exception/RouteNotFoundException.php
Executable file
22
thinkphp/library/think/exception/RouteNotFoundException.php
Executable file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\exception;
|
||||
|
||||
class RouteNotFoundException extends HttpException
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(404, 'Route Not Found');
|
||||
}
|
||||
|
||||
}
|
33
thinkphp/library/think/exception/TemplateNotFoundException.php
Executable file
33
thinkphp/library/think/exception/TemplateNotFoundException.php
Executable file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\exception;
|
||||
|
||||
class TemplateNotFoundException extends \RuntimeException
|
||||
{
|
||||
protected $template;
|
||||
|
||||
public function __construct($message, $template = '')
|
||||
{
|
||||
$this->message = $message;
|
||||
$this->template = $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模板文件
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getTemplate()
|
||||
{
|
||||
return $this->template;
|
||||
}
|
||||
}
|
47
thinkphp/library/think/exception/ThrowableError.php
Executable file
47
thinkphp/library/think/exception/ThrowableError.php
Executable file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\exception;
|
||||
|
||||
class ThrowableError extends \ErrorException
|
||||
{
|
||||
public function __construct(\Throwable $e)
|
||||
{
|
||||
|
||||
if ($e instanceof \ParseError) {
|
||||
$message = 'Parse error: ' . $e->getMessage();
|
||||
$severity = E_PARSE;
|
||||
} elseif ($e instanceof \TypeError) {
|
||||
$message = 'Type error: ' . $e->getMessage();
|
||||
$severity = E_RECOVERABLE_ERROR;
|
||||
} else {
|
||||
$message = 'Fatal error: ' . $e->getMessage();
|
||||
$severity = E_ERROR;
|
||||
}
|
||||
|
||||
parent::__construct(
|
||||
$message,
|
||||
$e->getCode(),
|
||||
$severity,
|
||||
$e->getFile(),
|
||||
$e->getLine()
|
||||
);
|
||||
|
||||
$this->setTrace($e->getTrace());
|
||||
}
|
||||
|
||||
protected function setTrace($trace)
|
||||
{
|
||||
$traceReflector = new \ReflectionProperty('Exception', 'trace');
|
||||
$traceReflector->setAccessible(true);
|
||||
$traceReflector->setValue($this, $trace);
|
||||
}
|
||||
}
|
33
thinkphp/library/think/exception/ValidateException.php
Executable file
33
thinkphp/library/think/exception/ValidateException.php
Executable file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\exception;
|
||||
|
||||
class ValidateException extends \RuntimeException
|
||||
{
|
||||
protected $error;
|
||||
|
||||
public function __construct($error)
|
||||
{
|
||||
$this->error = $error;
|
||||
$this->message = is_array($error) ? implode("\n\r", $error) : $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证错误信息
|
||||
* @access public
|
||||
* @return array|string
|
||||
*/
|
||||
public function getError()
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
}
|
119
thinkphp/library/think/log/driver/File.php
Executable file
119
thinkphp/library/think/log/driver/File.php
Executable file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\log\driver;
|
||||
|
||||
use think\App;
|
||||
|
||||
/**
|
||||
* 本地化调试输出到文件
|
||||
*/
|
||||
class File
|
||||
{
|
||||
protected $config = [
|
||||
'time_format' => ' c ',
|
||||
'file_size' => 2097152,
|
||||
'path' => LOG_PATH,
|
||||
'apart_level' => [],
|
||||
];
|
||||
|
||||
protected $writed = [];
|
||||
|
||||
// 实例化并传入参数
|
||||
public function __construct($config = [])
|
||||
{
|
||||
if (is_array($config)) {
|
||||
$this->config = array_merge($this->config, $config);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志写入接口
|
||||
* @access public
|
||||
* @param array $log 日志信息
|
||||
* @return bool
|
||||
*/
|
||||
public function save(array $log = [])
|
||||
{
|
||||
$cli = IS_CLI ? '_cli' : '';
|
||||
$destination = $this->config['path'] . date('Ym') . DS . date('d') . $cli . '.log';
|
||||
|
||||
$path = dirname($destination);
|
||||
!is_dir($path) && mkdir($path, 0755, true);
|
||||
|
||||
$info = '';
|
||||
foreach ($log as $type => $val) {
|
||||
$level = '';
|
||||
foreach ($val as $msg) {
|
||||
if (!is_string($msg)) {
|
||||
$msg = var_export($msg, true);
|
||||
}
|
||||
$level .= '[ ' . $type . ' ] ' . $msg . "\r\n";
|
||||
}
|
||||
if (in_array($type, $this->config['apart_level'])) {
|
||||
// 独立记录的日志级别
|
||||
$filename = $path . DS . date('d') . '_' . $type . $cli . '.log';
|
||||
$this->write($level, $filename, true);
|
||||
} else {
|
||||
$info .= $level;
|
||||
}
|
||||
}
|
||||
if ($info) {
|
||||
return $this->write($info, $destination);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function write($message, $destination, $apart = false)
|
||||
{
|
||||
//检测日志文件大小,超过配置大小则备份日志文件重新生成
|
||||
if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) {
|
||||
rename($destination, dirname($destination) . DS . time() . '-' . basename($destination));
|
||||
$this->writed[$destination] = false;
|
||||
}
|
||||
|
||||
if (empty($this->writed[$destination]) && !IS_CLI) {
|
||||
if (App::$debug && !$apart) {
|
||||
// 获取基本信息
|
||||
if (isset($_SERVER['HTTP_HOST'])) {
|
||||
$current_uri = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
} else {
|
||||
$current_uri = "cmd:" . implode(' ', $_SERVER['argv']);
|
||||
}
|
||||
|
||||
$runtime = round(microtime(true) - THINK_START_TIME, 10);
|
||||
$reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
|
||||
$time_str = ' [运行时间:' . number_format($runtime, 6) . 's][吞吐率:' . $reqs . 'req/s]';
|
||||
$memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
|
||||
$memory_str = ' [内存消耗:' . $memory_use . 'kb]';
|
||||
$file_load = ' [文件加载:' . count(get_included_files()) . ']';
|
||||
|
||||
$message = '[ info ] ' . $current_uri . $time_str . $memory_str . $file_load . "\r\n" . $message;
|
||||
}
|
||||
$now = date($this->config['time_format']);
|
||||
$server = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '0.0.0.0';
|
||||
$remote = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0';
|
||||
$method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'CLI';
|
||||
$uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
|
||||
$message = "---------------------------------------------------------------\r\n[{$now}] {$server} {$remote} {$method} {$uri}\r\n" . $message;
|
||||
|
||||
$this->writed[$destination] = true;
|
||||
}
|
||||
|
||||
if (IS_CLI) {
|
||||
$now = date($this->config['time_format']);
|
||||
$message = "[{$now}]" . $message;
|
||||
}
|
||||
|
||||
return error_log($message, 3, $destination);
|
||||
}
|
||||
|
||||
}
|
250
thinkphp/library/think/log/driver/Socket.php
Executable file
250
thinkphp/library/think/log/driver/Socket.php
Executable file
@ -0,0 +1,250 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: luofei614 <weibo.com/luofei614>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\log\driver;
|
||||
|
||||
use think\App;
|
||||
|
||||
/**
|
||||
* github: https://github.com/luofei614/SocketLog
|
||||
* @author luofei614<weibo.com/luofei614>
|
||||
*/
|
||||
class Socket
|
||||
{
|
||||
public $port = 1116; //SocketLog 服务的http的端口号
|
||||
|
||||
protected $config = [
|
||||
// socket服务器地址
|
||||
'host' => 'localhost',
|
||||
// 是否显示加载的文件列表
|
||||
'show_included_files' => false,
|
||||
// 日志强制记录到配置的client_id
|
||||
'force_client_ids' => [],
|
||||
// 限制允许读取日志的client_id
|
||||
'allow_client_ids' => [],
|
||||
];
|
||||
|
||||
protected $css = [
|
||||
'sql' => 'color:#009bb4;',
|
||||
'sql_warn' => 'color:#009bb4;font-size:14px;',
|
||||
'error' => 'color:#f4006b;font-size:14px;',
|
||||
'page' => 'color:#40e2ff;background:#171717;',
|
||||
'big' => 'font-size:20px;color:red;',
|
||||
];
|
||||
|
||||
protected $allowForceClientIds = []; //配置强制推送且被授权的client_id
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $config 缓存参数
|
||||
* @access public
|
||||
*/
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
if (!empty($config)) {
|
||||
$this->config = array_merge($this->config, $config);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试输出接口
|
||||
* @access public
|
||||
* @param array $log 日志信息
|
||||
* @return bool
|
||||
*/
|
||||
public function save(array $log = [])
|
||||
{
|
||||
if (!$this->check()) {
|
||||
return false;
|
||||
}
|
||||
$trace = [];
|
||||
if (App::$debug) {
|
||||
$runtime = round(microtime(true) - THINK_START_TIME, 10);
|
||||
$reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
|
||||
$time_str = ' [运行时间:' . number_format($runtime, 6) . 's][吞吐率:' . $reqs . 'req/s]';
|
||||
$memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
|
||||
$memory_str = ' [内存消耗:' . $memory_use . 'kb]';
|
||||
$file_load = ' [文件加载:' . count(get_included_files()) . ']';
|
||||
|
||||
if (isset($_SERVER['HTTP_HOST'])) {
|
||||
$current_uri = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
} else {
|
||||
$current_uri = 'cmd:' . implode(' ', $_SERVER['argv']);
|
||||
}
|
||||
// 基本信息
|
||||
$trace[] = [
|
||||
'type' => 'group',
|
||||
'msg' => $current_uri . $time_str . $memory_str . $file_load,
|
||||
'css' => $this->css['page'],
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($log as $type => $val) {
|
||||
$trace[] = [
|
||||
'type' => 'groupCollapsed',
|
||||
'msg' => '[ ' . $type . ' ]',
|
||||
'css' => isset($this->css[$type]) ? $this->css[$type] : '',
|
||||
];
|
||||
foreach ($val as $msg) {
|
||||
if (!is_string($msg)) {
|
||||
$msg = var_export($msg, true);
|
||||
}
|
||||
$trace[] = [
|
||||
'type' => 'log',
|
||||
'msg' => $msg,
|
||||
'css' => '',
|
||||
];
|
||||
}
|
||||
$trace[] = [
|
||||
'type' => 'groupEnd',
|
||||
'msg' => '',
|
||||
'css' => '',
|
||||
];
|
||||
}
|
||||
|
||||
if ($this->config['show_included_files']) {
|
||||
$trace[] = [
|
||||
'type' => 'groupCollapsed',
|
||||
'msg' => '[ file ]',
|
||||
'css' => '',
|
||||
];
|
||||
$trace[] = [
|
||||
'type' => 'log',
|
||||
'msg' => implode("\n", get_included_files()),
|
||||
'css' => '',
|
||||
];
|
||||
$trace[] = [
|
||||
'type' => 'groupEnd',
|
||||
'msg' => '',
|
||||
'css' => '',
|
||||
];
|
||||
}
|
||||
|
||||
$trace[] = [
|
||||
'type' => 'groupEnd',
|
||||
'msg' => '',
|
||||
'css' => '',
|
||||
];
|
||||
|
||||
$tabid = $this->getClientArg('tabid');
|
||||
if (!$client_id = $this->getClientArg('client_id')) {
|
||||
$client_id = '';
|
||||
}
|
||||
|
||||
if (!empty($this->allowForceClientIds)) {
|
||||
//强制推送到多个client_id
|
||||
foreach ($this->allowForceClientIds as $force_client_id) {
|
||||
$client_id = $force_client_id;
|
||||
$this->sendToClient($tabid, $client_id, $trace, $force_client_id);
|
||||
}
|
||||
} else {
|
||||
$this->sendToClient($tabid, $client_id, $trace, '');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送给指定客户端
|
||||
* @author Zjmainstay
|
||||
* @param $tabid
|
||||
* @param $client_id
|
||||
* @param $logs
|
||||
* @param $force_client_id
|
||||
*/
|
||||
protected function sendToClient($tabid, $client_id, $logs, $force_client_id)
|
||||
{
|
||||
$logs = [
|
||||
'tabid' => $tabid,
|
||||
'client_id' => $client_id,
|
||||
'logs' => $logs,
|
||||
'force_client_id' => $force_client_id,
|
||||
];
|
||||
$msg = @json_encode($logs);
|
||||
$address = '/' . $client_id; //将client_id作为地址, server端通过地址判断将日志发布给谁
|
||||
$this->send($this->config['host'], $msg, $address);
|
||||
}
|
||||
|
||||
protected function check()
|
||||
{
|
||||
$tabid = $this->getClientArg('tabid');
|
||||
//是否记录日志的检查
|
||||
if (!$tabid && !$this->config['force_client_ids']) {
|
||||
return false;
|
||||
}
|
||||
//用户认证
|
||||
$allow_client_ids = $this->config['allow_client_ids'];
|
||||
if (!empty($allow_client_ids)) {
|
||||
//通过数组交集得出授权强制推送的client_id
|
||||
$this->allowForceClientIds = array_intersect($allow_client_ids, $this->config['force_client_ids']);
|
||||
if (!$tabid && count($this->allowForceClientIds)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$client_id = $this->getClientArg('client_id');
|
||||
if (!in_array($client_id, $allow_client_ids)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->allowForceClientIds = $this->config['force_client_ids'];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getClientArg($name)
|
||||
{
|
||||
static $args = [];
|
||||
|
||||
$key = 'HTTP_USER_AGENT';
|
||||
|
||||
if (isset($_SERVER['HTTP_SOCKETLOG'])) {
|
||||
$key = 'HTTP_SOCKETLOG';
|
||||
}
|
||||
|
||||
if (!isset($_SERVER[$key])) {
|
||||
return;
|
||||
}
|
||||
if (empty($args)) {
|
||||
if (!preg_match('/SocketLog\((.*?)\)/', $_SERVER[$key], $match)) {
|
||||
$args = ['tabid' => null];
|
||||
return;
|
||||
}
|
||||
parse_str($match[1], $args);
|
||||
}
|
||||
if (isset($args[$name])) {
|
||||
return $args[$name];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $host - $host of socket server
|
||||
* @param string $message - 发送的消息
|
||||
* @param string $address - 地址
|
||||
* @return bool
|
||||
*/
|
||||
protected function send($host, $message = '', $address = '/')
|
||||
{
|
||||
$url = 'http://' . $host . ':' . $this->port . $address;
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $message);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
$headers = [
|
||||
"Content-Type: application/json;charset=UTF-8",
|
||||
];
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); //设置header
|
||||
return curl_exec($ch);
|
||||
}
|
||||
|
||||
}
|
30
thinkphp/library/think/log/driver/Test.php
Executable file
30
thinkphp/library/think/log/driver/Test.php
Executable file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\log\driver;
|
||||
|
||||
/**
|
||||
* 模拟测试输出
|
||||
*/
|
||||
class Test
|
||||
{
|
||||
/**
|
||||
* 日志写入接口
|
||||
* @access public
|
||||
* @param array $log 日志信息
|
||||
* @return bool
|
||||
*/
|
||||
public function save(array $log = [])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
BIN
thinkphp/library/think/log/driver/code.jpg
Executable file
BIN
thinkphp/library/think/log/driver/code.jpg
Executable file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
93
thinkphp/library/think/model/Collection.php
Executable file
93
thinkphp/library/think/model/Collection.php
Executable file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: zhangyajun <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model;
|
||||
|
||||
use think\Collection as BaseCollection;
|
||||
use think\Model;
|
||||
|
||||
class Collection extends BaseCollection
|
||||
{
|
||||
/**
|
||||
* 返回数组中指定的一列
|
||||
* @param string $column_key
|
||||
* @param string|null $index_key
|
||||
* @return array
|
||||
*/
|
||||
public function column($column_key, $index_key = null)
|
||||
{
|
||||
if (function_exists('array_column')) {
|
||||
return array_column($this->toArray(), $column_key, $index_key);
|
||||
}
|
||||
return parent::column($column_key, $index_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟预载入关联查询
|
||||
* @access public
|
||||
* @param mixed $relation 关联
|
||||
* @return $this
|
||||
*/
|
||||
public function load($relation)
|
||||
{
|
||||
$item = current($this->items);
|
||||
$item->eagerlyResultSet($this->items, $relation);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置需要隐藏的输出属性
|
||||
* @access public
|
||||
* @param array $hidden 属性列表
|
||||
* @param bool $override 是否覆盖
|
||||
* @return $this
|
||||
*/
|
||||
public function hidden($hidden = [], $override = false)
|
||||
{
|
||||
$this->each(function ($model) use ($hidden, $override) {
|
||||
/** @var Model $model */
|
||||
$model->hidden($hidden, $override);
|
||||
});
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置需要输出的属性
|
||||
* @param array $visible
|
||||
* @param bool $override 是否覆盖
|
||||
* @return $this
|
||||
*/
|
||||
public function visible($visible = [], $override = false)
|
||||
{
|
||||
$this->each(function ($model) use ($visible, $override) {
|
||||
/** @var Model $model */
|
||||
$model->visible($visible, $override);
|
||||
});
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置需要追加的输出属性
|
||||
* @access public
|
||||
* @param array $append 属性列表
|
||||
* @param bool $override 是否覆盖
|
||||
* @return $this
|
||||
*/
|
||||
public function append($append = [], $override = false)
|
||||
{
|
||||
$this->each(function ($model) use ($append, $override) {
|
||||
/** @var Model $model */
|
||||
$model && $model->append($append, $override);
|
||||
});
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
322
thinkphp/library/think/model/Merge.php
Executable file
322
thinkphp/library/think/model/Merge.php
Executable file
@ -0,0 +1,322 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model;
|
||||
|
||||
use think\Db;
|
||||
use think\db\Query;
|
||||
use think\Model;
|
||||
|
||||
class Merge extends Model
|
||||
{
|
||||
|
||||
protected $relationModel = []; // HAS ONE 关联的模型列表
|
||||
protected $fk = ''; // 外键名 默认为主表名_id
|
||||
protected $mapFields = []; // 需要处理的模型映射字段,避免混淆 array( id => 'user.id' )
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
* @param array|object $data 数据
|
||||
*/
|
||||
public function __construct($data = [])
|
||||
{
|
||||
parent::__construct($data);
|
||||
|
||||
// 设置默认外键名 仅支持单一外键
|
||||
if (empty($this->fk)) {
|
||||
$this->fk = strtolower($this->name) . '_id';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找单条记录
|
||||
* @access public
|
||||
* @param mixed $data 主键值或者查询条件(闭包)
|
||||
* @param string|array $with 关联预查询
|
||||
* @param bool $cache 是否缓存
|
||||
* @return \think\Model
|
||||
*/
|
||||
public static function get($data = null, $with = [], $cache = false)
|
||||
{
|
||||
$query = self::parseQuery($data, $with, $cache);
|
||||
$query = self::attachQuery($query);
|
||||
return $query->find($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 附加查询表达式
|
||||
* @access protected
|
||||
* @param \think\db\Query $query 查询对象
|
||||
* @return \think\db\Query
|
||||
*/
|
||||
protected static function attachQuery($query)
|
||||
{
|
||||
$class = new static();
|
||||
$master = $class->name;
|
||||
$fields = self::getModelField($query, $master, '', $class->mapFields, $class->field);
|
||||
$query->alias($master)->field($fields);
|
||||
|
||||
foreach ($class->relationModel as $key => $model) {
|
||||
$name = is_int($key) ? $model : $key;
|
||||
$table = is_int($key) ? $query->getTable($name) : $model;
|
||||
$query->join($table . ' ' . $name, $name . '.' . $class->fk . '=' . $master . '.' . $class->getPk());
|
||||
$fields = self::getModelField($query, $name, $table, $class->mapFields, $class->field);
|
||||
$query->field($fields);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取关联模型的字段 并解决混淆
|
||||
* @access protected
|
||||
* @param \think\db\Query $query 查询对象
|
||||
* @param string $name 模型名称
|
||||
* @param string $table 关联表名称
|
||||
* @param array $map 字段映射
|
||||
* @param array $fields 查询字段
|
||||
* @return array
|
||||
*/
|
||||
protected static function getModelField($query, $name, $table = '', $map = [], $fields = [])
|
||||
{
|
||||
// 获取模型的字段信息
|
||||
$fields = $fields ?: $query->getTableInfo($table, 'fields');
|
||||
$array = [];
|
||||
foreach ($fields as $field) {
|
||||
if ($key = array_search($name . '.' . $field, $map)) {
|
||||
// 需要处理映射字段
|
||||
$array[] = $name . '.' . $field . ' AS ' . $key;
|
||||
} else {
|
||||
$array[] = $field;
|
||||
}
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找所有记录
|
||||
* @access public
|
||||
* @param mixed $data 主键列表或者查询条件(闭包)
|
||||
* @param array|string $with 关联预查询
|
||||
* @param bool $cache
|
||||
* @return array|false|string
|
||||
*/
|
||||
public static function all($data = null, $with = [], $cache = false)
|
||||
{
|
||||
$query = self::parseQuery($data, $with, $cache);
|
||||
$query = self::attachQuery($query);
|
||||
return $query->select($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理写入的模型数据
|
||||
* @access public
|
||||
* @param string $model 模型名称
|
||||
* @param array $data 数据
|
||||
* @return array
|
||||
*/
|
||||
protected function parseData($model, $data)
|
||||
{
|
||||
$item = [];
|
||||
foreach ($data as $key => $val) {
|
||||
if ($this->fk != $key && array_key_exists($key, $this->mapFields)) {
|
||||
list($name, $key) = explode('.', $this->mapFields[$key]);
|
||||
if ($model == $name) {
|
||||
$item[$key] = $val;
|
||||
}
|
||||
} else {
|
||||
$item[$key] = $val;
|
||||
}
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存模型数据 以及关联数据
|
||||
* @access public
|
||||
* @param mixed $data 数据
|
||||
* @param array $where 更新条件
|
||||
* @param string $sequence 自增序列名
|
||||
* @return false|int
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function save($data = [], $where = [], $sequence = null)
|
||||
{
|
||||
if (!empty($data)) {
|
||||
// 数据自动验证
|
||||
if (!$this->validateData($data)) {
|
||||
return false;
|
||||
}
|
||||
// 数据对象赋值
|
||||
foreach ($data as $key => $value) {
|
||||
$this->setAttr($key, $value, $data);
|
||||
}
|
||||
if (!empty($where)) {
|
||||
$this->isUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 数据自动完成
|
||||
$this->autoCompleteData($this->auto);
|
||||
|
||||
// 自动写入更新时间
|
||||
if ($this->autoWriteTimestamp && $this->updateTime && !isset($this->data[$this->updateTime])) {
|
||||
$this->setAttr($this->updateTime, null);
|
||||
}
|
||||
|
||||
// 事件回调
|
||||
if (false === $this->trigger('before_write', $this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$db = $this->db();
|
||||
$db->startTrans();
|
||||
$pk = $this->getPk();
|
||||
try {
|
||||
if ($this->isUpdate) {
|
||||
// 自动写入
|
||||
$this->autoCompleteData($this->update);
|
||||
|
||||
if (false === $this->trigger('before_update', $this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($where) && !empty($this->updateWhere)) {
|
||||
$where = $this->updateWhere;
|
||||
}
|
||||
|
||||
// 获取有更新的数据
|
||||
$data = $this->getChangedData();
|
||||
// 保留主键数据
|
||||
foreach ($this->data as $key => $val) {
|
||||
if ($this->isPk($key)) {
|
||||
$data[$key] = $val;
|
||||
}
|
||||
}
|
||||
// 处理模型数据
|
||||
$data = $this->parseData($this->name, $data);
|
||||
if (is_string($pk) && isset($data[$pk])) {
|
||||
if (!isset($where[$pk])) {
|
||||
unset($where);
|
||||
$where[$pk] = $data[$pk];
|
||||
}
|
||||
unset($data[$pk]);
|
||||
}
|
||||
// 写入主表数据
|
||||
$result = $db->strict(false)->where($where)->update($data);
|
||||
|
||||
// 写入附表数据
|
||||
foreach ($this->relationModel as $key => $model) {
|
||||
$name = is_int($key) ? $model : $key;
|
||||
$table = is_int($key) ? $db->getTable($model) : $model;
|
||||
// 处理关联模型数据
|
||||
$data = $this->parseData($name, $data);
|
||||
if (Db::table($table)->strict(false)->where($this->fk, $this->data[$this->getPk()])->update($data)) {
|
||||
$result = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 新增回调
|
||||
$this->trigger('after_update', $this);
|
||||
} else {
|
||||
// 自动写入
|
||||
$this->autoCompleteData($this->insert);
|
||||
|
||||
// 自动写入创建时间
|
||||
if ($this->autoWriteTimestamp && $this->createTime && !isset($this->data[$this->createTime])) {
|
||||
$this->setAttr($this->createTime, null);
|
||||
}
|
||||
|
||||
if (false === $this->trigger('before_insert', $this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 处理模型数据
|
||||
$data = $this->parseData($this->name, $this->data);
|
||||
// 写入主表数据
|
||||
$result = $db->name($this->name)->strict(false)->insert($data);
|
||||
if ($result) {
|
||||
$insertId = $db->getLastInsID($sequence);
|
||||
// 写入外键数据
|
||||
if ($insertId) {
|
||||
if (is_string($pk)) {
|
||||
$this->data[$pk] = $insertId;
|
||||
}
|
||||
$this->data[$this->fk] = $insertId;
|
||||
}
|
||||
|
||||
// 写入附表数据
|
||||
$source = $this->data;
|
||||
if ($insertId && is_string($pk) && isset($source[$pk]) && $this->fk != $pk) {
|
||||
unset($source[$pk]);
|
||||
}
|
||||
foreach ($this->relationModel as $key => $model) {
|
||||
$name = is_int($key) ? $model : $key;
|
||||
$table = is_int($key) ? $db->getTable($model) : $model;
|
||||
// 处理关联模型数据
|
||||
$data = $this->parseData($name, $source);
|
||||
Db::table($table)->strict(false)->insert($data);
|
||||
}
|
||||
}
|
||||
// 标记为更新
|
||||
$this->isUpdate = true;
|
||||
// 新增回调
|
||||
$this->trigger('after_insert', $this);
|
||||
}
|
||||
$db->commit();
|
||||
// 写入回调
|
||||
$this->trigger('after_write', $this);
|
||||
|
||||
$this->origin = $this->data;
|
||||
return $result;
|
||||
} catch (\Exception $e) {
|
||||
$db->rollback();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除当前的记录 并删除关联数据
|
||||
* @access public
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
if (false === $this->trigger('before_delete', $this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$db = $this->db();
|
||||
$db->startTrans();
|
||||
try {
|
||||
$result = $db->delete($this->data);
|
||||
if ($result) {
|
||||
// 获取主键数据
|
||||
$pk = $this->data[$this->getPk()];
|
||||
|
||||
// 删除关联数据
|
||||
foreach ($this->relationModel as $key => $model) {
|
||||
$table = is_int($key) ? $db->getTable($model) : $model;
|
||||
$query = new Query;
|
||||
$query->table($table)->where($this->fk, $pk)->delete();
|
||||
}
|
||||
}
|
||||
$this->trigger('after_delete', $this);
|
||||
$db->commit();
|
||||
return $result;
|
||||
} catch (\Exception $e) {
|
||||
$db->rollback();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
44
thinkphp/library/think/model/Pivot.php
Executable file
44
thinkphp/library/think/model/Pivot.php
Executable file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
class Pivot extends Model
|
||||
{
|
||||
|
||||
/** @var Model */
|
||||
public $parent;
|
||||
|
||||
protected $autoWriteTimestamp = false;
|
||||
|
||||
/**
|
||||
* 架构函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型
|
||||
* @param array|object $data 数据
|
||||
* @param string $table 中间数据表名
|
||||
*/
|
||||
public function __construct(Model $parent = null, $data = [], $table = '')
|
||||
{
|
||||
$this->parent = $parent;
|
||||
|
||||
if (is_null($this->name)) {
|
||||
$this->name = $table;
|
||||
}
|
||||
|
||||
parent::__construct($data);
|
||||
|
||||
$this->class = $this->name;
|
||||
}
|
||||
|
||||
}
|
131
thinkphp/library/think/model/Relation.php
Executable file
131
thinkphp/library/think/model/Relation.php
Executable file
@ -0,0 +1,131 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model;
|
||||
|
||||
use think\db\Query;
|
||||
use think\Exception;
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* Class Relation
|
||||
* @package think\model
|
||||
*
|
||||
* @mixin Query
|
||||
*/
|
||||
abstract class Relation
|
||||
{
|
||||
// 父模型对象
|
||||
protected $parent;
|
||||
/** @var Model 当前关联的模型类 */
|
||||
protected $model;
|
||||
/** @var Query 关联模型查询对象 */
|
||||
protected $query;
|
||||
// 关联表外键
|
||||
protected $foreignKey;
|
||||
// 关联表主键
|
||||
protected $localKey;
|
||||
// 基础查询
|
||||
protected $baseQuery;
|
||||
|
||||
/**
|
||||
* 获取关联的所属模型
|
||||
* @access public
|
||||
* @return Model
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前的关联模型类
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getModel()
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取关联的查询对象
|
||||
* @access public
|
||||
* @return Query
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装关联数据集
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @return mixed
|
||||
*/
|
||||
protected function resultSetBuild($resultSet)
|
||||
{
|
||||
return (new $this->model)->toCollection($resultSet);
|
||||
}
|
||||
|
||||
protected function getQueryFields($model)
|
||||
{
|
||||
$fields = $this->query->getOptions('field');
|
||||
return $this->getRelationQueryFields($fields, $model);
|
||||
}
|
||||
|
||||
protected function getRelationQueryFields($fields, $model)
|
||||
{
|
||||
if ($fields) {
|
||||
|
||||
if (is_string($fields)) {
|
||||
$fields = explode(',', $fields);
|
||||
}
|
||||
|
||||
foreach ($fields as &$field) {
|
||||
if (false === strpos($field, '.')) {
|
||||
$field = $model . '.' . $field;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$fields = $model . '.*';
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(仅执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function baseQuery()
|
||||
{}
|
||||
|
||||
public function __call($method, $args)
|
||||
{
|
||||
if ($this->query) {
|
||||
// 执行基础查询
|
||||
$this->baseQuery();
|
||||
|
||||
$result = call_user_func_array([$this->query, $method], $args);
|
||||
if ($result instanceof Query) {
|
||||
return $this;
|
||||
} else {
|
||||
$this->baseQuery = false;
|
||||
return $result;
|
||||
}
|
||||
} else {
|
||||
throw new Exception('method not exists:' . __CLASS__ . '->' . $method);
|
||||
}
|
||||
}
|
||||
}
|
BIN
thinkphp/library/think/model/code.jpg
Executable file
BIN
thinkphp/library/think/model/code.jpg
Executable file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
219
thinkphp/library/think/model/relation/BelongsTo.php
Executable file
219
thinkphp/library/think/model/relation/BelongsTo.php
Executable file
@ -0,0 +1,219 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use think\db\Query;
|
||||
use think\Loader;
|
||||
use think\Model;
|
||||
|
||||
class BelongsTo extends OneToOne
|
||||
{
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 关联主键
|
||||
* @param string $joinType JOIN类型
|
||||
* @param string $relation 关联名
|
||||
*/
|
||||
public function __construct(Model $parent, $model, $foreignKey, $localKey, $joinType = 'INNER', $relation = null)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->model = $model;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->localKey = $localKey;
|
||||
$this->joinType = $joinType;
|
||||
$this->query = (new $model)->db();
|
||||
$this->relation = $relation;
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @access public
|
||||
* @return array|false|\PDOStatement|string|Model
|
||||
*/
|
||||
public function getRelation($subRelation = '', $closure = null)
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
}
|
||||
$relationModel = $this->query
|
||||
->where($this->localKey, $this->parent->$foreignKey)
|
||||
->relation($subRelation)
|
||||
->find();
|
||||
|
||||
if ($relationModel) {
|
||||
$relationModel->setParent(clone $this->parent);
|
||||
}
|
||||
|
||||
return $relationModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @return Query
|
||||
*/
|
||||
public function has($operator = '>=', $count = 1, $id = '*')
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @return Query
|
||||
*/
|
||||
public function hasWhere($where = [])
|
||||
{
|
||||
$table = $this->query->getTable();
|
||||
$model = basename(str_replace('\\', '/', get_class($this->parent)));
|
||||
$relation = basename(str_replace('\\', '/', $this->model));
|
||||
if (is_array($where)) {
|
||||
foreach ($where as $key => $val) {
|
||||
if (false === strpos($key, '.')) {
|
||||
$where[$relation . '.' . $key] = $val;
|
||||
unset($where[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->parent->db()->alias($model)
|
||||
->field($model . '.*')
|
||||
->join($table . ' ' . $relation, $model . '.' . $this->foreignKey . '=' . $relation . '.' . $this->localKey, $this->joinType)
|
||||
->where($where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据集)
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$range = [];
|
||||
foreach ($resultSet as $result) {
|
||||
// 获取关联外键列表
|
||||
if (isset($result->$foreignKey)) {
|
||||
$range[] = $result->$foreignKey;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
$data = $this->eagerlyWhere($this, [
|
||||
$localKey => [
|
||||
'in',
|
||||
$range,
|
||||
],
|
||||
], $localKey, $relation, $subRelation, $closure);
|
||||
// 关联属性名
|
||||
$attr = Loader::parseName($relation);
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
// 关联模型
|
||||
if (!isset($data[$result->$foreignKey])) {
|
||||
$relationModel = null;
|
||||
} else {
|
||||
$relationModel = $data[$result->$foreignKey];
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->isUpdate(true);
|
||||
}
|
||||
|
||||
if (!empty($this->bindAttr)) {
|
||||
// 绑定关联属性
|
||||
$this->bindAttr($relationModel, $result, $this->bindAttr);
|
||||
} else {
|
||||
// 设置关联属性
|
||||
$result->setRelation($attr, $relationModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据)
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
protected function eagerlyOne(&$result, $relation, $subRelation, $closure)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
$data = $this->eagerlyWhere($this, [$localKey => $result->$foreignKey], $localKey, $relation, $subRelation, $closure);
|
||||
// 关联模型
|
||||
if (!isset($data[$result->$foreignKey])) {
|
||||
$relationModel = null;
|
||||
} else {
|
||||
$relationModel = $data[$result->$foreignKey];
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->isUpdate(true);
|
||||
}
|
||||
if (!empty($this->bindAttr)) {
|
||||
// 绑定关联属性
|
||||
$this->bindAttr($relationModel, $result, $this->bindAttr);
|
||||
} else {
|
||||
// 设置关联属性
|
||||
$result->setRelation(Loader::parseName($relation), $relationModel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加关联数据
|
||||
* @access public
|
||||
* @param Model $model 关联模型对象
|
||||
* @return Model
|
||||
*/
|
||||
public function associate($model)
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
$pk = $model->getPk();
|
||||
|
||||
$this->parent->setAttr($foreignKey, $model->$pk);
|
||||
$this->parent->save();
|
||||
|
||||
return $this->parent->setRelation($this->relation, $model);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销关联数据
|
||||
* @access public
|
||||
* @return Model
|
||||
*/
|
||||
public function dissociate()
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$this->parent->setAttr($foreignKey, null);
|
||||
$this->parent->save();
|
||||
|
||||
return $this->parent->setRelation($this->relation, null);
|
||||
}
|
||||
}
|
579
thinkphp/library/think/model/relation/BelongsToMany.php
Executable file
579
thinkphp/library/think/model/relation/BelongsToMany.php
Executable file
@ -0,0 +1,579 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use think\Collection;
|
||||
use think\db\Query;
|
||||
use think\Exception;
|
||||
use think\Loader;
|
||||
use think\Model;
|
||||
use think\model\Pivot;
|
||||
use think\model\Relation;
|
||||
use think\Paginator;
|
||||
|
||||
class BelongsToMany extends Relation
|
||||
{
|
||||
// 中间表表名
|
||||
protected $middle;
|
||||
// 中间表模型名称
|
||||
protected $pivotName;
|
||||
// 中间表模型对象
|
||||
protected $pivot;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $table 中间表名
|
||||
* @param string $foreignKey 关联模型外键
|
||||
* @param string $localKey 当前模型关联键
|
||||
*/
|
||||
public function __construct(Model $parent, $model, $table, $foreignKey, $localKey)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->model = $model;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->localKey = $localKey;
|
||||
if (false !== strpos($table, '\\')) {
|
||||
$this->pivotName = $table;
|
||||
$this->middle = basename(str_replace('\\', '/', $table));
|
||||
} else {
|
||||
$this->middle = $table;
|
||||
}
|
||||
$this->query = (new $model)->db();
|
||||
$this->pivot = $this->newPivot();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置中间表模型
|
||||
* @param $pivot
|
||||
* @return $this
|
||||
*/
|
||||
public function pivot($pivot)
|
||||
{
|
||||
$this->pivotName = $pivot;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例化中间表模型
|
||||
* @param $data
|
||||
* @return Pivot
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function newPivot($data = [])
|
||||
{
|
||||
$class = $this->pivotName ?: '\\think\\model\\Pivot';
|
||||
$pivot = new $class($this->parent, $data, $this->middle);
|
||||
if ($pivot instanceof Pivot) {
|
||||
return $pivot;
|
||||
} else {
|
||||
throw new Exception('pivot model must extends: \think\model\Pivot');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 合成中间表模型
|
||||
* @param array|Collection|Paginator $models
|
||||
*/
|
||||
protected function hydratePivot($models)
|
||||
{
|
||||
foreach ($models as $model) {
|
||||
$pivot = [];
|
||||
foreach ($model->getData() as $key => $val) {
|
||||
if (strpos($key, '__')) {
|
||||
list($name, $attr) = explode('__', $key, 2);
|
||||
if ('pivot' == $name) {
|
||||
$pivot[$attr] = $val;
|
||||
unset($model->$key);
|
||||
}
|
||||
}
|
||||
}
|
||||
$model->setRelation('pivot', $this->newPivot($pivot));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建关联查询Query对象
|
||||
* @return Query
|
||||
*/
|
||||
protected function buildQuery()
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
$localKey = $this->localKey;
|
||||
$pk = $this->parent->getPk();
|
||||
// 关联查询
|
||||
$condition['pivot.' . $localKey] = $this->parent->$pk;
|
||||
return $this->belongsToManyQuery($foreignKey, $localKey, $condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @return false|\PDOStatement|string|\think\Collection
|
||||
*/
|
||||
public function getRelation($subRelation = '', $closure = null)
|
||||
{
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
}
|
||||
$result = $this->buildQuery()->relation($subRelation)->select();
|
||||
$this->hydratePivot($result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重载select方法
|
||||
* @param null $data
|
||||
* @return false|\PDOStatement|string|Collection
|
||||
*/
|
||||
public function select($data = null)
|
||||
{
|
||||
$result = $this->buildQuery()->select($data);
|
||||
$this->hydratePivot($result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重载paginate方法
|
||||
* @param null $listRows
|
||||
* @param bool $simple
|
||||
* @param array $config
|
||||
* @return Paginator
|
||||
*/
|
||||
public function paginate($listRows = null, $simple = false, $config = [])
|
||||
{
|
||||
$result = $this->buildQuery()->paginate($listRows, $simple, $config);
|
||||
$this->hydratePivot($result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重载find方法
|
||||
* @param null $data
|
||||
* @return array|false|\PDOStatement|string|Model
|
||||
*/
|
||||
public function find($data = null)
|
||||
{
|
||||
$result = $this->buildQuery()->find($data);
|
||||
if ($result) {
|
||||
$this->hydratePivot([$result]);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找多条记录 如果不存在则抛出异常
|
||||
* @access public
|
||||
* @param array|string|Query|\Closure $data
|
||||
* @return array|\PDOStatement|string|Model
|
||||
*/
|
||||
public function selectOrFail($data = null)
|
||||
{
|
||||
return $this->failException(true)->select($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找单条记录 如果不存在则抛出异常
|
||||
* @access public
|
||||
* @param array|string|Query|\Closure $data
|
||||
* @return array|\PDOStatement|string|Model
|
||||
*/
|
||||
public function findOrFail($data = null)
|
||||
{
|
||||
return $this->failException(true)->find($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @return Query
|
||||
*/
|
||||
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @return Query
|
||||
* @throws Exception
|
||||
*/
|
||||
public function hasWhere($where = [])
|
||||
{
|
||||
throw new Exception('relation not support: hasWhere');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置中间表的查询条件
|
||||
* @param $field
|
||||
* @param null $op
|
||||
* @param null $condition
|
||||
* @return $this
|
||||
*/
|
||||
public function wherePivot($field, $op = null, $condition = null)
|
||||
{
|
||||
$field = 'pivot.' . $field;
|
||||
$this->query->where($field, $op, $condition);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据集)
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$pk = $resultSet[0]->getPk();
|
||||
$range = [];
|
||||
foreach ($resultSet as $result) {
|
||||
// 获取关联外键列表
|
||||
if (isset($result->$pk)) {
|
||||
$range[] = $result->$pk;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
// 查询关联数据
|
||||
$data = $this->eagerlyManyToMany([
|
||||
'pivot.' . $localKey => [
|
||||
'in',
|
||||
$range,
|
||||
],
|
||||
], $relation, $subRelation);
|
||||
// 关联属性名
|
||||
$attr = Loader::parseName($relation);
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
if (!isset($data[$result->$pk])) {
|
||||
$data[$result->$pk] = [];
|
||||
}
|
||||
|
||||
$result->setRelation($attr, $this->resultSetBuild($data[$result->$pk]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(单个数据)
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResult(&$result, $relation, $subRelation, $closure)
|
||||
{
|
||||
$pk = $result->getPk();
|
||||
if (isset($result->$pk)) {
|
||||
$pk = $result->$pk;
|
||||
// 查询管理数据
|
||||
$data = $this->eagerlyManyToMany(['pivot.' . $this->localKey => $pk], $relation, $subRelation);
|
||||
|
||||
// 关联数据封装
|
||||
if (!isset($data[$pk])) {
|
||||
$data[$pk] = [];
|
||||
}
|
||||
$result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$pk]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联统计
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param \Closure $closure 闭包
|
||||
* @return integer
|
||||
*/
|
||||
public function relationCount($result, $closure)
|
||||
{
|
||||
$pk = $result->getPk();
|
||||
$count = 0;
|
||||
if (isset($result->$pk)) {
|
||||
$pk = $result->$pk;
|
||||
$count = $this->belongsToManyQuery($this->foreignKey, $this->localKey, ['pivot.' . $this->localKey => $pk])->count();
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取关联统计子查询
|
||||
* @access public
|
||||
* @param \Closure $closure 闭包
|
||||
* @return string
|
||||
*/
|
||||
public function getRelationCountQuery($closure)
|
||||
{
|
||||
return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [
|
||||
'pivot.' . $this->localKey => [
|
||||
'exp',
|
||||
'=' . $this->parent->getTable() . '.' . $this->parent->getPk(),
|
||||
],
|
||||
])->fetchSql()->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 多对多 关联模型预查询
|
||||
* @access public
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyManyToMany($where, $relation, $subRelation = '')
|
||||
{
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
$list = $this->belongsToManyQuery($this->foreignKey, $this->localKey, $where)->with($subRelation)->select();
|
||||
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
foreach ($list as $set) {
|
||||
$pivot = [];
|
||||
foreach ($set->getData() as $key => $val) {
|
||||
if (strpos($key, '__')) {
|
||||
list($name, $attr) = explode('__', $key, 2);
|
||||
if ('pivot' == $name) {
|
||||
$pivot[$attr] = $val;
|
||||
unset($set->$key);
|
||||
}
|
||||
}
|
||||
}
|
||||
$set->setRelation('pivot', $this->newPivot($pivot));
|
||||
$data[$pivot[$this->localKey]][] = $set;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* BELONGS TO MANY 关联查询
|
||||
* @access public
|
||||
* @param string $foreignKey 关联模型关联键
|
||||
* @param string $localKey 当前模型关联键
|
||||
* @param array $condition 关联查询条件
|
||||
* @return Query
|
||||
*/
|
||||
protected function belongsToManyQuery($foreignKey, $localKey, $condition = [])
|
||||
{
|
||||
// 关联查询封装
|
||||
$tableName = $this->query->getTable();
|
||||
$table = $this->pivot->getTable();
|
||||
$fields = $this->getQueryFields($tableName);
|
||||
|
||||
$query = $this->query->field($fields)
|
||||
->field(true, false, $table, 'pivot', 'pivot__');
|
||||
|
||||
if (empty($this->baseQuery)) {
|
||||
$relationFk = $this->query->getPk();
|
||||
$query->join($table . ' pivot', 'pivot.' . $foreignKey . '=' . $tableName . '.' . $relationFk)
|
||||
->where($condition);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存(新增)当前关联数据对象
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
|
||||
* @param array $pivot 中间表额外数据
|
||||
* @return integer
|
||||
*/
|
||||
public function save($data, array $pivot = [])
|
||||
{
|
||||
// 保存关联表/中间表数据
|
||||
return $this->attach($data, $pivot);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存当前关联数据对象
|
||||
* @access public
|
||||
* @param array $dataSet 数据集
|
||||
* @param array $pivot 中间表额外数据
|
||||
* @param bool $samePivot 额外数据是否相同
|
||||
* @return integer
|
||||
*/
|
||||
public function saveAll(array $dataSet, array $pivot = [], $samePivot = false)
|
||||
{
|
||||
$result = false;
|
||||
foreach ($dataSet as $key => $data) {
|
||||
if (!$samePivot) {
|
||||
$pivotData = isset($pivot[$key]) ? $pivot[$key] : [];
|
||||
} else {
|
||||
$pivotData = $pivot;
|
||||
}
|
||||
$result = $this->attach($data, $pivotData);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 附加关联的一个中间表数据
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组、关联模型对象 或者 关联对象的主键
|
||||
* @param array $pivot 中间表额外数据
|
||||
* @return array|Pivot
|
||||
* @throws Exception
|
||||
*/
|
||||
public function attach($data, $pivot = [])
|
||||
{
|
||||
if (is_array($data)) {
|
||||
if (key($data) === 0) {
|
||||
$id = $data;
|
||||
} else {
|
||||
// 保存关联表数据
|
||||
$model = new $this->model;
|
||||
$model->save($data);
|
||||
$id = $model->getLastInsID();
|
||||
}
|
||||
} elseif (is_numeric($data) || is_string($data)) {
|
||||
// 根据关联表主键直接写入中间表
|
||||
$id = $data;
|
||||
} elseif ($data instanceof Model) {
|
||||
// 根据关联表主键直接写入中间表
|
||||
$relationFk = $data->getPk();
|
||||
$id = $data->$relationFk;
|
||||
}
|
||||
|
||||
if ($id) {
|
||||
// 保存中间表数据
|
||||
$pk = $this->parent->getPk();
|
||||
$pivot[$this->localKey] = $this->parent->$pk;
|
||||
$ids = (array) $id;
|
||||
foreach ($ids as $id) {
|
||||
$pivot[$this->foreignKey] = $id;
|
||||
$this->pivot->insert($pivot, true);
|
||||
$result[] = $this->newPivot($pivot);
|
||||
}
|
||||
if (count($result) == 1) {
|
||||
// 返回中间表模型对象
|
||||
$result = $result[0];
|
||||
}
|
||||
return $result;
|
||||
} else {
|
||||
throw new Exception('miss relation data');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解除关联的一个中间表数据
|
||||
* @access public
|
||||
* @param integer|array $data 数据 可以使用关联对象的主键
|
||||
* @param bool $relationDel 是否同时删除关联表数据
|
||||
* @return integer
|
||||
*/
|
||||
public function detach($data = null, $relationDel = false)
|
||||
{
|
||||
if (is_array($data)) {
|
||||
$id = $data;
|
||||
} elseif (is_numeric($data) || is_string($data)) {
|
||||
// 根据关联表主键直接写入中间表
|
||||
$id = $data;
|
||||
} elseif ($data instanceof Model) {
|
||||
// 根据关联表主键直接写入中间表
|
||||
$relationFk = $data->getPk();
|
||||
$id = $data->$relationFk;
|
||||
}
|
||||
// 删除中间表数据
|
||||
$pk = $this->parent->getPk();
|
||||
$pivot[$this->localKey] = $this->parent->$pk;
|
||||
if (isset($id)) {
|
||||
$pivot[$this->foreignKey] = is_array($id) ? ['in', $id] : $id;
|
||||
}
|
||||
$this->pivot->where($pivot)->delete();
|
||||
// 删除关联表数据
|
||||
if (isset($id) && $relationDel) {
|
||||
$model = $this->model;
|
||||
$model::destroy($id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据同步
|
||||
* @param array $ids
|
||||
* @param bool $detaching
|
||||
* @return array
|
||||
*/
|
||||
public function sync($ids, $detaching = true)
|
||||
{
|
||||
$changes = [
|
||||
'attached' => [],
|
||||
'detached' => [],
|
||||
'updated' => [],
|
||||
];
|
||||
$pk = $this->parent->getPk();
|
||||
$current = $this->pivot->where($this->localKey, $this->parent->$pk)
|
||||
->column($this->foreignKey);
|
||||
$records = [];
|
||||
|
||||
foreach ($ids as $key => $value) {
|
||||
if (!is_array($value)) {
|
||||
$records[$value] = [];
|
||||
} else {
|
||||
$records[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$detach = array_diff($current, array_keys($records));
|
||||
|
||||
if ($detaching && count($detach) > 0) {
|
||||
$this->detach($detach);
|
||||
|
||||
$changes['detached'] = $detach;
|
||||
}
|
||||
|
||||
foreach ($records as $id => $attributes) {
|
||||
if (!in_array($id, $current)) {
|
||||
$this->attach($id, $attributes);
|
||||
$changes['attached'][] = $id;
|
||||
} elseif (count($attributes) > 0 &&
|
||||
$this->attach($id, $attributes)
|
||||
) {
|
||||
$changes['updated'][] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
return $changes;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(进执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function baseQuery()
|
||||
{
|
||||
if (empty($this->baseQuery) && $this->parent->getData()) {
|
||||
$pk = $this->parent->getPk();
|
||||
$table = $this->pivot->getTable();
|
||||
$this->query->join($table . ' pivot', 'pivot.' . $this->foreignKey . '=' . $this->query->getTable() . '.' . $this->query->getPk())->where('pivot.' . $this->localKey, $this->parent->$pk);
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
294
thinkphp/library/think/model/relation/HasMany.php
Executable file
294
thinkphp/library/think/model/relation/HasMany.php
Executable file
@ -0,0 +1,294 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use think\db\Query;
|
||||
use think\Loader;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
|
||||
class HasMany extends Relation
|
||||
{
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 当前模型主键
|
||||
*/
|
||||
public function __construct(Model $parent, $model, $foreignKey, $localKey)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->model = $model;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->localKey = $localKey;
|
||||
$this->query = (new $model)->db();
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @return false|\PDOStatement|string|\think\Collection
|
||||
*/
|
||||
public function getRelation($subRelation = '', $closure = null)
|
||||
{
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
}
|
||||
$list = $this->relation($subRelation)->select();
|
||||
$parent = clone $this->parent;
|
||||
|
||||
foreach ($list as &$model) {
|
||||
$model->setParent($parent);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$range = [];
|
||||
foreach ($resultSet as $result) {
|
||||
// 获取关联外键列表
|
||||
if (isset($result->$localKey)) {
|
||||
$range[] = $result->$localKey;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
$data = $this->eagerlyOneToMany(new $this->model, [
|
||||
$this->foreignKey => [
|
||||
'in',
|
||||
$range,
|
||||
],
|
||||
], $relation, $subRelation, $closure);
|
||||
// 关联属性名
|
||||
$attr = Loader::parseName($relation);
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
if (!isset($data[$result->$localKey])) {
|
||||
$data[$result->$localKey] = [];
|
||||
}
|
||||
|
||||
foreach ($data[$result->$localKey] as &$relationModel) {
|
||||
$relationModel->setParent(clone $result);
|
||||
}
|
||||
|
||||
$result->setRelation($attr, $this->resultSetBuild($data[$result->$localKey]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResult(&$result, $relation, $subRelation, $closure)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
|
||||
if (isset($result->$localKey)) {
|
||||
$data = $this->eagerlyOneToMany(new $this->model, [$this->foreignKey => $result->$localKey], $relation, $subRelation, $closure);
|
||||
// 关联数据封装
|
||||
if (!isset($data[$result->$localKey])) {
|
||||
$data[$result->$localKey] = [];
|
||||
}
|
||||
|
||||
foreach ($data[$result->$localKey] as &$relationModel) {
|
||||
$relationModel->setParent(clone $result);
|
||||
}
|
||||
|
||||
$result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$result->$localKey]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联统计
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param \Closure $closure 闭包
|
||||
* @return integer
|
||||
*/
|
||||
public function relationCount($result, $closure)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$count = 0;
|
||||
if (isset($result->$localKey)) {
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
}
|
||||
$count = $this->query->where([$this->foreignKey => $result->$localKey])->count();
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建关联统计子查询
|
||||
* @access public
|
||||
* @param \Closure $closure 闭包
|
||||
* @return string
|
||||
*/
|
||||
public function getRelationCountQuery($closure)
|
||||
{
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
}
|
||||
$localKey = $this->localKey ?: $this->parent->getPk();
|
||||
return $this->query->where([
|
||||
$this->foreignKey => [
|
||||
'exp',
|
||||
'=' . $this->parent->getTable() . '.' . $localKey,
|
||||
],
|
||||
])->fetchSql()->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 一对多 关联模型预查询
|
||||
* @access public
|
||||
* @param object $model 关联模型对象
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @param bool $closure
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyOneToMany($model, $where, $relation, $subRelation = '', $closure = false)
|
||||
{
|
||||
$foreignKey = $this->foreignKey;
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $model]);
|
||||
}
|
||||
$list = $model->where($where)->with($subRelation)->select();
|
||||
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
foreach ($list as $set) {
|
||||
$data[$set->$foreignKey][] = $set;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存(新增)当前关联数据对象
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
|
||||
* @return Model|false
|
||||
*/
|
||||
public function save($data)
|
||||
{
|
||||
if ($data instanceof Model) {
|
||||
$data = $data->getData();
|
||||
}
|
||||
// 保存关联表数据
|
||||
$model = new $this->model;
|
||||
$data[$this->foreignKey] = $this->parent->{$this->localKey};
|
||||
return $model->save($data) ? $model : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存当前关联数据对象
|
||||
* @access public
|
||||
* @param array $dataSet 数据集
|
||||
* @return integer
|
||||
*/
|
||||
public function saveAll(array $dataSet)
|
||||
{
|
||||
$result = false;
|
||||
foreach ($dataSet as $key => $data) {
|
||||
$result = $this->save($data);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @return Query
|
||||
*/
|
||||
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
|
||||
{
|
||||
$table = $this->query->getTable();
|
||||
$model = basename(str_replace('\\', '/', get_class($this->parent)));
|
||||
$relation = basename(str_replace('\\', '/', $this->model));
|
||||
|
||||
return $this->parent->db()
|
||||
->alias($model)
|
||||
->field($model . '.*')
|
||||
->join($table . ' ' . $relation, $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType)
|
||||
->group($relation . '.' . $this->foreignKey)
|
||||
->having('count(' . $id . ')' . $operator . $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @return Query
|
||||
*/
|
||||
public function hasWhere($where = [])
|
||||
{
|
||||
$table = $this->query->getTable();
|
||||
$model = basename(str_replace('\\', '/', get_class($this->parent)));
|
||||
$relation = basename(str_replace('\\', '/', $this->model));
|
||||
if (is_array($where)) {
|
||||
foreach ($where as $key => $val) {
|
||||
if (false === strpos($key, '.')) {
|
||||
$where[$relation . '.' . $key] = $val;
|
||||
unset($where[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->parent->db()->alias($model)
|
||||
->field($model . '.*')
|
||||
->join($table . ' ' . $relation, $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey)
|
||||
->where($where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(进执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function baseQuery()
|
||||
{
|
||||
if (empty($this->baseQuery)) {
|
||||
if (isset($this->parent->{$this->localKey})) {
|
||||
// 关联查询带入关联条件
|
||||
$this->query->where($this->foreignKey, $this->parent->{$this->localKey});
|
||||
}
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
144
thinkphp/library/think/model/relation/HasManyThrough.php
Executable file
144
thinkphp/library/think/model/relation/HasManyThrough.php
Executable file
@ -0,0 +1,144 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use think\db\Query;
|
||||
use think\Exception;
|
||||
use think\Loader;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
|
||||
class HasManyThrough extends Relation
|
||||
{
|
||||
// 中间关联表外键
|
||||
protected $throughKey;
|
||||
// 中间表模型
|
||||
protected $through;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $through 中间模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $throughKey 关联外键
|
||||
* @param string $localKey 关联主键
|
||||
*/
|
||||
public function __construct(Model $parent, $model, $through, $foreignKey, $throughKey, $localKey)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->model = $model;
|
||||
$this->through = $through;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->throughKey = $throughKey;
|
||||
$this->localKey = $localKey;
|
||||
$this->query = (new $model)->db();
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @return false|\PDOStatement|string|\think\Collection
|
||||
*/
|
||||
public function getRelation($subRelation = '', $closure = null)
|
||||
{
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
}
|
||||
|
||||
return $this->relation($subRelation)->select();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @return Query
|
||||
*/
|
||||
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @return Query
|
||||
*/
|
||||
public function hasWhere($where = [])
|
||||
{
|
||||
throw new Exception('relation not support: hasWhere');
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
|
||||
{}
|
||||
|
||||
/**
|
||||
* 预载入关联查询 返回模型对象
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResult(&$result, $relation, $subRelation, $closure)
|
||||
{}
|
||||
|
||||
/**
|
||||
* 关联统计
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param \Closure $closure 闭包
|
||||
* @return integer
|
||||
*/
|
||||
public function relationCount($result, $closure)
|
||||
{}
|
||||
|
||||
/**
|
||||
* 执行基础查询(进执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function baseQuery()
|
||||
{
|
||||
if (empty($this->baseQuery) && $this->parent->getData()) {
|
||||
$through = $this->through;
|
||||
$alias = Loader::parseName(basename(str_replace('\\', '/', $this->model)));
|
||||
$throughTable = $through::getTable();
|
||||
$pk = (new $through)->getPk();
|
||||
$throughKey = $this->throughKey;
|
||||
$modelTable = $this->parent->getTable();
|
||||
$this->query->field($alias . '.*')->alias($alias)
|
||||
->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey)
|
||||
->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey)
|
||||
->where($throughTable . '.' . $this->foreignKey, $this->parent->{$this->localKey});
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
189
thinkphp/library/think/model/relation/HasOne.php
Executable file
189
thinkphp/library/think/model/relation/HasOne.php
Executable file
@ -0,0 +1,189 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use think\db\Query;
|
||||
use think\Loader;
|
||||
use think\Model;
|
||||
|
||||
class HasOne extends OneToOne
|
||||
{
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $foreignKey 关联外键
|
||||
* @param string $localKey 当前模型主键
|
||||
* @param string $joinType JOIN类型
|
||||
*/
|
||||
public function __construct(Model $parent, $model, $foreignKey, $localKey, $joinType = 'INNER')
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->model = $model;
|
||||
$this->foreignKey = $foreignKey;
|
||||
$this->localKey = $localKey;
|
||||
$this->joinType = $joinType;
|
||||
$this->query = (new $model)->db();
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @return array|false|\PDOStatement|string|Model
|
||||
*/
|
||||
public function getRelation($subRelation = '', $closure = null)
|
||||
{
|
||||
// 执行关联定义方法
|
||||
$localKey = $this->localKey;
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
}
|
||||
// 判断关联类型执行查询
|
||||
$relationModel = $this->query->where($this->foreignKey, $this->parent->$localKey)->relation($subRelation)->find();
|
||||
|
||||
if ($relationModel) {
|
||||
$relationModel->setParent(clone $this->parent);
|
||||
}
|
||||
|
||||
return $relationModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @return Query
|
||||
*/
|
||||
public function has()
|
||||
{
|
||||
$table = $this->query->getTable();
|
||||
$model = basename(str_replace('\\', '/', get_class($this->parent)));
|
||||
$relation = basename(str_replace('\\', '/', $this->model));
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
return $this->parent->db()
|
||||
->alias($model)
|
||||
->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey) {
|
||||
$query->table([$table => $relation])->field($relation . '.' . $foreignKey)->whereExp($model . '.' . $localKey, '=' . $relation . '.' . $foreignKey);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @return Query
|
||||
*/
|
||||
public function hasWhere($where = [])
|
||||
{
|
||||
$table = $this->query->getTable();
|
||||
$model = basename(str_replace('\\', '/', get_class($this->parent)));
|
||||
$relation = basename(str_replace('\\', '/', $this->model));
|
||||
if (is_array($where)) {
|
||||
foreach ($where as $key => $val) {
|
||||
if (false === strpos($key, '.')) {
|
||||
$where[$relation . '.' . $key] = $val;
|
||||
unset($where[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->parent->db()->alias($model)
|
||||
->field($model . '.*')
|
||||
->join($table . ' ' . $relation, $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $this->joinType)
|
||||
->where($where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据集)
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
|
||||
$range = [];
|
||||
foreach ($resultSet as $result) {
|
||||
// 获取关联外键列表
|
||||
if (isset($result->$localKey)) {
|
||||
$range[] = $result->$localKey;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
$data = $this->eagerlyWhere($this, [
|
||||
$foreignKey => [
|
||||
'in',
|
||||
$range,
|
||||
],
|
||||
], $foreignKey, $relation, $subRelation, $closure);
|
||||
// 关联属性名
|
||||
$attr = Loader::parseName($relation);
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
// 关联模型
|
||||
if (!isset($data[$result->$localKey])) {
|
||||
$relationModel = null;
|
||||
} else {
|
||||
$relationModel = $data[$result->$localKey];
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->isUpdate(true);
|
||||
}
|
||||
if (!empty($this->bindAttr)) {
|
||||
// 绑定关联属性
|
||||
$this->bindAttr($relationModel, $result, $this->bindAttr);
|
||||
} else {
|
||||
// 设置关联属性
|
||||
$result->setRelation($attr, $relationModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据)
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
protected function eagerlyOne(&$result, $relation, $subRelation, $closure)
|
||||
{
|
||||
$localKey = $this->localKey;
|
||||
$foreignKey = $this->foreignKey;
|
||||
$data = $this->eagerlyWhere($this, [$foreignKey => $result->$localKey], $foreignKey, $relation, $subRelation, $closure);
|
||||
|
||||
// 关联模型
|
||||
if (!isset($data[$result->$localKey])) {
|
||||
$relationModel = null;
|
||||
} else {
|
||||
$relationModel = $data[$result->$localKey];
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->isUpdate(true);
|
||||
}
|
||||
if (!empty($this->bindAttr)) {
|
||||
// 绑定关联属性
|
||||
$this->bindAttr($relationModel, $result, $this->bindAttr);
|
||||
} else {
|
||||
$result->setRelation(Loader::parseName($relation), $relationModel);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
285
thinkphp/library/think/model/relation/MorphMany.php
Executable file
285
thinkphp/library/think/model/relation/MorphMany.php
Executable file
@ -0,0 +1,285 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use think\db\Query;
|
||||
use think\Exception;
|
||||
use think\Loader;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
|
||||
class MorphMany extends Relation
|
||||
{
|
||||
// 多态字段
|
||||
protected $morphKey;
|
||||
protected $morphType;
|
||||
// 多态类型
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $morphKey 关联外键
|
||||
* @param string $morphType 多态字段名
|
||||
* @param string $type 多态类型
|
||||
*/
|
||||
public function __construct(Model $parent, $model, $morphKey, $morphType, $type)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->model = $model;
|
||||
$this->type = $type;
|
||||
$this->morphKey = $morphKey;
|
||||
$this->morphType = $morphType;
|
||||
$this->query = (new $model)->db();
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @return false|\PDOStatement|string|\think\Collection
|
||||
*/
|
||||
public function getRelation($subRelation = '', $closure = null)
|
||||
{
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
}
|
||||
$list = $this->relation($subRelation)->select();
|
||||
$parent = clone $this->parent;
|
||||
|
||||
foreach ($list as &$model) {
|
||||
$model->setParent($parent);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @return Query
|
||||
*/
|
||||
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
|
||||
{
|
||||
throw new Exception('relation not support: has');
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @return Query
|
||||
*/
|
||||
public function hasWhere($where = [])
|
||||
{
|
||||
throw new Exception('relation not support: hasWhere');
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
|
||||
{
|
||||
$morphType = $this->morphType;
|
||||
$morphKey = $this->morphKey;
|
||||
$type = $this->type;
|
||||
$range = [];
|
||||
foreach ($resultSet as $result) {
|
||||
$pk = $result->getPk();
|
||||
// 获取关联外键列表
|
||||
if (isset($result->$pk)) {
|
||||
$range[] = $result->$pk;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
$data = $this->eagerlyMorphToMany([
|
||||
$morphKey => ['in', $range],
|
||||
$morphType => $type,
|
||||
], $relation, $subRelation, $closure);
|
||||
// 关联属性名
|
||||
$attr = Loader::parseName($relation);
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
if (!isset($data[$result->$pk])) {
|
||||
$data[$result->$pk] = [];
|
||||
}
|
||||
foreach ($data[$result->$pk] as &$relationModel) {
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->isUpdate(true);
|
||||
}
|
||||
$result->setRelation($attr, $this->resultSetBuild($data[$result->$pk]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResult(&$result, $relation, $subRelation, $closure)
|
||||
{
|
||||
$pk = $result->getPk();
|
||||
if (isset($result->$pk)) {
|
||||
$data = $this->eagerlyMorphToMany([
|
||||
$this->morphKey => $result->$pk,
|
||||
$this->morphType => $this->type,
|
||||
], $relation, $subRelation, $closure);
|
||||
|
||||
if (!isset($data[$result->$pk])) {
|
||||
$data[$result->$pk] = [];
|
||||
}
|
||||
|
||||
foreach ($data[$result->$pk] as &$relationModel) {
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->isUpdate(true);
|
||||
}
|
||||
|
||||
$result->setRelation(Loader::parseName($relation), $this->resultSetBuild($data[$result->$pk]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联统计
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param \Closure $closure 闭包
|
||||
* @return integer
|
||||
*/
|
||||
public function relationCount($result, $closure)
|
||||
{
|
||||
$pk = $result->getPk();
|
||||
$count = 0;
|
||||
if (isset($result->$pk)) {
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
}
|
||||
$count = $this->query->where([$this->morphKey => $result->$pk, $this->morphType => $this->type])->count();
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取关联统计子查询
|
||||
* @access public
|
||||
* @param \Closure $closure 闭包
|
||||
* @return string
|
||||
*/
|
||||
public function getRelationCountQuery($closure)
|
||||
{
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
}
|
||||
|
||||
return $this->query->where([
|
||||
$this->morphKey => [
|
||||
'exp',
|
||||
'=' . $this->parent->getTable() . '.' . $this->parent->getPk(),
|
||||
],
|
||||
$this->morphType => $this->type,
|
||||
])->fetchSql()->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 多态一对多 关联模型预查询
|
||||
* @access public
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @param bool|\Closure $closure 闭包
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyMorphToMany($where, $relation, $subRelation = '', $closure = false)
|
||||
{
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this]);
|
||||
}
|
||||
$list = $this->query->where($where)->with($subRelation)->select();
|
||||
$morphKey = $this->morphKey;
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
foreach ($list as $set) {
|
||||
$data[$set->$morphKey][] = $set;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存(新增)当前关联数据对象
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
|
||||
* @return Model|false
|
||||
*/
|
||||
public function save($data)
|
||||
{
|
||||
if ($data instanceof Model) {
|
||||
$data = $data->getData();
|
||||
}
|
||||
// 保存关联表数据
|
||||
$pk = $this->parent->getPk();
|
||||
|
||||
$model = new $this->model;
|
||||
$data[$this->morphKey] = $this->parent->$pk;
|
||||
$data[$this->morphType] = $this->type;
|
||||
return $model->save($data) ? $model : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存当前关联数据对象
|
||||
* @access public
|
||||
* @param array $dataSet 数据集
|
||||
* @return integer
|
||||
*/
|
||||
public function saveAll(array $dataSet)
|
||||
{
|
||||
$result = false;
|
||||
foreach ($dataSet as $key => $data) {
|
||||
$result = $this->save($data);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(进执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function baseQuery()
|
||||
{
|
||||
if (empty($this->baseQuery) && $this->parent->getData()) {
|
||||
$pk = $this->parent->getPk();
|
||||
$map[$this->morphKey] = $this->parent->$pk;
|
||||
$map[$this->morphType] = $this->type;
|
||||
$this->query->where($map);
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
229
thinkphp/library/think/model/relation/MorphOne.php
Executable file
229
thinkphp/library/think/model/relation/MorphOne.php
Executable file
@ -0,0 +1,229 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use think\db\Query;
|
||||
use think\Exception;
|
||||
use think\Loader;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
|
||||
class MorphOne extends Relation
|
||||
{
|
||||
// 多态字段
|
||||
protected $morphKey;
|
||||
protected $morphType;
|
||||
// 多态类型
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $model 模型名
|
||||
* @param string $morphKey 关联外键
|
||||
* @param string $morphType 多态字段名
|
||||
* @param string $type 多态类型
|
||||
*/
|
||||
public function __construct(Model $parent, $model, $morphKey, $morphType, $type)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->model = $model;
|
||||
$this->type = $type;
|
||||
$this->morphKey = $morphKey;
|
||||
$this->morphType = $morphType;
|
||||
$this->query = (new $model)->db();
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @return false|\PDOStatement|string|\think\Collection
|
||||
*/
|
||||
public function getRelation($subRelation = '', $closure = null)
|
||||
{
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this->query]);
|
||||
}
|
||||
$relationModel = $this->relation($subRelation)->find();
|
||||
|
||||
if ($relationModel) {
|
||||
$relationModel->setParent(clone $this->parent);
|
||||
}
|
||||
|
||||
return $relationModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @return Query
|
||||
*/
|
||||
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @return Query
|
||||
*/
|
||||
public function hasWhere($where = [])
|
||||
{
|
||||
throw new Exception('relation not support: hasWhere');
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
|
||||
{
|
||||
$morphType = $this->morphType;
|
||||
$morphKey = $this->morphKey;
|
||||
$type = $this->type;
|
||||
$range = [];
|
||||
foreach ($resultSet as $result) {
|
||||
$pk = $result->getPk();
|
||||
// 获取关联外键列表
|
||||
if (isset($result->$pk)) {
|
||||
$range[] = $result->$pk;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
$data = $this->eagerlyMorphToOne([
|
||||
$morphKey => ['in', $range],
|
||||
$morphType => $type,
|
||||
], $relation, $subRelation, $closure);
|
||||
// 关联属性名
|
||||
$attr = Loader::parseName($relation);
|
||||
// 关联数据封装
|
||||
foreach ($resultSet as $result) {
|
||||
if (!isset($data[$result->$pk])) {
|
||||
$relationModel = null;
|
||||
} else {
|
||||
$relationModel = $data[$result->$pk];
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->isUpdate(true);
|
||||
}
|
||||
|
||||
$result->setRelation($attr, $relationModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResult(&$result, $relation, $subRelation, $closure)
|
||||
{
|
||||
$pk = $result->getPk();
|
||||
if (isset($result->$pk)) {
|
||||
$pk = $result->$pk;
|
||||
$data = $this->eagerlyMorphToOne([
|
||||
$this->morphKey => $pk,
|
||||
$this->morphType => $this->type,
|
||||
], $relation, $subRelation, $closure);
|
||||
|
||||
if (isset($data[$pk])) {
|
||||
$relationModel = $data[$pk];
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->isUpdate(true);
|
||||
} else {
|
||||
$relationModel = null;
|
||||
}
|
||||
|
||||
$result->setRelation(Loader::parseName($relation), $relationModel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 多态一对一 关联模型预查询
|
||||
* @access public
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @param bool|\Closure $closure 闭包
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyMorphToOne($where, $relation, $subRelation = '', $closure = false)
|
||||
{
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $this]);
|
||||
}
|
||||
$list = $this->query->where($where)->with($subRelation)->find();
|
||||
$morphKey = $this->morphKey;
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
foreach ($list as $set) {
|
||||
$data[$set->$morphKey][] = $set;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存(新增)当前关联数据对象
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
|
||||
* @return Model|false
|
||||
*/
|
||||
public function save($data)
|
||||
{
|
||||
if ($data instanceof Model) {
|
||||
$data = $data->getData();
|
||||
}
|
||||
// 保存关联表数据
|
||||
$pk = $this->parent->getPk();
|
||||
|
||||
$model = new $this->model;
|
||||
$data[$this->morphKey] = $this->parent->$pk;
|
||||
$data[$this->morphType] = $this->type;
|
||||
return $model->save($data) ? $model : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行基础查询(进执行一次)
|
||||
* @access protected
|
||||
* @return void
|
||||
*/
|
||||
protected function baseQuery()
|
||||
{
|
||||
if (empty($this->baseQuery) && $this->parent->getData()) {
|
||||
$pk = $this->parent->getPk();
|
||||
$map[$this->morphKey] = $this->parent->$pk;
|
||||
$map[$this->morphType] = $this->type;
|
||||
$this->query->where($map);
|
||||
$this->baseQuery = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
275
thinkphp/library/think/model/relation/MorphTo.php
Executable file
275
thinkphp/library/think/model/relation/MorphTo.php
Executable file
@ -0,0 +1,275 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use think\Exception;
|
||||
use think\Loader;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
|
||||
class MorphTo extends Relation
|
||||
{
|
||||
// 多态字段
|
||||
protected $morphKey;
|
||||
protected $morphType;
|
||||
// 多态别名
|
||||
protected $alias;
|
||||
protected $relation;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
* @param Model $parent 上级模型对象
|
||||
* @param string $morphType 多态字段名
|
||||
* @param string $morphKey 外键名
|
||||
* @param array $alias 多态别名定义
|
||||
* @param string $relation 关联名
|
||||
*/
|
||||
public function __construct(Model $parent, $morphType, $morphKey, $alias = [], $relation = null)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
$this->morphType = $morphType;
|
||||
$this->morphKey = $morphKey;
|
||||
$this->alias = $alias;
|
||||
$this->relation = $relation;
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟获取关联数据
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包查询条件
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRelation($subRelation = '', $closure = null)
|
||||
{
|
||||
$morphKey = $this->morphKey;
|
||||
$morphType = $this->morphType;
|
||||
// 多态模型
|
||||
$model = $this->parseModel($this->parent->$morphType);
|
||||
// 主键数据
|
||||
$pk = $this->parent->$morphKey;
|
||||
$relationModel = (new $model)->relation($subRelation)->find($pk);
|
||||
|
||||
if ($relationModel) {
|
||||
$relationModel->setParent(clone $this->parent);
|
||||
}
|
||||
return $relationModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param string $operator 比较操作符
|
||||
* @param integer $count 个数
|
||||
* @param string $id 关联表的统计字段
|
||||
* @param string $joinType JOIN类型
|
||||
* @return Query
|
||||
*/
|
||||
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联条件查询当前模型
|
||||
* @access public
|
||||
* @param mixed $where 查询条件(数组或者闭包)
|
||||
* @return Query
|
||||
*/
|
||||
public function hasWhere($where = [])
|
||||
{
|
||||
throw new Exception('relation not support: hasWhere');
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析模型的完整命名空间
|
||||
* @access public
|
||||
* @param string $model 模型名(或者完整类名)
|
||||
* @return string
|
||||
*/
|
||||
protected function parseModel($model)
|
||||
{
|
||||
if (isset($this->alias[$model])) {
|
||||
$model = $this->alias[$model];
|
||||
}
|
||||
if (false === strpos($model, '\\')) {
|
||||
$path = explode('\\', get_class($this->parent));
|
||||
array_pop($path);
|
||||
array_push($path, Loader::parseName($model, 1));
|
||||
$model = implode('\\', $path);
|
||||
}
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置多态别名
|
||||
* @access public
|
||||
* @param array $alias 别名定义
|
||||
* @return $this
|
||||
*/
|
||||
public function setAlias($alias)
|
||||
{
|
||||
$this->alias = $alias;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除关联查询参数
|
||||
* @access public
|
||||
* @return $this
|
||||
*/
|
||||
public function removeOption()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
|
||||
{
|
||||
$morphKey = $this->morphKey;
|
||||
$morphType = $this->morphType;
|
||||
$range = [];
|
||||
foreach ($resultSet as $result) {
|
||||
// 获取关联外键列表
|
||||
if (!empty($result->$morphKey)) {
|
||||
$range[$result->$morphType][] = $result->$morphKey;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($range)) {
|
||||
// 关联属性名
|
||||
$attr = Loader::parseName($relation);
|
||||
foreach ($range as $key => $val) {
|
||||
// 多态类型映射
|
||||
$model = $this->parseModel($key);
|
||||
$obj = new $model;
|
||||
$pk = $obj->getPk();
|
||||
$list = $obj->all($val, $subRelation);
|
||||
$data = [];
|
||||
foreach ($list as $k => $vo) {
|
||||
$data[$vo->$pk] = $vo;
|
||||
}
|
||||
foreach ($resultSet as $result) {
|
||||
if ($key == $result->$morphType) {
|
||||
// 关联模型
|
||||
if (!isset($data[$result->$morphKey])) {
|
||||
throw new Exception('relation data not exists :' . $this->model);
|
||||
} else {
|
||||
$relationModel = $data[$result->$morphKey];
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->isUpdate(true);
|
||||
|
||||
$result->setRelation($attr, $relationModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResult(&$result, $relation, $subRelation, $closure)
|
||||
{
|
||||
$morphKey = $this->morphKey;
|
||||
$morphType = $this->morphType;
|
||||
// 多态类型映射
|
||||
$model = $this->parseModel($result->{$this->morphType});
|
||||
$this->eagerlyMorphToOne($model, $relation, $result, $subRelation);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联统计
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param \Closure $closure 闭包
|
||||
* @return integer
|
||||
*/
|
||||
public function relationCount($result, $closure)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 多态MorphTo 关联模型预查询
|
||||
* @access public
|
||||
* @param object $model 关联模型对象
|
||||
* @param string $relation 关联名
|
||||
* @param $result
|
||||
* @param string $subRelation 子关联
|
||||
* @return void
|
||||
*/
|
||||
protected function eagerlyMorphToOne($model, $relation, &$result, $subRelation = '')
|
||||
{
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
$pk = $this->parent->{$this->morphKey};
|
||||
$data = (new $model)->with($subRelation)->find($pk);
|
||||
if ($data) {
|
||||
$data->setParent(clone $result);
|
||||
$data->isUpdate(true);
|
||||
}
|
||||
$result->setRelation(Loader::parseName($relation), $data ?: null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加关联数据
|
||||
* @access public
|
||||
* @param Model $model 关联模型对象
|
||||
* @param string $type 多态类型
|
||||
* @return Model
|
||||
*/
|
||||
public function associate($model, $type = '')
|
||||
{
|
||||
$morphKey = $this->morphKey;
|
||||
$morphType = $this->morphType;
|
||||
$pk = $model->getPk();
|
||||
|
||||
$this->parent->setAttr($morphKey, $model->$pk);
|
||||
$this->parent->setAttr($morphType, $type ?: get_class($model));
|
||||
$this->parent->save();
|
||||
|
||||
return $this->parent->setRelation($this->relation, $model);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销关联数据
|
||||
* @access public
|
||||
* @return Model
|
||||
*/
|
||||
public function dissociate()
|
||||
{
|
||||
$morphKey = $this->morphKey;
|
||||
$morphType = $this->morphType;
|
||||
|
||||
$this->parent->setAttr($morphKey, null);
|
||||
$this->parent->setAttr($morphType, null);
|
||||
$this->parent->save();
|
||||
|
||||
return $this->parent->setRelation($this->relation, null);
|
||||
}
|
||||
|
||||
}
|
324
thinkphp/library/think/model/relation/OneToOne.php
Executable file
324
thinkphp/library/think/model/relation/OneToOne.php
Executable file
@ -0,0 +1,324 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\model\relation;
|
||||
|
||||
use think\db\Query;
|
||||
use think\Exception;
|
||||
use think\Loader;
|
||||
use think\Model;
|
||||
use think\model\Relation;
|
||||
|
||||
/**
|
||||
* Class OneToOne
|
||||
* @package think\model\relation
|
||||
*
|
||||
*/
|
||||
abstract class OneToOne extends Relation
|
||||
{
|
||||
// 预载入方式 0 -JOIN 1 -IN
|
||||
protected $eagerlyType = 1;
|
||||
// 当前关联的JOIN类型
|
||||
protected $joinType;
|
||||
// 要绑定的属性
|
||||
protected $bindAttr = [];
|
||||
// 关联方法名
|
||||
protected $relation;
|
||||
|
||||
/**
|
||||
* 设置join类型
|
||||
* @access public
|
||||
* @param string $type JOIN类型
|
||||
* @return $this
|
||||
*/
|
||||
public function joinType($type)
|
||||
{
|
||||
$this->joinType = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(JOIN方式)
|
||||
* @access public
|
||||
* @param Query $query 查询对象
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @param \Closure $closure 闭包条件
|
||||
* @param bool $first
|
||||
* @return void
|
||||
*/
|
||||
public function eagerly(Query $query, $relation, $subRelation, $closure, $first)
|
||||
{
|
||||
$name = Loader::parseName(basename(str_replace('\\', '/', $query->getModel())));
|
||||
$alias = $name;
|
||||
if ($first) {
|
||||
$table = $query->getTable();
|
||||
$query->table([$table => $alias]);
|
||||
if ($query->getOptions('field')) {
|
||||
$field = $query->getOptions('field');
|
||||
$query->removeOption('field');
|
||||
} else {
|
||||
$field = true;
|
||||
}
|
||||
$query->field($field, false, $table, $alias);
|
||||
$field = null;
|
||||
}
|
||||
|
||||
// 预载入封装
|
||||
$joinTable = $this->query->getTable();
|
||||
$joinAlias = $relation;
|
||||
$query->via($joinAlias);
|
||||
|
||||
if ($this instanceof BelongsTo) {
|
||||
$query->join($joinTable . ' ' . $joinAlias, $alias . '.' . $this->foreignKey . '=' . $joinAlias . '.' . $this->localKey, $this->joinType);
|
||||
} else {
|
||||
$query->join($joinTable . ' ' . $joinAlias, $alias . '.' . $this->localKey . '=' . $joinAlias . '.' . $this->foreignKey, $this->joinType);
|
||||
}
|
||||
|
||||
if ($closure) {
|
||||
// 执行闭包查询
|
||||
call_user_func_array($closure, [ & $query]);
|
||||
// 使用withField指定获取关联的字段,如
|
||||
// $query->where(['id'=>1])->withField('id,name');
|
||||
if ($query->getOptions('with_field')) {
|
||||
$field = $query->getOptions('with_field');
|
||||
$query->removeOption('with_field');
|
||||
}
|
||||
} elseif (isset($this->option['field'])) {
|
||||
$field = $this->option['field'];
|
||||
}
|
||||
$query->field(isset($field) ? $field : true, false, $joinTable, $joinAlias, $relation . '__');
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据集)
|
||||
* @param array $resultSet
|
||||
* @param string $relation
|
||||
* @param string $subRelation
|
||||
* @param \Closure $closure
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure);
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据)
|
||||
* @param Model $result
|
||||
* @param string $relation
|
||||
* @param string $subRelation
|
||||
* @param \Closure $closure
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function eagerlyOne(&$result, $relation, $subRelation, $closure);
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据集)
|
||||
* @access public
|
||||
* @param array $resultSet 数据集
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
|
||||
{
|
||||
if (1 == $this->eagerlyType) {
|
||||
// IN查询
|
||||
$this->eagerlySet($resultSet, $relation, $subRelation, $closure);
|
||||
} else {
|
||||
// 模型关联组装
|
||||
foreach ($resultSet as $result) {
|
||||
$this->match($this->model, $relation, $result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预载入关联查询(数据)
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param string $relation 当前关联名
|
||||
* @param string $subRelation 子关联名
|
||||
* @param \Closure $closure 闭包
|
||||
* @return void
|
||||
*/
|
||||
public function eagerlyResult(&$result, $relation, $subRelation, $closure)
|
||||
{
|
||||
if (1 == $this->eagerlyType) {
|
||||
// IN查询
|
||||
$this->eagerlyOne($result, $relation, $subRelation, $closure);
|
||||
} else {
|
||||
// 模型关联组装
|
||||
$this->match($this->model, $relation, $result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存(新增)当前关联数据对象
|
||||
* @access public
|
||||
* @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键
|
||||
* @return Model|false
|
||||
*/
|
||||
public function save($data)
|
||||
{
|
||||
if ($data instanceof Model) {
|
||||
$data = $data->getData();
|
||||
}
|
||||
$model = new $this->model;
|
||||
// 保存关联表数据
|
||||
$data[$this->foreignKey] = $this->parent->{$this->localKey};
|
||||
return $model->save($data) ? $model : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置预载入方式
|
||||
* @access public
|
||||
* @param integer $type 预载入方式 0 JOIN查询 1 IN查询
|
||||
* @return $this
|
||||
*/
|
||||
public function setEagerlyType($type)
|
||||
{
|
||||
$this->eagerlyType = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取预载入方式
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getEagerlyType()
|
||||
{
|
||||
return $this->eagerlyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定关联表的属性到父模型属性
|
||||
* @access public
|
||||
* @param mixed $attr 要绑定的属性列表
|
||||
* @return $this
|
||||
*/
|
||||
public function bind($attr)
|
||||
{
|
||||
if (is_string($attr)) {
|
||||
$attr = explode(',', $attr);
|
||||
}
|
||||
$this->bindAttr = $attr;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取绑定属性
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getBindAttr()
|
||||
{
|
||||
return $this->bindAttr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联统计
|
||||
* @access public
|
||||
* @param Model $result 数据对象
|
||||
* @param \Closure $closure 闭包
|
||||
* @return integer
|
||||
*/
|
||||
public function relationCount($result, $closure)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 一对一 关联模型预查询拼装
|
||||
* @access public
|
||||
* @param string $model 模型名称
|
||||
* @param string $relation 关联名
|
||||
* @param Model $result 模型对象实例
|
||||
* @return void
|
||||
*/
|
||||
protected function match($model, $relation, &$result)
|
||||
{
|
||||
// 重新组装模型数据
|
||||
foreach ($result->getData() as $key => $val) {
|
||||
if (strpos($key, '__')) {
|
||||
list($name, $attr) = explode('__', $key, 2);
|
||||
if ($name == $relation) {
|
||||
$list[$name][$attr] = $val;
|
||||
unset($result->$key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($list[$relation])) {
|
||||
$relationModel = new $model($list[$relation]);
|
||||
$relationModel->setParent(clone $result);
|
||||
$relationModel->isUpdate(true);
|
||||
|
||||
if (!empty($this->bindAttr)) {
|
||||
$this->bindAttr($relationModel, $result, $this->bindAttr);
|
||||
}
|
||||
} else {
|
||||
$relationModel = null;
|
||||
}
|
||||
$result->setRelation(Loader::parseName($relation), $relationModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定关联属性到父模型
|
||||
* @access protected
|
||||
* @param Model $model 关联模型对象
|
||||
* @param Model $result 父模型对象
|
||||
* @param array $bindAttr 绑定属性
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function bindAttr($model, &$result, $bindAttr)
|
||||
{
|
||||
foreach ($bindAttr as $key => $attr) {
|
||||
$key = is_numeric($key) ? $attr : $key;
|
||||
if (isset($result->$key)) {
|
||||
throw new Exception('bind attr has exists:' . $key);
|
||||
} else {
|
||||
$result->setAttr($key, $model ? $model->$attr : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 一对一 关联模型预查询(IN方式)
|
||||
* @access public
|
||||
* @param object $model 关联模型对象
|
||||
* @param array $where 关联预查询条件
|
||||
* @param string $key 关联键名
|
||||
* @param string $relation 关联名
|
||||
* @param string $subRelation 子关联
|
||||
* @param bool|\Closure $closure
|
||||
* @return array
|
||||
*/
|
||||
protected function eagerlyWhere($model, $where, $key, $relation, $subRelation = '', $closure = false)
|
||||
{
|
||||
// 预载入关联查询 支持嵌套预载入
|
||||
if ($closure) {
|
||||
call_user_func_array($closure, [ & $model]);
|
||||
if ($field = $model->getOptions('with_field')) {
|
||||
$model->field($field)->removeOption('with_field');
|
||||
}
|
||||
}
|
||||
$list = $model->where($where)->with($subRelation)->select();
|
||||
|
||||
// 组装模型数据
|
||||
$data = [];
|
||||
foreach ($list as $set) {
|
||||
$data[$set->$key] = $set;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
83
thinkphp/library/think/paginator/Collection.php
Executable file
83
thinkphp/library/think/paginator/Collection.php
Executable file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: zhangyajun <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\paginator;
|
||||
|
||||
use Exception;
|
||||
use think\Paginator;
|
||||
|
||||
/**
|
||||
* Class Collection
|
||||
* @package think\paginator
|
||||
* @method integer total()
|
||||
* @method integer listRows()
|
||||
* @method integer currentPage()
|
||||
* @method string render()
|
||||
* @method Paginator fragment($fragment)
|
||||
* @method Paginator appends($key, $value)
|
||||
* @method integer lastPage()
|
||||
* @method boolean hasPages()
|
||||
*/
|
||||
class Collection extends \think\Collection
|
||||
{
|
||||
|
||||
/** @var Paginator */
|
||||
protected $paginator;
|
||||
|
||||
public function __construct($items = [], Paginator $paginator = null)
|
||||
{
|
||||
$this->paginator = $paginator;
|
||||
parent::__construct($items);
|
||||
}
|
||||
|
||||
public static function make($items = [], Paginator $paginator = null)
|
||||
{
|
||||
return new static($items, $paginator);
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
if ($this->paginator) {
|
||||
try {
|
||||
$total = $this->total();
|
||||
} catch (Exception $e) {
|
||||
$total = null;
|
||||
}
|
||||
/*
|
||||
return [
|
||||
'total' => $total,
|
||||
'per_page' => $this->listRows(),
|
||||
'current_page' => $this->currentPage(),
|
||||
'data' => parent::toArray()
|
||||
];
|
||||
*/
|
||||
//modify by GZShangTao
|
||||
return [
|
||||
'Total' => $total,
|
||||
'PerPage' => $this->listRows(),
|
||||
'CurrentPage' => $this->currentPage(),
|
||||
'TotalPage' => $this->getTotalPage(),
|
||||
'Rows' => parent::toArray()
|
||||
];
|
||||
} else {
|
||||
return parent::toArray();
|
||||
}
|
||||
}
|
||||
|
||||
public function __call($method, $args)
|
||||
{
|
||||
if ($this->paginator && method_exists($this->paginator, $method)) {
|
||||
return call_user_func_array([$this->paginator, $method], $args);
|
||||
} else {
|
||||
throw new Exception('method not exists:' . __CLASS__ . '->' . $method);
|
||||
}
|
||||
}
|
||||
}
|
205
thinkphp/library/think/paginator/driver/Bootstrap.php
Executable file
205
thinkphp/library/think/paginator/driver/Bootstrap.php
Executable file
@ -0,0 +1,205 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2017 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: zhangyajun <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\paginator\driver;
|
||||
|
||||
use think\Paginator;
|
||||
|
||||
class Bootstrap extends Paginator
|
||||
{
|
||||
|
||||
/**
|
||||
* 上一页按钮
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
protected function getPreviousButton($text = "«")
|
||||
{
|
||||
|
||||
if ($this->currentPage() <= 1) {
|
||||
return $this->getDisabledTextWrapper($text);
|
||||
}
|
||||
|
||||
$url = $this->url(
|
||||
$this->currentPage() - 1
|
||||
);
|
||||
|
||||
return $this->getPageLinkWrapper($url, $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下一页按钮
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
protected function getNextButton($text = '»')
|
||||
{
|
||||
if (!$this->hasMore) {
|
||||
return $this->getDisabledTextWrapper($text);
|
||||
}
|
||||
|
||||
$url = $this->url($this->currentPage() + 1);
|
||||
|
||||
return $this->getPageLinkWrapper($url, $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 页码按钮
|
||||
* @return string
|
||||
*/
|
||||
protected function getLinks()
|
||||
{
|
||||
if ($this->simple)
|
||||
return '';
|
||||
|
||||
$block = [
|
||||
'first' => null,
|
||||
'slider' => null,
|
||||
'last' => null
|
||||
];
|
||||
|
||||
$side = 3;
|
||||
$window = $side * 2;
|
||||
|
||||
if ($this->lastPage < $window + 6) {
|
||||
$block['first'] = $this->getUrlRange(1, $this->lastPage);
|
||||
} elseif ($this->currentPage <= $window) {
|
||||
$block['first'] = $this->getUrlRange(1, $window + 2);
|
||||
$block['last'] = $this->getUrlRange($this->lastPage - 1, $this->lastPage);
|
||||
} elseif ($this->currentPage > ($this->lastPage - $window)) {
|
||||
$block['first'] = $this->getUrlRange(1, 2);
|
||||
$block['last'] = $this->getUrlRange($this->lastPage - ($window + 2), $this->lastPage);
|
||||
} else {
|
||||
$block['first'] = $this->getUrlRange(1, 2);
|
||||
$block['slider'] = $this->getUrlRange($this->currentPage - $side, $this->currentPage + $side);
|
||||
$block['last'] = $this->getUrlRange($this->lastPage - 1, $this->lastPage);
|
||||
}
|
||||
|
||||
$html = '';
|
||||
|
||||
if (is_array($block['first'])) {
|
||||
$html .= $this->getUrlLinks($block['first']);
|
||||
}
|
||||
|
||||
if (is_array($block['slider'])) {
|
||||
$html .= $this->getDots();
|
||||
$html .= $this->getUrlLinks($block['slider']);
|
||||
}
|
||||
|
||||
if (is_array($block['last'])) {
|
||||
$html .= $this->getDots();
|
||||
$html .= $this->getUrlLinks($block['last']);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染分页html
|
||||
* @return mixed
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
if ($this->hasPages()) {
|
||||
if ($this->simple) {
|
||||
return sprintf(
|
||||
'<ul class="pager">%s %s</ul>',
|
||||
$this->getPreviousButton(),
|
||||
$this->getNextButton()
|
||||
);
|
||||
} else {
|
||||
return sprintf(
|
||||
'<ul class="pagination">%s %s %s</ul>',
|
||||
$this->getPreviousButton(),
|
||||
$this->getLinks(),
|
||||
$this->getNextButton()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个可点击的按钮
|
||||
*
|
||||
* @param string $url
|
||||
* @param int $page
|
||||
* @return string
|
||||
*/
|
||||
protected function getAvailablePageWrapper($url, $page)
|
||||
{
|
||||
return '<li><a href="' . htmlentities($url) . '">' . $page . '</a></li>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个禁用的按钮
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
protected function getDisabledTextWrapper($text)
|
||||
{
|
||||
return '<li class="disabled"><span>' . $text . '</span></li>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个激活的按钮
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
protected function getActivePageWrapper($text)
|
||||
{
|
||||
return '<li class="active"><span>' . $text . '</span></li>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成省略号按钮
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDots()
|
||||
{
|
||||
return $this->getDisabledTextWrapper('...');
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量生成页码按钮.
|
||||
*
|
||||
* @param array $urls
|
||||
* @return string
|
||||
*/
|
||||
protected function getUrlLinks(array $urls)
|
||||
{
|
||||
$html = '';
|
||||
|
||||
foreach ($urls as $page => $url) {
|
||||
$html .= $this->getPageLinkWrapper($url, $page);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成普通页码按钮
|
||||
*
|
||||
* @param string $url
|
||||
* @param int $page
|
||||
* @return string
|
||||
*/
|
||||
protected function getPageLinkWrapper($url, $page)
|
||||
{
|
||||
if ($page == $this->currentPage()) {
|
||||
return $this->getActivePageWrapper($page);
|
||||
}
|
||||
|
||||
return $this->getAvailablePageWrapper($url, $page);
|
||||
}
|
||||
}
|
233
thinkphp/library/think/process/Builder.php
Executable file
233
thinkphp/library/think/process/Builder.php
Executable file
@ -0,0 +1,233 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\process;
|
||||
|
||||
use think\Process;
|
||||
|
||||
class Builder
|
||||
{
|
||||
private $arguments;
|
||||
private $cwd;
|
||||
private $env = null;
|
||||
private $input;
|
||||
private $timeout = 60;
|
||||
private $options = [];
|
||||
private $inheritEnv = true;
|
||||
private $prefix = [];
|
||||
private $outputDisabled = false;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @param string[] $arguments 参数
|
||||
*/
|
||||
public function __construct(array $arguments = [])
|
||||
{
|
||||
$this->arguments = $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个实例
|
||||
* @param string[] $arguments 参数
|
||||
* @return self
|
||||
*/
|
||||
public static function create(array $arguments = [])
|
||||
{
|
||||
return new static($arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个参数
|
||||
* @param string $argument 参数
|
||||
* @return self
|
||||
*/
|
||||
public function add($argument)
|
||||
{
|
||||
$this->arguments[] = $argument;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个前缀
|
||||
* @param string|array $prefix
|
||||
* @return self
|
||||
*/
|
||||
public function setPrefix($prefix)
|
||||
{
|
||||
$this->prefix = is_array($prefix) ? $prefix : [$prefix];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置参数
|
||||
* @param string[] $arguments
|
||||
* @return self
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
{
|
||||
$this->arguments = $arguments;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置工作目录
|
||||
* @param null|string $cwd
|
||||
* @return self
|
||||
*/
|
||||
public function setWorkingDirectory($cwd)
|
||||
{
|
||||
$this->cwd = $cwd;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否初始化环境变量
|
||||
* @param bool $inheritEnv
|
||||
* @return self
|
||||
*/
|
||||
public function inheritEnvironmentVariables($inheritEnv = true)
|
||||
{
|
||||
$this->inheritEnv = $inheritEnv;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置环境变量
|
||||
* @param string $name
|
||||
* @param null|string $value
|
||||
* @return self
|
||||
*/
|
||||
public function setEnv($name, $value)
|
||||
{
|
||||
$this->env[$name] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加环境变量
|
||||
* @param array $variables
|
||||
* @return self
|
||||
*/
|
||||
public function addEnvironmentVariables(array $variables)
|
||||
{
|
||||
$this->env = array_replace($this->env, $variables);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输入
|
||||
* @param mixed $input
|
||||
* @return self
|
||||
*/
|
||||
public function setInput($input)
|
||||
{
|
||||
$this->input = Utils::validateInput(sprintf('%s::%s', __CLASS__, __FUNCTION__), $input);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置超时时间
|
||||
* @param float|null $timeout
|
||||
* @return self
|
||||
*/
|
||||
public function setTimeout($timeout)
|
||||
{
|
||||
if (null === $timeout) {
|
||||
$this->timeout = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
$timeout = (float) $timeout;
|
||||
|
||||
if ($timeout < 0) {
|
||||
throw new \InvalidArgumentException('The timeout value must be a valid positive integer or float number.');
|
||||
}
|
||||
|
||||
$this->timeout = $timeout;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置proc_open选项
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
* @return self
|
||||
*/
|
||||
public function setOption($name, $value)
|
||||
{
|
||||
$this->options[$name] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁止输出
|
||||
* @return self
|
||||
*/
|
||||
public function disableOutput()
|
||||
{
|
||||
$this->outputDisabled = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启输出
|
||||
* @return self
|
||||
*/
|
||||
public function enableOutput()
|
||||
{
|
||||
$this->outputDisabled = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个Process实例
|
||||
* @return Process
|
||||
*/
|
||||
public function getProcess()
|
||||
{
|
||||
if (0 === count($this->prefix) && 0 === count($this->arguments)) {
|
||||
throw new \LogicException('You must add() command arguments before calling getProcess().');
|
||||
}
|
||||
|
||||
$options = $this->options;
|
||||
|
||||
$arguments = array_merge($this->prefix, $this->arguments);
|
||||
$script = implode(' ', array_map([__NAMESPACE__ . '\\Utils', 'escapeArgument'], $arguments));
|
||||
|
||||
if ($this->inheritEnv) {
|
||||
// include $_ENV for BC purposes
|
||||
$env = array_replace($_ENV, $_SERVER, $this->env);
|
||||
} else {
|
||||
$env = $this->env;
|
||||
}
|
||||
|
||||
$process = new Process($script, $this->cwd, $env, $this->input, $this->timeout, $options);
|
||||
|
||||
if ($this->outputDisabled) {
|
||||
$process->disableOutput();
|
||||
}
|
||||
|
||||
return $process;
|
||||
}
|
||||
}
|
75
thinkphp/library/think/process/Utils.php
Executable file
75
thinkphp/library/think/process/Utils.php
Executable file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | TopThink [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2015 http://www.topthink.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: zhangyajun <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\process;
|
||||
|
||||
class Utils
|
||||
{
|
||||
|
||||
/**
|
||||
* 转义字符串
|
||||
* @param string $argument
|
||||
* @return string
|
||||
*/
|
||||
public static function escapeArgument($argument)
|
||||
{
|
||||
|
||||
if ('' === $argument) {
|
||||
return escapeshellarg($argument);
|
||||
}
|
||||
$escapedArgument = '';
|
||||
$quote = false;
|
||||
foreach (preg_split('/(")/i', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) {
|
||||
if ('"' === $part) {
|
||||
$escapedArgument .= '\\"';
|
||||
} elseif (self::isSurroundedBy($part, '%')) {
|
||||
// Avoid environment variable expansion
|
||||
$escapedArgument .= '^%"' . substr($part, 1, -1) . '"^%';
|
||||
} else {
|
||||
// escape trailing backslash
|
||||
if ('\\' === substr($part, -1)) {
|
||||
$part .= '\\';
|
||||
}
|
||||
$quote = true;
|
||||
$escapedArgument .= $part;
|
||||
}
|
||||
}
|
||||
if ($quote) {
|
||||
$escapedArgument = '"' . $escapedArgument . '"';
|
||||
}
|
||||
return $escapedArgument;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证并进行规范化Process输入。
|
||||
* @param string $caller
|
||||
* @param mixed $input
|
||||
* @return string
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public static function validateInput($caller, $input)
|
||||
{
|
||||
if (null !== $input) {
|
||||
if (is_resource($input)) {
|
||||
return $input;
|
||||
}
|
||||
if (is_scalar($input)) {
|
||||
return (string) $input;
|
||||
}
|
||||
throw new \InvalidArgumentException(sprintf('%s only accepts strings or stream resources.', $caller));
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
private static function isSurroundedBy($arg, $char)
|
||||
{
|
||||
return 2 < strlen($arg) && $char === $arg[0] && $char === $arg[strlen($arg) - 1];
|
||||
}
|
||||
|
||||
}
|
43
thinkphp/library/think/process/exception/Faild.php
Executable file
43
thinkphp/library/think/process/exception/Faild.php
Executable file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\process\exception;
|
||||
|
||||
|
||||
use think\Process;
|
||||
|
||||
class Failed extends \RuntimeException
|
||||
{
|
||||
|
||||
private $process;
|
||||
|
||||
public function __construct(Process $process)
|
||||
{
|
||||
if ($process->isSuccessful()) {
|
||||
throw new \InvalidArgumentException('Expected a failed process, but the given process was successful.');
|
||||
}
|
||||
|
||||
$error = sprintf('The command "%s" failed.' . "\nExit Code: %s(%s)", $process->getCommandLine(), $process->getExitCode(), $process->getExitCodeText());
|
||||
|
||||
if (!$process->isOutputDisabled()) {
|
||||
$error .= sprintf("\n\nOutput:\n================\n%s\n\nError Output:\n================\n%s", $process->getOutput(), $process->getErrorOutput());
|
||||
}
|
||||
|
||||
parent::__construct($error);
|
||||
|
||||
$this->process = $process;
|
||||
}
|
||||
|
||||
public function getProcess()
|
||||
{
|
||||
return $this->process;
|
||||
}
|
||||
}
|
42
thinkphp/library/think/process/exception/Failed.php
Executable file
42
thinkphp/library/think/process/exception/Failed.php
Executable file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\process\exception;
|
||||
|
||||
use think\Process;
|
||||
|
||||
class Failed extends \RuntimeException
|
||||
{
|
||||
|
||||
private $process;
|
||||
|
||||
public function __construct(Process $process)
|
||||
{
|
||||
if ($process->isSuccessful()) {
|
||||
throw new \InvalidArgumentException('Expected a failed process, but the given process was successful.');
|
||||
}
|
||||
|
||||
$error = sprintf('The command "%s" failed.' . "\nExit Code: %s(%s)", $process->getCommandLine(), $process->getExitCode(), $process->getExitCodeText());
|
||||
|
||||
if (!$process->isOutputDisabled()) {
|
||||
$error .= sprintf("\n\nOutput:\n================\n%s\n\nError Output:\n================\n%s", $process->getOutput(), $process->getErrorOutput());
|
||||
}
|
||||
|
||||
parent::__construct($error);
|
||||
|
||||
$this->process = $process;
|
||||
}
|
||||
|
||||
public function getProcess()
|
||||
{
|
||||
return $this->process;
|
||||
}
|
||||
}
|
61
thinkphp/library/think/process/exception/Timeout.php
Executable file
61
thinkphp/library/think/process/exception/Timeout.php
Executable file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\process\exception;
|
||||
|
||||
use think\Process;
|
||||
|
||||
class Timeout extends \RuntimeException
|
||||
{
|
||||
|
||||
const TYPE_GENERAL = 1;
|
||||
const TYPE_IDLE = 2;
|
||||
|
||||
private $process;
|
||||
private $timeoutType;
|
||||
|
||||
public function __construct(Process $process, $timeoutType)
|
||||
{
|
||||
$this->process = $process;
|
||||
$this->timeoutType = $timeoutType;
|
||||
|
||||
parent::__construct(sprintf('The process "%s" exceeded the timeout of %s seconds.', $process->getCommandLine(), $this->getExceededTimeout()));
|
||||
}
|
||||
|
||||
public function getProcess()
|
||||
{
|
||||
return $this->process;
|
||||
}
|
||||
|
||||
public function isGeneralTimeout()
|
||||
{
|
||||
return $this->timeoutType === self::TYPE_GENERAL;
|
||||
}
|
||||
|
||||
public function isIdleTimeout()
|
||||
{
|
||||
return $this->timeoutType === self::TYPE_IDLE;
|
||||
}
|
||||
|
||||
public function getExceededTimeout()
|
||||
{
|
||||
switch ($this->timeoutType) {
|
||||
case self::TYPE_GENERAL:
|
||||
return $this->process->getTimeout();
|
||||
|
||||
case self::TYPE_IDLE:
|
||||
return $this->process->getIdleTimeout();
|
||||
|
||||
default:
|
||||
throw new \LogicException(sprintf('Unknown timeout type "%d".', $this->timeoutType));
|
||||
}
|
||||
}
|
||||
}
|
93
thinkphp/library/think/process/pipes/Pipes.php
Executable file
93
thinkphp/library/think/process/pipes/Pipes.php
Executable file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\process\pipes;
|
||||
|
||||
abstract class Pipes
|
||||
{
|
||||
|
||||
/** @var array */
|
||||
public $pipes = [];
|
||||
|
||||
/** @var string */
|
||||
protected $inputBuffer = '';
|
||||
/** @var resource|null */
|
||||
protected $input;
|
||||
|
||||
/** @var bool */
|
||||
private $blocked = true;
|
||||
|
||||
const CHUNK_SIZE = 16384;
|
||||
|
||||
/**
|
||||
* 返回用于 proc_open 描述符的数组
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getDescriptors();
|
||||
|
||||
/**
|
||||
* 返回一个数组的索引由其相关的流,以防这些管道使用的临时文件的文件名。
|
||||
* @return string[]
|
||||
*/
|
||||
abstract public function getFiles();
|
||||
|
||||
/**
|
||||
* 文件句柄和管道中读取数据。
|
||||
* @param bool $blocking 是否使用阻塞调用
|
||||
* @param bool $close 是否要关闭管道,如果他们已经到达 EOF。
|
||||
* @return string[]
|
||||
*/
|
||||
abstract public function readAndWrite($blocking, $close = false);
|
||||
|
||||
/**
|
||||
* 返回当前状态如果有打开的文件句柄或管道。
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function areOpen();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
foreach ($this->pipes as $pipe) {
|
||||
fclose($pipe);
|
||||
}
|
||||
$this->pipes = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查系统调用已被中断
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasSystemCallBeenInterrupted()
|
||||
{
|
||||
$lastError = error_get_last();
|
||||
|
||||
return isset($lastError['message']) && false !== stripos($lastError['message'], 'interrupted system call');
|
||||
}
|
||||
|
||||
protected function unblock()
|
||||
{
|
||||
if (!$this->blocked) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->pipes as $pipe) {
|
||||
stream_set_blocking($pipe, 0);
|
||||
}
|
||||
if (null !== $this->input) {
|
||||
stream_set_blocking($this->input, 0);
|
||||
}
|
||||
|
||||
$this->blocked = false;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user