瀏覽代碼

[更新]ComposerUpdate

Anyon 6 年之前
父節點
當前提交
f5dffb2b79

+ 1 - 1
thinkphp/LICENSE.txt

@@ -1,6 +1,6 @@
 
 ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
-版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn)
+版权所有Copyright © 2006-2018 by ThinkPHP (http://thinkphp.cn)
 All rights reserved。
 ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
 

+ 3 - 8
thinkphp/library/think/App.php

@@ -20,7 +20,7 @@ use think\route\Dispatch;
  */
 class App extends Container
 {
-    const VERSION = '5.1.24';
+    const VERSION = '5.1.25';
 
     /**
      * 当前模块路径
@@ -606,14 +606,9 @@ class App extends Container
         if (!empty($routeKey)) {
             try {
                 if ($option) {
-                    $this->cache
-                        ->connect($option)
-                        ->tag('route_cache')
-                        ->set($routeKey, $dispatch);
+                    $this->cache->connect($option)->tag('route_cache')->set($routeKey, $dispatch);
                 } else {
-                    $this->cache
-                        ->tag('route_cache')
-                        ->set($routeKey, $dispatch);
+                    $this->cache->tag('route_cache')->set($routeKey, $dispatch);
                 }
             } catch (\Exception $e) {
                 // 存在闭包的时候缓存无效

+ 3 - 1
thinkphp/library/think/Config.php

@@ -71,7 +71,9 @@ class Config implements \ArrayAccess
      */
     public function setYaconf($yaconf)
     {
-        $this->yaconf = $yaconf;
+        if ($this->yaconf) {
+            $this->yaconf = $yaconf;
+        }
     }
 
     /**

+ 0 - 10
thinkphp/library/think/Console.php

@@ -573,16 +573,6 @@ class Console
             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));

+ 26 - 4
thinkphp/library/think/Controller.php

@@ -71,9 +71,21 @@ class Controller
         if ($this->middleware) {
             foreach ($this->middleware as $key => $val) {
                 if (!is_int($key)) {
-                    if (isset($val['only']) && !in_array($this->request->action(), $val['only'])) {
+                    $only = $except = null;
+
+                    if (isset($val['only'])) {
+                        $only = array_map(function ($item) {
+                            return strtolower($item);
+                        }, $val['only']);
+                    } elseif (isset($val['except'])) {
+                        $except = array_map(function ($item) {
+                            return strtolower($item);
+                        }, $val['except']);
+                    }
+
+                    if (isset($only) && !in_array($this->request->action(), $only)) {
                         continue;
-                    } elseif (isset($val['except']) && in_array($this->request->action(), $val['except'])) {
+                    } elseif (isset($except) && in_array($this->request->action(), $except)) {
                         continue;
                     } else {
                         $val = $key;
@@ -108,14 +120,24 @@ class Controller
             if (is_string($options['only'])) {
                 $options['only'] = explode(',', $options['only']);
             }
-            if (!in_array($this->request->action(), $options['only'])) {
+
+            $only = array_map(function ($val) {
+                return strtolower($val);
+            }, $options['only']);
+
+            if (!in_array($this->request->action(), $only)) {
                 return;
             }
         } elseif (isset($options['except'])) {
             if (is_string($options['except'])) {
                 $options['except'] = explode(',', $options['except']);
             }
-            if (in_array($this->request->action(), $options['except'])) {
+
+            $except = array_map(function ($val) {
+                return strtolower($val);
+            }, $options['except']);
+
+            if (in_array($this->request->action(), $except)) {
                 return;
             }
         }

+ 0 - 8
thinkphp/library/think/Hook.php

@@ -187,8 +187,6 @@ class Hook
      */
     protected function execTag($class, $tag = '', $params = null)
     {
-        $this->app->isDebug() && $this->app['debug']->remark('behavior_start', 'time');
-
         $method = Loader::parseName($tag, 1, false);
 
         if ($class instanceof \Closure) {
@@ -209,12 +207,6 @@ class Hook
 
         $result = $this->app->invoke($call, [$params]);
 
-        if ($this->app->isDebug()) {
-            $debug = $this->app['debug'];
-            $debug->remark('behavior_end', 'time');
-            $this->app->log('[ BEHAVIOR ] Run ' . $class . ' @' . $tag . ' [ RunTime:' . $debug->getRangeTime('behavior_start', 'behavior_end') . 's ]');
-        }
-
         return $result;
     }
 

+ 6 - 0
thinkphp/library/think/Log.php

@@ -211,12 +211,18 @@ class Log implements LoggerInterface
             if (!$this->app->isDebug() && isset($log['debug'])) {
                 unset($log['debug']);
             }
+
+            foreach ($log as $level => $info) {
+                $this->app['hook']->listen('log_level', [$level, $info]);
+            }
         } else {
             // 记录允许级别
             $log = [];
             foreach ($this->config['level'] as $level) {
                 if (isset($this->log[$level])) {
                     $log[$level] = $this->log[$level];
+                    // 监听log_level
+                    $this->app['hook']->listen('log_level', [$level, $log[$level]]);
                 }
             }
         }

+ 11 - 0
thinkphp/library/think/Route.php

@@ -958,6 +958,17 @@ class Route
     }
 
     /**
+     * 清空路由规则
+     * @access public
+     * @return void
+     */
+    public function clear()
+    {
+        $this->app['rule_name']->clear();
+        $this->group->clear();
+    }
+
+    /**
      * 设置全局的路由分组参数
      * @access public
      * @param  string    $method     方法名

+ 43 - 101
thinkphp/library/think/db/Builder.php

@@ -87,10 +87,9 @@ abstract class Builder
      * @param  array     $data      数据
      * @param  array     $fields    字段信息
      * @param  array     $bind      参数绑定
-     * @param  string    $suffix    参数绑定后缀
      * @return array
      */
-    protected function parseData(Query $query, $data = [], $fields = [], $bind = [], $suffix = '')
+    protected function parseData(Query $query, $data = [], $fields = [], $bind = [])
     {
         if (empty($data)) {
             return [];
@@ -120,7 +119,7 @@ abstract class Builder
                 $result[$item] = $val->getValue();
                 continue;
             } elseif (!is_scalar($val) && (in_array($key, (array) $query->getOptions('json')) || 'json' == $this->connection->getFieldsType($options['table'], $key))) {
-                $val = json_encode($val);
+                $val = json_encode($val, JSON_UNESCAPED_UNICODE);
             } elseif (is_object($val) && method_exists($val, '__toString')) {
                 // 对象数据写入
                 $val = $val->__toString();
@@ -129,7 +128,7 @@ abstract class Builder
             if (false !== strpos($key, '->')) {
                 list($key, $name) = explode('->', $key);
                 $item             = $this->parseKey($query, $key);
-                $result[$item]    = 'json_set(' . $item . ', \'$.' . $name . '\', ' . $this->parseDataBind($query, $key, $val, $bind, $suffix) . ')';
+                $result[$item]    = 'json_set(' . $item . ', \'$.' . $name . '\', ' . $this->parseDataBind($query, $key, $val, $bind) . ')';
             } elseif (false === strpos($key, '.') && !in_array($key, $fields, true)) {
                 if ($options['strict']) {
                     throw new Exception('fields not exists:[' . $key . ']');
@@ -149,7 +148,7 @@ abstract class Builder
                 }
             } elseif (is_scalar($val)) {
                 // 过滤非标量数据
-                $result[$item] = $this->parseDataBind($query, $key, $val, $bind, $suffix);
+                $result[$item] = $this->parseDataBind($query, $key, $val, $bind);
             }
         }
 
@@ -163,22 +162,17 @@ abstract class Builder
      * @param  string    $key       字段名
      * @param  mixed     $data      数据
      * @param  array     $bind      绑定数据
-     * @param  string    $suffix    绑定后缀
      * @return string
      */
-    protected function parseDataBind(Query $query, $key, $data, $bind = [], $suffix = '')
+    protected function parseDataBind(Query $query, $key, $data, $bind = [])
     {
-        // 过滤非标量数据
-        if (0 === strpos($data, ':') && $query->isBind(substr($data, 1))) {
-            return $data;
+        if ($data instanceof Expression) {
+            return $data->getValue();
         }
 
-        $key  = str_replace(['.', '->'], '_', $key);
-        $name = 'data__' . $key . $suffix;
+        $query->bind($data, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR);
 
-        $query->bind($name, $data, isset($bind[$key]) ? $bind[$key] : PDO::PARAM_STR);
-
-        return ':' . $name;
+        return '?';
     }
 
     /**
@@ -362,7 +356,7 @@ abstract class Builder
     }
 
     // where子单元分析
-    protected function parseWhereItem(Query $query, $field, $val, $rule = '', $binds = [], $bindName = null)
+    protected function parseWhereItem(Query $query, $field, $val, $rule = '', $binds = [])
     {
         // 字段分析
         $key = $field ? $this->parseKey($query, $field, true) : '';
@@ -386,8 +380,7 @@ abstract class Builder
             }
 
             foreach ($val as $k => $item) {
-                $bindName = 'where_' . str_replace('.', '_', $field) . '_' . $k;
-                $str[]    = $this->parseWhereItem($query, $field, $item, $rule, $binds, $bindName);
+                $str[] = $this->parseWhereItem($query, $field, $item, $rule, $binds);
             }
 
             return '( ' . implode(' ' . $rule . ' ', $str) . ' )';
@@ -399,13 +392,6 @@ abstract class Builder
             $exp = $this->exp[$exp];
         }
 
-        $bindName = $bindName ?: 'where_' . $rule . '_' . str_replace(['.', '-'], '_', $field);
-
-        if (preg_match('/\W/', $bindName)) {
-            // 处理带非单词字符的字段名
-            $bindName = md5($bindName);
-        }
-
         if ($value instanceof Expression) {
 
         } elseif (is_object($value) && method_exists($value, '__toString')) {
@@ -421,20 +407,14 @@ abstract class Builder
         }
 
         if (is_scalar($value) && !in_array($exp, ['EXP', 'NOT NULL', 'NULL', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN']) && strpos($exp, 'TIME') === false) {
-            if (strpos($value, ':') !== 0 || !$query->isBind(substr($value, 1))) {
-                if ($query->isBind($bindName)) {
-                    $bindName .= '_' . str_replace('.', '_', uniqid('', true));
-                }
-
-                $query->bind($bindName, $value, $bindType);
-                $value = ':' . $bindName;
-            }
+            $query->bind($value, $bindType);
+            $value = '?';
         }
 
         // 解析查询表达式
         foreach ($this->parser as $fun => $parse) {
             if (in_array($exp, $parse)) {
-                $whereStr = $this->$fun($query, $key, $exp, $value, $field, $bindName, $bindType, isset($val[2]) ? $val[2] : 'AND');
+                $whereStr = $this->$fun($query, $key, $exp, $value, $field, $bindType, isset($val[2]) ? $val[2] : 'AND');
                 break;
             }
         }
@@ -454,19 +434,17 @@ abstract class Builder
      * @param  string    $exp
      * @param  mixed     $value
      * @param  string    $field
-     * @param  string    $bindName
      * @param  integer   $bindType
      * @param  string    $logic
      * @return string
      */
-    protected function parseLike(Query $query, $key, $exp, $value, $field, $bindName, $bindType, $logic)
+    protected function parseLike(Query $query, $key, $exp, $value, $field, $bindType, $logic)
     {
         // 模糊匹配
         if (is_array($value)) {
-            foreach ($value as $k => $item) {
-                $bindKey        = $bindName . '_' . intval($k);
-                $bind[$bindKey] = [$item, $bindType];
-                $array[]        = $key . ' ' . $exp . ' :' . $bindKey;
+            foreach ($value as $item) {
+                $bind[]  = [$item, $bindType];
+                $array[] = $key . ' ' . $exp . ' ?';
             }
 
             $query->bind($bind);
@@ -487,11 +465,10 @@ abstract class Builder
      * @param  string       $exp
      * @param  array        $value
      * @param  string       $field
-     * @param  string       $bindName
      * @param  integer      $bindType
      * @return string
      */
-    protected function parseColumn(Query $query, $key, $exp, array $value, $field, $bindName, $bindType)
+    protected function parseColumn(Query $query, $key, $exp, array $value, $field, $bindType)
     {
         // 字段比较查询
         list($op, $field2) = $value;
@@ -511,11 +488,10 @@ abstract class Builder
      * @param  string       $exp
      * @param  Expression   $value
      * @param  string       $field
-     * @param  string       $bindName
      * @param  integer      $bindType
      * @return string
      */
-    protected function parseExp(Query $query, $key, $exp, Expression $value, $field, $bindName, $bindType)
+    protected function parseExp(Query $query, $key, $exp, Expression $value, $field, $bindType)
     {
         // 表达式查询
         return '( ' . $key . ' ' . $value->getValue() . ' )';
@@ -529,11 +505,10 @@ abstract class Builder
      * @param  string    $exp
      * @param  mixed     $value
      * @param  string    $field
-     * @param  string    $bindName
      * @param  integer   $bindType
      * @return string
      */
-    protected function parseNull(Query $query, $key, $exp, $value, $field, $bindName, $bindType)
+    protected function parseNull(Query $query, $key, $exp, $value, $field, $bindType)
     {
         // NULL 查询
         return $key . ' IS ' . $exp;
@@ -547,33 +522,20 @@ abstract class Builder
      * @param  string    $exp
      * @param  mixed     $value
      * @param  string    $field
-     * @param  string    $bindName
      * @param  integer   $bindType
      * @return string
      */
-    protected function parseBetween(Query $query, $key, $exp, $value, $field, $bindName, $bindType)
+    protected function parseBetween(Query $query, $key, $exp, $value, $field, $bindType)
     {
         // BETWEEN 查询
         $data = is_array($value) ? $value : explode(',', $value);
 
-        if ($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],
-        ];
+        $bind[] = [$data[0], $bindType];
+        $bind[] = [$data[1], $bindType];
 
         $query->bind($bind);
 
-        $between = ':' . $bindKey1 . ' AND :' . $bindKey2;
-
-        return $key . ' ' . $exp . ' ' . $between;
+        return $key . ' ' . $exp . ' ? AND ? ';
     }
 
     /**
@@ -584,11 +546,10 @@ abstract class Builder
      * @param  string    $exp
      * @param  mixed     $value
      * @param  string    $field
-     * @param  string    $bindName
      * @param  integer   $bindType
      * @return string
      */
-    protected function parseExists(Query $query, $key, $exp, $value, $field, $bindName, $bindType)
+    protected function parseExists(Query $query, $key, $exp, $value, $field, $bindType)
     {
         // EXISTS 查询
         if ($value instanceof \Closure) {
@@ -610,13 +571,12 @@ abstract class Builder
      * @param  string    $exp
      * @param  mixed     $value
      * @param  string    $field
-     * @param  string    $bindName
      * @param  integer   $bindType
      * @return string
      */
-    protected function parseTime(Query $query, $key, $exp, $value, $field, $bindName, $bindType)
+    protected function parseTime(Query $query, $key, $exp, $value, $field, $bindType)
     {
-        return $key . ' ' . substr($exp, 0, 2) . ' ' . $this->parseDateTime($query, $value, $field, $bindName, $bindType);
+        return $key . ' ' . substr($exp, 0, 2) . ' ' . $this->parseDateTime($query, $value, $field, $bindType);
     }
 
     /**
@@ -627,11 +587,10 @@ abstract class Builder
      * @param  string    $exp
      * @param  mixed     $value
      * @param  string    $field
-     * @param  string    $bindName
      * @param  integer   $bindType
      * @return string
      */
-    protected function parseCompare(Query $query, $key, $exp, $value, $field, $bindName, $bindType)
+    protected function parseCompare(Query $query, $key, $exp, $value, $field, $bindType)
     {
         if (is_array($value)) {
             throw new Exception('where express error:' . $exp . var_export($value, true));
@@ -653,20 +612,19 @@ abstract class Builder
      * @param  string    $exp
      * @param  mixed     $value
      * @param  string    $field
-     * @param  string    $bindName
      * @param  integer   $bindType
      * @return string
      */
-    protected function parseBetweenTime(Query $query, $key, $exp, $value, $field, $bindName, $bindType)
+    protected function parseBetweenTime(Query $query, $key, $exp, $value, $field, $bindType)
     {
         if (is_string($value)) {
             $value = explode(',', $value);
         }
 
         return $key . ' ' . substr($exp, 0, -4)
-        . $this->parseDateTime($query, $value[0], $field, $bindName . '_between_1', $bindType)
+        . $this->parseDateTime($query, $value[0], $field, $bindType)
         . ' AND '
-        . $this->parseDateTime($query, $value[1], $field, $bindName . '_between_2', $bindType);
+        . $this->parseDateTime($query, $value[1], $field, $bindType);
 
     }
 
@@ -678,11 +636,10 @@ abstract class Builder
      * @param  string    $exp
      * @param  mixed     $value
      * @param  string    $field
-     * @param  string    $bindName
      * @param  integer   $bindType
      * @return string
      */
-    protected function parseIn(Query $query, $key, $exp, $value, $field, $bindName, $bindType)
+    protected function parseIn(Query $query, $key, $exp, $value, $field, $bindType)
     {
         // IN 查询
         if ($value instanceof \Closure) {
@@ -692,17 +649,10 @@ abstract class Builder
 
             $bind  = [];
             $array = [];
-            $i     = 0;
 
             foreach ($value as $k => $v) {
-                $i++;
-                if ($query->isBind($bindName . '_in_' . $i)) {
-                    $bindKey = $bindName . '_in_' . uniqid() . '_' . $i;
-                } else {
-                    $bindKey = $bindName . '_in_' . $i;
-                }
-                $bind[$bindKey] = [$v, $bindType];
-                $array[]        = ':' . $bindKey;
+                $bind[]  = [$v, $bindType];
+                $array[] = '?';
             }
 
             $zone = implode(',', $array);
@@ -736,12 +686,10 @@ abstract class Builder
      * @param  Query     $query        查询对象
      * @param  string    $value
      * @param  string    $key
-     * @param  array     $options
-     * @param  string    $bindName
      * @param  integer   $bindType
      * @return string
      */
-    protected function parseDateTime(Query $query, $value, $key, $bindName = null, $bindType = null)
+    protected function parseDateTime(Query $query, $value, $key, $bindType = null)
     {
         $options = $query->getOptions();
 
@@ -776,15 +724,9 @@ abstract class Builder
             }
         }
 
-        $bindName = $bindName ?: $key;
+        $query->bind($value, $bindType);
 
-        if ($query->isBind($bindName)) {
-            $bindName .= '_' . str_replace('.', '_', uniqid('', true));
-        }
-
-        $query->bind($bindName, $value, $bindType);
-
-        return ':' . $bindName;
+        return '?';
     }
 
     /**
@@ -855,11 +797,11 @@ abstract class Builder
         foreach ($order as $key => $val) {
             if ($val instanceof Expression) {
                 $array[] = $val->getValue();
-            } elseif (is_array($val)) {
+            } elseif (is_array($val) && !preg_match('/\W/', $key)) {
                 $array[] = $this->parseOrderField($query, $key, $val);
             } elseif ('[rand]' == $val) {
                 $array[] = $this->parseRand($query);
-            } else {
+            } elseif (is_string($val)) {
                 if (is_numeric($key)) {
                     list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' ');
                 } else {
@@ -901,7 +843,7 @@ abstract class Builder
         $bind    = $this->connection->getFieldsBind($options['table']);
 
         foreach ($val as $k => $item) {
-            $val[$k] = $this->parseDataBind($query, $key, $item, $bind, $k);
+            $val[$k] = $this->parseDataBind($query, $key, $item, $bind);
         }
 
         return 'field(' . $this->parseKey($query, $key, true) . ',' . implode(',', $val) . ')' . $sort;
@@ -1114,8 +1056,8 @@ abstract class Builder
         // 获取绑定信息
         $bind = $this->connection->getFieldsBind($options['table']);
 
-        foreach ($dataSet as $k => $data) {
-            $data = $this->parseData($query, $data, $allowFields, $bind, '_' . $k);
+        foreach ($dataSet as $data) {
+            $data = $this->parseData($query, $data, $allowFields, $bind);
 
             $values[] = 'SELECT ' . implode(',', array_values($data));
 

+ 20 - 11
thinkphp/library/think/db/Connection.php

@@ -24,6 +24,7 @@ use think\Loader;
 
 abstract class Connection
 {
+    const PARAM_FLOAT          = 21;
     protected static $instance = [];
     /** @var PDOStatement PDO操作实例 */
     protected $PDOStatement;
@@ -303,7 +304,9 @@ abstract class Connection
     {
         if (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) {
             $bind = PDO::PARAM_STR;
-        } elseif (preg_match('/(int|double|float|decimal|real|numeric|serial|bit)/is', $type)) {
+        } elseif (preg_match('/(double|float|decimal|real|numeric)/is', $type)) {
+            $bind = self::PARAM_FLOAT;
+        } elseif (preg_match('/(int|serial|bit)/is', $type)) {
             $bind = PDO::PARAM_INT;
         } elseif (preg_match('/bool/is', $type)) {
             $bind = PDO::PARAM_BOOL;
@@ -1343,13 +1346,14 @@ abstract class Connection
         }
 
         if (is_null($field)) {
-            $field = '*';
-        } elseif ($key && '*' != $field) {
-            $field = $key . ',' . $field;
+            $field = ['*'];
+        } elseif (is_string($field)) {
+            $field = array_map('trim', explode(',', $field));
         }
 
-        if (is_string($field)) {
-            $field = array_map('trim', explode(',', $field));
+        if ($key && ['*'] != $field) {
+            array_unshift($field, $key);
+            $field = array_unique($field);
         }
 
         $query->setOption('field', $field);
@@ -1357,6 +1361,7 @@ abstract class Connection
         // 生成查询SQL
         $sql = $this->builder->select($query);
 
+        // 还原field参数
         if (isset($options['field'])) {
             $query->setOption('field', $options['field']);
         } else {
@@ -1378,7 +1383,7 @@ abstract class Connection
         } else {
             $resultSet = $pdo->fetchAll(PDO::FETCH_ASSOC);
 
-            if ('*' == $field && $key) {
+            if (['*'] == $field && $key) {
                 $result = array_column($resultSet, null, $key);
             } elseif ($resultSet) {
                 $fields = array_keys($resultSet[0]);
@@ -1454,10 +1459,10 @@ abstract class Connection
             $value = is_array($val) ? $val[0] : $val;
             $type  = is_array($val) ? $val[1] : PDO::PARAM_STR;
 
-            if (PDO::PARAM_STR == $type) {
-                $value = '\'' . addslashes($value) . '\'';
-            } elseif (PDO::PARAM_INT == $type) {
+            if (PDO::PARAM_INT == $type || self::PARAM_FLOAT == $type) {
                 $value = (float) $value;
+            } elseif (PDO::PARAM_STR == $type) {
+                $value = '\'' . addslashes($value) . '\'';
             }
 
             // 判断占位符
@@ -1490,7 +1495,11 @@ abstract class Connection
             if (is_array($val)) {
                 if (PDO::PARAM_INT == $val[1] && '' === $val[0]) {
                     $val[0] = 0;
+                } elseif (self::PARAM_FLOAT == $val[1]) {
+                    $val[0] = (float) $val[0];
+                    $val[1] = PDO::PARAM_STR;
                 }
+
                 $result = $this->PDOStatement->bindValue($param, $val[0], $val[1]);
             } else {
                 $result = $this->PDOStatement->bindValue($param, $val);
@@ -2023,7 +2032,7 @@ abstract class Connection
 
         // 分布式数据库配置解析
         foreach (['username', 'password', 'hostname', 'hostport', 'database', 'dsn', 'charset'] as $name) {
-            $_config[$name] = explode(',', $this->config[$name]);
+            $_config[$name] = is_string($this->config[$name]) ? explode(',', $this->config[$name]) : $this->config[$name];
         }
 
         // 主服务器序号

+ 33 - 27
thinkphp/library/think/db/Query.php

@@ -177,7 +177,7 @@ class Query
             call_user_func_array([$this->model, $method], $args);
             return $this;
         } else {
-            throw new Exception('method not exist:' . static::class . '->' . $method);
+            throw new Exception('method not exist:' . ($this->model ? get_class($this->model) : static::class) . '->' . $method);
         }
     }
 
@@ -628,6 +628,9 @@ class Query
             $result = (float) $result;
         }
 
+        // 查询完成后清空聚合字段信息
+        $this->removeOption('field');
+
         return $result;
     }
 
@@ -653,10 +656,12 @@ class Query
                 $query->fetchSql(true);
             }
 
-            return $query->aggregate('COUNT', '*', true);
+            $count = $query->aggregate('COUNT', '*');
+        } else {
+            $count = $this->aggregate('COUNT', $field);
         }
 
-        return $this->aggregate('COUNT', $field, true);
+        return is_string($count) ? $count : (int) $count;
     }
 
     /**
@@ -1024,17 +1029,12 @@ class Query
      * 表达式方式指定查询字段
      * @access public
      * @param  string $field    字段名
-     * @param  array  $bind     参数绑定
      * @return $this
      */
-    public function fieldRaw($field, array $bind = [])
+    public function fieldRaw($field)
     {
         $this->options['field'][] = $this->raw($field);
 
-        if ($bind) {
-            $this->bind($bind);
-        }
-
         return $this;
     }
 
@@ -1424,11 +1424,17 @@ class Query
      */
     public function whereExp($field, $condition, $bind = [], $logic = 'AND')
     {
-        $this->options['where'][$logic][] = [$field, 'EXP', $this->raw($condition)];
-
         if ($bind) {
-            $this->bind($bind);
+            foreach ($bind as $key => $value) {
+                if (!is_numeric($key)) {
+                    $where = str_replace(':' . $key, '?', $where);
+                }
+            }
+            $this->bind(array_values($bind));
         }
+
+        $this->options['where'][$logic][] = [$field, 'EXP', $this->raw($condition)];
+
         return $this;
     }
 
@@ -1442,12 +1448,18 @@ class Query
      */
     public function whereRaw($where, $bind = [], $logic = 'AND')
     {
-        $this->options['where'][$logic][] = $this->raw($where);
-
         if ($bind) {
-            $this->bind($bind);
+            foreach ($bind as $key => $value) {
+                if (!is_numeric($key)) {
+                    $where = str_replace(':' . $key, '?', $where);
+                }
+            }
+
+            $this->bind(array_values($bind));
         }
 
+        $this->options['where'][$logic][] = $this->raw($where);
+
         return $this;
     }
 
@@ -1875,17 +1887,12 @@ class Query
      * 表达式方式指定Field排序
      * @access public
      * @param  string $field 排序字段
-     * @param  array  $bind  参数绑定
      * @return $this
      */
-    public function orderRaw($field, array $bind = [])
+    public function orderRaw($field)
     {
         $this->options['order'][] = $this->raw($field);
 
-        if ($bind) {
-            $this->bind($bind);
-        }
-
         return $this;
     }
 
@@ -2424,17 +2431,16 @@ class Query
     /**
      * 参数绑定
      * @access public
-     * @param  mixed   $key   参数名
      * @param  mixed   $value 绑定变量值
      * @param  integer $type  绑定类型
      * @return $this
      */
-    public function bind($key, $value = false, $type = PDO::PARAM_STR)
+    public function bind($value = false, $type = PDO::PARAM_STR)
     {
-        if (is_array($key)) {
-            $this->bind = array_merge($this->bind, $key);
+        if (is_array($value)) {
+            $this->bind = array_merge($this->bind, $value);
         } else {
-            $this->bind[$key] = [$value, $type];
+            $this->bind[] = [$value, $type];
         }
 
         return $this;
@@ -3197,7 +3203,7 @@ class Query
     {
         $result = $this->with($with)->cache($cache);
 
-        if (is_array($data) && key($data) !== 0) {
+        if ((is_array($data) && key($data) !== 0) || $data instanceof Where) {
             $result = $result->where($data);
             $data   = null;
         } elseif ($data instanceof \Closure) {

+ 1 - 2
thinkphp/library/think/db/Where.php

@@ -79,8 +79,7 @@ class Where implements ArrayAccess
      * 分析查询表达式
      * @access protected
      * @param  string   $field     查询字段
-     * @param  string   $op        查询表达式
-     * @param  mixed    $condition 查询条件
+     * @param  array    $where     查询条件
      * @return array
      */
     protected function parseItem($field, $where = [])

+ 11 - 8
thinkphp/library/think/log/driver/File.php

@@ -129,6 +129,17 @@ class File
      */
     protected function getMasterLogFile()
     {
+        if ($this->config['max_files']) {
+            $files = glob($this->config['path'] . '*.log');
+
+            try {
+                if (count($files) > $this->config['max_files']) {
+                    unlink($files[0]);
+                }
+            } catch (\Exception $e) {
+            }
+        }
+
         if ($this->config['single']) {
             $name = is_string($this->config['single']) ? $this->config['single'] : 'single';
 
@@ -138,14 +149,6 @@ class File
 
             if ($this->config['max_files']) {
                 $filename = date('Ymd') . $cli . '.log';
-                $files    = glob($this->config['path'] . '*.log');
-
-                try {
-                    if (count($files) > $this->config['max_files']) {
-                        unlink($files[0]);
-                    }
-                } catch (\Exception $e) {
-                }
             } else {
                 $filename = date('Ym') . DIRECTORY_SEPARATOR . date('d') . $cli . '.log';
             }

+ 3 - 1
thinkphp/library/think/log/driver/Socket.php

@@ -30,6 +30,8 @@ class Socket
         'force_client_ids'    => [],
         // 限制允许读取日志的client_id
         'allow_client_ids'    => [],
+        //输出到浏览器默认展开的日志级别
+        'expand_level'        => ['debug'],
     ];
 
     protected $css = [
@@ -95,7 +97,7 @@ class Socket
 
         foreach ($log as $type => $val) {
             $trace[] = [
-                'type' => 'groupCollapsed',
+                'type' => in_array($type, $this->config['expand_level']) ? 'group' : 'groupCollapsed',
                 'msg'  => '[ ' . $type . ' ]',
                 'css'  => isset($this->css[$type]) ? $this->css[$type] : '',
             ];

+ 20 - 0
thinkphp/library/think/model/relation/BelongsTo.php

@@ -69,6 +69,26 @@ class BelongsTo extends OneToOne
     }
 
     /**
+     * 创建关联统计子查询
+     * @access public
+     * @param  \Closure $closure 闭包
+     * @param  string   $aggregate 聚合查询方法
+     * @param  string   $field 字段
+     * @return string
+     */
+    public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*')
+    {
+        if ($closure) {
+            $closure($this->query);
+        }
+
+        return $this->query
+            ->whereExp($this->localKey, '=' . $this->parent->getTable() . '.' . $this->foreignKey)
+            ->fetchSql()
+            ->$aggregate($field);
+    }
+
+    /**
      * 根据关联条件查询当前模型
      * @access public
      * @param  string  $operator 比较操作符

+ 20 - 0
thinkphp/library/think/model/relation/HasOne.php

@@ -69,6 +69,26 @@ class HasOne extends OneToOne
     }
 
     /**
+     * 创建关联统计子查询
+     * @access public
+     * @param  \Closure $closure 闭包
+     * @param  string   $aggregate 聚合查询方法
+     * @param  string   $field 字段
+     * @return string
+     */
+    public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*')
+    {
+        if ($closure) {
+            $closure($this->query);
+        }
+
+        return $this->query
+            ->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->parent->getPk())
+            ->fetchSql()
+            ->$aggregate($field);
+    }
+
+    /**
      * 根据关联条件查询当前模型
      * @access public
      * @param  string  $operator 比较操作符

+ 1 - 1
thinkphp/library/think/model/relation/MorphMany.php

@@ -195,7 +195,7 @@ class MorphMany extends Relation
 
         if (isset($result->$pk)) {
             if ($closure) {
-                $closur($this->query);
+                $closure($this->query);
             }
 
             $count = $this->query

+ 4 - 1
thinkphp/library/think/response/Redirect.php

@@ -99,15 +99,18 @@ class Redirect extends Response
     /**
      * 跳转到上次记住的url
      * @access public
+     * @param  string  $url 闪存数据不存在时的跳转地址
      * @return $this
      */
-    public function restore()
+    public function restore($url = null)
     {
         $session = $this->app['session'];
 
         if ($session->has('redirect_url')) {
             $this->data = $session->get('redirect_url');
             $session->delete('redirect_url');
+        } elseif ($url) {
+            $this->data = $url;
         }
 
         return $this;

+ 5 - 0
thinkphp/library/think/route/AliasRule.php

@@ -75,6 +75,11 @@ class AliasRule extends Domain
             $this->mergeGroupOptions();
         }
 
+        if (isset($this->option['ext'])) {
+            // 路由ext参数 优先于系统配置的URL伪静态后缀参数
+            $bind = preg_replace('/\.(' . $request->ext() . ')$/i', '', $bind);
+        }
+
         $this->parseBindAppendParam($this->route);
 
         if (0 === strpos($this->route, '\\')) {

+ 18 - 0
thinkphp/library/think/route/RuleGroup.php

@@ -584,4 +584,22 @@ class RuleGroup extends Rule
         return isset($this->rules[strtolower($method)]) ? $this->rules[strtolower($method)] : [];
     }
 
+    /**
+     * 清空分组下的路由规则
+     * @access public
+     * @return void
+     */
+    public function clear()
+    {
+        $this->rules = [
+            '*'       => [],
+            'get'     => [],
+            'post'    => [],
+            'put'     => [],
+            'patch'   => [],
+            'delete'  => [],
+            'head'    => [],
+            'options' => [],
+        ];
+    }
 }

+ 10 - 0
thinkphp/library/think/route/RuleName.php

@@ -133,4 +133,14 @@ class RuleName
         return $result;
     }
 
+    /**
+     * 清空路由规则
+     * @access public
+     * @return void
+     */
+    public function clear()
+    {
+        $this->item = [];
+        $this->rule = [];
+    }
 }

+ 1 - 1
vendor/autoload.php

@@ -4,4 +4,4 @@
 
 require_once __DIR__ . '/composer/autoload_real.php';
 
-return ComposerAutoloaderInit1d3094fd985c01a522304485af2b5f26::getLoader();
+return ComposerAutoloaderInit7ac0a16c6bb261e6fc9b66fbcc229d16::getLoader();

+ 7 - 7
vendor/composer/autoload_real.php

@@ -2,7 +2,7 @@
 
 // autoload_real.php @generated by Composer
 
-class ComposerAutoloaderInit1d3094fd985c01a522304485af2b5f26
+class ComposerAutoloaderInit7ac0a16c6bb261e6fc9b66fbcc229d16
 {
     private static $loader;
 
@@ -19,15 +19,15 @@ class ComposerAutoloaderInit1d3094fd985c01a522304485af2b5f26
             return self::$loader;
         }
 
-        spl_autoload_register(array('ComposerAutoloaderInit1d3094fd985c01a522304485af2b5f26', 'loadClassLoader'), true, true);
+        spl_autoload_register(array('ComposerAutoloaderInit7ac0a16c6bb261e6fc9b66fbcc229d16', 'loadClassLoader'), true, true);
         self::$loader = $loader = new \Composer\Autoload\ClassLoader();
-        spl_autoload_unregister(array('ComposerAutoloaderInit1d3094fd985c01a522304485af2b5f26', 'loadClassLoader'));
+        spl_autoload_unregister(array('ComposerAutoloaderInit7ac0a16c6bb261e6fc9b66fbcc229d16', 'loadClassLoader'));
 
         $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
         if ($useStaticLoader) {
             require_once __DIR__ . '/autoload_static.php';
 
-            call_user_func(\Composer\Autoload\ComposerStaticInit1d3094fd985c01a522304485af2b5f26::getInitializer($loader));
+            call_user_func(\Composer\Autoload\ComposerStaticInit7ac0a16c6bb261e6fc9b66fbcc229d16::getInitializer($loader));
         } else {
             $map = require __DIR__ . '/autoload_namespaces.php';
             foreach ($map as $namespace => $path) {
@@ -48,19 +48,19 @@ class ComposerAutoloaderInit1d3094fd985c01a522304485af2b5f26
         $loader->register(true);
 
         if ($useStaticLoader) {
-            $includeFiles = Composer\Autoload\ComposerStaticInit1d3094fd985c01a522304485af2b5f26::$files;
+            $includeFiles = Composer\Autoload\ComposerStaticInit7ac0a16c6bb261e6fc9b66fbcc229d16::$files;
         } else {
             $includeFiles = require __DIR__ . '/autoload_files.php';
         }
         foreach ($includeFiles as $fileIdentifier => $file) {
-            composerRequire1d3094fd985c01a522304485af2b5f26($fileIdentifier, $file);
+            composerRequire7ac0a16c6bb261e6fc9b66fbcc229d16($fileIdentifier, $file);
         }
 
         return $loader;
     }
 }
 
-function composerRequire1d3094fd985c01a522304485af2b5f26($fileIdentifier, $file)
+function composerRequire7ac0a16c6bb261e6fc9b66fbcc229d16($fileIdentifier, $file)
 {
     if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
         require $file;

+ 4 - 4
vendor/composer/autoload_static.php

@@ -4,7 +4,7 @@
 
 namespace Composer\Autoload;
 
-class ComposerStaticInit1d3094fd985c01a522304485af2b5f26
+class ComposerStaticInit7ac0a16c6bb261e6fc9b66fbcc229d16
 {
     public static $files = array (
         '1cfd2761b63b0a29ed23657ea394cb2d' => __DIR__ . '/..' . '/topthink/think-captcha/src/helper.php',
@@ -303,9 +303,9 @@ class ComposerStaticInit1d3094fd985c01a522304485af2b5f26
     public static function getInitializer(ClassLoader $loader)
     {
         return \Closure::bind(function () use ($loader) {
-            $loader->prefixLengthsPsr4 = ComposerStaticInit1d3094fd985c01a522304485af2b5f26::$prefixLengthsPsr4;
-            $loader->prefixDirsPsr4 = ComposerStaticInit1d3094fd985c01a522304485af2b5f26::$prefixDirsPsr4;
-            $loader->classMap = ComposerStaticInit1d3094fd985c01a522304485af2b5f26::$classMap;
+            $loader->prefixLengthsPsr4 = ComposerStaticInit7ac0a16c6bb261e6fc9b66fbcc229d16::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = ComposerStaticInit7ac0a16c6bb261e6fc9b66fbcc229d16::$prefixDirsPsr4;
+            $loader->classMap = ComposerStaticInit7ac0a16c6bb261e6fc9b66fbcc229d16::$classMap;
 
         }, null, ClassLoader::class);
     }

+ 18 - 18
vendor/composer/installed.json

@@ -50,17 +50,17 @@
     },
     {
         "name": "topthink/framework",
-        "version": "v5.1.24",
-        "version_normalized": "5.1.24.0",
+        "version": "v5.1.25",
+        "version_normalized": "5.1.25.0",
         "source": {
             "type": "git",
             "url": "https://github.com/top-think/framework.git",
-            "reference": "03ee17d1ea9c432a698fa1d2411df53974420187"
+            "reference": "2e684d5fa38bd2d8ee864982f3114a28992da895"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/top-think/framework/zipball/03ee17d1ea9c432a698fa1d2411df53974420187",
-            "reference": "03ee17d1ea9c432a698fa1d2411df53974420187",
+            "url": "https://api.github.com/repos/top-think/framework/zipball/2e684d5fa38bd2d8ee864982f3114a28992da895",
+            "reference": "2e684d5fa38bd2d8ee864982f3114a28992da895",
             "shasum": "",
             "mirrors": [
                 {
@@ -82,7 +82,7 @@
             "sebastian/phpcpd": "2.*",
             "squizlabs/php_codesniffer": "2.*"
         },
-        "time": "2018-09-05T03:37:46+00:00",
+        "time": "2018-10-04T05:04:50+00:00",
         "type": "think-framework",
         "installation-source": "dist",
         "notification-url": "https://packagist.org/downloads/",
@@ -215,17 +215,17 @@
     },
     {
         "name": "symfony/options-resolver",
-        "version": "v3.4.15",
-        "version_normalized": "3.4.15.0",
+        "version": "v3.4.17",
+        "version_normalized": "3.4.17.0",
         "source": {
             "type": "git",
             "url": "https://github.com/symfony/options-resolver.git",
-            "reference": "6debc476953a45969ab39afe8dee0b825f356dc7"
+            "reference": "1cf7d8e704a9cc4164c92e430f2dfa3e6983661d"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/symfony/options-resolver/zipball/6debc476953a45969ab39afe8dee0b825f356dc7",
-            "reference": "6debc476953a45969ab39afe8dee0b825f356dc7",
+            "url": "https://api.github.com/repos/symfony/options-resolver/zipball/1cf7d8e704a9cc4164c92e430f2dfa3e6983661d",
+            "reference": "1cf7d8e704a9cc4164c92e430f2dfa3e6983661d",
             "shasum": "",
             "mirrors": [
                 {
@@ -237,7 +237,7 @@
         "require": {
             "php": "^5.5.9|>=7.0.8"
         },
-        "time": "2018-07-26T08:45:46+00:00",
+        "time": "2018-09-17T17:29:18+00:00",
         "type": "library",
         "extra": {
             "branch-alias": {
@@ -443,17 +443,17 @@
     },
     {
         "name": "zoujingli/wechat-developer",
-        "version": "v1.1.13",
-        "version_normalized": "1.1.13.0",
+        "version": "v1.1.14",
+        "version_normalized": "1.1.14.0",
         "source": {
             "type": "git",
             "url": "https://github.com/zoujingli/WeChatDeveloper.git",
-            "reference": "39682a047e5c8f9c66107924dfed4e2080e97eb4"
+            "reference": "328d21d32c3d06f5f050185a740f00fe475b6a03"
         },
         "dist": {
             "type": "zip",
-            "url": "https://api.github.com/repos/zoujingli/WeChatDeveloper/zipball/39682a047e5c8f9c66107924dfed4e2080e97eb4",
-            "reference": "39682a047e5c8f9c66107924dfed4e2080e97eb4",
+            "url": "https://api.github.com/repos/zoujingli/WeChatDeveloper/zipball/328d21d32c3d06f5f050185a740f00fe475b6a03",
+            "reference": "328d21d32c3d06f5f050185a740f00fe475b6a03",
             "shasum": "",
             "mirrors": [
                 {
@@ -467,7 +467,7 @@
             "ext-openssl": "*",
             "php": ">=5.4"
         },
-        "time": "2018-07-11T02:02:41+00:00",
+        "time": "2018-09-12T06:12:49+00:00",
         "type": "library",
         "installation-source": "dist",
         "autoload": {

+ 17 - 73
vendor/symfony/options-resolver/OptionsResolver.php

@@ -353,11 +353,9 @@ class OptionsResolver implements Options
      *
      * The normalizer should be a closure with the following signature:
      *
-     * ```php
-     * function (Options $options, $value) {
-     *     // ...
-     * }
-     * ```
+     *     function (Options $options, $value) {
+     *         // ...
+     *     }
      *
      * The closure is invoked when {@link resolve()} is called. The closure
      * has access to the resolved values of other options through the passed
@@ -383,11 +381,7 @@ class OptionsResolver implements Options
         }
 
         if (!isset($this->defined[$option])) {
-            throw new UndefinedOptionsException(sprintf(
-                'The option "%s" does not exist. Defined options are: "%s".',
-                $option,
-                implode('", "', array_keys($this->defined))
-            ));
+            throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
         }
 
         $this->normalizers[$option] = $normalizer;
@@ -426,11 +420,7 @@ class OptionsResolver implements Options
         }
 
         if (!isset($this->defined[$option])) {
-            throw new UndefinedOptionsException(sprintf(
-                'The option "%s" does not exist. Defined options are: "%s".',
-                $option,
-                implode('", "', array_keys($this->defined))
-            ));
+            throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
         }
 
         $this->allowedValues[$option] = \is_array($allowedValues) ? $allowedValues : array($allowedValues);
@@ -471,11 +461,7 @@ class OptionsResolver implements Options
         }
 
         if (!isset($this->defined[$option])) {
-            throw new UndefinedOptionsException(sprintf(
-                'The option "%s" does not exist. Defined options are: "%s".',
-                $option,
-                implode('", "', array_keys($this->defined))
-            ));
+            throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
         }
 
         if (!\is_array($allowedValues)) {
@@ -516,11 +502,7 @@ class OptionsResolver implements Options
         }
 
         if (!isset($this->defined[$option])) {
-            throw new UndefinedOptionsException(sprintf(
-                'The option "%s" does not exist. Defined options are: "%s".',
-                $option,
-                implode('", "', array_keys($this->defined))
-            ));
+            throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
         }
 
         $this->allowedTypes[$option] = (array) $allowedTypes;
@@ -555,11 +537,7 @@ class OptionsResolver implements Options
         }
 
         if (!isset($this->defined[$option])) {
-            throw new UndefinedOptionsException(sprintf(
-                'The option "%s" does not exist. Defined options are: "%s".',
-                $option,
-                implode('", "', array_keys($this->defined))
-            ));
+            throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
         }
 
         if (!isset($this->allowedTypes[$option])) {
@@ -664,11 +642,7 @@ class OptionsResolver implements Options
             ksort($clone->defined);
             ksort($diff);
 
-            throw new UndefinedOptionsException(sprintf(
-                (\count($diff) > 1 ? 'The options "%s" do not exist.' : 'The option "%s" does not exist.').' Defined options are: "%s".',
-                implode('", "', array_keys($diff)),
-                implode('", "', array_keys($clone->defined))
-            ));
+            throw new UndefinedOptionsException(sprintf((\count($diff) > 1 ? 'The options "%s" do not exist.' : 'The option "%s" does not exist.').' Defined options are: "%s".', implode('", "', array_keys($diff)), implode('", "', array_keys($clone->defined))));
         }
 
         // Override options set by the user
@@ -683,10 +657,7 @@ class OptionsResolver implements Options
         if (\count($diff) > 0) {
             ksort($diff);
 
-            throw new MissingOptionsException(sprintf(
-                \count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.',
-                implode('", "', array_keys($diff))
-            ));
+            throw new MissingOptionsException(sprintf(\count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.', implode('", "', array_keys($diff))));
         }
 
         // Lock the container
@@ -730,17 +701,10 @@ class OptionsResolver implements Options
         // Check whether the option is set at all
         if (!array_key_exists($option, $this->defaults)) {
             if (!isset($this->defined[$option])) {
-                throw new NoSuchOptionException(sprintf(
-                    'The option "%s" does not exist. Defined options are: "%s".',
-                    $option,
-                    implode('", "', array_keys($this->defined))
-                ));
+                throw new NoSuchOptionException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
             }
 
-            throw new NoSuchOptionException(sprintf(
-                'The optional option "%s" has no value set. You should make sure it is set with "isset" before reading it.',
-                $option
-            ));
+            throw new NoSuchOptionException(sprintf('The optional option "%s" has no value set. You should make sure it is set with "isset" before reading it.', $option));
         }
 
         $value = $this->defaults[$option];
@@ -750,10 +714,7 @@ class OptionsResolver implements Options
             // If the closure is already being called, we have a cyclic
             // dependency
             if (isset($this->calling[$option])) {
-                throw new OptionDefinitionException(sprintf(
-                    'The options "%s" have a cyclic dependency.',
-                    implode('", "', array_keys($this->calling))
-                ));
+                throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', implode('", "', array_keys($this->calling))));
             }
 
             // The following section must be protected from cyclic
@@ -809,7 +770,9 @@ class OptionsResolver implements Options
 
                     // Don't include closures in the exception message
                     continue;
-                } elseif ($value === $allowedValue) {
+                }
+
+                if ($value === $allowedValue) {
                     $success = true;
                     break;
                 }
@@ -840,10 +803,7 @@ class OptionsResolver implements Options
             // If the closure is already being called, we have a cyclic
             // dependency
             if (isset($this->calling[$option])) {
-                throw new OptionDefinitionException(sprintf(
-                    'The options "%s" have a cyclic dependency.',
-                    implode('", "', array_keys($this->calling))
-                ));
+                throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', implode('", "', array_keys($this->calling))));
             }
 
             $normalizer = $this->normalizers[$option];
@@ -1102,20 +1062,4 @@ class OptionsResolver implements Options
     {
         return (\function_exists($isFunction = 'is_'.$type) && $isFunction($value)) || $value instanceof $type;
     }
-
-    /**
-     * @return array
-     */
-    private function getInvalidValues(array $arrayValues, $type)
-    {
-        $invalidValues = array();
-
-        foreach ($arrayValues as $key => $value) {
-            if (!self::isValueValidType($type, $value)) {
-                $invalidValues[$key] = $value;
-            }
-        }
-
-        return $invalidValues;
-    }
 }

+ 19 - 0
vendor/zoujingli/wechat-developer/WeChat/Contracts/BasicWeChat.php

@@ -109,6 +109,25 @@ class BasicWeChat
     }
 
     /**
+     * 设置外部接口 AccessToken
+     * @param string $access_token
+     * @throws \WeChat\Exceptions\LocalCacheException
+     * @author 高一平 <iam@gaoyiping.com>
+     *
+     * 当用户使用自己的缓存驱动时,直接实例化对象后可直接设置 AccessToekn
+     * - 多用于分布式项目时保持 AccessToken 统一
+     * - 使用此方法后就由用户来保证传入的 AccessToekn 为有效 AccessToekn
+     */
+    public function setAccessToken($access_token)
+    {
+        if (!is_string($access_token)) {
+            throw new InvalidArgumentException("Invalid AccessToken type, need string.");
+        }
+        $cache = $this->config->get('appid') . '_access_token';
+        Tools::setCache($cache, $this->access_token = $access_token);
+    }
+
+    /**
      * 清理删除accessToken
      * @return bool
      */

+ 6 - 10
vendor/zoujingli/wechat-developer/WeChat/Custom.php

@@ -28,14 +28,13 @@ class Custom extends BasicWeChat
      * 添加客服帐号
      * @param string $kf_account 客服账号
      * @param string $nickname 客服昵称
-     * @param string $password 账号密码
      * @return array
      * @throws Exceptions\InvalidResponseException
      * @throws Exceptions\LocalCacheException
      */
-    public function addAccount($kf_account, $nickname, $password)
+    public function addAccount($kf_account, $nickname)
     {
-        $data = ['kf_account' => $kf_account, 'nickname' => $nickname, 'password' => $password];
+        $data = ['kf_account' => $kf_account, 'nickname' => $nickname];
         $url = "https://api.weixin.qq.com/customservice/kfaccount/add?access_token=ACCESS_TOKEN";
         $this->registerApi($url, __FUNCTION__, func_get_args());
         return $this->httpPostForJson($url, $data);
@@ -45,14 +44,13 @@ class Custom extends BasicWeChat
      * 修改客服帐号
      * @param string $kf_account 客服账号
      * @param string $nickname 客服昵称
-     * @param string $password 账号密码
      * @return array
      * @throws Exceptions\InvalidResponseException
      * @throws Exceptions\LocalCacheException
      */
-    public function updateAccount($kf_account, $nickname, $password)
+    public function updateAccount($kf_account, $nickname)
     {
-        $data = ['kf_account' => $kf_account, 'nickname' => $nickname, 'password' => $password];
+        $data = ['kf_account' => $kf_account, 'nickname' => $nickname];
         $url = "https://api.weixin.qq.com/customservice/kfaccount/update?access_token=ACCESS_TOKEN";
         $this->registerApi($url, __FUNCTION__, func_get_args());
         return $this->httpPostForJson($url, $data);
@@ -61,15 +59,13 @@ class Custom extends BasicWeChat
     /**
      * 删除客服帐号
      * @param string $kf_account 客服账号
-     * @param string $nickname 客服昵称
-     * @param string $password 账号密码
      * @return array
      * @throws Exceptions\InvalidResponseException
      * @throws Exceptions\LocalCacheException
      */
-    public function deleteAccount($kf_account, $nickname, $password)
+    public function deleteAccount($kf_account)
     {
-        $data = ['kf_account' => $kf_account, 'nickname' => $nickname, 'password' => $password];
+        $data = ['kf_account' => $kf_account];
         $url = "https://api.weixin.qq.com/customservice/kfaccount/del?access_token=ACCESS_TOKEN";
         $this->registerApi($url, __FUNCTION__, func_get_args());
         return $this->httpPostForJson($url, $data);

+ 25 - 0
vendor/zoujingli/wechat-developer/WePay/Refund.php

@@ -15,6 +15,8 @@
 namespace WePay;
 
 use WeChat\Contracts\BasicPay;
+use WeChat\Contracts\Tools;
+use WeChat\Exceptions\InvalidResponseException;
 
 /**
  * 微信商户退款
@@ -48,4 +50,27 @@ class Refund extends BasicPay
         return $this->callPostApi($url, $options);
     }
 
+    /**
+     * 获取退款通知
+     * @return array
+     * @throws InvalidResponseException
+     */
+    public function getNotify()
+    {
+        $data = Tools::xml2arr(file_get_contents("php://input"));
+        if (!isset($data['return_code']) || $data['return_code'] !== 'SUCCESS') {
+            throw new InvalidResponseException('获取退款通知XML失败!');
+        }
+        if (!class_exists('Prpcrypt', false)) {
+            include dirname(__DIR__) . '/WeChat/Contracts/Prpcrypt.php';
+        }
+        $pc = new \Prpcrypt(md5($this->config->get('mch_key')));
+        $array = $pc->decrypt(base64_decode($data['req_info']));
+        if (intval($array[0]) > 0) {
+            throw new InvalidResponseException($array[1], $array[0]);
+        }
+        $data['decode'] = $array[1];
+        return $data;
+    }
+
 }