rabin il y a 2 mois
Parent
commit
ac879638e7

+ 309 - 0
src/Dever/Helper/Bc.php

@@ -0,0 +1,309 @@
+<?php namespace Dever\Helper;
+use \Exception;
+use \ReflectionFunction;
+#扩展的bcmatch,简单直接用Dever::Math('calc', '1*2/10')就行
+/* 
+echo Bc::evaluate('1*2/10');
+echo Bc::evaluate('price * qty + tax', [
+    'price' => '12.50',
+    'qty' => '3',
+    'tax' => '2.50'
+]); // 输出 40.00
+
+echo Bc::evaluate('max(10, 20) + abs(-3)'); // 输出 23.00
+
+// 自定义函数注册
+Bc::registerFunction('discount', fn($price) => bcmul($price, '0.9', 2));
+echo Bc::evaluate('discount(100)'); // 输出 90.00
+
+$expr = 'max(10, min(5, 2)) + a > 5 && b < 10';
+$vars = ['a' => 3, 'b' => 9];
+$result = Bc::evaluate($expr, $vars, 4);
+
+*/
+class Bc
+{
+    private static $scale = 2;
+    private static $variables = [];
+    private static $functions = [
+        'abs' => 'abs',
+        'max' => 'max',
+        'min' => 'min',
+    ];
+
+    private static $operators = [
+        '+' => ['precedence' => 1, 'assoc' => 'L', 'args' => 2],
+        '-' => ['precedence' => 1, 'assoc' => 'L', 'args' => 2],
+        '*' => ['precedence' => 2, 'assoc' => 'L', 'args' => 2],
+        '/' => ['precedence' => 2, 'assoc' => 'L', 'args' => 2],
+
+        '>'  => ['precedence' => 0, 'assoc' => 'L', 'args' => 2],
+        '<'  => ['precedence' => 0, 'assoc' => 'L', 'args' => 2],
+        '>=' => ['precedence' => 0, 'assoc' => 'L', 'args' => 2],
+        '<=' => ['precedence' => 0, 'assoc' => 'L', 'args' => 2],
+        '==' => ['precedence' => 0, 'assoc' => 'L', 'args' => 2],
+        '!=' => ['precedence' => 0, 'assoc' => 'L', 'args' => 2],
+
+        '&&' => ['precedence' => -1, 'assoc' => 'L', 'args' => 2],
+        '||' => ['precedence' => -2, 'assoc' => 'L', 'args' => 2],
+
+        'u-' => ['precedence' => 3, 'assoc' => 'R', 'args' => 1], // unary minus
+    ];
+
+    // 评估表达式
+    public static function evaluate(string $expr, array $vars = [], int $scale = 2): string
+    {
+        self::$scale = $scale;
+        self::$variables = $vars;
+
+        try {
+            $tokens = self::tokenize($expr);
+            $tokens = self::replaceVariables($tokens);
+            $tokens = self::handleUnaryOperators($tokens);
+            $rpn = self::toRPN($tokens);
+            $result = self::compute($rpn);
+            return $result;
+        } catch (Exception $e) {
+            throw new Exception("Expression error: " . $e->getMessage());
+        }
+    }
+
+    // 注册自定义函数
+    public static function registerFunction(string $name, callable $callback): void
+    {
+        self::$functions[$name] = $callback;
+    }
+
+    // 分词
+    private static function tokenize(string $expr): array
+    {
+        $expr = preg_replace('/\s+/', '', $expr); // 去除所有空格
+
+        // 匹配运算符、数字、函数名、变量
+        preg_match_all('/
+            (>=|<=|==|!=|&&|\|\|)     # 多字符运算符
+            |[+\-*\/(),<>]             # 单字符运算符和括号
+            |\d*\.\d+|\d+              # 浮点数或整数
+            |[a-zA-Z_]\w*              # 变量或函数名
+        /x', $expr, $matches);
+
+        if (empty($matches[0])) {
+            throw new Exception("无法解析表达式");
+        }
+        return $matches[0];
+    }
+
+    // 替换变量为对应值(仅替换完整变量名,避免误替换函数名)
+    private static function replaceVariables(array $tokens): array
+    {
+        return array_map(function ($token) {
+            if (isset(self::$variables[$token])) {
+                return (string)self::$variables[$token];
+            }
+            return $token;
+        }, $tokens);
+    }
+
+    // 处理一元负号:例如 -5 转换成 u-
+    private static function handleUnaryOperators(array $tokens): array
+    {
+        $result = [];
+        $prev = null;
+        foreach ($tokens as $token) {
+            if ($token === '-' && ($prev === null || (isset(self::$operators[$prev]) && $prev !== ')'))) {
+                $result[] = 'u-';
+            } else {
+                $result[] = $token;
+            }
+            $prev = end($result);
+        }
+        return $result;
+    }
+
+    // 转换为逆波兰表达式(RPN)
+    private static function toRPN(array $tokens): array
+    {
+        $output = [];
+        $stack = [];
+        $functions = array_keys(self::$functions);
+        $ops = self::$operators;
+
+        foreach ($tokens as $token) {
+            if (is_numeric($token)) {
+                $output[] = $token;
+            } elseif (in_array($token, $functions)) {
+                $stack[] = $token;
+            } elseif ($token === ',') {
+                while (!empty($stack) && end($stack) !== '(') {
+                    $output[] = array_pop($stack);
+                }
+                if (empty($stack)) {
+                    throw new Exception("函数参数分隔符错误");
+                }
+            } elseif (isset($ops[$token])) {
+                while (!empty($stack)) {
+                    $top = end($stack);
+                    if ($top === '(') break;
+                    if (!isset($ops[$top])) break;
+
+                    $currOp = $ops[$token];
+                    $topOp = $ops[$top];
+
+                    if (
+                        ($currOp['assoc'] === 'L' && $currOp['precedence'] <= $topOp['precedence']) ||
+                        ($currOp['assoc'] === 'R' && $currOp['precedence'] < $topOp['precedence'])
+                    ) {
+                        $output[] = array_pop($stack);
+                    } else {
+                        break;
+                    }
+                }
+                $stack[] = $token;
+            } elseif ($token === '(') {
+                $stack[] = $token;
+            } elseif ($token === ')') {
+                while (!empty($stack) && end($stack) !== '(') {
+                    $output[] = array_pop($stack);
+                }
+                if (empty($stack)) {
+                    throw new Exception("括号不匹配");
+                }
+                array_pop($stack); // 弹出 '('
+
+                if (!empty($stack) && in_array(end($stack), $functions)) {
+                    $output[] = array_pop($stack);
+                }
+            } else {
+                // 非数字、运算符、函数名,直接输出(已经替换过变量)
+                $output[] = $token;
+            }
+        }
+
+        while (!empty($stack)) {
+            $top = array_pop($stack);
+            if ($top === '(' || $top === ')') {
+                throw new Exception("括号不匹配");
+            }
+            $output[] = $top;
+        }
+
+        return $output;
+    }
+
+    // 计算逆波兰表达式
+    private static function compute(array $rpn): string
+    {
+        $stack = [];
+        $useBc = extension_loaded('bcmath');
+
+        foreach ($rpn as $token) {
+            if (is_numeric($token)) {
+                $stack[] = $token;
+            } elseif (isset(self::$functions[$token])) {
+                // 取出所有栈顶元素作为参数(用可变参数调用)
+                $fn = self::$functions[$token];
+
+                // 由于无法准确知道参数个数,尝试把栈中所有元素都传过去(自定义函数需自行处理)
+                // 这里我们只取最后一个参数,或者修改接口更合理
+                // 简化:只支持单参数函数(或 max/min等变参),调用方式如下:
+                if (is_string($fn) && in_array($fn, ['max', 'min'])) {
+                    // max,min 取所有栈顶元素直到遇到非数字
+                    $args = [];
+                    while (!empty($stack) && is_numeric(end($stack))) {
+                        $args[] = array_pop($stack);
+                    }
+                    $args = array_reverse($args);
+                    $result = call_user_func_array($fn, $args);
+                } else {
+                    // 取一个参数
+                    if (empty($stack)) {
+                        throw new Exception("函数参数不足");
+                    }
+                    $arg = array_pop($stack);
+                    $result = call_user_func($fn, $arg);
+                }
+                $stack[] = $result;
+            } elseif (isset(self::$operators[$token])) {
+                $op = self::$operators[$token];
+                $args = [];
+                for ($i = 0; $i < $op['args']; $i++) {
+                    if (empty($stack)) {
+                        throw new Exception("操作数不足");
+                    }
+                    array_unshift($args, array_pop($stack));
+                }
+
+                if ($useBc) {
+                    switch ($token) {
+                        case '+': $res = bcadd($args[0], $args[1], self::$scale); break;
+                        case '-': $res = bcsub($args[0], $args[1], self::$scale); break;
+                        case '*': $res = bcmul($args[0], $args[1], self::$scale); break;
+                        case '/': 
+                            if (bccomp($args[1], '0', self::$scale) == 0) {
+                                throw new Exception("除数不能为零");
+                            }
+                            $res = bcdiv($args[0], $args[1], self::$scale);
+                            break;
+                        case '>':  $res = bccomp($args[0], $args[1], self::$scale) === 1 ? '1' : '0'; break;
+                        case '<':  $res = bccomp($args[0], $args[1], self::$scale) === -1 ? '1' : '0'; break;
+                        case '>=': $res = bccomp($args[0], $args[1], self::$scale) >= 0 ? '1' : '0'; break;
+                        case '<=': $res = bccomp($args[0], $args[1], self::$scale) <= 0 ? '1' : '0'; break;
+                        case '==': $res = bccomp($args[0], $args[1], self::$scale) === 0 ? '1' : '0'; break;
+                        case '!=': $res = bccomp($args[0], $args[1], self::$scale) !== 0 ? '1' : '0'; break;
+                        case '&&':
+                            $res = ((bccomp($args[0], '0', self::$scale) !== 0) && (bccomp($args[1], '0', self::$scale) !== 0)) ? '1' : '0';
+                            break;
+                        case '||':
+                            $res = ((bccomp($args[0], '0', self::$scale) !== 0) || (bccomp($args[1], '0', self::$scale) !== 0)) ? '1' : '0';
+                            break;
+                        case 'u-':
+                            $res = bcmul('-1', $args[0], self::$scale);
+                            break;
+                        default:
+                            throw new Exception("未知操作符:$token");
+                    }
+                } else {
+                    // 不支持 bcmath 时,用浮点数运算
+                    switch ($token) {
+                        case '+': $res = $args[0] + $args[1]; break;
+                        case '-': $res = $args[0] - $args[1]; break;
+                        case '*': $res = $args[0] * $args[1]; break;
+                        case '/':
+                            if ($args[1] == 0) {
+                                throw new Exception("除数不能为零");
+                            }
+                            $res = $args[0] / $args[1];
+                            break;
+                        case '>':  $res = ($args[0] > $args[1]) ? '1' : '0'; break;
+                        case '<':  $res = ($args[0] < $args[1]) ? '1' : '0'; break;
+                        case '>=': $res = ($args[0] >= $args[1]) ? '1' : '0'; break;
+                        case '<=': $res = ($args[0] <= $args[1]) ? '1' : '0'; break;
+                        case '==': $res = ($args[0] == $args[1]) ? '1' : '0'; break;
+                        case '!=': $res = ($args[0] != $args[1]) ? '1' : '0'; break;
+                        case '&&':
+                            $res = ($args[0] && $args[1]) ? '1' : '0';
+                            break;
+                        case '||':
+                            $res = ($args[0] || $args[1]) ? '1' : '0';
+                            break;
+                        case 'u-':
+                            $res = -$args[0];
+                            break;
+                        default:
+                            throw new Exception("未知操作符:$token");
+                    }
+                }
+
+                $stack[] = (string)$res;
+            } else {
+                throw new Exception("未知标记:$token");
+            }
+        }
+
+        if (count($stack) !== 1) {
+            throw new Exception("表达式解析错误,结果堆栈异常");
+        }
+
+        return $stack[0];
+    }
+}

+ 100 - 0
src/Dever/Helper/Math.php

@@ -1,6 +1,106 @@
 <?php namespace Dever\Helper;
+use Dever;
 class Math
 {
+    private static $bc = null;
+    public static function check(): bool
+    {
+        if (self::$bc === null) {
+            self::$bc = extension_loaded('bcmath');
+        }
+        return self::$bc;
+    }
+
+    # 格式化展示
+    public static function format($number, $scale = 2)
+    {
+        return number_format((float)$number, $scale, '.', '');
+    }
+
+    # 加法
+    public static function add($left, $right, $scale = 2)
+    {
+        if (self::check()) {
+            return bcadd((string)$left, (string)$right, $scale);
+        }
+        return self::format((float)$left + (float)$right, $scale);
+    }
+
+    # 减法
+    public static function sub($left, $right, $scale = 2)
+    {
+        if (self::check()) {
+            return bcsub($left, $right, $scale);
+        }
+        return self::format((float)$left - (float)$right, $scale);
+    }
+
+    # 乘法
+    public static function mul($left, $right, $scale = 2)
+    {
+        if (self::check()) {
+            return bcmul($left, $right, $scale);
+        }
+        return self::format((float)$left * (float)$right, $scale);
+    }
+
+    # 除法
+    public static function div($left, $right, $scale = 2)
+    {
+        if (self::check()) {
+            return bcdiv($left, $right, $scale);
+        }
+        return self::format((float)$left / (float)$right, $scale);
+    }
+
+    # 先乘法后除法
+    public static function mulDiv($left, $right, $after, $scale = 2)
+    {
+        $num = self::mul($left, $right);
+        return self::div($num, $after);
+    }
+
+    # 先除法后乘法
+    public static function divMul($left, $right, $after, $scale = 2)
+    {
+        $num = self::div($left, $right);
+        return self::mul($num, $after);
+    }
+
+    # 四舍五入
+    public static function round($number, $scale = 2)
+    {
+        if (self::check()) {
+            $factor = bcpow('10', (string)($precision + 1));
+            $tmp = bcmul($number, $factor, 0); // 扩大精度 1 位并截断
+            $lastDigit = (int)bcmod($tmp, '10');
+            $roundUp = $lastDigit >= 5 ? '1' : '0';
+
+            $scaled = bcdiv($tmp, '10', 0); // 移除最后一位(整数部分)
+            if ($roundUp === '1') {
+                $scaled = bcadd($scaled, '1');
+            }
+            return bcdiv($scaled, bcpow('10', (string)$precision), $precision);
+        }
+        return self::format($number, $scale);
+    }
+
+    # 复杂算式
+    public static function calc($expr, $vars = [], $scale = 2)
+    {
+        if (self::check()) {
+            return Bc::evaluate($expr, $vars, $scale);
+        }
+        if (preg_match('/[^0-9\.\+\-\*\/\%\(\) \$a-zA-Z_]/', $expr)) {
+            return 0;
+        }
+        foreach ($vars as $k => $v) {
+            $expr = preg_replace('/\b' . preg_quote($k, '/') . '\b/', $v, $expr);
+        }
+        $number = eval('return ' . $expr . ';');
+        return self::format($number, $scale);
+    }
+
 	# 笛卡尔积
     public static function cartesian($data)
     {

+ 28 - 20
src/Dever/Helper/Str.php

@@ -83,34 +83,42 @@ class Str
         );
         return $d;
     }
-    public static function uid($uid, $type = 'encode')
+    public static function uid($uid, $type = true, $salt = 123, $xorKey = 456)
     {
-        $source_string = 'E5FCDG3HQA4B1NOPIJ2RSTUV67MWX89KLYZ';
-        if ($type == 'encode') {
+        $source = 'E5FCDG3HQA4B1NOPIJ2RSTUV67MWX89KLYZ';
+        $base = strlen($source);
+        $minLength = 6;
+        if ($type) {
+            $num = ($uid * $salt) ^ $xorKey;
             $code = '';
-            $num = $uid;
             while ($num > 0) {
-                $mod = $num % 35;
-                $num = ($num - $mod) / 35;
-                $code = $source_string[$mod].$code;
+                $mod = $num % $base;
+                $num = intdiv($num, $base);
+                $code = $source[$mod] . $code;
             }
-            if (empty($code[3])) {
-                $code = str_pad($code,4,'0',STR_PAD_LEFT);
+            $padLen = $minLength - strlen($code);
+            for ($i = 0; strlen($code) < $minLength; $i++) {
+                $padChar = $source[($i * 7 + $uid) % $base];
+                $code = $padChar . $code;
             }
-            return $code;
+            return $code . $padLen; // 补位长度加到最后1位,解码时识别
         } else {
-            $code = $uid;
-            if (strrpos($code, '0') !== false)
-            $code = substr($code, strrpos($code, '0')+1);
-            $len = strlen($code);
-            $code = strrev($code);
+            $padLen = (int)substr($uid, -1);        // 最后一位是补位长度
+            $uid = substr($uid, 0, -1);             // 去掉末尾
+            $uid = substr($uid, $padLen);           // 去掉前面 padLen 个补位字符
+
             $num = 0;
-            for ($i=0; $i < $len; $i++) {
-                $num += strpos($source_string, $code[$i]) * pow(35, $i);
+            $len = strlen($uid);
+            for ($i = 0; $i < $len; $i++) {
+                $pos = strpos($source, $uid[$i]);
+                if ($pos === false) return 0;
+                $num = $num * $base + $pos;
             }
-            return $num;
+            $num = $num ^ $xorKey;
+            return intdiv($num, $salt);
         }
     }
+
     public static function idtostr($id)
     {
         if (!is_numeric($id) || $id < 0) {
@@ -283,7 +291,7 @@ class Str
         }
         return false;
     }
-    public static function val($show, $data = [])
+    public static function val($show, $data = [], $state = false)
     {
         if ($data && strpos($show, '{') !== false && strpos($show, '{"') === false) {
             $func = function ($r) use ($data) {
@@ -295,7 +303,7 @@ class Str
             $show = preg_replace_callback('/{(.*?)}/', $func, $show);
         }
 
-        if (strstr($show, '"') || strstr($show, "'")) {
+        if (strstr($show, '"') || strstr($show, "'") || $state) {
             $eval = '$show =  ' . $show . ';';
         } else {
             $eval = '$show = "' . $show . '";';

+ 1 - 1
src/Dever/Library.php

@@ -41,7 +41,7 @@ class Library
     public static function load($class, $app, $path)
     {
         if (strstr($class, 'manage/')) {
-            $auth = new \Manage\Lib\Auth(true);
+            $auth = new \Manage\Lib\Auth();
             $class = str_replace('manage/', 'lib/', $class);
             $path = 'manage';
         }

+ 22 - 18
src/Dever/Model.php

@@ -125,11 +125,15 @@ class Model
     {
         return $this->store->optimize($this->config['table']);
     }
-    public function load($param, $set = [], $lock = false)
+    public function load($param, $set = [], $version = false)
     {
-        return $this->store->load($this->config['table'], $param, $set, $this->config['struct'], $lock);
+        return $this->store->load($this->config['table'], $param, $set, $this->config['struct'], $version);
     }
-    public function select($param, $set = [], $lock = false)
+    public function sql($param, $set = [], $version = false)
+    {
+        return $this->store->sql($this->config['table'], $param, $set, $this->config['struct'], $version);
+    }
+    public function select($param, $set = [], $version = false)
     {
         if (isset($this->partition['where']) && $this->partition['where']) {
             $param = array_merge($this->partition['where'], $param);
@@ -148,16 +152,16 @@ class Model
         if (isset($set['num'])) {
             $set['limit'] = Paginator::init($set['num'], $set['page'] ?? 1, function()use($param){return $this->count($param);});
         }
-        if ($lock && isset($this->config['type']) && $this->config['type'] == 'myisam') {
-            $lock = false;
+        if ($version && isset($this->config['type']) && $this->config['type'] == 'myisam') {
+            $version = false;
         }
-        $result = $this->store->select($this->config['table'], $param, $set, $this->config['struct'], $lock);
+        $result = $this->store->select($this->config['table'], $param, $set, $this->config['struct'], $version);
         if (isset($set['num']) && empty($set['page'])) {
             Paginator::status(empty($result));
         }
         return $result;
     }
-    public function find($param, $set = [], $lock = false)
+    public function find($param, $set = [], $version = false)
     {
         if (isset($this->partition['where']) && $this->partition['where']) {
             if (is_numeric($param)) {
@@ -165,7 +169,7 @@ class Model
             }
             $param = array_merge($this->partition['where'], $param);
         }
-        return $this->store->find($this->config['table'], $param, $set, $this->config['struct'], $lock);
+        return $this->store->find($this->config['table'], $param, $set, $this->config['struct'], $version);
     }
     public function sum($param, $field)
     {
@@ -174,11 +178,11 @@ class Model
     public function column($param, $field = 'name', $default = '')
     {
         $info = $this->find($param, ['col' => $field . ' as value']);
-        return $info ? $info['value'] : $default;
+        return $info && $info['value'] ? $info['value'] : $default;
     }
     public function columns($param, $field = 'id')
     {
-        return $this->kv($param, ['col' => $field]);
+        return $this->kv($param, ['col' => $field, 'kv' => $field]);
     }
     public function count($param)
     {
@@ -199,7 +203,7 @@ class Model
             if (empty($set['kv'])) {
                 $set['kv'] = ['id', 'name'];
             }
-            if (isset($set['kv'][1])) {
+            if (is_array($set['kv']) && isset($set['kv'][1])) {
                 foreach ($data as $k => $v) {
                     $result[$v[$set['kv'][0]]] = $v[$set['kv'][1]];
                 }
@@ -211,9 +215,9 @@ class Model
         }
         return $result;
     }
-    public function up($param, $data, $lock = false)
+    public function up($param, $data, $version = false)
     {
-        $info = $this->find($param, [], $lock);
+        $info = $this->find($param, [], $version);
         if ($info) {
             $state = $this->update($info['id'], $data);
             if ($state) {
@@ -234,13 +238,13 @@ class Model
         }
         return $this->store->insert($this->config['table'], $data, $this->config['struct']);
     }
-    public function update($param, $data, $lock = false)
+    public function update($param, $data, $version = false)
     {
-        if ($lock && isset($this->config['struct']['lock'])) {
-            $info = $this->find($param, ['col' => 'id,lock']);
+        if ($version && isset($this->config['struct']['version'])) {
+            $info = $this->find($param, ['col' => 'id,version']);
             if ($info) {
-                $param['lock'] = $info['lock'];
-                $data['lock'] = ['+', 1];
+                $param['version'] = $info['version'];
+                $data['version'] = ['+', 1];
             } else {
                 return false;
             }

+ 61 - 53
src/Dever/Sql.php

@@ -150,7 +150,7 @@ class Sql
             return self::desc($table);
         }
     }
-    public static function select($table, $param, &$bind, $set = [], $field = [], $lock = false, $type = '')
+    public static function select($table, $param, &$bind, $set = [], $field = [], $version = false, $type = '')
     {
         $col = '*';
         $rule = '';
@@ -186,7 +186,7 @@ class Sql
                 }
             }
         }
-        if ($lock) {
+        if ($version) {
             $rule .= ' FOR UPDATE';
         }
         return 'SELECT ' . $col . ' FROM ' . $table . self::where($param, $bind, $field, $type) . $rule;
@@ -233,67 +233,75 @@ class Sql
             return '';
         }
     }
-    private static function field(&$s, &$b, &$i, $k, $e, $v, $f, $t)
+    private static function field(&$sql, &$bind, &$num, $key, $symbol, $value, $field, $type)
     {
-        $type = '';
-        if (is_string($b)) {
-            $type = $b;
-            $b = [];
-        }
-        $g = '';
-        if (strstr($k, '.')) {
-            $g = explode('.', $k);
-            $k = $g[1];
-            $g = $g[0] . '.';
-        } elseif ($f && empty($f[$k]) && strpos('id,cdate', $k) === false) {
+        $prefix = '';
+        if (strstr($key, '.')) {
+            $temp = explode('.', $key);
+            $key = $temp[1];
+            $prefix = $temp[0] . '.';
+        } elseif ($field && empty($field[$key]) && strpos('id,cdate', $key) === false) {
             return;
         }
-        $s .= ' and ';
-        $p = ':'.$k.$i;
-        $x = '`';
-        if ($t == 'Influxdb') {
-            //$e = '=';
-            if ($k == 'cdate') {
-                $k = 'time';
-                $v = date('Y-m-d H:i:s', $v-28800);
+        $sql .= ' and ';
+
+        $state = false;
+        $index = '';
+        $link = '`';
+        if ($type == 'Influxdb') {
+            
+            if ($key == 'cdate') {
+                $key = 'time';
+                $value = date('Y-m-d H:i:s', $value-28800);
             }
-            $x = '"';
-            $p = "'".$v."'";
-            if (strpos($e,'in') !== false) {
-                $e = '=';
+            $link = '"';
+            if (strpos($symbol, 'in') !== false) {
+                $symbol = '=';
             }
+        } elseif (is_string($value) && stristr($value, 'select ')) {
+            $index = $value;
+        } elseif (is_array($bind)) {
+            $state = true;
+            $index = ':'.$key.$num;
         }
-        $k = $g.$x.$k.$x;
-        if (strpos($e,'in') !== false) {
-            if (!is_array($v)) {
-                $v = explode(',', $v);
-            }
-            $t = '';
-            foreach ($v as $ti => $tj) {
-                if ($ti > 0) {
-                    $ti = $p.'_'.$ti;
-                    $t .= ','.$ti;
-                    $b[$ti] = $tj;
-                } else {
-                    $v = $tj;
-                    $t .= $p;
+        if ($state == false && $index == '') {
+            $index = "'".$value."'";
+        }
+        $key = $prefix.$link.$key.$link;
+        if (strpos($symbol, 'in') !== false) {
+            if ($state) {
+                if (!is_array($value)) {
+                    $value = explode(',', $value);
                 }
+                $in = '';
+                foreach ($value as $k => $v) {
+                    if ($k > 0) {
+                        $k = $index.'_'.$k;
+                        $in .= ','.$k;
+                        $state && $bind[$k] = $v;
+                    } else {
+                        $value = $v;
+                        $in .= $index;
+                    }
+                }
+            } else {
+                $in = $index;
             }
-            $s .= $k.' ' .$e.' ('.$t.')';
-        } elseif ($e == 'like') {
-            $s .= 'instr('.$k.','.$p.')'.' > 0';
-        } elseif ($e == 'group') {
-            $v = ','.$v.',';
-            $s .= 'instr(concat(",",'.$k.',","),'.$p.')' . ' > 0';
-        } elseif ($e == 'between') {
-            $b[$p.'_e'] = $v[1];
-            $v = $v[0];
-            $s .= $k.' between '.$p.' and '.$p.'_e';
+            $sql .= $key.' ' .$symbol.' ('.$in.')';
+        } elseif ($symbol == 'like') {
+            $sql .= 'instr('.$key.','.$index.')'.' > 0';
+        } elseif ($symbol == 'group') {
+            $value = ','.$value.',';
+            $sql .= 'instr(concat(",",'.$key.',","),'.$index.')' . ' > 0';
+        } elseif ($symbol == 'between') {
+            $state && $bind[$index.'_e'] = $value[1];
+            $value = $value[0];
+            $sql .= $key.' between '.$index.' and '.$index.'_e';
         } else {
-            $s .= $k.$e.$p;
+            $sql .= $key.$symbol.$index;
         }
-        $b[$p] = $v;
-        $i++;
+        $state && $bind[$index] = $value;
+        $num++;
     }
     public static function insert($table, $data, &$bind, $field)
     {

+ 1 - 1
src/Dever/Store/Base.php

@@ -40,7 +40,7 @@ class Base
     {
         Debug::add($msg, $this->type);
     }
-    public function sql(&$sql, $bind)
+    public function bsql(&$sql, $bind)
     {
         foreach ($bind as $k => $v) {
             if (strstr($sql, 'select') && strpos($v, ',')) {

+ 12 - 7
src/Dever/Store/Pdo.php

@@ -106,24 +106,29 @@ class Pdo extends Base
             $this->error(array('sql' => $sql, 'msg' => $exception->getMessage()));
         }
         if (Debug::$shell) {
-            $this->sql($sql, $bind);
+            $this->bsql($sql, $bind);
             $this->log(array('sql' => $sql, 'count' => $handle->rowCount()));
         }
         return $handle;
     }
-    public function load($table, $param, $set, $field, $lock)
+    public function load($table, $param, $set, $field, $version)
     {
         $bind = [];
-        $sql = Sql::select($table, $param, $bind, $set, $field, $lock);
+        $sql = Sql::select($table, $param, $bind, $set, $field, $version);
         return $this->query($sql, $bind);
     }
-    public function select($table, $param, $set, $field, $lock)
+    public function sql($table, $param, $set, $field, $version)
     {
-        return $this->load($table, $param, $set, $field, $lock)->fetchAll();
+        $bind = '';
+        return Sql::select($table, $param, $bind, $set, $field, $version);
     }
-    public function find($table, $param, $set, $field, $lock)
+    public function select($table, $param, $set, $field, $version)
     {
-        return $this->load($table, $param, $set, $field, $lock)->fetch();
+        return $this->load($table, $param, $set, $field, $version)->fetchAll();
+    }
+    public function find($table, $param, $set, $field, $version)
+    {
+        return $this->load($table, $param, $set, $field, $version)->fetch();
     }
     public function count($table, $param, $field)
     {