diff --git a/.htaccess b/.htaccess index 929995c6..eb32a314 100644 --- a/.htaccess +++ b/.htaccess @@ -4,5 +4,5 @@ RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] + RewriteRule ^(.*)$ index.php?s=/$1 [QSA,PT,L] \ No newline at end of file diff --git a/application/admin/static/css/style.css b/application/admin/static/css/style.css index ef836efb..1cf453cf 100644 --- a/application/admin/static/css/style.css +++ b/application/admin/static/css/style.css @@ -134,7 +134,7 @@ th.visible-xxs,td.visible-xxs { margin-top:0; margin-bottom:0; position:relative; - min-height:680px; + min-height:903px; padding:15px 15px 35px 15px; margin-left:220px; } diff --git a/application/common/controller/Fornt.php b/application/common/controller/Fornt.php index d4de7ff4..3dcfd7a9 100644 --- a/application/common/controller/Fornt.php +++ b/application/common/controller/Fornt.php @@ -47,9 +47,9 @@ class Fornt extends Base { } $this->view->config('view_path', $view_path . $view_path_pre) ->config('tpl_replace_string',array( - '__IMG__' => '/' . $view_path . 'static/images', - '__JS__' => '/' . $view_path . 'static/js', - '__CSS__' => '/' . $view_path . 'static/css', + '__IMG__' => BASE_PATH . '/' . $view_path . 'static/images', + '__JS__' => BASE_PATH . '/' . $view_path . 'static/js', + '__CSS__' => BASE_PATH . '/' . $view_path . 'static/css', )); } } diff --git a/core/library/think/App.php b/core/library/think/App.php index 4eddc7cd..671377b1 100644 --- a/core/library/think/App.php +++ b/core/library/think/App.php @@ -141,11 +141,13 @@ class App break; case 'controller': // 执行控制器操作 - $data = Loader::action($dispatch['controller']); + $vars = Request::instance()->param(); + $data = Loader::action($dispatch['controller'], array_merge($vars, $dispatch['var'])); break; case 'method': // 执行回调方法 - $data = self::invokeMethod($dispatch['method']); + $vars = Request::instance()->param(); + $data = self::invokeMethod($dispatch['method'], array_merge($vars, $dispatch['var'])); break; case 'function': // 执行闭包 @@ -220,7 +222,7 @@ class App public static function invokeMethod($method, $vars = []) { if (is_array($method)) { - $class = is_object($method[0]) ? $method[0] : new $method[0](Request::instance()); + $class = is_object($method[0]) ? $method[0] : self::invokeClass($method[0]); $reflect = new \ReflectionMethod($class, $method[1]); } else { // 静态方法 @@ -386,7 +388,7 @@ class App } elseif (is_callable([$instance, '_empty'])) { // 空操作 $call = [$instance, '_empty']; - $vars = [$action]; + $vars = [$actionName]; } else { // 操作不存在 throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()'); diff --git a/core/library/think/Model.php b/core/library/think/Model.php index 181ed8e9..fde0c318 100644 --- a/core/library/think/Model.php +++ b/core/library/think/Model.php @@ -156,8 +156,14 @@ abstract class Model implements \JsonSerializable, \ArrayAccess { $model = $this->class; if (!isset(self::$links[$model])) { + // 合并数据库配置 + if (is_array($this->connection)) { + $connection = array_merge(Config::get('database'), $this->connection); + } else { + $connection = $this->connection; + } // 设置当前模型 确保查询返回模型对象 - $query = Db::connect($this->connection)->model($model, $this->query); + $query = Db::connect($connection)->model($model, $this->query); // 设置当前数据表和模型名 if (!empty($this->table)) { @@ -294,7 +300,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess } // 标记字段更改 - if (!isset($this->data[$name]) || ($this->data[$name] != $value && !in_array($name, $this->change))) { + if (!isset($this->data[$name]) || (0 !== strcmp($this->data[$name], $value) && !in_array($name, $this->change))) { $this->change[] = $name; } // 设置数据对象属性 @@ -1472,4 +1478,47 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $this->initialize(); } + /** + * 模型事件快捷方法 + */ + protected static function beforeInsert($callback, $override = false) + { + self::event('before_insert', $callback, $override); + } + + protected static function afterInsert($callback, $override = false) + { + self::event('after_insert', $callback, $override); + } + + protected static function beforeUpdate($callback, $override = false) + { + self::event('before_update', $callback, $override); + } + + protected static function afterUpdate($callback, $override = false) + { + self::event('after_update', $callback, $override); + } + + protected static function beforeWrite($callback, $override = false) + { + self::event('before_write', $callback, $override); + } + + protected static function afterWrite($callback, $override = false) + { + self::event('after_write', $callback, $override); + } + + protected static function beforeDelete($callback, $override = false) + { + self::event('before_delete', $callback, $override); + } + + protected static function afterDelete($callback, $override = false) + { + self::event('after_delete', $callback, $override); + } + } diff --git a/core/library/think/Request.php b/core/library/think/Request.php index 45b65423..79c349f8 100644 --- a/core/library/think/Request.php +++ b/core/library/think/Request.php @@ -686,7 +686,12 @@ class Request public function post($name = '', $default = null, $filter = '') { if (empty($this->post)) { - $this->post = $_POST; + $content = $this->input; + if (empty($_POST) && strpos($content, '":')) { + $this->post = json_decode($content, true); + } else { + $this->post = $_POST; + } } if (is_array($name)) { $this->param = []; @@ -1258,7 +1263,7 @@ class Request } // IP地址合法验证 $long = sprintf("%u", ip2long($ip)); - $ip = $long ? array($ip, $long) : array('0.0.0.0', 0); + $ip = $long ? [$ip, $long] : ['0.0.0.0', 0]; return $ip[$type]; } diff --git a/core/library/think/Response.php b/core/library/think/Response.php index a202ce76..2b84ed17 100644 --- a/core/library/think/Response.php +++ b/core/library/think/Response.php @@ -103,6 +103,16 @@ class Response 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::set($cache[0], [$data, $this->header], $cache[1]); + } + } + if (!headers_sent() && !empty($this->header)) { // 发送状态码 http_response_code($this->code); @@ -111,16 +121,7 @@ class Response header($name . ':' . $val); } } - if (200 == $this->code) { - $cache = Request::instance()->getCache(); - if ($cache) { - header('Cache-Control: max-age=' . $cache[1] . ',must-revalidate'); - header('Last-Modified:' . gmdate('D, d M Y H:i:s') . ' GMT'); - header('Expires:' . gmdate('D, d M Y H:i:s', $_SERVER['REQUEST_TIME'] + $cache[1]) . ' GMT'); - $header['Content-Type'] = $this->header['Content-Type']; - Cache::set($cache[0], [$data, $header], $cache[1]); - } - } + echo $data; if (function_exists('fastcgi_finish_request')) { diff --git a/core/library/think/Route.php b/core/library/think/Route.php index 96e47895..e2ebd655 100644 --- a/core/library/think/Route.php +++ b/core/library/think/Route.php @@ -1223,7 +1223,7 @@ class Route $find = true; break; } else { - $dir .= DS . $val; + $dir .= DS . Loader::parseName($val); } } if ($find) { @@ -1480,12 +1480,15 @@ class Route $result = ['type' => 'redirect', 'url' => $route, 'status' => isset($option['status']) ? $option['status'] : 301]; } elseif (false !== strpos($route, '\\')) { // 路由到方法 - $route = str_replace('/', '@', $route); - $method = strpos($route, '@') ? explode('@', $route) : $route; - $result = ['type' => 'method', 'method' => $method]; + list($path, $var) = self::parseUrlPath($route); + $route = str_replace('/', '@', implode('/', $path)); + $method = strpos($route, '@') ? explode('@', $route) : $route; + $result = ['type' => 'method', 'method' => $method, 'var' => $var]; } elseif (0 === strpos($route, '@')) { // 路由到控制器 - $result = ['type' => 'controller', 'controller' => substr($route, 1)]; + $route = substr($route, 1); + list($route, $var) = self::parseUrlPath($route); + $result = ['type' => 'controller', 'controller' => implode('/', $route), 'var' => $var]; } else { // 路由到模块/控制器/操作 $result = self::parseModule($route); @@ -1496,7 +1499,7 @@ class Route if (is_array($cache)) { list($key, $expire) = $cache; } else { - $key = $pathinfo; + $key = str_replace('|', '/', $pathinfo); $expire = $cache; } $request->cache($key, $expire); diff --git a/core/library/think/Validate.php b/core/library/think/Validate.php index fc38d89a..ac2f1925 100644 --- a/core/library/think/Validate.php +++ b/core/library/think/Validate.php @@ -34,6 +34,8 @@ class Validate // 验证提示信息 protected $message = []; + // 验证字段描述 + protected $field = []; // 验证规则默认提示信息 protected static $typeMsg = [ @@ -107,11 +109,13 @@ class Validate * @access public * @param array $rules 验证规则 * @param array $message 验证提示信息 + * @param array $field 验证字段描述信息 */ - public function __construct(array $rules = [], $message = []) + public function __construct(array $rules = [], $message = [], $field = []) { $this->rule = array_merge($this->rule, $rules); $this->message = array_merge($this->message, $message); + $this->field = $field; } /** @@ -119,12 +123,13 @@ class Validate * @access public * @param array $rules 验证规则 * @param array $message 验证提示信息 + * @param array $field 验证字段描述信息 * @return Validate */ - public static function make($rules = [], $message = []) + public static function make($rules = [], $message = [], $field = []) { if (is_null(self::$instance)) { - self::$instance = new self($rules, $message); + self::$instance = new self($rules, $message, $field); } return self::$instance; } @@ -216,6 +221,17 @@ class Validate return $this; } + /** + * 判断是否存在某个验证场景 + * @access public + * @param string $name 场景名 + * @return bool + */ + public function hasScene($name) + { + return isset($this->scene[$name]); + } + /** * 设置批量验证 * @access public @@ -280,7 +296,7 @@ class Validate // 字段|描述 用于指定属性名称 list($key, $title) = explode('|', $key); } else { - $title = $key; + $title = isset($this->field[$key]) ? $this->field[$key] : $key; } // 场景检测 diff --git a/core/library/think/View.php b/core/library/think/View.php index da087036..5fb9a142 100644 --- a/core/library/think/View.php +++ b/core/library/think/View.php @@ -19,6 +19,8 @@ class View public $engine; // 模板变量 protected $data = []; + // 用于静态赋值的模板变量 + protected static $var = []; // 视图输出替换 protected $replace = []; @@ -50,6 +52,22 @@ class View 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 @@ -116,7 +134,7 @@ class View public function fetch($template = '', $vars = [], $replace = [], $config = [], $renderContent = false) { // 模板变量 - $vars = array_merge($this->data, $vars); + $vars = array_merge(self::$var, $this->data, $vars); // 页面缓存 ob_start(); diff --git a/core/library/think/db/Builder.php b/core/library/think/db/Builder.php index c963a704..c60d139a 100644 --- a/core/library/think/db/Builder.php +++ b/core/library/think/db/Builder.php @@ -306,7 +306,7 @@ abstract class Builder 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 .= '_' . uniqid(); + $bindName .= '_' . str_replace('.', '_', uniqid('', true)); } $this->query->bind($bindName, $value, $bindType); $value = ':' . $bindName; @@ -333,8 +333,13 @@ abstract class Builder $bind = []; $array = []; foreach ($value as $k => $v) { - $bind[$bindName . '_in_' . $k] = [$v, $bindType]; - $array[] = ':' . $bindName . '_in_' . $k; + if ($this->query->isBind($bindName . '_in_' . $k)) { + $bindKey = $bindName . '_in_' . uniqid() . '_' . $k; + } else { + $bindKey = $bindName . '_in_' . $k; + } + $bind[$bindKey] = [$v, $bindType]; + $array[] = ':' . $bindKey; } $this->query->bind($bind); $zone = implode(',', $array); @@ -347,12 +352,19 @@ abstract class Builder // 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 = [ - $bindName . '_between_1' => [$data[0], $bindType], - $bindName . '_between_2' => [$data[1], $bindType], + $bindKey1 => [$data[0], $bindType], + $bindKey2 => [$data[1], $bindType], ]; $this->query->bind($bind); - $between = ':' . $bindName . '_between_1' . ' AND :' . $bindName . '_between_2'; + $between = ':' . $bindKey1 . ' AND :' . $bindKey2; } else { $between = $this->parseValue($data[0], $field) . ' AND ' . $this->parseValue($data[1], $field); } @@ -410,7 +422,10 @@ abstract class Builder $info = $type[$key]; } if (isset($info)) { - $value = strtotime($value) ?: $value; + if (is_numeric($value) && strtotime($value)) { + $value = strtotime($value) ?: $value; + } + if (preg_match('/(datetime|timestamp)/is', $info)) { // 日期及时间戳类型 $value = date('Y-m-d H:i:s', $value); diff --git a/core/library/think/db/Connection.php b/core/library/think/db/Connection.php index 9806b827..d9b7978f 100644 --- a/core/library/think/db/Connection.php +++ b/core/library/think/db/Connection.php @@ -421,8 +421,8 @@ abstract class Connection $type = is_array($val) ? $val[1] : PDO::PARAM_STR; if (PDO::PARAM_STR == $type) { $value = $this->quote($value); - } elseif (PDO::PARAM_INT == $type && '' === $value) { - $value = 0; + } elseif (PDO::PARAM_INT == $type) { + $value = (float) $value; } // 判断占位符 $sql = is_numeric($key) ? diff --git a/core/library/think/db/Query.php b/core/library/think/db/Query.php index 5a5597e1..39ab03a9 100644 --- a/core/library/think/db/Query.php +++ b/core/library/think/db/Query.php @@ -917,6 +917,9 @@ class Query if (is_array($field)) { // 数组批量查询 $where = $field; + foreach ($where as $k => $val) { + $this->options['multi'][$k][] = $val; + } } elseif ($field && is_string($field)) { // 字符串查询 $where[$field] = ['null', '']; @@ -931,15 +934,32 @@ class Query $where[$field] = ['eq', $op]; } else { $where[$field] = [$op, $condition]; + // 记录一个字段多次查询条件 + $this->options['multi'][$field][] = $where[$field]; } if (!empty($where)) { if (!isset($this->options['where'][$logic])) { $this->options['where'][$logic] = []; } + if (is_string($field) && $this->checkMultiField($field)) { + $where[$field] = $this->options['multi'][$field]; + } elseif (is_array($field)) { + foreach ($field as $key => $val) { + if ($this->checkMultiField($key)) { + $where[$key] = $this->options['multi'][$key]; + } + } + } $this->options['where'][$logic] = array_merge($this->options['where'][$logic], $where); } } + // 检查是否存在一个字段多次查询条件 + private function checkMultiField($field) + { + return isset($this->options['multi'][$field]) && count($this->options['multi'][$field]) > 1; + } + /** * 去除某个查询条件 * @access public @@ -1416,9 +1436,10 @@ class Query } list($guid) = explode(' ', $tableName); - if (!isset(self::$info[$guid])) { + $db = $this->getConfig('database'); + if (!isset(self::$info[$db . '.' . $guid])) { if (!strpos($guid, '.')) { - $schema = $this->getConfig('database') . '.' . $guid; + $schema = $db . '.' . $guid; } else { $schema = $guid; } @@ -1444,9 +1465,9 @@ class Query } else { $pk = null; } - self::$info[$guid] = ['fields' => $fields, 'type' => $type, 'bind' => $bind, 'pk' => $pk]; + self::$info[$db . '.' . $guid] = ['fields' => $fields, 'type' => $type, 'bind' => $bind, 'pk' => $pk]; } - return $fetch ? self::$info[$guid][$fetch] : self::$info[$guid]; + return $fetch ? self::$info[$db . '.' . $guid][$fetch] : self::$info[$db . '.' . $guid]; } /** @@ -2138,19 +2159,30 @@ class Query $column = $column ?: $this->getPk($table); $bind = $this->bind; $resultSet = $this->limit($count)->order($column, 'asc')->select(); + if (strpos($column, '.')) { + list($alias, $key) = explode('.', $column); + } else { + $key = $column; + } + if ($resultSet instanceof Collection) { + $resultSet = $resultSet->all(); + } while (!empty($resultSet)) { if (false === call_user_func($callback, $resultSet)) { return false; } $end = end($resultSet); - $lastId = is_array($end) ? $end[$column] : $end->$column; + $lastId = is_array($end) ? $end[$key] : $end->$key; $resultSet = $this->options($options) ->limit($count) ->bind($bind) ->where($column, '>', $lastId) ->order($column, 'asc') ->select(); + if ($resultSet instanceof Collection) { + $resultSet = $resultSet->all(); + } } return true; } diff --git a/core/library/think/response/Redirect.php b/core/library/think/response/Redirect.php index 0d65e5e3..a3104c2f 100644 --- a/core/library/think/response/Redirect.php +++ b/core/library/think/response/Redirect.php @@ -78,14 +78,17 @@ class Redirect extends Response /** * 记住当前url后跳转 + * @return $this */ public function remember() { Session::set('redirect_url', Request::instance()->url()); + return $this; } /** * 跳转到上次记住的url + * @return $this */ public function restore() { @@ -93,5 +96,6 @@ class Redirect extends Response $this->data = Session::get('redirect_url'); Session::delete('redirect_url'); } + return $this; } } diff --git a/core/library/think/session/driver/Memcache.php b/core/library/think/session/driver/Memcache.php index 2c040271..4ef044a4 100644 --- a/core/library/think/session/driver/Memcache.php +++ b/core/library/think/session/driver/Memcache.php @@ -87,6 +87,7 @@ class Memcache extends SessionHandler * @access public * @param string $sessID * @param String $sessData + * @return bool */ public function write($sessID, $sessData) { @@ -97,6 +98,7 @@ class Memcache extends SessionHandler * 删除Session * @access public * @param string $sessID + * @return bool */ public function destroy($sessID) { @@ -107,6 +109,7 @@ class Memcache extends SessionHandler * Session 垃圾回收 * @access public * @param string $sessMaxLifeTime + * @return true */ public function gc($sessMaxLifeTime) { diff --git a/core/library/think/session/driver/Memcached.php b/core/library/think/session/driver/Memcached.php index 4187204a..01d0f1e0 100644 --- a/core/library/think/session/driver/Memcached.php +++ b/core/library/think/session/driver/Memcached.php @@ -95,6 +95,7 @@ class Memcached extends SessionHandler * @access public * @param string $sessID * @param String $sessData + * @return bool */ public function write($sessID, $sessData) { @@ -105,6 +106,7 @@ class Memcached extends SessionHandler * 删除Session * @access public * @param string $sessID + * @return bool */ public function destroy($sessID) { @@ -115,6 +117,7 @@ class Memcached extends SessionHandler * Session 垃圾回收 * @access public * @param string $sessMaxLifeTime + * @return true */ public function gc($sessMaxLifeTime) { diff --git a/core/library/think/session/driver/Redis.php b/core/library/think/session/driver/Redis.php index e3dc9983..52f14ab7 100644 --- a/core/library/think/session/driver/Redis.php +++ b/core/library/think/session/driver/Redis.php @@ -108,11 +108,11 @@ class Redis extends SessionHandler * 删除Session * @access public * @param string $sessID - * @return bool|void + * @return bool */ public function destroy($sessID) { - $this->handler->delete($this->config['session_name'] . $sessID); + return $this->handler->delete($this->config['session_name'] . $sessID) > 0; } /** diff --git a/core/library/think/view/driver/Php.php b/core/library/think/view/driver/Php.php index 31fe7807..4f3d7e85 100644 --- a/core/library/think/view/driver/Php.php +++ b/core/library/think/view/driver/Php.php @@ -119,9 +119,11 @@ class Php // 分析模板文件规则 $request = Request::instance(); $controller = Loader::parseName($request->controller()); - if ($controller && 0 !== strpos($template, '/')) { - $depr = $this->config['view_depr']; + $depr = $this->config['view_depr']; + if (0 !== strpos($template, '/')) { $template = str_replace(['/', ':'], $depr, $template); + } + if ($controller) { if ('' == $template) { // 如果模板文件名为空 按照默认规则定位 $template = str_replace('.', DS, $controller) . $depr . $request->action(); diff --git a/core/library/think/view/driver/Think.php b/core/library/think/view/driver/Think.php index db1d9816..8e673fde 100644 --- a/core/library/think/view/driver/Think.php +++ b/core/library/think/view/driver/Think.php @@ -121,9 +121,11 @@ class Think } $controller = Loader::parseName($request->controller()); - if ($controller && 0 !== strpos($template, '/')) { - $depr = $this->config['view_depr']; + $depr = $this->config['view_depr']; + if (0 !== strpos($template, '/')) { $template = str_replace(['/', ':'], $depr, $template); + } + if ($controller) { if ('' == $template) { // 如果模板文件名为空 按照默认规则定位 $template = str_replace('.', DS, $controller) . $depr . $request->action(); diff --git a/uploads/editor/20160909/7a8c9b9dadd3bdefa8f8ee2873a7270c.jpg b/uploads/editor/20160909/7a8c9b9dadd3bdefa8f8ee2873a7270c.jpg deleted file mode 100644 index 721e3135..00000000 Binary files a/uploads/editor/20160909/7a8c9b9dadd3bdefa8f8ee2873a7270c.jpg and /dev/null differ diff --git a/uploads/editor/20160909/cfad0417236d14b12159641383473264.jpg b/uploads/editor/20160909/cfad0417236d14b12159641383473264.jpg deleted file mode 100644 index afc8bea6..00000000 Binary files a/uploads/editor/20160909/cfad0417236d14b12159641383473264.jpg and /dev/null differ diff --git a/uploads/editor/20161011/6f421bdfe56b7524b25509698c82a372.jpg b/uploads/editor/20161011/6f421bdfe56b7524b25509698c82a372.jpg deleted file mode 100644 index a0bbefa2..00000000 Binary files a/uploads/editor/20161011/6f421bdfe56b7524b25509698c82a372.jpg and /dev/null differ diff --git a/uploads/editor/20161011/d442d4453b0d5c16907fb37faf341080.jpg b/uploads/editor/20161011/d442d4453b0d5c16907fb37faf341080.jpg deleted file mode 100644 index a0bbefa2..00000000 Binary files a/uploads/editor/20161011/d442d4453b0d5c16907fb37faf341080.jpg and /dev/null differ