diff --git a/application/api/config.php b/application/api/config.php new file mode 100644 index 00000000..18af4cb4 --- /dev/null +++ b/application/api/config.php @@ -0,0 +1,12 @@ + +// +---------------------------------------------------------------------- + +return array( + 'default_return_type' => 'json', +); \ No newline at end of file diff --git a/core/base.php b/core/base.php index 66d53e9f..80a6e28f 100644 --- a/core/base.php +++ b/core/base.php @@ -10,7 +10,7 @@ // +---------------------------------------------------------------------- define('THINK_VERSION', '5.0.0 RC4'); -define('THINK_START_TIME', number_format(microtime(true), 8, '.', '')); +define('THINK_START_TIME', microtime(true)); define('THINK_START_MEM', memory_get_usage()); define('EXT', '.php'); define('DS', DIRECTORY_SEPARATOR); diff --git a/core/helper.php b/core/helper.php index 57b8ae49..5c5f8442 100644 --- a/core/helper.php +++ b/core/helper.php @@ -327,7 +327,7 @@ if (!function_exists('cookie')) { Cookie::clear($value); } elseif ('' === $value) { // 获取 - return Cookie::get($name); + return 0 === strpos($name, '?') ? Cookie::has(substr($name, 1), $option) : Cookie::get($name); } elseif (is_null($value)) { // 删除 return Cookie::delete($name); @@ -497,12 +497,16 @@ if (!function_exists('redirect')) { if (!function_exists('abort')) { /** * 抛出HTTP异常 - * @param integer $code 状态码 - * @param string $message 错误信息 - * @param array $header 参数 + * @param integer|Response $code 状态码 或者 Response对象实例 + * @param string $message 错误信息 + * @param array $header 参数 */ function abort($code, $message = null, $header = []) { - throw new \think\exception\HttpException($code, $message, null, $header); + if ($code instanceof Response) { + throw new \think\exception\HttpResponseException($code); + } else { + throw new \think\exception\HttpException($code, $message, null, $header); + } } } diff --git a/core/library/think/App.php b/core/library/think/App.php index 0b7ff00a..2e50a40f 100644 --- a/core/library/think/App.php +++ b/core/library/think/App.php @@ -82,7 +82,7 @@ class App } $config = self::initCommon(); - + $request->filter($config['default_filter']); try { // 开启多语言机制 @@ -104,6 +104,7 @@ class App } // 记录当前调度信息 $request->dispatch($dispatch); + // 记录路由信息 self::$debug && Log::record('[ ROUTE ] ' . var_export($dispatch, true), 'info'); // 监听app_begin @@ -415,6 +416,8 @@ class App // 加载初始化文件 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; // 加载模块配置 @@ -466,7 +469,7 @@ class App */ public static function routeCheck($request, array $config) { - $path = rtrim($request->path(), '/'); + $path = $request->path(); $depr = $config['pathinfo_depr']; $result = false; // 路由检测 diff --git a/core/library/think/Cache.php b/core/library/think/Cache.php index dcf3dd05..10ff01c8 100644 --- a/core/library/think/Cache.php +++ b/core/library/think/Cache.php @@ -69,16 +69,30 @@ class Cache } /** - * 读取缓存 + * 判断缓存是否存在 * @access public - * @param string $name 缓存标识 - * @return mixed + * @param string $name 缓存变量名 + * @return bool */ - public static function get($name) + public static function has($name) { self::init(); self::$readTimes++; - return self::$handler->get($name); + return self::$handler->has($name); + } + + /** + * 读取缓存 + * @access public + * @param string $name 缓存标识 + * @param mixed $default 默认值 + * @return mixed + */ + public static function get($name, $default = false) + { + self::init(); + self::$readTimes++; + return self::$handler->get($name, $default); } /** @@ -96,6 +110,36 @@ class Cache return self::$handler->set($name, $value, $expire); } + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @param int $expire 有效时间 0为永久 + * @return false|int + */ + public function inc($name, $step = 1, $expire = null) + { + self::init(); + self::$writeTimes++; + return self::$handler->inc($name, $step, $expire); + } + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @param int $expire 有效时间 0为永久 + * @return false|int + */ + public function dec($name, $step = 1, $expire = null) + { + self::init(); + self::$writeTimes++; + return self::$handler->dec($name, $step, $expire); + } + /** * 删除缓存 * @access public @@ -105,6 +149,7 @@ class Cache public static function rm($name) { self::init(); + self::$writeTimes++; return self::$handler->rm($name); } @@ -116,6 +161,7 @@ class Cache public static function clear() { self::init(); + self::$writeTimes++; return self::$handler->clear(); } diff --git a/core/library/think/Controller.php b/core/library/think/Controller.php index a34d90fd..941ffd18 100644 --- a/core/library/think/Controller.php +++ b/core/library/think/Controller.php @@ -50,9 +50,7 @@ class Controller $this->request = $request; // 控制器初始化 - if (method_exists($this, '_initialize')) { - $this->_initialize(); - } + $this->_initialize(); // 前置操作方法 if ($this->beforeActionList) { @@ -64,6 +62,11 @@ class Controller } } + // 初始化 + protected function _initialize() + { + } + /** * 前置操作 * @access protected @@ -183,7 +186,7 @@ class Controller } } // 是否批量验证 - if($batch || $this->batchValidate){ + if ($batch || $this->batchValidate) { $v->batch(true); } diff --git a/core/library/think/Cookie.php b/core/library/think/Cookie.php index eb31c769..648b2c60 100644 --- a/core/library/think/Cookie.php +++ b/core/library/think/Cookie.php @@ -30,6 +30,8 @@ class Cookie 'setcookie' => true, ]; + protected static $init; + /** * Cookie初始化 * @param array $config @@ -44,6 +46,7 @@ class Cookie if (!empty(self::$config['httponly'])) { ini_set('session.cookie_httponly', 1); } + self::$init = true; } /** @@ -71,6 +74,7 @@ class Cookie */ public static function set($name, $value = '', $option = null) { + !isset(self::$init) && self::init(); // 参数设置(会覆盖黙认设置) if (!is_null($option)) { if (is_numeric($option)) { @@ -103,6 +107,7 @@ class Cookie */ 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]); @@ -116,6 +121,7 @@ class Cookie */ public static function get($name, $prefix = null) { + !isset(self::$init) && self::init(); $prefix = !is_null($prefix) ? $prefix : self::$config['prefix']; $name = $prefix . $name; if (isset($_COOKIE[$name])) { @@ -139,6 +145,7 @@ class Cookie */ 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; @@ -160,7 +167,7 @@ class Cookie if (empty($_COOKIE)) { return; } - + !isset(self::$init) && self::init(); // 要删除的cookie前缀,不指定则删除config设置的指定前缀 $config = self::$config; $prefix = !is_null($prefix) ? $prefix : $config['prefix']; diff --git a/core/library/think/File.php b/core/library/think/File.php index bd39209f..7095d6b5 100644 --- a/core/library/think/File.php +++ b/core/library/think/File.php @@ -23,6 +23,8 @@ class File extends SplFileObject private $error = ''; // 当前完整文件名 protected $filename; + // 上传文件名 + protected $saveName; // 文件上传命名规则 protected $rule = 'date'; // 文件上传验证规则 @@ -31,6 +33,8 @@ class File extends SplFileObject protected $isTest; // 上传文件信息 protected $info; + // 文件hash信息 + protected $hash = []; public function __construct($filename, $mode = 'r') { @@ -70,6 +74,50 @@ class File extends SplFileObject 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; + } + + /** + * 获取文件的md5散列值 + * @return $this + */ + public function md5() + { + if (!isset($this->hash['md5'])) { + $this->hash['md5'] = md5_file($this->filename); + } + return $this->hash['md5']; + } + + /** + * 获取文件的sha1散列值 + * @return $this + */ + public function sha1() + { + if (!isset($this->hash['sha1'])) { + $this->hash['sha1'] = sha1_file($this->filename); + } + return $this->hash['sha1']; + } + /** * 检查目录是否可写 * @param string $path 目录 @@ -183,7 +231,6 @@ class File extends SplFileObject if (!in_array($extension, $ext)) { return false; } - return true; } @@ -250,6 +297,12 @@ class File extends SplFileObject */ 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 = '非法上传文件'; @@ -262,28 +315,32 @@ class File extends SplFileObject } $path = rtrim($path, DS) . DS; // 文件保存命名规则 - $savename = $this->getSaveName($savename); + $saveName = $this->buildSaveName($savename); + $filename = $path . $saveName; // 检测目录 - if (false === $this->checkPath(dirname($path . $savename))) { + if (false === $this->checkPath(dirname($filename))) { return false; } /* 不覆盖同名文件 */ - if (!$replace && is_file($path . $savename)) { - $this->error = '存在同名文件' . $path . $savename; + if (!$replace && is_file($filename)) { + $this->error = '存在同名文件' . $filename; return false; } /* 移动文件 */ if ($this->isTest) { - rename($this->filename, $path . $savename); - } elseif (!move_uploaded_file($this->filename, $path . $savename)) { + rename($this->filename, $filename); + } elseif (!move_uploaded_file($this->filename, $filename)) { $this->error = '文件上传保存错误!'; return false; } - - return new SplFileInfo($path . $savename); + // 返回 File对象实例 + $file = new self($filename); + $file->setSaveName($saveName); + $file->setUploadInfo($this->info); + return $file; } /** @@ -291,7 +348,7 @@ class File extends SplFileObject * @param string|bool $savename 保存的文件名 默认自动生成 * @return string */ - protected function getSaveName($savename) + protected function buildSaveName($savename) { if (true === $savename) { // 自动生成文件名 @@ -323,6 +380,34 @@ class File extends SplFileObject return $savename; } + /** + * 获取错误代码信息 + * @param int $errorNo 错误号 + */ + private function error($errorNo) + { + switch ($errorNo) { + case 1: + case 2: + $this->error = '上传文件大小超过了最大值!'; + break; + case 3: + $this->error = '文件只有部分被上传!'; + break; + case 4: + $this->error = '没有文件被上传!'; + break; + case 6: + $this->error = '找不到临时文件夹!'; + break; + case 7: + $this->error = '文件写入失败!'; + break; + default: + $this->error = '未知上传错误!'; + } + } + /** * 获取错误信息 * @return mixed diff --git a/core/library/think/Route.php b/core/library/think/Route.php index 92147c8f..779e40fb 100644 --- a/core/library/think/Route.php +++ b/core/library/think/Route.php @@ -59,10 +59,10 @@ class Route private static $subDomain = ''; // 域名绑定 private static $bind = []; - // 当前分组 - private static $group = ''; - // 当前参数 - private static $option = []; + // 当前分组信息 + private static $group = []; + // 路由命名标识(用于快速URL生成) + private static $name = []; /** * 注册变量规则 @@ -108,6 +108,21 @@ class Route self::$bind = ['type' => $type, $type => $bind]; } + /** + * 设置路由绑定 + * @access public + * @param string $name 路由命名标识 + * @return string|array + */ + public static function name($name = '') + { + if ('' === $name) { + return self::$name; + } else { + return isset(self::$name[$name]) ? self::$name[$name] : null; + } + } + /** * 读取路由绑定 * @access public @@ -160,10 +175,9 @@ class Route if (empty($val)) { continue; } - if (0 === strpos($key, '[')) { + if (is_string($key) && 0 === strpos($key, '[')) { $key = substr($key, 1, -1); self::group($key, $val); - } elseif (is_array($val)) { self::setRule($key, $val[0], $type, $val[1], isset($val[2]) ? $val[2] : []); } else { @@ -184,15 +198,20 @@ class Route */ public static function rule($rule, $route = '', $type = '*', $option = [], $pattern = []) { - $group = self::$group; - $option = array_merge(self::$option, $option); - $type = strtoupper($type); + $group = self::getGroup('name'); + if (!is_null($group)) { + // 路由分组 + $option = array_merge(self::getGroup('option'), $option); + $pattern = array_merge(self::getGroup('pattern'), $pattern); + } + + $type = strtoupper($type); if (strpos($type, '|')) { $option['method'] = $type; $type = '*'; } - if (is_array($rule)) { + if (is_array($rule) && empty($route)) { foreach ($rule as $key => $val) { if (is_numeric($key)) { $key = array_shift($val); @@ -225,6 +244,9 @@ class Route */ protected static function setRule($rule, $route, $type = '*', $option = [], $pattern = [], $group = '') { + if (is_array($rule)) { + list($name, $rule) = $rule; + } if ('$' == substr($rule, -1, 1)) { // 是否完整匹配 $option['complete_match'] = true; @@ -234,6 +256,9 @@ class Route $rule = trim($rule, '/'); } $vars = self::parseVar($rule); + if (isset($name)) { + self::$name[$name] = [$rule, $vars]; + } if ($group) { self::$rules[$type][$group]['rule'][] = ['rule' => $rule, 'route' => $route, 'var' => $vars, 'option' => $option, 'pattern' => $pattern]; } else { @@ -251,29 +276,33 @@ class Route } /** - * 设置当前的路由分组 + * 获取当前的分组信息 * @access public - * @param array $option 路由参数 - * @return void + * @param string $type 分组信息名称 name option pattern + * @return mixed */ - public static function setGroup($name) + public static function getGroup($type) { - if (self::$group) { - self::$group = self::$group . '/' . ltrim($name, '/'); + if (isset(self::$group[$type])) { + return self::$group[$type]; } else { - self::$group = $name; + return null; } } /** - * 设置当前的路由参数 + * 设置当前的路由分组 * @access public - * @param array $option 路由参数 + * @param string $name 分组名称 + * @param array $option 分组路由参数 + * @param array $pattern 分组变量规则 * @return void */ - public static function setOption($option) + public static function setGroup($name, $option = [], $pattern = []) { - self::$option = $option; + self::$group['name'] = $name; + self::$group['option'] = $option ?: []; + self::$group['pattern'] = $pattern ?: []; } /** @@ -282,29 +311,31 @@ class Route * @param string|array $name 分组名称或者参数 * @param array|\Closure $routes 路由地址 * @param array $option 路由参数 - * @param string $type 请求类型 * @param array $pattern 变量规则 * @return void */ - public static function group($name, $routes, $option = [], $type = '*', $pattern = []) + public static function group($name, $routes, $option = [], $pattern = []) { if (is_array($name)) { $option = $name; $name = isset($option['name']) ? $option['name'] : ''; } - $type = strtoupper($type); if (!empty($name)) { // 分组 if ($routes instanceof \Closure) { - $curentGroup = self::$group; - self::setGroup($name); + $currentGroup = self::getGroup('name'); + if ($currentGroup) { + $name = $currentGroup . '/' . ltrim($name, '/'); + } + $currentOption = self::getGroup('option'); + $currentPattern = self::getGroup('pattern'); + self::setGroup($name, $option, $pattern); call_user_func_array($routes, []); - self::$group = $curentGroup; - - self::$rules[$type][$name]['route'] = ''; - self::$rules[$type][$name]['var'] = self::parseVar($name); - self::$rules[$type][$name]['option'] = $option; - self::$rules[$type][$name]['pattern'] = $pattern; + self::setGroup($currentGroup, $currentOption, $currentPattern); + self::$rules['*'][$name]['route'] = ''; + self::$rules['*'][$name]['var'] = self::parseVar($name); + self::$rules['*'][$name]['option'] = $option; + self::$rules['*'][$name]['pattern'] = $pattern; } else { foreach ($routes as $key => $val) { @@ -321,26 +352,29 @@ class Route $vars = self::parseVar($key); $item[] = ['rule' => $key, 'route' => $route, 'var' => $vars, 'option' => isset($option1) ? $option1 : $option, 'pattern' => isset($pattern1) ? $pattern1 : $pattern]; } - self::$rules[$type][$name] = ['rule' => $item, 'route' => '', 'var' => [], 'option' => $option, 'pattern' => $pattern]; + self::$rules['*'][$name] = ['rule' => $item, 'route' => '', 'var' => [], 'option' => $option, 'pattern' => $pattern]; } - if ('*' == $type) { - foreach (['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'] as $method) { - if (!isset(self::$rules[$method][$name])) { - self::$rules[$method][$name] = true; - } else { - self::$rules[$method][$name] = array_merge(self::$rules['*'][$name], self::$rules[$method][$name]); - } + + foreach (['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'] as $method) { + if (!isset(self::$rules[$method][$name])) { + self::$rules[$method][$name] = true; + } else { + self::$rules[$method][$name] = array_merge(self::$rules['*'][$name], self::$rules[$method][$name]); } } + } else { if ($routes instanceof \Closure) { // 闭包注册 - self::setOption($option); + $currentGroup = self::getGroup('name'); + $currentOption = self::getGroup('option'); + $currentPattern = self::getGroup('pattern'); + self::setGroup($name, $option, $pattern); call_user_func_array($routes, []); - self::setOption([]); + self::setGroup($currentGroup, $currentOption, $currentPattern); } else { // 批量注册路由 - self::rule($routes, '', $type, $option, $pattern); + self::rule($routes, '', '*', $option, $pattern); } } } @@ -470,7 +504,7 @@ class Route $val[1] = str_replace(':id', ':' . $option['var'][$rule], $val[1]); } $item = ltrim($rule . $val[1], '/'); - self::rule($item ? $item . '$' : '', $route . '/' . $val[2], $val[0], $option, $pattern); + self::rule($item . '$', $route . '/' . $val[2], $val[0], $option, $pattern); } } } @@ -650,16 +684,16 @@ class Route if (0 === strpos($result, '\\')) { // 绑定到命名空间 例如 \app\index\behavior - self::$bind = ['type' => 'namespace', 'namespace' => $result]; + self::$bind = ['type' => 'namespace', 'namespace' => $result, 'domain' => true]; } elseif (0 === strpos($result, '@')) { - // 绑定到类 例如 \app\index\controller\User - self::$bind = ['type' => 'class', 'class' => substr($result, 1)]; + // 绑定到类 例如 @app\index\controller\User + self::$bind = ['type' => 'class', 'class' => substr($result, 1), 'domain' => true]; } elseif (0 === strpos($result, '[')) { // 绑定到分组 例如 [user] - self::$bind = ['type' => 'group', 'group' => substr($result, 1, -1)]; + self::$bind = ['type' => 'group', 'group' => substr($result, 1, -1), 'domain' => true]; } else { // 绑定到模块/控制器 例如 index/user - self::$bind = ['type' => 'module', 'module' => $result]; + self::$bind = ['type' => 'module', 'module' => $result, 'domain' => true]; } } } @@ -701,14 +735,18 @@ class Route if (false !== $return) { return $return; } - + if ('/' != $url) { + $url = rtrim($url, '/'); + } if (isset($rules[$url])) { // 静态路由规则检测 $rule = $rules[$url]; if (true === $rule) { $rule = self::$rules['*'][$url]; } - return self::parseRule($url, $rule['route'], $url, $rule['option']); + if (!empty($rule['route'])) { + return self::parseRule($url, $rule['route'], $url, $rule['option']); + } } // 路由规则检测 @@ -733,6 +771,9 @@ class Route if (true === $item) { $item = self::$rules['*'][$key]; } + if (!isset($item['rule'])) { + continue; + } $rule = $item['rule']; $route = $item['route']; $vars = $item['var']; @@ -751,7 +792,7 @@ class Route } else { $str = $key; } - if (0 !== strpos($url, $str)) { + if (is_string($str) && 0 !== strpos($url, $str)) { continue; } @@ -759,7 +800,7 @@ class Route if (false !== $result) { return $result; } - } else { + } elseif ($route) { if ('__miss__' == $rule) { // 指定MISS路由 $miss = $item; @@ -838,13 +879,18 @@ class Route return self::bindToNamespace($url, self::$bind['namespace'], $depr); case 'module': // 如果有模块/控制器绑定 针对路由到 模块/控制器 有效 - $url = self::$bind['module'] . '/' . $url; + $url = (empty(self::$bind['domain']) ? self::$bind['module'] . '/' : '') . ltrim($url, '/'); break; case 'group': // 绑定到路由分组 $key = self::$bind['group']; if (array_key_exists($key, $rules)) { - $rules = [$key => $rules[self::$bind['group']]]; + $item = $rules[self::$bind['group']]; + if (!empty(self::$bind['domain']) && true === $item) { + $rules = [self::$rules['*'][$key]]; + } else { + $rules = [$key => $item]; + } } } } @@ -1089,7 +1135,8 @@ class Route foreach ($m2 as $key => $val) { // val中定义了多个变量 if (false !== strpos($val, '<') && preg_match_all('/<(\w+(\??))>/', $val, $matches)) { - $value = []; + $value = []; + $replace = []; foreach ($matches[1] as $name) { if (strpos($name, '?')) { $name = substr($name, 0, -1); @@ -1128,7 +1175,7 @@ class Route return false; } $var[$name] = isset($m1[$key]) ? $m1[$key] : ''; - } elseif (0 !== strcasecmp($val, $m1[$key])) { + } elseif (!isset($m1[$key]) || 0 !== strcasecmp($val, $m1[$key])) { return false; } } @@ -1191,32 +1238,35 @@ class Route $paths = explode('/', $pathinfo); } // 获取路由地址规则 - $url = $route; + if (is_string($route) && isset($option['prefix'])) { + // 路由地址前缀 + $route = $option['prefix'] . $route; + } // 替换路由地址中的变量 - if (is_string($url) && !empty($matches)) { + if (is_string($route) && !empty($matches)) { foreach ($matches as $key => $val) { - if (false !== strpos($url, ':' . $key)) { - $url = str_replace(':' . $key, $val, $url); + if (false !== strpos($route, ':' . $key)) { + $route = str_replace(':' . $key, $val, $route); unset($matches[$key]); } } } - if ($url instanceof \Closure) { + if ($route instanceof \Closure) { // 执行闭包 - $result = ['type' => 'function', 'function' => $url, 'params' => $matches]; - } elseif (0 === strpos($url, '/') || 0 === strpos($url, 'http')) { + $result = ['type' => 'function', 'function' => $route, 'params' => $matches]; + } elseif (0 === strpos($route, '/') || 0 === strpos($route, 'http')) { // 路由到重定向地址 - $result = ['type' => 'redirect', 'url' => $url, 'status' => isset($option['status']) ? $option['status'] : 301]; - } elseif (0 === strpos($url, '\\')) { + $result = ['type' => 'redirect', 'url' => $route, 'status' => isset($option['status']) ? $option['status'] : 301]; + } elseif (0 === strpos($route, '\\')) { // 路由到方法 - $method = strpos($url, '@') ? explode('@', $url) : $url; + $method = strpos($route, '@') ? explode('@', $route) : $route; $result = ['type' => 'method', 'method' => $method, 'params' => $matches]; - } elseif (0 === strpos($url, '@')) { + } elseif (0 === strpos($route, '@')) { // 路由到控制器 - $result = ['type' => 'controller', 'controller' => substr($url, 1), 'params' => $matches]; + $result = ['type' => 'controller', 'controller' => substr($route, 1), 'params' => $matches]; } else { // 路由到模块/控制器/操作 - $result = self::parseModule($url); + $result = self::parseModule($route); } // 解析额外参数 self::parseUrlParams(empty($paths) ? '' : implode('/', $paths), $matches); diff --git a/core/library/think/Template.php b/core/library/think/Template.php index 97c01d4e..f22b3297 100644 --- a/core/library/think/Template.php +++ b/core/library/think/Template.php @@ -61,7 +61,7 @@ class Template */ public function __construct(array $config = []) { - $this->config['cache_path'] = RUNTIME_PATH . 'temp' . DS; + $this->config['cache_path'] = TEMP_PATH; $this->config = array_merge($this->config, $config); $this->config['taglib_begin'] = $this->stripPreg($this->config['taglib_begin']); $this->config['taglib_end'] = $this->stripPreg($this->config['taglib_end']); @@ -303,7 +303,7 @@ class Template { if ($cacheId && $this->config['display_cache']) { // 缓存页面输出 - return Cache::get($cacheId) ? true : false; + return Cache::has($cacheId); } return false; } @@ -699,7 +699,7 @@ class Template */ public function parseAttr($str, $name = null) { - $regex = '/\s+(?>(?[\w-]+)\s*)=(?>\s*)([\"\'])(?(?:(?!\\2).)*)\\2/is'; + $regex = '/\s+(?>(?P[\w-]+)\s*)=(?>\s*)([\"\'])(?P(?:(?!\\2).)*)\\2/is'; $array = []; if (preg_match_all($regex, $str, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { @@ -1114,9 +1114,9 @@ class Template switch ($tagName) { case 'block': if ($single) { - $regex = $begin . '(?:' . $tagName . '\b(?>(?:(?!name=).)*)\bname=([\'\"])(?[\$\w\-\/\.]+)\\1(?>[^' . $end . ']*)|\/' . $tagName . ')' . $end; + $regex = $begin . '(?:' . $tagName . '\b(?>(?:(?!name=).)*)\bname=([\'\"])(?P[\$\w\-\/\.]+)\\1(?>[^' . $end . ']*)|\/' . $tagName . ')' . $end; } else { - $regex = $begin . '(?:' . $tagName . '\b(?>(?:(?!name=).)*)\bname=([\'\"])(?[\$\w\-\/\.]+)\\1(?>(?:(?!' . $end . ').)*)|\/' . $tagName . ')' . $end; + $regex = $begin . '(?:' . $tagName . '\b(?>(?:(?!name=).)*)\bname=([\'\"])(?P[\$\w\-\/\.]+)\\1(?>(?:(?!' . $end . ').)*)|\/' . $tagName . ')' . $end; } break; case 'literal': @@ -1142,9 +1142,9 @@ class Template $name = 'name'; } if ($single) { - $regex = $begin . $tagName . '\b(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?[\$\w\-\/\.\:@,\\\\]+)\\1(?>[^' . $end . ']*)' . $end; + $regex = $begin . $tagName . '\b(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?P[\$\w\-\/\.\:@,\\\\]+)\\1(?>[^' . $end . ']*)' . $end; } else { - $regex = $begin . $tagName . '\b(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?[\$\w\-\/\.\:@,\\\\]+)\\1(?>(?:(?!' . $end . ').)*)' . $end; + $regex = $begin . $tagName . '\b(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?P[\$\w\-\/\.\:@,\\\\]+)\\1(?>(?:(?!' . $end . ').)*)' . $end; } break; } diff --git a/core/library/think/Url.php b/core/library/think/Url.php index cf570af0..9fe9d1fc 100644 --- a/core/library/think/Url.php +++ b/core/library/think/Url.php @@ -69,15 +69,21 @@ class Url $vars = array_merge($params, $vars); } - // 获取路由别名 - $alias = self::getRouteAlias(); - // 检测路由 - if (0 !== strpos($url, '/') && isset($alias[$url]) && $match = self::getRouteUrl($alias[$url], $vars)) { - // 处理路由规则中的特殊字符 - $url = str_replace('[--think--]', '', $match); + $rule = Route::name($url); + if ($rule && $match = self::getRuleUrl($rule, $vars)) { + // 匹配路由命名标识 快速生成 + $url = $match; } else { - // 路由不存在 直接解析 - $url = self::parseUrl($url); + // 获取路由别名 + $alias = self::getRouteAlias(); + // 检测路由 + if (0 !== strpos($url, '/') && isset($alias[$url]) && $match = self::getRouteUrl($alias[$url], $vars)) { + // 处理路由规则中的特殊字符 + $url = $match; + } else { + // 路由不存在 直接解析 + $url = self::parseUrl($url); + } } // 检测URL绑定 @@ -225,10 +231,6 @@ class Url { foreach ($alias as $key => $val) { list($url, $pattern, $param) = $val; - // 解析安全替换 - if (strpos($url, '$')) { - $url = str_replace('$', '[--think--]', $url); - } // 检查变量匹配 $array = $vars; $match = false; @@ -257,6 +259,23 @@ class Url return false; } + // 匹配路由地址 + public static function getRuleUrl($rule, &$vars = []) + { + list($url, $pattern) = $rule; + foreach ($pattern as $key => $val) { + if (isset($vars[$key])) { + $url = str_replace(['[:' . $key . ']', '<' . $key . '?>', ':' . $key . '', '<' . $key . '>'], $vars[$key], $url); + unset($vars[$key]); + } elseif (2 == $val) { + $url = str_replace(['[:' . $key . ']', '<' . $key . '?>'], '', $url); + } else { + return false; + } + } + return $url; + } + // 生成路由映射并缓存 private static function getRouteAlias() { diff --git a/core/library/think/cache/driver/File.php b/core/library/think/cache/driver/File.php index 29cd3773..c356f21c 100644 --- a/core/library/think/cache/driver/File.php +++ b/core/library/think/cache/driver/File.php @@ -11,8 +11,6 @@ namespace think\cache\driver; -use think\Cache; - /** * 文件类型缓存类 * @author liu21st @@ -86,17 +84,30 @@ class File return $this->options['path'] . $filename; } + /** + * 判断缓存是否存在 + * @access public + * @param string $name 缓存变量名 + * @return bool + */ + public function has($name) + { + $filename = $this->filename($name); + return is_file($filename); + } + /** * 读取缓存 * @access public * @param string $name 缓存变量名 + * @param mixed $default 默认值 * @return mixed */ - public function get($name) + public function get($name, $default = false) { $filename = $this->filename($name); if (!is_file($filename)) { - return false; + return $default; } $content = file_get_contents($filename); if (false !== $content) { @@ -104,7 +115,7 @@ class File if (0 != $expire && $_SERVER['REQUEST_TIME'] > filemtime($filename) + $expire) { //缓存过期删除缓存文件 $this->unlink($filename); - return false; + return $default; } $content = substr($content, 20, -3); if ($this->options['data_compress'] && function_exists('gzcompress')) { @@ -114,7 +125,7 @@ class File $content = unserialize($content); return $content; } else { - return false; + return $default; } } @@ -147,6 +158,42 @@ class File } } + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @param int $expire 有效时间 0为永久 + * @return false|int + */ + public function inc($name, $step = 1, $expire = null) + { + if ($this->has($name)) { + $value = $this->get($name) + $step; + } else { + $value = $step; + } + return $this->set($name, $value, $expire) ? $value : false; + } + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @param int $expire 有效时间 0为永久 + * @return false|int + */ + public function dec($name, $step = 1, $expire = null) + { + if ($this->has($name)) { + $value = $this->get($name) - $step; + } else { + $value = $step; + } + return $this->set($name, $value, $expire) ? $value : false; + } + /** * 删除缓存 * @access public diff --git a/core/library/think/cache/driver/Lite.php b/core/library/think/cache/driver/Lite.php index 0a3a838b..e83f73e4 100644 --- a/core/library/think/cache/driver/Lite.php +++ b/core/library/think/cache/driver/Lite.php @@ -11,8 +11,6 @@ namespace think\cache\driver; -use think\Cache; - /** * 文件类型缓存类 * @author liu21st @@ -54,12 +52,25 @@ class Lite } /** - * 读取缓存 + * 判断缓存是否存在 * @access public * @param string $name 缓存变量名 * @return mixed */ - public function get($name) + public function has($name) + { + $filename = $this->filename($name); + return is_file($filename); + } + + /** + * 读取缓存 + * @access public + * @param string $name 缓存变量名 + * @param mixed $default 默认值 + * @return mixed + */ + public function get($name, $default = false) { $filename = $this->filename($name); if (is_file($filename)) { @@ -68,11 +79,11 @@ class Lite if ($mtime < $_SERVER['REQUEST_TIME']) { // 清除已经过期的文件 unlink($filename); - return false; + return $default; } return include $filename; } else { - return false; + return $default; } } @@ -102,6 +113,42 @@ class Lite return $ret; } + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @param int $expire 有效时间 0为永久 + * @return false|int + */ + public function inc($name, $step = 1, $expire = null) + { + if ($this->has($name)) { + $value = $this->get($name) + $step; + } else { + $value = $step; + } + return $this->set($name, $value, $expire) ? $value : false; + } + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @param int $expire 有效时间 0为永久 + * @return false|int + */ + public function dec($name, $step = 1, $expire = null) + { + if ($this->has($name)) { + $value = $this->get($name) - $step; + } else { + $value = $step; + } + return $this->set($name, $value, $expire) ? $value : false; + } + /** * 删除缓存 * @access public diff --git a/core/library/think/cache/driver/Memcache.php b/core/library/think/cache/driver/Memcache.php index 8ca55f07..44de09b4 100644 --- a/core/library/think/cache/driver/Memcache.php +++ b/core/library/think/cache/driver/Memcache.php @@ -11,7 +11,6 @@ namespace think\cache\driver; -use think\Cache; use think\Exception; class Memcache @@ -56,15 +55,29 @@ class Memcache } } + /** + * 判断缓存 + * @access public + * @param string $name 缓存变量名 + * @return bool + */ + public function has($name) + { + $name = $this->options['prefix'] . $name; + return $this->handler->get($name) ? true : false; + } + /** * 读取缓存 * @access public * @param string $name 缓存变量名 + * @param mixed $default 默认值 * @return mixed */ - public function get($name) + public function get($name, $default = false) { - return $this->handler->get($this->options['prefix'] . $name); + $result = $this->handler->get($this->options['prefix'] . $name); + return false !== $result ? $result : $default; } /** @@ -87,6 +100,32 @@ class Memcache return false; } + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @param int $expire 有效时间 0为永久 + * @return false|int + */ + public function inc($name, $step = 1, $expire = null) + { + return $this->handler->increment($name, $step); + } + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @param int $expire 有效时间 0为永久 + * @return false|int + */ + public function dec($name, $step = 1, $expire = null) + { + return $this->handler->decrement($name, $step); + } + /** * 删除缓存 * @param string $name 缓存变量名 diff --git a/core/library/think/cache/driver/Memcached.php b/core/library/think/cache/driver/Memcached.php index 50082a1e..a53533fb 100644 --- a/core/library/think/cache/driver/Memcached.php +++ b/core/library/think/cache/driver/Memcached.php @@ -11,8 +11,6 @@ namespace think\cache\driver; -use think\Cache; - class Memcached { protected $handler; @@ -62,15 +60,29 @@ class Memcached } } + /** + * 判断缓存 + * @access public + * @param string $name 缓存变量名 + * @return bool + */ + public function has($name) + { + $name = $this->options['prefix'] . $name; + return $this->handler->get($name) ? true : false; + } + /** * 读取缓存 * @access public * @param string $name 缓存变量名 + * @param mixed $default 默认值 * @return mixed */ - public function get($name) + public function get($name, $default = false) { - return $this->handler->get($this->options['prefix'] . $name); + $result = $this->handler->get($this->options['prefix'] . $name); + return false !== $result ? $result : $default; } /** @@ -94,6 +106,32 @@ class Memcached return false; } + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @param int $expire 有效时间 0为永久 + * @return false|int + */ + public function inc($name, $step = 1, $expire = null) + { + return $this->handler->increment($name, $step); + } + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @param int $expire 有效时间 0为永久 + * @return false|int + */ + public function dec($name, $step = 1, $expire = null) + { + return $this->handler->decrement($name, $step); + } + /** * 删除缓存 * @param string $name 缓存变量名 diff --git a/core/library/think/cache/driver/Redis.php b/core/library/think/cache/driver/Redis.php index 25febbf0..c1749e27 100644 --- a/core/library/think/cache/driver/Redis.php +++ b/core/library/think/cache/driver/Redis.php @@ -11,9 +11,6 @@ namespace think\cache\driver; -use think\Cache; -use think\Exception; - /** * Redis缓存驱动,适合单机部署、有前端代理实现高可用的场景,性能最好 * 有需要在业务层实现读写分离、或者使用RedisCluster的需求,请使用Redisd驱动 @@ -56,15 +53,30 @@ class Redis } } + /** + * 判断缓存 + * @access public + * @param string $name 缓存变量名 + * @return bool + */ + public function has($name) + { + return $this->handler->get($this->options['prefix'] . $name) ? true : false; + } + /** * 读取缓存 * @access public * @param string $name 缓存变量名 + * @param mixed $default 默认值 * @return mixed */ - public function get($name) + public function get($name, $default = false) { - $value = $this->handler->get($this->options['prefix'] . $name); + $value = $this->handler->get($this->options['prefix'] . $name); + if (is_null($value)) { + return $default; + } $jsonData = json_decode($value, true); // 检测是否为JSON数据 true 返回JSON解析数组, false返回源数据 byron sampson return (null === $jsonData) ? $value : $jsonData; @@ -94,6 +106,32 @@ class Redis return $result; } + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @param int $expire 有效时间 0为永久 + * @return false|int + */ + public function inc($name, $step = 1, $expire = null) + { + return $this->handler->incrby($name, $step); + } + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @param int $expire 有效时间 0为永久 + * @return false|int + */ + public function dec($name, $step = 1, $expire = null) + { + return $this->handler->decrby($name, $step); + } + /** * 删除缓存 * @access public diff --git a/core/library/think/cache/driver/Redisd.php b/core/library/think/cache/driver/Redisd.php deleted file mode 100644 index e3bc80e7..00000000 --- a/core/library/think/cache/driver/Redisd.php +++ /dev/null @@ -1,325 +0,0 @@ - -// +---------------------------------------------------------------------- - -namespace think\cache\driver; - -use think\App; -use think\Cache; -use think\Exception; -use think\Log; - -/** -配置参数: -'cache' => [ -'type' => 'Redisd' -'host' => 'A:6379,B:6379', //redis服务器ip,多台用逗号隔开;读写分离开启时,默认写A,当A主挂时,再尝试写B -'slave' => 'B:6379,C:6379', //redis服务器ip,多台用逗号隔开;读写分离开启时,所有IP随机读,其中一台挂时,尝试读其它节点,可以配置权重 -'port' => 6379, //默认的端口号 -'password' => '', //AUTH认证密码,当redis服务直接暴露在外网时推荐 -'timeout' => 10, //连接超时时间 -'expire' => false, //默认过期时间,默认为永不过期 -'prefix' => '', //缓存前缀,不宜过长 -'persistent' => false, //是否长连接 false=短连接,推荐长连接 -], - -单例获取: -$redis = \think\Cache::connect(Config::get('cache')); -$redis->master(true)->setnx('key'); -$redis->master(false)->get('key'); - */ - -/** - * ThinkPHP Redis简单主从实现的高可用方案 - * - * 扩展依赖:https://github.com/phpredis/phpredis - * - * 一主一从的实践经验 - * 1, A、B为主从,正常情况下,A写,B读,通过异步同步到B(或者双写,性能有损失) - * 2, B挂,则读写均落到A - * 3, A挂,则尝试升级B为主,并断开主从尝试写入(要求开启slave-read-only no) - * 4, 手工恢复A,并加入B的从 - * - * 优化建议 - * 1,key不宜过长,value过大时请自行压缩 - * 2,gzcompress在php7下有兼容问题 - * - * @todo - * 1, 增加对redisCluster的兼容 - * 2, 增加tp5下的单元测试 - * - * @author 尘缘 <130775@qq.com> - */ -class Redisd -{ - protected static $redis_rw_handler; - protected static $redis_err_pool; - protected $handler = null; - - protected $options = [ - 'host' => '127.0.0.1', - 'slave' => '', - 'port' => 6379, - 'password' => '', - 'timeout' => 10, - 'expire' => false, - 'persistent' => false, - 'prefix' => '', - 'serialize' => \Redis::SERIALIZER_PHP, - ]; - - /** - * 为了在单次php请求中复用redis连接,第一次获取的options会被缓存,第二次使用不同的$options,将会无效 - * - * @param array $options 缓存参数 - * @access public - */ - public function __construct($options = []) - { - if (!extension_loaded('redis')) { - throw new \BadFunctionCallException('not support: redis'); - } - - $this->options = $options = array_merge($this->options, $options); - $this->options['func'] = $options['persistent'] ? 'pconnect' : 'connect'; - - $host = explode(",", trim($this->options['host'], ",")); - $host = array_map("trim", $host); - $slave = explode(",", trim($this->options['slave'], ",")); - $slave = array_map("trim", $slave); - - $this->options["server_slave"] = empty($slave) ? $host : $slave; - $this->options["servers"] = count($slave); - $this->options["server_master"] = array_shift($host); - $this->options["server_master_failover"] = $host; - } - - /** - * 主从选择器,配置多个Host则自动启用读写分离,默认主写,随机从读 - * 随机从读的场景适合读频繁,且php与redis从位于单机的架构,这样可以减少网络IO - * 一致Hash适合超高可用,跨网络读取,且从节点较多的情况,本业务不考虑该需求 - * - * @access public - * @param bool $master true 默认主写 - * @return Redisd - */ - public function master($master = true) - { - if (isset(self::$redis_rw_handler[$master])) { - $this->handler = self::$redis_rw_handler[$master]; - return $this; - } - - //如果不为主,则从配置的host剔除主,并随机读从,失败以后再随机选择从 - //另外一种方案是根据key的一致性hash选择不同的node,但读写频繁的业务中可能打开大量的文件句柄 - if (!$master && $this->options["servers"] > 1) { - shuffle($this->options["server_slave"]); - $host = array_shift($this->options["server_slave"]); - } else { - $host = $this->options["server_master"]; - } - - $this->handler = new \Redis(); - $func = $this->options['func']; - - $parse = parse_url($host); - $host = isset($parse['host']) ? $parse['host'] : $host; - $port = isset($parse['host']) ? $parse['port'] : $this->options['port']; - - //发生错误则摘掉当前节点 - try { - $result = $this->handler->$func($host, $port, $this->options['timeout']); - if (false === $result) { - $this->handler->getLastError(); - } - - if (null != $this->options['password']) { - $this->handler->auth($this->options['password']); - } - - $this->handler->setOption(\Redis::OPT_SERIALIZER, $this->options['serialize']); - if (strlen($this->options['prefix'])) { - $this->handler->setOption(\Redis::OPT_PREFIX, $this->options['prefix']); - } - - App::$debug && Log::record("[ CACHE ] INIT Redisd : {$host}:{$port} master->" . var_export($master, true), Log::ALERT); - } catch (\RedisException $e) { - //phpredis throws a RedisException object if it can't reach the Redis server. - //That can happen in case of connectivity issues, if the Redis service is down, or if the redis host is overloaded. - //In any other problematic case that does not involve an unreachable server - //(such as a key not existing, an invalid command, etc), phpredis will return FALSE. - - Log::record(sprintf("redisd->%s:%s:%s:%s", $master ? "master" : "salve", $host, $port, $e->getMessage()), Log::ALERT); - - //主节点挂了以后,尝试连接主备,断开主备的主从连接进行升主 - if ($master) { - if (!count($this->options["server_master_failover"])) { - throw new Exception("redisd master: no more server_master_failover. {$host}:{$port} : " . $e->getMessage()); - return false; - } - - $this->options["server_master"] = array_shift($this->options["server_master_failover"]); - $this->master(); - - Log::record(sprintf("master is down, try server_master_failover : %s", $this->options["server_master"]), Log::ERROR); - - //如果是slave,断开主从升主,需要手工同步新主的数据到旧主上 - //目前这块的逻辑未经过严格测试 - //$this->handler->slaveof(); - } else { - //尝试failover,如果有其它节点则进行其它节点的尝试 - foreach ($this->options["server_slave"] as $k => $v) { - if (trim($v) == trim($host)) { - unset($this->options["server_slave"][$k]); - } - } - - //如果无可用节点,则抛出异常 - if (!count($this->options["server_slave"])) { - Log::record("已无可用Redis读节点", Log::ERROR); - throw new Exception("redisd slave: no more server_slave. {$host}:{$port} : " . $e->getMessage()); - return false; - } else { - Log::record("salve {$host}:{$port} is down, try another one.", Log::ALERT); - return $this->master(false); - } - } - } catch (\Exception $e) { - throw new Exception($e->getMessage(), $e->getCode()); - } - - self::$redis_rw_handler[$master] = $this->handler; - return $this; - } - - /** - * 读取缓存 - * - * @access public - * @param string $name 缓存key - * @param bool $master 指定主从节点,可以从主节点获取结果 - * @return mixed - */ - public function get($name, $master = false) - { - $this->master($master); - - try { - $value = $this->handler->get($name); - } catch (\RedisException $e) { - unset(self::$redis_rw_handler[0]); - - $this->master(); - return $this->get($name); - } catch (\Exception $e) { - Log::record($e->getMessage(), Log::ERROR); - } - - return isset($value) ? $value : null; - } - - /** - * 写入缓存 - * - * @access public - * @param string $name 缓存key - * @param mixed $value 缓存value - * @param integer $expire 过期时间,单位秒 - * @return boolen - */ - public function set($name, $value, $expire = null) - { - $this->master(true); - - if (is_null($expire)) { - $expire = $this->options['expire']; - } - - try { - if (null === $value) { - return $this->handler->delete($name); - } - - if (is_int($expire) && $expire) { - $result = $this->handler->setex($name, $expire, $value); - } else { - $result = $this->handler->set($name, $value); - } - } catch (\RedisException $e) { - unset(self::$redis_rw_handler[1]); - - $this->master(true); - return $this->set($name, $value, $expire); - } catch (\Exception $e) { - Log::record($e->getMessage()); - } - - return $result; - } - - /** - * 删除缓存 - * - * @access public - * @param string $name 缓存变量名 - * @return boolen - */ - public function rm($name) - { - $this->master(true); - return $this->handler->delete($name); - } - - /** - * 清除缓存 - * - * @access public - * @return boolen - */ - public function clear() - { - $this->master(true); - return $this->handler->flushDB(); - } - - /** - * 返回句柄对象,可执行其它高级方法 - * 需要先执行 $redis->master() 连接到 DB - * - * @access public - * @param bool $master 指定主从节点,可以从主节点获取结果 - * @return \Redis - */ - public function handler($master = true) - { - $this->master($master); - return $this->handler; - } - - /** - * 析构释放连接 - * - * @access public - */ - public function __destruct() - { - //该方法仅在connect连接时有效 - //当使用pconnect时,连接会被重用,连接的生命周期是fpm进程的生命周期,而非一次php的执行。 - //如果代码中使用pconnect, close的作用仅是使当前php不能再进行redis请求,但无法真正关闭redis长连接,连接在后续请求中仍然会被重用,直至fpm进程生命周期结束。 - - try { - if (method_exists($this->handler, "close")) { - $this->handler->close(); - } - - } catch (\Exception $e) { - } - } -} diff --git a/core/library/think/cache/driver/Sqlite.php b/core/library/think/cache/driver/Sqlite.php index e4689d61..6814773a 100644 --- a/core/library/think/cache/driver/Sqlite.php +++ b/core/library/think/cache/driver/Sqlite.php @@ -11,7 +11,6 @@ namespace think\cache\driver; -use think\Cache; use think\Exception; /** @@ -47,13 +46,28 @@ class Sqlite $this->handler = $func($this->options['db']); } + /** + * 判断缓存 + * @access public + * @param string $name 缓存变量名 + * @return bool + */ + public function has($name) + { + $name = $this->options['prefix'] . sqlite_escape_string($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) + public function get($name, $default = false) { $name = $this->options['prefix'] . sqlite_escape_string($name); $sql = 'SELECT value FROM ' . $this->options['table'] . ' WHERE var=\'' . $name . '\' AND (expire=0 OR expire >' . $_SERVER['REQUEST_TIME'] . ') LIMIT 1'; @@ -66,7 +80,7 @@ class Sqlite } return unserialize($content); } - return false; + return $default; } /** @@ -96,6 +110,42 @@ class Sqlite return false; } + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @param int $expire 有效时间 0为永久 + * @return false|int + */ + public function inc($name, $step = 1, $expire = null) + { + if ($this->has($name)) { + $value = $this->get($name) + $step; + } else { + $value = $step; + } + return $this->set($name, $value, $expire) ? $value : false; + } + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @param int $expire 有效时间 0为永久 + * @return false|int + */ + public function dec($name, $step = 1, $expire = null) + { + if ($this->has($name)) { + $value = $this->get($name) - $step; + } else { + $value = $step; + } + return $this->set($name, $value, $expire) ? $value : false; + } + /** * 删除缓存 * @access public diff --git a/core/library/think/cache/driver/Wincache.php b/core/library/think/cache/driver/Wincache.php index bc1b35de..940ecc4e 100644 --- a/core/library/think/cache/driver/Wincache.php +++ b/core/library/think/cache/driver/Wincache.php @@ -11,7 +11,6 @@ namespace think\cache\driver; -use think\Cache; use think\Exception; /** @@ -41,16 +40,29 @@ class Wincache } } + /** + * 判断缓存 + * @access public + * @param string $name 缓存变量名 + * @return bool + */ + public function has($name) + { + $name = $this->options['prefix'] . $name; + return wincache_ucache_exists($name); + } + /** * 读取缓存 * @access public * @param string $name 缓存变量名 + * @param mixed $default 默认值 * @return mixed */ - public function get($name) + public function get($name, $default = false) { $name = $this->options['prefix'] . $name; - return wincache_ucache_exists($name) ? wincache_ucache_get($name) : false; + return wincache_ucache_exists($name) ? wincache_ucache_get($name) : $default; } /** @@ -73,6 +85,32 @@ class Wincache return false; } + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @param int $expire 有效时间 0为永久 + * @return false|int + */ + public function inc($name, $step = 1, $expire = null) + { + return wincache_ucache_inc($name, $step, $expire); + } + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @param int $expire 有效时间 0为永久 + * @return false|int + */ + public function dec($name, $step = 1, $expire = null) + { + return wincache_ucache_dec($name, $step, $expire); + } + /** * 删除缓存 * @access public diff --git a/core/library/think/cache/driver/Xcache.php b/core/library/think/cache/driver/Xcache.php index bb6f277e..99b01f6b 100644 --- a/core/library/think/cache/driver/Xcache.php +++ b/core/library/think/cache/driver/Xcache.php @@ -11,7 +11,6 @@ namespace think\cache\driver; -use think\Cache; use think\Exception; /** @@ -41,19 +40,29 @@ class Xcache } } + /** + * 判断缓存 + * @access public + * @param string $name 缓存变量名 + * @return bool + */ + public function has($name) + { + $name = $this->options['prefix'] . $name; + return xcache_isset($name); + } + /** * 读取缓存 * @access public * @param string $name 缓存变量名 + * @param mixed $default 默认值 * @return mixed */ - public function get($name) + public function get($name, $default = false) { $name = $this->options['prefix'] . $name; - if (xcache_isset($name)) { - return xcache_get($name); - } - return false; + return xcache_isset($name) ? xcache_get($name) : $default; } /** @@ -76,6 +85,32 @@ class Xcache return false; } + /** + * 自增缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @param int $expire 有效时间 0为永久 + * @return false|int + */ + public function inc($name, $step = 1, $expire = null) + { + return xcache_inc($name, $step, $expire); + } + + /** + * 自减缓存(针对数值缓存) + * @access public + * @param string $name 缓存变量名 + * @param int $step 步长 + * @param int $expire 有效时间 0为永久 + * @return false|int + */ + public function dec($name, $step = 1, $expire = null) + { + return xcache_dec($name, $step, $expire); + } + /** * 删除缓存 * @access public @@ -94,6 +129,10 @@ class Xcache */ public function clear() { - return; + if (function_exists('xcache_unset_by_prefix')) { + return xcache_unset_by_prefix($this->options['prefix']); + } else { + return false; + } } } diff --git a/core/library/think/db/Query.php b/core/library/think/db/Query.php index 3018e48e..d838dc39 100644 --- a/core/library/think/db/Query.php +++ b/core/library/think/db/Query.php @@ -564,8 +564,8 @@ class Query if ($lazyTime > 0) { // 延迟写入 $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition)); - $step = $this->lazyWrite($guid, $step, $lazyTime); - if (empty($step)) { + $step = $this->lazyWrite('inc', $guid, $step, $lazyTime); + if (false === $step) { return true; // 等待下次写入 } } @@ -591,8 +591,8 @@ class Query if ($lazyTime > 0) { // 延迟写入 $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition)); - $step = $this->lazyWrite($guid, -$step, $lazyTime); - if (empty($step)) { + $step = $this->lazyWrite('dec', $guid, $step, $lazyTime); + if (false === $step) { return true; // 等待下次写入 } } @@ -602,33 +602,30 @@ class Query /** * 延时更新检查 返回false表示需要延时 * 否则返回实际写入的数值 - * @access public + * @access protected + * @param string $type 自增或者自减 * @param string $guid 写入标识 * @param integer $step 写入步进值 * @param integer $lazyTime 延时时间(s) * @return false|integer */ - protected function lazyWrite($guid, $step, $lazyTime) + protected function lazyWrite($type, $guid, $step, $lazyTime) { - if (false !== ($value = Cache::get($guid))) { - // 存在缓存写入数据 - if ($_SERVER['REQUEST_TIME'] > Cache::get($guid . '_time') + $lazyTime) { - // 延时更新时间到了,删除缓存数据 并实际写入数据库 - Cache::rm($guid); - Cache::rm($guid . '_time'); - return $value + $step; - } else { - // 追加数据到缓存 - Cache::set($guid, $value + $step, 0); - return false; - } - } else { - // 没有缓存数据 - Cache::set($guid, $step, 0); + if (!Cache::has($guid . '_time')) { // 计时开始 Cache::set($guid . '_time', $_SERVER['REQUEST_TIME'], 0); - return false; + Cache::$type($guid, $step, 0); + } elseif ($_SERVER['REQUEST_TIME'] > Cache::get($guid . '_time') + $lazyTime) { + // 删除缓存 + $value = Cache::$type($guid, $step, 0); + Cache::rm($guid); + Cache::rm($guid . '_time'); + return 0 === $value ? false : $value; + } else { + // 更新缓存 + Cache::$type($guid, $step, 0); } + return false; } /** diff --git a/core/library/think/debug/Console.php b/core/library/think/debug/Console.php index 6933d5af..1763fc66 100644 --- a/core/library/think/debug/Console.php +++ b/core/library/think/debug/Console.php @@ -53,7 +53,7 @@ class Console return false; } // 获取基本信息 - $runtime = number_format(microtime(true), 8, '.', '') - THINK_START_TIME; + $runtime = number_format(microtime(true) - THINK_START_TIME, 10); $reqs = number_format(1 / $runtime, 2); $mem = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); diff --git a/core/library/think/debug/Html.php b/core/library/think/debug/Html.php index a1d42fb5..1061b372 100644 --- a/core/library/think/debug/Html.php +++ b/core/library/think/debug/Html.php @@ -53,7 +53,7 @@ class Html return false; } // 获取基本信息 - $runtime = number_format(microtime(true), 8, '.', '') - THINK_START_TIME; + $runtime = number_format(microtime(true) - THINK_START_TIME, 10); $reqs = number_format(1 / $runtime, 2); $mem = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2); diff --git a/core/library/think/log/driver/File.php b/core/library/think/log/driver/File.php index bbc9b76e..d1d2852d 100644 --- a/core/library/think/log/driver/File.php +++ b/core/library/think/log/driver/File.php @@ -54,8 +54,9 @@ class File } else { $current_uri = "cmd:" . implode(' ', $_SERVER['argv']); } - $runtime = (number_format(microtime(true), 8, '.', '') - THINK_START_TIME) ?: 0.00000001; - $reqs = number_format(1 / number_format($runtime, 8), 2); + + $runtime = number_format(microtime(true) - THINK_START_TIME, 10); + $reqs = 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]'; diff --git a/core/library/think/log/driver/Socket.php b/core/library/think/log/driver/Socket.php index facdf0e3..51b0fa56 100644 --- a/core/library/think/log/driver/Socket.php +++ b/core/library/think/log/driver/Socket.php @@ -63,8 +63,8 @@ class Socket if (!$this->check()) { return false; } - $runtime = number_format(microtime(true), 8, '.', '') - THINK_START_TIME; - $reqs = number_format(1 / number_format($runtime, 8), 2); + $runtime = number_format(microtime(true) - THINK_START_TIME, 10); + $reqs = 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]'; diff --git a/core/library/think/template/TagLib.php b/core/library/think/template/TagLib.php index b0e40f57..101593bf 100644 --- a/core/library/think/template/TagLib.php +++ b/core/library/think/template/TagLib.php @@ -224,7 +224,7 @@ class TagLib */ public function parseAttr($str, $name, $alias = '') { - $regex = '/\s+(?>(?[\w-]+)\s*)=(?>\s*)([\"\'])(?(?:(?!\\2).)*)\\2/is'; + $regex = '/\s+(?>(?P[\w-]+)\s*)=(?>\s*)([\"\'])(?P(?:(?!\\2).)*)\\2/is'; $result = []; if (preg_match_all($regex, $str, $matches)) { foreach ($matches['name'] as $key => $val) { diff --git a/core/library/traits/controller/Jump.php b/core/library/traits/controller/Jump.php index 10f2c765..b8b2b573 100644 --- a/core/library/traits/controller/Jump.php +++ b/core/library/traits/controller/Jump.php @@ -27,11 +27,11 @@ trait Jump /** * 操作成功跳转的快捷方法 * @access protected - * @param mixed $msg 提示信息 - * @param string $url 跳转的URL地址 - * @param mixed $data 返回的数据 - * @param integer $wait 跳转等待时间 - * @return array + * @param mixed $msg 提示信息 + * @param string $url 跳转的URL地址 + * @param mixed $data 返回的数据 + * @param integer $wait 跳转等待时间 + * @return void */ protected function success($msg = '', $url = null, $data = '', $wait = 3) { @@ -42,7 +42,7 @@ trait Jump } if (is_null($url) && isset($_SERVER["HTTP_REFERER"])) { $url = $_SERVER["HTTP_REFERER"]; - } else { + } elseif ('' !== $url) { $url = preg_match('/^(https?:|\/)/', $url) ? $url : Url::build($url); } $result = [ @@ -58,16 +58,17 @@ trait Jump $result = ViewTemplate::instance(Config::get('template'), Config::get('view_replace_str')) ->fetch(Config::get('dispatch_success_tmpl'), $result); } - return Response::create($result, $type); + $response = Response::create($result, $type); + throw new HttpResponseException($response); } /** * 操作错误跳转的快捷方法 * @access protected - * @param mixed $msg 提示信息 - * @param string $url 跳转的URL地址 - * @param mixed $data 返回的数据 - * @param integer $wait 跳转等待时间 + * @param mixed $msg 提示信息 + * @param string $url 跳转的URL地址 + * @param mixed $data 返回的数据 + * @param integer $wait 跳转等待时间 * @return void */ protected function error($msg = '', $url = null, $data = '', $wait = 3) @@ -79,7 +80,7 @@ trait Jump } if (is_null($url)) { $url = 'javascript:history.back(-1);'; - } else { + } elseif ('' !== $url) { $url = preg_match('/^(https?:|\/)/', $url) ? $url : Url::build($url); } $result = [ @@ -102,11 +103,11 @@ trait Jump /** * 返回封装后的API数据到客户端 * @access protected - * @param mixed $data 要返回的数据 - * @param integer $code 返回的code - * @param mixed $msg 提示信息 - * @param string $type 返回数据格式 - * @return mixed + * @param mixed $data 要返回的数据 + * @param integer $code 返回的code + * @param mixed $msg 提示信息 + * @param string $type 返回数据格式 + * @return void */ protected function result($data, $code = 0, $msg = '', $type = '') { @@ -116,16 +117,17 @@ trait Jump 'time' => $_SERVER['REQUEST_TIME'], 'data' => $data, ]; - $type = $type ?: $this->getResponseType(); - return Response::create($result, $type); + $type = $type ?: $this->getResponseType(); + $response = Response::create($result, $type); + throw new HttpResponseException($response); } /** * URL重定向 * @access protected - * @param string $url 跳转的URL表达式 - * @param array|integer $params 其它URL参数 - * @param integer $code http code + * @param string $url 跳转的URL表达式 + * @param array|integer $params 其它URL参数 + * @param integer $code http code * @return void */ protected function redirect($url, $params = [], $code = 302)