[
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 wintercoder\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.MD",
    "content": "# 在线MYSQL测试数据生成工具\n\n根据SQL表结构生成Mysql测试数据，根据字段名猜测类型，生成默认值\n\n[English Readme](./README_EN.MD)\n\n## 生成规则\n\n普通规则见[官网](http://www.datamaker.online/)，此处讲个规则的组合使用\n\n**自增日期（组自增） + 常量列表（组模式）**\n\n使用场景： 一个肯德基总店下有一堆子店，希望生成[每个子店每天]的数据，一行记录包含一个店一天的汇总数据\n\n使用方法： 在子店ID 字段上选择 `常量列表（组模式）` 用逗号分隔写上各店的ID，日期选择 `自增日期（组自增）` 即可\n\n**Unique Key**\n\n有 `UNIQUE KEY (user_id,platform) ` 的情况\n\n方案一：\n这种platform只有1和2的可以考虑使用 给 `platform` 选 `常量列表（组模式）` 填 `1,2`，然后user_id选自增之类保证不重就OK。\n\n方案二：\n选择重复Key选项，测试数据一般跳过就好，注意选 `REPLACE INTO` 可能出现 `AUTO_INCREMENT` + 2 的情况\n\n## 个性化\n你可以通过部署到自己的服务器上，对一些字段进行默认值配置，非常**适合公司内网**\n部署也方便，代码里无任何框架依赖，能跑PHP就行\n\n### 配置\n`/conf/common.ini` 为通用配置\n`/conf/local.ini` 为个性化配置，适合不宜暴露的默认值，如测试账号ID等，该文件已加入 `.gitignore` 里，如果发现不生效则跑下命令\n`git update-index --assume-unchanged conf/local.ini`\n\n配置样例：\n\n```\n[0]\nkey = index_day\nmethod = INCR_DAY\nvalue = 20180301\nway = match\n[1]\nkey = url\nmethod = RAND_PIC_URL\nvalue = 300,400\nway = search\n```\n`key` : 字段名\n`method` : 生成规则，如下表\n`value` : 给前端的输入框值\n`way` : 输入的key跟配置文件的key匹配规则，目前支持 `match` 完全相等、`search` 字符串包含，默认为`match`\n\n\n| 规则英文 | 中文 |  参数 |\n|----------|------|------|\n| INCR_INT | 自增int  | from  : 从from开始自增，步长1\n| RAND_INT | 随机int  | from,to  : 生成[from,to]闭区间整数\n| RAND_FLOAT | 随机浮点  | from,to,round  : 生成[from,to]闭区间浮点，保留round位小数，这三个参数都是整数\n| INCR_DAY | 日期自增     | from  : 从from开始自增，步长1天，格式20180304\n| INCR\\_DAY\\_GROUPLY | 日期自增（组模式） | 同上，但每组值里的这个日期不变，下条SQL才变\n| RAND_TIMESTAMP |  随机时间戳     | from,to  : 生成[from,to]这些天里的秒级时间戳，如 20180304,20180305\n| RAND\\_TIMESTAMP\\_MYSQL |  随机时间（Mysql格式）     | from,to  : 生成[from,to]这些天里的秒级时间，格式：2018-04-07 18:08:34\n| IGNORE |   不生成该列    |\n| CONST_STR |   常量    | 常量值\n| CONST\\_STR\\_LIST |   常量列表（组模式）    | 输入 a,b，每条SQL都输出 a,b，此时组数固定\n| RAND_STR |   随机串    | length : 英文串长度\n| RAND\\_STR\\_LIST |   随机串（指定列表）    | a,b,c : 该字段只出现a,b,c之一，可重复出现\n| INCR\\_STR\\_SUFFIX  |   前缀+自增数字    | 输入 小王，输出 小王1，小王2，用于人名等\n| RAND\\_PIC\\_URL |   图片地址    | width,height  : 图片的宽高\n\n## 添加新生成规则流程\n\n假设叫 rand_what\n\n1. 前端 `index.php`\n\n\t`getDefaultValueByMethod()` 增加该规则的默认值  \n\t`getHoverContent()` 增加该规则的hover文案  \n\t`fillTabelWithData()` 增加对应代码  \n\n2. 后端 `gensql.php`  \n\t\n\t`ValueGenerator` 类里新增 `randWhat()` 函数就行， 即下划线转驼峰\n\n\n\n## License\n\n**The MIT License**\n"
  },
  {
    "path": "README_EN.MD",
    "content": "# Online Sql Test Data Generator\n\n\nGenerate sql test data by Mysql table structure   \n\n\n\nInput:\n\n```\nCREATE TABLE `student` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `user_id` int(11) NOT NULL,\n  `name` varchar(20) NOT NULL ,\n  PRIMARY KEY (`id`),\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ;\n```\n\nOutput:\n\n```\nINSERT INTO `student (user_id,name) VALUES ('10001','John1'),('10002','John2');\n\nINSERT INTO `student (user_id,name) VALUES ('10003','John3'),('10004','John4');\n\n```\n\n\n\n**[Try It Now ](http://datamaker.online/index_en.php)**\n\n\n\n## Individuation\nYou can deploy this project on your server to set default value for frequently used fields, good for local area network like company\n\nEasy to deploy: It only require PHP (without any framework) and webserver \n\n### Config\n`/conf/common.ini` is common config  \n`/conf/local.ini` is personal config, good for same private value like test account.   For safe, you should run   \n`git update-index --assume-unchanged conf/local.ini`   \nto avoid traced to git \n\nSample：\n\n```\n[0]\nkey = index_day \nmethod = INCR_DAY\nvalue = 20180301\nway = match\n[1]\nkey = url \t\t\t\t\nmethod = RAND_PIC_LIST \nvalue = 300,400\t\t\t\nway = search\t\t\t\t\n```\n`key` : field name   \n`method` : generate  rule in the following table  \n`value` : default value  for front-end  \n`way` : `match`：match this config if the input key is equals to key ; `search` match this config if the input key contains this key;  the defalut value is `match` \n\n\n| Rule Method | Description |  Parameter |\n|----------|------|------|\n| INCR_INT | auto increment integer  | `from`  : increase from `from`\n| RAND_INT | random int  | `from,to`  : random in interval `[from,to]`\n| RAND_FLOAT | random float  | `from,to,round`  : Random in interval `[from,to]` , retains `round` digits after the decimal point\n| INCR_DAY | increment day    | `from`  : increase from `from` with format 20180304\n| INCR\\_DAY\\_GROUPLY | increment day (grouply same) | same as above，but the value is same in the same group \n| RAND_TIMESTAMP |  random timestamp | `from,to`  : timestamp in day `[from,to]`, input format is 20180304,20180305\n| RAND\\_TIMESTAMP\\_MYSQL |  random time (Mysql)     | `from,to`  : same as above，but the result format is 2018-04-07 18:08:34\n| IGNORE |   ignore this column    | \n| CONST_STR |   const value    | \n| CONST\\_STR\\_LIST |   const (group list) | `a,b`: every group in sql will output `a,b` \n| RAND_STR |   random string    | `length` : the length of string with alphabet\n| RAND\\_STR\\_LIST |   random string in list    | `a,b,c` : random pick one in `a,b,c` \n| INCR\\_STR\\_SUFFIX  |  Prefix + Number (Incr) | `test` : it will ouput test1,test2,test3 ...\n| RAND\\_PIC\\_URL |  picture url  | `width,height`  : the width and height of url\n\n## How to add new generator\n\nWhen you add a method call `rand_what`\n\n1. Frontend: `index.php`\n\n\t`getDefaultValueByMethod()` add the default value  \n\t`getHoverContent()` add the hover content  \n\t`fillTabelWithData()` modify it  \n\n2. Backend`gensql.php`  \n\t\n\tAdd the function call `randWhat()` In class `ValueGenerator` ( Camel-Case of `rand_what` )\n\n\n\n## License\n\n**The MIT License**\n"
  },
  {
    "path": "conf/common.ini",
    "content": "[0]\nkey = avatar\nmethod = RAND_PIC_URL\nvalue = 300,400\nway = search\n[1]\nkey = photo\nmethod = RAND_PIC_URL\nvalue = 300,400\nway = search\n[2]\nkey = picture\nmethod = RAND_PIC_URL\nvalue = 300,400\nway = search\n[3]\nkey = url\nmethod = RAND_PIC_URL\nvalue = 300,400\nway = search\n[4]\nkey = time\nmethod = RAND_TIMESTAMP\nvalue = 20180407,20180408\nway = search\n[5]\nkey = phone\nmethod = RAND_INT\nvalue = 15602000001,15602009999\nway = search\n[6]\nkey = insert_day\nmethod = INCR_DAY_GROUPLY\nvalue = 20180301\nway = match\n[7]\nkey = index_day\nmethod = INCR_DAY\nvalue = 20180301\nway = match\n[8]\nkey = status\nmethod = RAND_STR_LIST\nvalue = 0,1\nway = search\n[9]\nkey = data_day\nmethod = INCR_DAY_GROUPLY\nvalue = 20180301\nway = match\n[10]\nkey = is_\nmethod = RAND_INT\nvalue = 0,1\nway = search\n[11]\nkey = flag\nmethod = RAND_INT\nvalue = 0,1\nway = search\n[12]\nkey = score\nmethod = RAND_INT\nvalue = 0,100\nway = search\n"
  },
  {
    "path": "conf/local.ini",
    "content": ""
  },
  {
    "path": "gensql.php",
    "content": "<?php\n\nclass ValueGenerator{\n\n    public function __call($name, $arguments){\n        echo \"生成规则 $name 不存在\\n\";exit();\n    }\n\n    /**\n     * 日期自增，全局自增模式\n     * @param $input array\n     *      从 from值 开始自增 格式: 20180301\n     * @param $groupSize int 每组多少条合并成一个数组，也是该次函数返回的数组大小\n     * @param $sqlCounter int  当前是第几条SQL，用于在多条SQL之间仍然保持自增\n     * @return array\n     */\n    public function incrDay($input,$groupSize,$sqlCounter)\n    {\n        $begin = $input;\n        $offset = ($sqlCounter - 1) * $groupSize;\n        $ret = []; $count = 0;\n\n        $begin = date('Ymd',strtotime(\"{$begin} + {$offset} day\"));\n        while ($count < $groupSize) {\n            $ret []= date('Ymd',strtotime(\"{$begin} + {$count} day\"));\n            $count++;\n        }\n        return $ret;\n    }\n\n    /**\n     * 日期自增，组递增模式，每组的日期相同\n     * @param $input array\n     *      从 from值 开始自增 格式: 20180301\n     * @param $groupSize int 每组多少条合并成一个数组，也是该次函数返回的数组大小\n     * @param $sqlCounter int  当前是第几条SQL，用于在多条SQL之间仍然保持自增\n     * @return array\n     */\n    public function incrDayGrouply($input,$groupSize,$sqlCounter)\n    {\n        $begin = $input;\n        $offset = ($sqlCounter - 1) ;\n        $ret = []; $count = 0;\n\n        $begin = date('Ymd',strtotime(\"{$begin} + {$offset} day\"));\n        while ($count < $groupSize) {\n            $ret []= date('Ymd',strtotime(\"{$begin} + 0 day\"));\n            $count++;\n        }\n        return $ret;\n    }\n    /**\n     * 自增int\n     * @param $input array\n     *      从 from值 开始自增\n     * @param $groupSize int 每组多少条合并成一个数组，也是该次函数返回的数组大小\n     * @param $sqlCounter int  当前是第几条SQL，用于在多条SQL之间仍然保持自增\n     * @return array\n     */\n    public function incrInt($input,$groupSize,$sqlCounter)\n    {\n        $begin = intval($input);\n        $offset = ($sqlCounter - 1) * $groupSize;\n        $ret = []; $count = 0;\n        $begin += $offset;\n        while ($count < $groupSize) {\n            $ret []= $begin++;\n            $count++;\n        }\n        return $ret;\n    }\n    /**\n     * 随机int\n     * @param $input string\n     *      闭区间生成 [from,to]\n     * @param $groupSize int 每组多少条合并成一个数组，也是该次函数返回的数组大小\n     * @return array\n     */\n    public function randInt($input,$groupSize)\n    {\n        $exp = explode(',',$input);\n        $from = !empty($exp[0]) ? intval($exp[0]) : 1;\n        $to = !empty($exp[1]) ? intval($exp[1]) : 100;\n        $ret = [];\n        $count = 0;\n        while ($count++ < $groupSize) {\n            $ret []= mt_rand($from,$to);\n        }\n        return $ret;\n    }\n    /**\n     * 随机浮点\n     * @param $input string\n     *      闭区间生成 [from,to] 位数为 n，默认3位\n     * @param $groupSize int 每组多少条合并成一个数组，也是该次函数返回的数组大小\n     * @return array\n     */\n    public function randFloat($input,$groupSize)\n    {\n        $exp = explode(',',$input);\n        if(empty($exp) ){\n            $from = 0; $to = 1;$wei = 3;\n        }else{\n            $from =  intval($exp[0]); $to = intval($exp[1]); $wei = intval($exp[2]);\n            if(empty($exp[2])) $wei = 3;\n        }\n\n        $randNum = $from + mt_rand() / mt_getrandmax() * ($to - $from);\n        $ret = [];\n        $count = 0;\n        while ($count++ < $groupSize) {\n            $ret []= sprintf(\"%.{$wei}f\",$randNum);\n        }\n        return $ret;\n    }\n\n    /**\n     * 指定日期里的 随机时间戳\n     * @param $input string\n     *      [from,to] 中的时间戳\n     * @param $groupSize int 每组多少条合并成一个数组，也是该次函数返回的数组大小\n     * @return array\n     */\n    public function randTimestamp($input,$groupSize)\n    {\n        $exp = explode(',',$input);\n        $from = !empty($exp[0]) ? intval($exp[0]) : 20180401;\n        $to = !empty($exp[1]) ? intval($exp[1]) : 20380101;\n        $diff = strtotime($to) - strtotime($from);\n        $ret = [];\n        $count = 0;\n        while ($count++ < $groupSize) {\n            $ret []= strtotime($from) + mt_rand(0,$diff);\n        }\n        return $ret;\n    }\n\n    /**\n     * 指定日期里的 随机时间戳 (2018-04-08 11:16:00 格式)\n     * @param $input string\n     *      [from,to] 中的时间戳\n     * @param $groupSize int 每组多少条合并成一个数组，也是该次函数返回的数组大小\n     * @return array\n     */\n    public function randTimestampMysql($input,$groupSize)\n    {\n        $ret = $this->randTimestamp($input,$groupSize);\n        foreach ($ret as &$item){\n            $item =  date(\"Y-m-d H:i:s\",$item);\n        }\n        return $ret;\n    }\n\n    /**\n     * 常量\n     * @param $input\n     *          value 常量值\n     * @param $groupSize int 每组多少条合并成一个数组，也是该次函数返回的数组大小\n     * @return array\n     */\n    public function constStr($input,$groupSize){\n        return explode('$#$',str_repeat($input.'$#$',$groupSize));\n    }\n\n\n    /**\n     * 常量列表\n     * @param $input\n     *          value 常量值\n     * @param $groupSize int 每组多少条合并成一个数组，也是该次函数返回的数组大小\n     * @return array\n     */\n    public function constStrList($input,$groupSize){\n        $input = !empty($input) ? $input : '百度$#$阿里$#$腾讯';\n        return explode('$#$',$input);\n    }\n\n    /**\n     * 随机字符串（字符集：大小写字母）\n     * @param $input array\n     *          length 长度\n     * @param $groupSize int 每组多少条合并成一个数组，也是该次函数返回的数组大小\n     * @return array\n     */\n    public function randStr($length,$groupSize)\n    {\n        $ret = [];\n        $count = 0;\n        while ($count++ < $groupSize) {\n            $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';\n            $ranStr = '';\n            for ($i = 0; $i < $length; $i++) {\n                $ranStr .= $characters[rand(0, strlen($characters) - 1)];\n            }\n            $ret []= $ranStr;\n        }\n        return $ret;\n    }\n    /**\n     * 在字符串列表里 随机，全局可重复\n     * @param $input array\n     *          length 长度\n     * @param $groupSize int 每组多少条合并成一个数组，也是该次函数返回的数组大小\n     * @return array\n     */\n    public function randStrList($input,$groupSize)\n    {\n        $input = !empty($input) ? $input : 'ofo,mobike';\n        $value = explode(',',$input);\n        $ret = [];\n        $count = 0;\n        while ($count++ < $groupSize) {\n            $ranStr = $value[rand(0, count($value) - 1)];\n            $ret []= $ranStr;\n        }\n        return $ret;\n    }\n\n\n    /**\n     * 自增中文，根据 前缀+1 这种，如程序员1，程序员2\n     * @param $input array\n     *          pre_str 前缀\n     * @param $groupSize int 每组多少条合并成一个数组，也是该次函数返回的数组大小\n     * @return array\n     */\n    public function incrStrPrefix($input,$groupSize,$sqlCounter)\n    {\n        $pre = !empty($input) ? $input : '测试店';\n        $inputArr['from'] = 1;\n        $intArr = $this->incrInt($inputArr,$groupSize,$sqlCounter);\n        $ret = [];\n        foreach ($intArr as $intVal){\n            $str = $pre . $intVal;\n            $ret []= $str;\n        }\n        return $ret;\n    }\n\n    /**\n     * 随机图片，目前是 http://lorempixel.com/\n     * 也可以考虑用百度的 http://image.baidu.com/channel/listjson?pn=0&rn=30&tag1=%E7%BE%8E%E5%A5%B3&tag2=%E5%85%A8%E9%83%A8&ie=utf8\n     * @param $input\n     * @param $groupSize int 每组多少条合并成一个数组，也是该次函数返回的数组大小\n     * @return array\n     */\n    public function randPicUrl($input,$groupSize){\n        $exp = explode(',',$input);\n        $width = !empty($exp[0]) ? intval($exp[0]) : 300;\n        $height = !empty($exp[1]) ? intval($exp[1]) : 300;\n        $url = \"http://lorempixel.com/{$width}/{$height}/\";\n\n        return explode(',',str_repeat($url.',',$groupSize));\n    }\n}\n\nclass WorkHandler{\n\n    // /**\n    //  * 同步的数据导出，格式为.sql\n    // */\n    // public function syncExportStr2File($fileName, $content){\n    //     header_remove();\n    //     ini_set('memory_limit', '128M');\n    //     set_time_limit(1800);\n    //     header(\"Content-type:text/html;charset=utf-8\");\n    //     // header(\"Content-Transfer-Encoding: binary\");\n    //     // header(\"Content-Type: application/force-download;\");\n    //     header(\"Content-type: application/octet-stream\");\n    //     header(\"Cache-control: no-cache\");\n    //     header(\"Content-Transfer-Encoding: binary\");\n    //     header(\"Content-Disposition: attachment; filename=$fileName.sql\");\n    //     header(\"Expires: 0\");\n    //     header(\"Cache-control: private\");\n    //     header(\"Pragma: no-cache\");\n    //     header('Content-Length: ' . strlen($content));\n\n    //     // $content = iconv('UTF-8', 'GBK//IGNORE', $content);\n    //     echo $content.PHP_EOL;\n    //     exit();\n    // }\n\n    public function checkParams($input){\n        $input = json_decode($input,true);  //json转数组\n        if(empty($input) || empty($input['list'])){\n            echo \"参数不是JSON格式\";exit();\n        }\n        $input['count'] = intval($input['count']);\n        $input['group_size'] = intval($input['group_size']);\n        if( $input['count'] <= 0 || $input['group_size'] <= 0\n            || $input['count'] >= 5000 || $input['group_size'] >= 5000) {\n            echo \"条数、组数只能是 1 到 5000 以内\";exit();\n        }\n    }\n\n    public function execute($input){\n        $this->checkParams($input);\n\n        $input = json_decode($input,true);  //json转数组\n        $inputList = $input['list'];\n\n        //对于自增ID等忽略的类型，删除它并重排下标，不能用 array_splice ，删除多个时会乱\n        foreach ($inputList as $key => $item){\n            if($item['method'] == 'ignore'){\n                unset($inputList[$key]);\n            }\n        }\n        $inputList = array_values($inputList);  //从0开始 重建下标\n\n        $genCount = !empty($input['count']) ? $input['count'] : 5 ;\n        $tableName = !empty($input['table_name']) ? $input['table_name'] : 'test';\n        $groupSize = $input['group_size'];              //每个SQL有多少value组\n        $insertWay = !empty($input['insert_way']) ? $input['insert_way'] : 'INSERT INTO ';\n\n        $keyArr = array_column($inputList,'key');\n\n        $generator = new ValueGenerator();\n\n        $sql = '';\n        //insert的SQL条数\n        for($genI = 1; $genI <= $genCount; $genI++) {\n\n            $genResult = [];    //生成结果数组： key => 生成方法+下标 防止同样的方法覆盖数据，value => 按该方法生成的数据数组\n            foreach ($inputList as $itemIndex => $item){\n                $genService = $this->camelize($item['method']); //方法名转驼峰\n                $genResult [ $item['method'].$itemIndex ] = $generator->$genService($item['value'],$groupSize,$genI );\n            }\n            $valueStr = '';\n\n            //每个SQ有多少value组\n            for($cnt = 0; $cnt < $groupSize; $cnt++){\n\n                $valueStr .= '(';\n                //取每个字段的值，单引号引起来\n                foreach ($genResult as $result) {\n                    $valueStr .= \"'{$result[$cnt]}',\";\n                }\n                $valueStr = rtrim($valueStr,',');\n                $valueStr .= '),';\n            }\n            $valueStr = rtrim($valueStr,',');\n            $valueStr .= ';';\n            $sql .= $insertWay . \"{$tableName} (\" . implode(\",\",$keyArr) .\") VALUES $valueStr\";\n            $sql .= \"\\n\";\n            $sql .= \"\\n\";\n        }\n        //一直没成功，还怀疑是jq的post方法不是超链导致，最终用前端去做了\n        // $this->syncExportStr2File('datamake_'.date('YmdHis', time()).'sql' ,$sql);\n        echo $sql;\n    }\n\n    /**\n     * 下划线转驼峰\n     * @param $str\n     * @param string $separator\n     * @return string\n     */\n    private function camelize($str,$separator='_')\n    {\n        $str = $separator. str_replace($separator, \" \", strtolower($str));\n        return ltrim(str_replace(\" \", \"\", ucwords($str)), $separator );\n    }\n}\n\n\ndate_default_timezone_set(\"Asia/Shanghai\");\n\n$postData = file_get_contents('php://input');   //提交的是JSON，不能直接$post获取\nif(empty($postData)){\n    echo \"我是空白\";\n    exit();\n}\n//echo ($postData);exit();\n\n\n$handler = new WorkHandler();\n$handler->execute($postData);\nexit();\n\n/*\n$parser = new CreateSqlParser();\n$ret = $parser->execute($sql);\nif(0 == ($ret['error'])){\n\n    $input = json_encode($ret['data']);\n    $handler = new WorkHandler();\n    $handler->execute($input);\n} else{\n    echo json_encode($ret);exit();\n\n}\n\n*/"
  },
  {
    "path": "index.php",
    "content": "<html>\n<head>\n    <title>SQL测试数据生成</title>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\n    <script src=\"https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js\"></script>\n    \n    <link href=\"https://cdn.staticfile.net/bootstrap/3.3.7/css/bootstrap.min.css\" rel=\"stylesheet\">\n    <script src=\"https://cdn.staticfile.net/bootstrap/3.3.7/js/bootstrap.bundle.js\" ></script>\n\n\n    <link href=\"https://cdn.bootcss.com/bootstrap-table/1.12.1/bootstrap-table.min.css\" rel=\"stylesheet\">\n    <script src=\"https://cdn.bootcss.com/bootstrap-table/1.12.1/bootstrap-table.min.js\"></script>\n    <!--    下拉列表 1.3有BUG，选2.0版本 -->\n<!--    <link href=\"https://cdn.bootcss.com/bootstrap-select/2.0.0-beta1/css/bootstrap-select.min.css\" rel=\"stylesheet\" />-->\n<!--    <link href=\"https://cdn.bootcss.com/bootstrap-select/1.13.0-beta/css/bootstrap-select.min.css\" rel=\"stylesheet\">-->\n<!--    <script src=\"https://cdn.bootcss.com/bootstrap-select/1.13.0-beta/js/bootstrap-select.min.js\"></script>-->\n\n    <link href=\"https://cdn.bootcss.com/bootstrap-select/2.0.0-beta1/css/bootstrap-select.min.css\" rel=\"stylesheet\">\n    <script src=\"https://cdn.bootcss.com/bootstrap-select/2.0.0-beta1/js/bootstrap-select.min.js\"></script>\n    <link rel=\"shortcut icon\" href=\"./favicon.png\">\n\n\n</head>\n<style> </style>\n<!--<body style=\"background-image: url(http://img02.tooopen.com/images/20160601/tooopen_sy_163908772474.jpg);\">-->\n<body style=\" background-color: #f7f8fa;\">\n\n<div class=\"container\">\n    <div class=\"row clearfix\">\n        <div class=\"col-md-12 column\">\n            <nav class=\"navbar navbar-default\" role=\"navigation\">\n                <div class=\"navbar-header\" >\n                    <button type=\"button\" class=\"navbar-toggle\" data-toggle=\"collapse\" data-target=\"#bs-example-navbar-collapse-1\"> <span class=\"sr-only\">Toggle navigation</span><span class=\"icon-bar\"></span><span class=\"icon-bar\"></span><span class=\"icon-bar\"></span></button> <a class=\"navbar-brand\" href=\"#\">SQL测试数据生成</a>\n                </div>\n\n                <div class=\"collapse navbar-collapse\" id=\"bs-example-navbar-collapse-1\">\n                    <ul class=\"nav navbar-nav\">\n<!--                        <li class=\"active\">-->\n<!--                            <a href=\"#\">Link</a>-->\n<!--                        </li>-->\n                    </ul>\n                    <ul class=\"nav navbar-nav navbar-left\">\n                        <li class=\"dropdown\">\n                            <a href=\"#\" class=\"dropdown-toggle\" data-toggle=\"dropdown\">切换语言<strong class=\"caret\"></strong></a>\n                            <ul class=\"dropdown-menu\">\n                                <li>\n                                    <a  id=\"btn_to_chinese\"  href=\"./\" >中文</a>\n                                </li>\n                                <li>\n                                    <a id=\"btn_to_english\"  href=\"./index_en.php\">English</a>\n                                </li>\n                            </ul>\n                        </li>\n                    </ul>\n                    <ul class=\"nav navbar-nav navbar-right\">\n                        <li class=\"dropdown\">\n                            <a href=\"#\" class=\"dropdown-toggle\" data-toggle=\"dropdown\">保存配置<strong class=\"caret\"></strong></a>\n                            <ul class=\"dropdown-menu\">\n                                <li>\n                                    <a  id=\"btn_import\"  href=\"javascript:void(0);\" onclick=\"onImportBtnClick()\" >导入配置</a>\n                                </li>\n                                <li>\n                                    <a id=\"btn_export\"  href=\"#\">导出配置</a>\n                                </li>\n<!--                                <li class=\"divider\">-->\n<!--                                </li>-->\n<!--                                <li>-->\n<!--                                    <a href=\"#\">Separated link</a>-->\n<!--                                </li>-->\n                            </ul>\n                        </li>\n                    </ul>\n\n                    <ul class=\"nav navbar-nav navbar-right\">\n                        <li>\n                            <a href=\"https://github.com/wintercoder/datamaker\"  target=\"_blank\" >Github</a>\n                        </li>\n                    </ul>\n                </div>\n\n            </nav>\n\n\n            <form role=\"form\" >\n                <div class=\"form-group\">\n                    <label>SQL表结构</label>\n<!--                    <label style=\"font-size:12px\" class=\"label label-info\">SQL表结构</label>-->\n                    <span class=\"help-block\"> 从 show create table tablename 获得</span>\n                    <textarea id=\"sql_create\" class=\"form-control\" rows=\"3\"\nplaceholder='CREATE TABLE `im_feed_reply` (\n      `id` int(11) NOT NULL AUTO_INCREMENT,\n) ENGINE=InnoDB AUTO_INCREMENT=1;\n'></textarea>\n                </div>\n                <button onclick=\"return false\" id=\"btn_get_default\" class=\"btn btn-default  btn-info\">下一步</button>\n<!--导入导出 已放右上角-->\n<!--                <button onclick=\"return false\" id=\"btn_export\" class=\"btn btn-default  btn-info\">导出</button>-->\n<!--                <button  id=\"btn_import\" onclick=\"onImportBtnClick()\" class=\"btn btn-default  btn-info\">导入</button>-->\n<!--导入时用于传文件的隐藏按钮-->\n                <input type=\"file\" id=\"btn_import_hidden\" style=\"display:none\">\n\n                <!--return false 禁用点击跳转，由js控制，避免点击后刷新页面-->\n            </form>\n\n            <script type=\"text/javascript\" charset=\"utf-8\" >\n                // 导入： 点击按钮时 触发上传文件\n                function onImportBtnClick() {\n                    $(\"#btn_import_hidden\").click();\n                }\n                // 导入\n                $(\"#btn_import_hidden\").on(\"change\", function() {\n                    //读取文件内容，转成对象，填充表格\n                    let fileReader = new FileReader();\n                    fileReader.onload = function(e) {\n                        var fileContent = e.target.result;\n                        var jsonObj = JSON.parse(fileContent);\n                        console.log(\"上传内容：\");\n                        console.log(jsonObj);\n                            //这几行关键，否则导入后可能不展示\n                        fillTabelWithData(jsonObj,true);\n\n\n                        showTime = 400;\n                        $('#select_table').show(showTime);\n                        $('#btn_group_gen').show(showTime);\n                        $('#tv_group_result').show(showTime);\n\n                    };\n                    fileReader.readAsText(this.files[0],'utf8');\n                });\n            </script>\n\n            <!--字段 生成规则 表格-->\n                <table id=\"select_table\" hidden class=\"table table-striped\" >\n                    <thead >\n                    <tr >\n                        <th data-field=\"key\">列名</th>\n                        <th data-field=\"method\">生成规则</th>\n                        <th data-field=\"method_option\">参数</th>\n                    </tr>\n                    </thead>\n                    <tbody id =\"table_tr\">\n\n                    </tbody>\n                </table>\n\n            <!-- 配置 -->\n            <div id=\"btn_group_gen\" hidden>\n                <!-- 生成SQL的条数等 -->\n                <div class=\"col-md-2 column\" >\n                    <button id=\"btn_commit_sql\" type=\"submit\" class=\"btn btn-info btn-default\">生成SQL语句</button>\n                    <input type=\"checkbox\" name=\"cb_is_download\" value=\"is_download\" /> 下载\n                </div>\n                <div class=\"col-md-4 column\" >\n                    <form class=\"form-horizontal\" role=\"form\">\n                        <div class=\"form-group\">\n                            <div class=\"col-sm-3\">\n                                <input type=\"number\" class=\"form-control\" id=\"tv_count\" value=\"2\"/>\n                            </div>\n                            <label class=\"col-3 control-label\">条SQL</label>\n                        </div>\n                    </form>\n                </div>\n\n                <div class=\"col-md-4 column\">\n                    <form class=\"form-horizontal\" role=\"form\">\n                        <div class=\"form-group\">\n                            <div class=\"col-sm-3 \" >\n                                <input type=\"number\" class=\"form-control\" id=\"tv_group_size\" value=\"3\"/>\n                            </div>\n                            <label class=\"col-2 control-label\">行记录合并成一组</label>\n                        </div>\n                    </form>\n\n                </div>\n\n                <div class=\"col-md-2 column \">\n                    <form class=\"form-horizontal form-inline\" role=\"form\">\n                        <label class=\"form-label \">重复Key</label>\n                        <select class=\"form-horizontal form-control  col-xs-12 selectpicker\" id=\"selectpicker_insertway\"  >\n                            <option value=\"INSERT INTO \" >不处理</option>\n                            <option value=\"INSERT IGNORE \"  >跳过</option>\n                            <option value=\"REPLACE INTO \"  >替换</option>\n                        </select>\n                    </form>\n                </div>\n\n            </div>\n\n            <input type=\"text\" id=\"tv_tablename_hidden\" value=\"我是表名\" style=\"display:none\" >  <!--  存放表名的隐藏字段-->\n\n            <script type=\"text/javascript\">\n                function getList(){\n                    //默认隐藏各种框\n                    var showTime =  400;\n                    $('#select_table').show(showTime);\n                    $('#btn_group_gen').show(showTime);\n                    $('#tv_group_result').show(showTime);\n\n                    // jquery ajax 请求\n                    $.getJSON({\n                        type:'post',\n                        url :\"./sqlparse.php\",\n                        data:{\n                            sql: $('#sql_create').val() //输入的SQL表结构字符串\n                        },success:function(response,status){\n                            fillTabelWithData(response.data,false);\n                        },error:function(data,statsu){\n                            alert(\"网络获取默认值失败！\");\n                        }\n                    })\n                }\n                //根据数据填充表格，用于默认值和导入，参数样例： {\"list\":[{\"key\":\"id\",\"method\":\"ignore\",\"value\":\"\"},{\"key\":\"parent_id\",\"method\":\"rand_int\",\"value\":\"1,100\"}]}\n                function fillTabelWithData(responseData,isImport) {\n                    $('#tv_tablename_hidden').val(responseData.table_name);   //隐藏框 存放表名\n\n                    $('#table_tr').html('');\n                    var str = '';\n                    $.each(responseData.list,function(i,val){\n                        str = '';\n                        str = str + '<tr id=item_' + i +'>';\n                        str = str + '<td> '+ '<input type=\"text\" class=\"form-control\" id=\"name_' + i +'\" value='+val.key +'></td>';\n\n                        //无法直接传对象当参数 ，也不能直接json字符串，否则跟onchange的双引号乱套，需要转码\n                        var jsonVal = JSON.stringify(val);\n                        jsonVal = jsonVal.replace(/\"/g,'&quot;');\n\n                        var selectStr = `\n                                    <select class=\"form-control selectpicker\" id=\"method_selectpicker_`+i+`\" onchange=\"selectOnChange(this,`+ jsonVal +`)\" >\n                                        <optgroup label=\"数字\">\n                                            <option value=\"incr_int\" >自增</option>\n                                            <option value=\"rand_int\"  >随机整数</option>\n                                            <option value=\"rand_float\"  >随机浮点</option>\n                                            <option value=\"incr_day\" >自增日期</option>\n                                            <option value=\"incr_day_grouply\" >自增日期（组自增）</option>\n                                            <option value=\"rand_timestamp\" >随机时间戳</option>\n                                            <option value=\"rand_timestamp_mysql\" >随机时间（Mysql格式）</option>\n                                            <option value=\"ignore\" >忽略该列</option>\n                                        </optgroup>\n                                        <optgroup label=\"字符串\">\n                                            <option value=\"const_str\" >常量</option>\n                                            <option value=\"const_str_list\" >常量列表（组模式）</option>\n                                            <option value=\"rand_str\">随机字符串</option>\n                                            <option value=\"rand_str_list\">随机字符串（指定列表）</option>\n                                            <option value=\"incr_str_prefix\">前缀 + 数字自增</option>\n                                            <option value=\"rand_pic_url\">图片URL</option>\n                                        </optgroup>\n                                        </select>\n                                `;\n\n                        str = str + '<td> '+selectStr+'  </td>';\n                        str = str + '<td>' + '<input type=\"text\"  id=\"tv_input_'+i+'\" class=\"form-control\" data-html=\"true\" data-trigger=\"hover focus\" data-toggle=\"tooltip\" data-placement=\"top\" data-content='+ getHoverContent(jsonVal) +'   value=\"\" >' +'  </td>';\n                        str = str + '</tr>';\n\n                        $('#table_tr').append(str);\n\n                        // 根据返回的method设置默认生成方法\n                        $('#method_selectpicker_'+i).selectpicker();\n                        $('#method_selectpicker_'+i).selectpicker('val',val.method);\n                        $('#method_selectpicker_'+i).selectpicker('refresh');\n                        $('#method_selectpicker_'+i).trigger('change');  //手动触发change事件\n\n\n\n                        //设置接口返回的默认值 和 对应hover\n                        if(isImport){\n                            $(\"#tv_input_\"+i).val( val.value );  //导入时才修改\n                        }\n                        $(\"#tv_input_\"+i).attr('data-content',getHoverContent( val.method ));\n                    });\n\n                    //开启 hover提示功能\n                    $(function () { $(\"[data-toggle='tooltip']\").popover(); });\n\n                }\n\n                //对生成规则的解释，鼠标hover在文本框上时显示 http://wiki.jikexueyuan.com/project/bootstrap4/components/tooltips/#section-1\n                function getHoverContent(method) {\n//                    jsonVal = jsonVal.replace(/\\&quot;/g,'\"');     var jsonObj = JSON.parse(jsonVal);    method = jsonObj.method;\n\n                    switch (method) {\n                        case 'incr_int':\n                            return '输入：3 </br> 输出：3，4，5 ...';\n                        case 'rand_int':\n                            return \"输入：1,100</br> 输出：在闭区间 [1,100] 中随机，可重复\";\n                        case 'rand_float':\n                            return \"输入：1,100,3</br> 输出：在闭区间 [1,100] 中随机浮点数，保留3位小数\";\n                        case 'incr_day':\n                            return \"输入：20180401</br> 输出：从 20180401 起日期自增，自动跨月\";\n                        case 'incr_day_grouply':\n                            return \"输入：20180401，2条SQL，3值合一组</br> 输出： <br />20180401,20180401,20180401<br />20180402,20180402,20180402<br /> 适合与常量列表组成叉积模式，生成每个子店铺每天各一个值\";\n                        case 'rand_timestamp':\n                            return \"输入：20180401,20180402</br> 输出：从 20180401 到 20180402 这两天里的随机时间戳\";\n                        case 'rand_timestamp_mysql':\n                            return \"输入：20180401,20180402</br> 输出：这两天里的随机时间  <br /> 格式： 2018-04-01 11:16:16\";\n                        case 'ignore':\n                            return \"不解释\";\n                        case 'const_str':\n                            return \"不解释\";\n                        case 'const_str_list':\n                            return \"输入：百度$#$阿里$#$腾讯；配置2条SQL，3值合一组</br> 输出： <br />百度$#$阿里$#$腾讯<br />百度$#$阿里$#$腾讯<br /> <br /> 按照列表中的元素生成，每个元素按顺序出现，输入元素间用特殊符（$#$）分隔。</br> <br />以前是用逗号，但怕内容本身包含逗号</br> <br />  有多个常量列表则并行出现，输出SQL以 min(元素个数) 为一组\";\n                        case 'rand_str':\n                            return '输入：长度</br> 输出：随机字符串，字符集：字母表';\n                        case 'rand_str_list':\n                            return '输入：摩拜,ofo</br> 输出：列表里随机选，可重复';\n                        case 'incr_str_prefix':\n                            return '输入：小王</br> 输出：小王1，小王2';\n                        case 'rand_pic_url':\n                            return '输入：300,400 </br> 输出：宽300，高400的图片地址';\n                    }\n                }\n\n                //选择 生成规则 时 设置默认值，打开网站第一次获取的会被网络请求的返回值覆盖，其他情况走这个逻辑\n                function selectOnChange(obj,params) {\n\n                    var method = obj.options[obj.selectedIndex].value;\n                    var parent = obj.parentNode.parentNode;\n                    var brother = obj.parentNode.nextSibling;\n                    var optionTxt = brother.children[0];    //参数文本框\n                    //修改hints\n                    optionTxt.placeholder = getDefaultValueByMethod(method);\n\n                    //更换 生成规则 后 更新 hover\n                    var currentId = obj.id.split('_')[2];   //当前点击第几行\n                    $(\"#tv_input_\"+currentId).attr('data-content',getHoverContent( method ));\n\n                    //每次 修改 规则 时 看是否有 常量列表，有就限制组数文本框输入\n                    $('#tv_group_size').attr(\"disabled\",false);\n                    $(\".selectpicker\").each(function () {\n                        var pickerVal = $(this).val();\n                        if(pickerVal === 'const_str_list'){\n                            $('#tv_group_size').attr(\"disabled\",true);\n                            $('#tv_group_size').val(\"-1\");\n                        }\n                    });\n                }\n\n                $(document).ready(function(){\n                    //TODO 测试时使用，自动填充表结构\n//                    getList();\n                });\n                //点击下一步后 根据sql 拉字段信息\n                $('#btn_get_default').click(function(){\n                    getList();\n                });\n\n                //导出配置，遍历表格拿数据，组成跟解析SQL后的json那样\n                $('#btn_export').click(function() {\n                    var fieldList = [];\n                    $(\"#table_tr\").find(\"tr\").each(function(){\n                        var tdArr = $(this).children();\n                        var key = tdArr.eq(0).find('input').val();     //字段名\n                        var method = tdArr.eq(1).find('select').val(); //method\n                        var option = tdArr.eq(2).find('input').val();  //参数\n\n                        var item = new Object();\n                        item['key'] = key;\n                        item['method'] = method;\n                        item['value'] = option;\n                        fieldList.push(item);\n                    });\n                    var exportData =  new Object();\n                    exportData['list'] = fieldList;\n                    exportData['table_name'] = $('#tv_tablename_hidden').val();\n                    exportData = JSON.stringify(exportData);\n                    createAndDownloadFile(\"config_export.txt\",exportData);\n                });\n\n                //提交表格，生成SQL\n                $(function(){\n                    $('#btn_commit_sql').click(function(){\n                        var params = new Object();\n                        var fieldList = [];\n                        var constListSize = 999999999;  //常量列表的元素个数，用于固定一组SQL有多少value\n\n                        //遍历tr拿表格各元素\n                        $(\"#table_tr\").find(\"tr\").each(function(){\n                            var tdArr = $(this).children();\n\n                            var key = tdArr.eq(0).find('input').val();     //字段名\n                            var method = tdArr.eq(1).find('select').val(); //method\n                            var inputValue = tdArr.eq(2).find('input').val();  //参数\n\n\n                            //什么都不填的时候，调接口的默认值\n                            if(inputValue == null || inputValue == \"\" || inputValue == 'undefined'){\n                                inputValue = getDefaultValueByMethod(method);\n                            }\n\n                            var item = new Object();\n                            item['key'] = key;\n                            item['method'] = method;\n                            item['value'] = inputValue;\n                            if(method == 'const_str_list'){\n                                constListSize = Math.min(constListSize,inputValue.split('$#$').length);\n                            }\n                            fieldList.push(item);\n                        });\n                        params['insert_way'] = $('#selectpicker_insertway').val();\n                        params['list'] = fieldList;\n                        params['group_size'] = $('#tv_group_size').val();\n                        params['group_size'] = params['group_size'] > 500 ? 500 : params['group_size'] ;\n                        if(constListSize !== 999999999){        //代表存在常量列表，将值固定为列表个数\n                            params['group_size'] = constListSize;\n                            $('#tv_group_size').val(constListSize);\n                        }\n\n                        params['count'] = $('#tv_count').val();\n                        if(params['count'] > 5000){\n                            $('#sql_result').val('条数超过5000，怕小宽带撑不住，请自行搭建服务');\n                            return;\n                        }\n\n                        var tablename = $('#tv_tablename_hidden').val();\n                        params['table_name'] = (tablename == '' || tablename == 'undefined') ? 'table_name' : tablename;\n\n\n                        //发JSON，生成SQL\n                        params = JSON.stringify(params);\n                        $.post('gensql.php',params,function(data){\n                          if( $('input[name=\"cb_is_download\"]:checked').length == 1){   //下载框被选中\n                                $('#sql_result').val('已触发浏览器下载');\n                                createAndDownloadFile('datamaker_' + params['table_name'] + '.sql',data);\n                          }else{\n                                //正常展示\n                                $('#sql_result').val(data);\n                          }\n                        });\n\n                    });\n                });\n\n\n                function getDefaultValueByMethod(methodParams) {\n                    switch (methodParams) {\n                        case 'incr_int':\n                            return 1;\n                        case 'rand_int':\n                            return '1,100';\n                        case 'rand_float':\n                            return '1,100,2';\n                        case 'incr_day':\n                        case 'incr_day_grouply':\n                            return 20180401;\n                        case 'ignore':\n                            return '不生成该列，适合自增列';\n                        case 'rand_timestamp':\n                        case 'rand_timestamp_mysql':\n                            return '20180401,20180404';\n                        case 'const_str':\n                            return 'Goolge';\n                        case 'const_str_list':\n                            return '百度$#$阿里$#$腾讯';\n                        case 'rand_str':\n                            return '5';\n                        case 'rand_str_list':\n                            return '摩拜,ofo,小蓝,悟空,7号电单车';\n                        case 'incr_str_prefix':\n                            // var incrStrPre = ['老王','射击狮','测试店','产品经理','程序员','码农','攻城狮','SB'];\n                            // return incrStrPre[ Math.floor((incrStrPre.length-1) * Math.random()) ] ;\n                            return 'PM-';\n                        case 'rand_pic_url':\n                            return '300,400';\n                    }\n                }\n\n\n                /**\n                 * 工具函数： 创建并下载文件，用于导出\n                 * @param  {String} fileName 文件名\n                 * @param  {String} content  文件内容\n                 */\n                function createAndDownloadFile(fileName, content) {\n                    var aTag = document.createElement('a');\n                    var blob = new Blob([content]);\n                    aTag.download = fileName;\n                    aTag.href = URL.createObjectURL(blob);\n                    aTag.click();\n                    URL.revokeObjectURL(blob);\n                }\n            </script>\n\n            <br/><br/><br/><br/>\n                <!-- SQL结果 -->\n                <div hidden id =\"tv_group_result\" class=\"col-md-12  column\">\n                    <textarea  id=\"sql_result\" class=\"form-control\" placeholder=\"我是存放 SQL 结果的地方\" rows=\"8\" ></textarea>\n                </div>\n\n        </div>\n    </div>\n</div>\n\n <div style=\"height: 40px;box-sizing: border-box; position: relative;bottom: 0;  width: 100%; margin-top:20px\">\n        <div style=\"text-align:center;width:100%;height:50;\">\n            <p class=\"copyright text-muted small\">Copyright &copy; 小光 2017.  <a href=\"http://beian.miit.gov.cn/\">你的备案号</a></p>\n        </div>\n    </div>\n\n\n\n</body>\n\n</html>\n\n"
  },
  {
    "path": "index_en.php",
    "content": "<html>\n<head>\n    <title>SQL Data Generator</title>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>\n    <link href=\"https://cdn.staticfile.net/bootstrap/3.3.7/css/bootstrap.min.css\" rel=\"stylesheet\">\n    <script src=\"https://cdn.staticfile.net/bootstrap/3.3.7/js/bootstrap.bundle.js\" ></script>\n\n    <link href=\"https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.12.1/bootstrap-table.min.css\" rel=\"stylesheet\">\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.12.1/bootstrap-table.min.js\"></script>\n    <!--    下拉列表 1.3有BUG，选2.0版本 -->\n<!--    <link href=\"https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/2.0.0-beta1/css/bootstrap-select.min.css\" rel=\"stylesheet\" />-->\n<!--    <link href=\"https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.0-beta/css/bootstrap-select.min.css\" rel=\"stylesheet\">-->\n<!--    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.13.0-beta/js/bootstrap-select.min.js\"></script>-->\n\n    <link href=\"https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/2.0.0-beta1/css/bootstrap-select.min.css\" rel=\"stylesheet\">\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/2.0.0-beta1/js/bootstrap-select.min.js\"></script>\n    <link rel=\"shortcut icon\" href=\"./favicon.png\">\n\n\n</head>\n<style> </style>\n<!--<body style=\"background-image: url(http://img02.tooopen.com/images/20160601/tooopen_sy_163908772474.jpg);\">-->\n<body style=\" background-color: #f7f8fa;\">\n\n<div class=\"container\">\n    <div class=\"row clearfix\">\n        <div class=\"col-md-12 column\">\n            <nav class=\"navbar navbar-default\" role=\"navigation\">\n                <div class=\"navbar-header\" >\n                    <button type=\"button\" class=\"navbar-toggle\" data-toggle=\"collapse\" data-target=\"#bs-example-navbar-collapse-1\"> <span class=\"sr-only\">Toggle navigation</span><span class=\"icon-bar\"></span><span class=\"icon-bar\"></span><span class=\"icon-bar\"></span></button> <a class=\"navbar-brand\" href=\"#\">SQL Data Generator</a>\n                </div>\n\n                <div class=\"collapse navbar-collapse\" id=\"bs-example-navbar-collapse-1\">\n                    <ul class=\"nav navbar-nav\">\n<!--                        <li class=\"active\">-->\n<!--                            <a href=\"#\">Link</a>-->\n<!--                        </li>-->\n                    </ul>\n                    <ul class=\"nav navbar-nav navbar-left\">\n                        <li class=\"dropdown\">\n                            <a href=\"#\" class=\"dropdown-toggle\" data-toggle=\"dropdown\">Language<strong class=\"caret\"></strong></a>\n                            <ul class=\"dropdown-menu\">\n                                <li>\n                                    <a  id=\"btn_to_chinese\"  href=\"./\" >中文</a>\n                                </li>\n                                <li>\n                                    <a id=\"btn_to_english\"  href=\"./index_en.php\">English</a>\n                                </li>\n                            </ul>\n                        </li>\n                    </ul>\n\n                    <ul class=\"nav navbar-nav navbar-right\">\n                        <li class=\"dropdown\">\n                            <a href=\"#\" class=\"dropdown-toggle\" data-toggle=\"dropdown\">Save Config<strong class=\"caret\"></strong></a>\n                            <ul class=\"dropdown-menu\">\n                                <li>\n                                    <a  id=\"btn_import\"  href=\"javascript:void(0);\" onclick=\"onImportBtnClick()\" >Import</a>\n                                </li>\n                                <li>\n                                    <a id=\"btn_export\"  href=\"#\">Export</a>\n                                </li>\n<!--                                <li class=\"divider\">-->\n<!--                                </li>-->\n<!--                                <li>-->\n<!--                                    <a href=\"#\">Separated link</a>-->\n<!--                                </li>-->\n                            </ul>\n                        </li>\n                    </ul>\n\n                    <ul class=\"nav navbar-nav navbar-right\">\n                        <li>\n                            <a href=\"https://github.com/wintercoder/datamaker\"  target=\"_blank\" >Github</a>\n                        </li>\n                    </ul>\n                </div>\n\n            </nav>\n\n\n            <form role=\"form\" >\n                <div class=\"form-group\">\n                    <label>SQL TABLE STRUCTURE</label>\n<!--                    <label style=\"font-size:12px\" class=\"label label-info\">SQL表结构</label>-->\n                    <span class=\"help-block\">show create table tablename </span>\n                    <textarea id=\"sql_create\" class=\"form-control\" rows=\"3\"\nplaceholder='CREATE TABLE `im_feed_reply` (\n      `id` int(11) NOT NULL AUTO_INCREMENT,\n) ENGINE=InnoDB AUTO_INCREMENT=1;\n'></textarea>\n                </div>\n                <button onclick=\"return false\" id=\"btn_get_default\" class=\"btn btn-default  btn-info\">Next</button>\n<!--导入导出 已放右上角-->\n<!--                <button onclick=\"return false\" id=\"btn_export\" class=\"btn btn-default  btn-info\">导出</button>-->\n<!--                <button  id=\"btn_import\" onclick=\"onImportBtnClick()\" class=\"btn btn-default  btn-info\">导入</button>-->\n<!--导入时用于传文件的隐藏按钮-->\n                <input type=\"file\" id=\"btn_import_hidden\" style=\"display:none\">\n\n                <!--return false 禁用点击跳转，由js控制，避免点击后刷新页面-->\n            </form>\n\n            <script type=\"text/javascript\" charset=\"utf-8\" >\n                // 导入： 点击按钮时 触发上传文件\n                function onImportBtnClick() {\n                    $(\"#btn_import_hidden\").click();\n                }\n                // 导入\n                $(\"#btn_import_hidden\").on(\"change\", function() {\n                    //读取文件内容，转成对象，填充表格\n                    let fileReader = new FileReader();\n                    fileReader.onload = function(e) {\n                        var fileContent = e.target.result;\n                        var jsonObj = JSON.parse(fileContent);\n                        console.log(\"上传内容：\");\n                        console.log(jsonObj);\n                        fillTabelWithData(jsonObj,true);\n\n                        //viewable\n                        showTime = 400;\n                        $('#select_table').show(showTime);\n                        $('#btn_group_gen').show(showTime);\n                        $('#tv_group_result').show(showTime);\n                    };\n                    fileReader.readAsText(this.files[0],'utf8');\n                });\n            </script>\n\n            <!--字段 生成规则 表格-->\n                <table id=\"select_table\" hidden class=\"table table-striped\" >\n                    <thead >\n                    <tr >\n                        <th data-field=\"key\">Column</th>\n                        <th data-field=\"method\">Rule</th>\n                        <th data-field=\"method_option\">Parameter</th>\n                    </tr>\n                    </thead>\n                    <tbody id =\"table_tr\">\n\n                    </tbody>\n                </table>\n\n            <div id=\"btn_group_gen\" hidden>\n                <!-- 生成SQL的条数等 -->\n                <div class=\"col-md-6 column\" >\n                    <button id=\"btn_commit_sql\" type=\"submit\" class=\"btn btn-info btn-default col-md3 \">Generate</button>\n                    <input type=\"checkbox\" name=\"cb_is_download\" value=\"is_download\" class=\" col-md3 \"/> Download\n                </div>\n                <div class=\"col-md-6 column\" >\n                    <form class=\"form-horizontal\" role=\"form\">\n                        <div class=\"form-group\">\n                            <div class=\"col-md-3\">\n                                <input type=\"number\" class=\"form-control\" id=\"tv_count\" value=\"2\"/>\n                            </div>\n                            <label class=\"col-3 control-label\">Statement</label>\n                        </div>\n                    </form>\n                </div>\n\n                <div class=\"col-md-6 column\">\n                    <form class=\"form-horizontal\" role=\"form\">\n                        <div class=\"form-group\">\n                            <label class=\"col-1 control-label\">Records Merge Into One Statement</label>\n                            <div class=\"col-sm-3\">\n                                <input type=\"number\" class=\"form-control\" id=\"tv_group_size\" value=\"3\"/>\n                            </div>\n                        </div>\n                    </form>\n                </div>\n\n                <div class=\"col-md-6 column \">\n                    <form class=\"form-horizontal form-inline\" role=\"form\">\n                        <label class=\"form-label \">On Duplicate</label>\n                        <select class=\"form-horizontal form-control  col-xs-12 selectpicker\" id=\"selectpicker_insertway\"  >\n                            <option value=\"INSERT INTO \">Do Nothing</option>\n                            <option value=\"INSERT IGNORE \">Ignore</option>\n                            <option value=\"REPLACE INTO \">Replace</option>\n                        </select>\n                    </form>\n                </div>\n\n            </div>\n\n            <input type=\"text\" id=\"tv_tablename_hidden\" value=\"我是表名\" style=\"display:none\" >  <!--  存放表名的隐藏字段-->\n\n            <script type=\"text/javascript\">\n                function getList(){\n                    //默认隐藏各种框\n                    var showTime =  400;\n                    $('#select_table').show(showTime);\n                    $('#btn_group_gen').show(showTime);\n                    $('#tv_group_result').show(showTime);\n\n                    // jquery ajax 请求\n                    $.getJSON({\n                        type:'post',\n                        url :\"./sqlparse.php\",\n                        data:{\n                            sql: $('#sql_create').val() //输入的SQL表结构字符串\n                        },success:function(response,status){\n                            fillTabelWithData(response.data,false);\n                        },error:function(data,statsu){\n                            alert(\"Network fail！\");\n                        }\n                    })\n                }\n                //根据数据填充表格，用于默认值和导入，参数样例： {\"list\":[{\"key\":\"id\",\"method\":\"ignore\",\"value\":\"\"},{\"key\":\"parent_id\",\"method\":\"rand_int\",\"value\":\"1,100\"}]}\n                function fillTabelWithData(responseData,isImport) {\n                    $('#tv_tablename_hidden').val(responseData.table_name);   //隐藏框 存放表名\n\n                    $('#table_tr').html('');\n                    var str = '';\n                    $.each(responseData.list,function(i,val){\n                        str = '';\n                        str = str + '<tr id=item_' + i +'>';\n                        str = str + '<td> '+ '<input type=\"text\" class=\"form-control\" id=\"name_' + i +'\" value='+val.key +'></td>';\n\n                        //无法直接传对象当参数 ，也不能直接json字符串，否则跟onchange的双引号乱套，需要转码\n                        var jsonVal = JSON.stringify(val);\n                        jsonVal = jsonVal.replace(/\"/g,'&quot;');\n\n                        var selectStr = `\n                                    <select class=\"form-control selectpicker\" id=\"method_selectpicker_`+i+`\" onchange=\"selectOnChange(this,`+ jsonVal +`)\" >\n                                        <optgroup label=\"Number\">\n                                            <option value=\"incr_int\" >Auto Increment</option>\n                                            <option value=\"rand_int\"  >Random Int</option>\n                                            <option value=\"rand_float\"  >Random Float</option>\n                                            <option value=\"incr_day\" >Auto Incr DAY</option>\n                                            <option value=\"incr_day_grouply\" >Auto Incr DAY (Group)</option>\n                                            <option value=\"rand_timestamp\" >Random Timestamp</option>\n                                            <option value=\"rand_timestamp_mysql\" >Random Time (Mysql) </option>\n                                            <option value=\"ignore\" >Ignore </option>\n                                        </optgroup>\n                                        <optgroup label=\"String\">\n                                            <option value=\"const_str\" >Const</option>\n                                            <option value=\"const_str_list\" >Const List (Group)</option>\n                                            <option value=\"rand_str\">Random String</option>\n                                            <option value=\"rand_str_list\">Random String In List</option>\n                                            <option value=\"incr_str_prefix\">Prefix + Number (Incr)</option>\n                                            <option value=\"rand_pic_url\">Picture URL</option>\n                                        </optgroup>\n                                        </select>\n                                `;\n\n                        str = str + '<td> '+selectStr+'  </td>';\n                        str = str + '<td>' + '<input type=\"text\"  id=\"tv_input_'+i+'\" class=\"form-control\" data-html=\"true\" data-trigger=\"hover focus\" data-toggle=\"tooltip\" data-placement=\"top\" data-content='+ getHoverContent(jsonVal) +'   value=\"\" >' +'  </td>';\n                        str = str + '</tr>';\n\n                        $('#table_tr').append(str);\n\n                        // 根据返回的method设置默认生成方法\n                        $('#method_selectpicker_'+i).selectpicker();\n                        $('#method_selectpicker_'+i).selectpicker('val',val.method);\n                        $('#method_selectpicker_'+i).selectpicker('refresh');\n                        $('#method_selectpicker_'+i).trigger('change');  //手动触发change事件\n\n                        //设置接口返回的默认值 和 对应hover\n                        if(isImport){\n                            $(\"#tv_input_\"+i).val( val.value );  //导入时才修改\n                        }\n                        $(\"#tv_input_\"+i).attr('data-content',getHoverContent( val.method ));\n                    });\n\n                    //开启 hover提示功能\n                    $(function () { $(\"[data-toggle='tooltip']\").popover(); });\n\n                }\n\n                //对生成规则的解释，鼠标hover在文本框上时显示 http://wiki.jikexueyuan.com/project/bootstrap4/components/tooltips/#section-1\n                function getHoverContent(method) {\n//                    jsonVal = jsonVal.replace(/\\&quot;/g,'\"');     var jsonObj = JSON.parse(jsonVal);    method = jsonObj.method;\n\n                    switch (method) {\n                        case 'incr_int':\n                            return 'Input: 3 </br> Output: 3,4,5 ...';\n                        case 'rand_int':\n                            return \"Input: 1,100</br> Output: Random in interval [1,100] \";\n                        case 'rand_float':\n                            return \"Input: 1,100,3</br> Output: Random in interval [1,100], retains 3 digits after the decimal point\";\n                        case 'incr_day':\n                            return \"Input: 20180429</br> Output:  20180429,20180430,20180501 ...\";\n                        case 'incr_day_grouply':\n                            return \"Input: </br>20180401;</br>   SQL line:2; </br>   Group with 3 value</br> Output:  <br />20180401,20180401,20180401<br />20180402,20180402,20180402<br />   Good for Every Shop/day Mode\";\n                        case 'rand_timestamp':\n                            return \"Input: 20180401,20180402</br> Output: Timestamp between 20180401 and 20180402\";\n                        case 'rand_timestamp_mysql':\n                            return \"Input: 20180401,20180402</br> Output: Time string with format:  2018-04-01 11:16:16\";\n                        case 'ignore':\n                            return \"Do not generate this column\";\n                        case 'const_str':\n                            return \"Const\";\n                        case 'const_str_list':\n                            return \"Input: </br>Google$#$Facebook$#$Microsoft; </br>Config: SQL line:2;</br>  Group with 3 value</br> Output:  <br />Google$#$Facebook$#$Microsoft<br />Google$#$Facebook$#$Microsoft\";\n                        case 'rand_str':\n                            return 'Input: Length of string</br> Output: Random String with alphabet';\n                        case 'rand_str_list':\n                            return 'Input: Hello,World</br> Output: Hello or World, random';\n                        case 'incr_str_prefix':\n                            return 'Input: William</br> Output: William1,William2';\n                        case 'rand_pic_url':\n                            return 'Input: 300,400 </br> Output: URL with widht:300, height:400';\n                    }\n                }\n\n                //选择 生成规则 时 设置默认值，打开网站第一次获取的会被网络请求的返回值覆盖，其他情况走这个逻辑\n                function selectOnChange(obj,params) {\n\n                    var method = obj.options[obj.selectedIndex].value;\n                    var parent = obj.parentNode.parentNode;\n                    var brother = obj.parentNode.nextSibling;\n                    var optionTxt = brother.children[0];    //参数文本框\n                    var incrStrPre = ['William','John','TestShop','Product','SB'];\n\n                    //修改hints\n                    optionTxt.placeholder = getDefaultValueByMethod(method);\n\n                    //更换 生成规则 后 更新 hover\n                    var currentId = obj.id.split('_')[2];   //当前点击第几行\n                    $(\"#tv_input_\"+currentId).attr('data-content',getHoverContent( method ));\n\n                    //每次 修改 规则 时 看是否有 常量列表，有就限制组数文本框输入\n                    $('#tv_group_size').attr(\"disabled\",false);\n                    $(\".selectpicker\").each(function () {\n                        var pickerVal = $(this).val();\n                        if(pickerVal === 'const_str_list'){\n                            $('#tv_group_size').attr(\"disabled\",true);\n                            $('#tv_group_size').val(\"-1\");\n                        }\n                    });\n                }\n\n                $(document).ready(function(){\n                    //TODO 测试时使用，自动填充表结构\n//                    getList();\n                });\n                //点击下一步后 根据sql 拉字段信息\n                $('#btn_get_default').click(function(){\n                    getList();\n                });\n\n                //导出配置，遍历表格拿数据，组成跟解析SQL后的json那样\n                $('#btn_export').click(function() {\n                    var fieldList = [];\n                    $(\"#table_tr\").find(\"tr\").each(function(){\n                        var tdArr = $(this).children();\n                        var key = tdArr.eq(0).find('input').val();     //字段名\n                        var method = tdArr.eq(1).find('select').val(); //method\n                        var option = tdArr.eq(2).find('input').val();  //参数\n\n                        var item = new Object();\n                        item['key'] = key;\n                        item['method'] = method;\n                        item['value'] = option;\n                        fieldList.push(item);\n                    });\n                    var exportData =  new Object();\n                    exportData['list'] = fieldList;\n                    exportData['table_name'] = $('#tv_tablename_hidden').val();\n                    exportData = JSON.stringify(exportData);\n                    createAndDownloadFile(\"config_export.txt\",exportData);\n                });\n\n                //提交表格，生成SQL\n                $(function(){\n                    $('#btn_commit_sql').click(function(){\n                        var params = new Object();\n                        var fieldList = [];\n                        var constListSize = 999999999;  //常量列表的元素个数，用于固定一组SQL有多少value\n\n                        //遍历tr拿表格各元素\n                        $(\"#table_tr\").find(\"tr\").each(function(){\n                            var tdArr = $(this).children();\n\n                            var key = tdArr.eq(0).find('input').val();     //字段名\n                            var method = tdArr.eq(1).find('select').val(); //method\n                            var inputValue = tdArr.eq(2).find('input').val();  //参数\n\n\n                            //什么都不填的时候，调接口的默认值\n                            if(inputValue == null || inputValue == \"\" || inputValue == 'undefined'){\n                                inputValue = getDefaultValueByMethod(method);\n                            }\n\n                            var item = new Object();\n                            item['key'] = key;\n                            item['method'] = method;\n                            item['value'] = inputValue;\n                            if(method == 'const_str_list'){\n                                constListSize = Math.min(constListSize,inputValue.split('$#$').length);\n                            }\n                            fieldList.push(item);\n                        });\n\n                        params['insert_way'] = $('#selectpicker_insertway').val();\n                        params['list'] = fieldList;\n                        params['group_size'] = $('#tv_group_size').val();\n                        params['group_size'] = params['group_size'] > 500 ? 500 : params['group_size'] ;\n                        if(constListSize !== 999999999){        //代表存在常量列表，将值固定为列表个数\n                            params['group_size'] = constListSize;\n                            $('#tv_group_size').val(constListSize);\n                        }\n\n                        params['count'] = $('#tv_count').val();\n                        if(params['count'] > 5000){\n                            $('#sql_result').val('count > 5000, better to build on your server');\n                            return;\n                        }\n                        var tablename = $('#tv_tablename_hidden').val();\n                        params['table_name'] = (tablename == '' || tablename == 'undefined') ? 'table_name' : tablename;\n\n\n                        //发JSON，生成SQL\n                        params = JSON.stringify(params);\n                        $.post('gensql.php',params,function(data){\n                           if( $('input[name=\"cb_is_download\"]:checked').length == 1){   //下载框被选中\n                                $('#sql_result').val('broswer should begin download');\n                                createAndDownloadFile('datamaker_' + params['table_name'] + '.sql',data);\n                          }else{\n                                //正常展示\n                                $('#sql_result').val(data);\n                          }\n                        });\n                    });\n                });\n\n\n                function getDefaultValueByMethod(methodParams) {\n                    switch (methodParams) {\n                        case 'incr_int':\n                            return 1;\n                        case 'rand_int':\n                            return '1,100';\n                        case 'rand_float':\n                            return '1,100,2';\n                        case 'incr_day':\n                        case 'incr_day_grouply':\n                            return 20180401;\n                        case 'ignore':\n                            return 'ignore this column, good for AUTO_INCREMENT';\n                        case 'rand_timestamp':\n                        case 'rand_timestamp_mysql':\n                            return '20180401,20180404';\n                        case 'const_str':\n                            return 'Goolge';\n                        case 'const_str_list':\n                            return 'Google$#$Facebook$#$Microsoft';\n                        case 'rand_str':\n                            return '5';\n                        case 'rand_str_list':\n                            return 'Google,Facebook,Microsoft,Apple';\n                        case 'incr_str_prefix':\n                            // var incrStrPre = ['老王','射击狮','测试店','产品经理','程序员','码农','攻城狮','SB'];\n                            // return incrStrPre[ Math.floor((incrStrPre.length-1) * Math.random()) ] ;\n                            return 'PM-';\n                        case 'rand_pic_url':\n                            return '300,400';\n                    }\n                }\n\n                /**\n                 * 工具函数： 创建并下载文件，用于导出\n                 * @param  {String} fileName 文件名\n                 * @param  {String} content  文件内容\n                 */\n                function createAndDownloadFile(fileName, content) {\n                    var aTag = document.createElement('a');\n                    var blob = new Blob([content]);\n                    aTag.download = fileName;\n                    aTag.href = URL.createObjectURL(blob);\n                    aTag.click();\n                    URL.revokeObjectURL(blob);\n                }\n            </script>\n\n            <br/><br/><br/><br/>\n                <!-- SQL结果 -->\n                <div hidden id =\"tv_group_result\" class=\"col-md-12  column\">\n                    <textarea  id=\"sql_result\" class=\"form-control\" placeholder=\"I show result\" rows=\"8\" ></textarea>\n                </div>\n\n        </div>\n    </div>\n</div>\n\n\n\n\n</body>\n</html>\n\n"
  },
  {
    "path": "sqlparse.php",
    "content": "<?php\n\nclass CreateSqlParser\n{\n\n    const RAND_INT = 'rand_int';\n    const RAND_FLOAT = 'rand_float';\n    const INCR_INT = 'incr_int';\n    const INCR_DAY = 'incr_day';\n    const INCR_DAY_GROUPLY = 'incr_day_grouply';\n    const RAND_TIMESTAMP = 'rand_timestamp';\n    const RAND_TIMESTAMP_MYSQL = 'rand_timestamp_mysql';\n    const IGNORE = 'ignore';\n\n    const INCR_STR_PREFIX = 'incr_str_prefix';\n    const RAND_STR = 'rand_str';\n    const RAND_STR_LIST = 'rand_str_list';\n    const CONST_STR = 'const_str';\n    const CONST_STR_LIST = 'const_str_list';\n    const RAND_PIC_URL = 'rand_pic_url';\n\n\n    const errorParseError = 1001;\n\n    public function getApiReturn($error, $msg, $data)\n    {\n        $ret['error'] = $error;\n        $ret['msg'] = $msg;\n        $ret['data'] = $data;\n        return $ret;\n    }\n\n    /**\n     * 解析字段名没有 ` 的SQL，正则以空格为分隔符，常见软件： datagrip\n     * 正则匹配规则： 若干空格 + 不含空格的字符串(字段名) + 空格 + 不含空格的字符串(类型) + 空格 + 包含换行的任意字符（额外信息） + 逗号或者）[其中右括号结尾是表里没有任何索引的情况]，支持跨行匹配\n     * @param $sql\n            CREATE TABLE t_supplier_product\n            (\n                id INT AUTO_INCREMENT\n                PRIMARY KEY,\n                supplier_id INT NOT NULL\n                COMMENT '供应商id',\n                product_detail_id INT NOT NULL\n                COMMENT '单品id',\n                price DOUBLE NOT NULL\n                COMMENT '采购价',\n                KEY (`supplier_id`)\n            )\n            COMMENT '供应商货品'\n            ENGINE = InnoDB\n            CHARSET = utf8;\n     * @return array 每个元素包含如下字段\n     * origin: 原SQL\n     * key: 字段名\n     * type: 类型，包含可选的长度 int(11) 、text\n     * others:  其他，NOT NULL AUTO_INCREMENT COMMENT '我是注释' 这种\n     */\n    private function parseWithoutBackQuote($sql){\n        $content = explode('(',$sql,2); //先拿到 create table 之后的避免 正则把 第一列 吃了\n        $sql = $content[1];\n\n        //提前删除 INDEX 相关 INDEX cust_id (cust_id) USING BTREE,\n        $sql = preg_replace(\"# *INDEX.+#\",\"\",$sql);\n\n        $pattern = \"#( *)([^\\s]+) ([^\\s]+) ([\\s\\S]+?)[,)]#im\";\n        preg_match_all($pattern, $sql, $matches);\n        $ret = [];\n        for ($cnt = 0; $cnt < count($matches[0]); $cnt++) {\n            if ( false !== stripos($matches[3][$cnt], 'KEY')\n                || false !== stripos($matches[2][$cnt], 'KEY')  ) { //排除  PRIMARY KEY (id) 和 KEY (id) ，虽然也可以像上面那样正则替换掉线\n                continue;\n            }\n\n            $item = [\n                'origin' => $matches[0][$cnt],\n                'key' => $matches[2][$cnt],\n                'type' => $matches[3][$cnt],\n                'others' => $matches[4][$cnt],\n            ];\n            $ret []= $item;\n        }\n        return $ret;\n    }\n\n\n    /**\n     * 解析字段名有 ` 包含的SQL，常见软件：Navicat\n     * 正则匹配规则： 任意字符若干（空格或KEY） + `列名`+ 空格 + 非空字符串（类型） + 空格 + 额外信息 + 逗号或者），其中右括号结尾是表里没有任何索引的情况，支持跨行匹配\n     * 如果需要改动，需要注意： COMMENT 换行、 表无索引时最后个字段的情况，目前以 ` 来区分字段而非空格\n     * @param $sql\n        CREATE TABLE `im_feed` (\n            `id` int(11) NOT NULL AUTO_INCREMENT,\n            `parent_id` int(11) NOT NULL DEFAULT '0',\n            `user_id` bigint(11) NOT NULL DEFAULT '0' COMMENT '学号或者老师工号',\n            `content` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '',\n            `is_deleted` tinyint(4) NOT NULL DEFAULT '0',\n            `photos` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '',\n            `create_time` int(11) NOT NULL DEFAULT '0',\n            PRIMARY KEY (`id`)\n        ) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n     * @return array 每个元素包含如下字段\n     * origin: 原SQL\n     * key: 字段名\n     * type: 类型，包含可选的长度 int(11) 、text\n     * others:  其他，NOT NULL AUTO_INCREMENT COMMENT '我是注释' 这种\n     */\n    private function parseWithBackQuote($sql){\n       //提前删除 INDEX 相关 INDEX cust_id (cust_id) USING BTREE,\n        $sql = preg_replace(\"# *INDEX.+#\",\"\",$sql);\n\n        $pattern = \"#(.*)`(.+)` ([^\\s]+) ([\\s\\S]+?)[,)]#im\";\n        preg_match_all($pattern, $sql, $matches);\n        // echo json_encode($matches);exit();\n        $ret = [];\n        for ($cnt = 0; $cnt < count($matches[0]); $cnt++) {\n            if (false !== stripos($matches[1][$cnt], 'KEY')) { //索引 排除\n                continue;\n            }\n\n            $item = [\n                'origin' => $matches[0][$cnt],\n                'key' => $matches[2][$cnt],\n                'type' => $matches[3][$cnt],\n                'others' => $matches[4][$cnt],\n            ];\n            $ret []= $item;\n        }\n        return $ret;\n    }\n\n    public function execute($input)\n    {\n        $sql = $input;\n        $ret = [];\n\n        //解析表名，兼容以下两种，返回的表名带不带 `都可以成功插入\n        //  CREATE TABLE `test` (\n        //  CREATE TABLE t_supplier_product\n        //  (\n        $sql = str_replace(\"IF NOT EXISTS\",\"\",$sql);\n\n        $pattern = \"#CREATE TABLE (.+?)[\\s]#i\";\n        preg_match($pattern, $sql, $matches);\n        if (empty($matches)) {\n            return $this->getApiReturn(self::errorParseError, '不是建表SQL，未包含 CREATE TABLE', []);\n        }\n        $ret['table_name'] = $matches[1];\n\n        //解析字段，推荐个在线正则网站 https://regexr.com/\n        $matchList = $this->parseWithBackQuote($sql);     //带`的解析失败则用不带`的解析，大部分SQL是带`的\n        if (empty($matchList)) {\n            $matchList = $this->parseWithoutBackQuote($sql);\n        }\n        $ret['list'] = [];\n        if (empty($matchList)) {\n            return $this->getApiReturn(self::errorParseError, '未查找到SQL字段', []);\n        }\n\n//        echo json_encode($matchList); exit();\n\n//        解析后拿到的每个item:\n//              \"origin\":\" `baoguang_pv` int(11) NOT NULL DEFAULT '0' COMMENT '昨日曝光pv',\",\n//              \"key\":\"baoguang_pv\",     ======= 字段名\n//              \"type\":\"bigint(20)\",     ======= 类型，包含可选的长度 int(11) 、text\n//              \"others\":\"NOT NULL DEFAULT '0' COMMENT '昨日曝光pv'\"  =======  其他，NOT NULL AUTO_INCREMENT COMMENT '我是注释' 这种\n        foreach ($matchList as $item){\n            $size = 0;\n            $type = $item['type'];\n            $sizeArr = explode('(',$item['type']);\n\n            //如果有()说明是有数字的那种\n            if(!empty($sizeArr) && count($sizeArr) >= 2 ) {\n                $type = $sizeArr[0];\n                $size = explode( \")\" ,$sizeArr[1]  )[0] ;\n            }\n            $entry = $this->genDefaultAttribute($item['key'], $type , $size, $item['others']);\n            $entry['key'] = $item['key'];\n            $ret['list'] [] = $entry;\n        }\n\n        $ret['group_size'] = 5;     //组大小\n        $ret['count'] = 3;          //多少条SQL\n\n        return $this->getApiReturn(0, '', $ret);\n    }\n\n    /**\n     * @param $key\n     * @param $type string SQL的字段类型 varchar,int\n     * @param $size  string SQL的字段类型后跟随的大小 如 varchar(10) 中的10\n     * @param $others string  varchar(10) 后面的一串其他完整内容，包含 自增、非空、默认值等\n     * @return array item 必备包含 'key' 字段名 'method' 生成规则 'value' 默认值\n     */\n    private function genDefaultAttribute($key, $type, $size, $others)\n    {\n        $type = strtolower(trim($type));\n        $incrStrPre = ['Boss', 'Player', 'Test', 'PM', 'Programmer', 'Worker', 'Actor', 'SB'];\n\n        switch ($type) {\n            case 'varchar':\n                $item = [\n                    'desc' => '前缀+自增',\n                    'method' => self::INCR_STR_PREFIX,\n                    'value' => $incrStrPre[rand(0, count($incrStrPre) - 1)],\n                ];\n                break;\n            case 'int':\n            case 'mediumint':\n            case 'integer':\n                $item = [\n                    'desc' => '随机整数',\n                    'method' => self::RAND_INT,\n                    'value' => '100,500',\n                ];\n                break;\n            case 'bigint':\n                $item = [\n                    'desc' => '随机整数',\n                    'method' => self::RAND_INT,\n                    'value' => \"1000000,99999999\",\n                ];\n                break;\n            case 'tinyint':\n                $item = [\n                    'desc' => '随机整数',\n                    'method' => self::RAND_INT,\n                    'value' => \"0,{$size}\",       //tinyint(4) 一般是0-4的枚举值\n                ];\n                break;\n            case 'float':\n            case 'double':\n                $item = [\n                    'desc' => '随机浮点',\n                    'method' => self::RAND_FLOAT,\n                    'value' => '1,10,5',\n                ];\n                break;\n            case 'date':        //这个待定\n            case 'datetime':\n            case 'timestamp':\n                $item = [\n                    'method' => self::RAND_TIMESTAMP_MYSQL,\n                    'value' => '20180407,20180408',\n                ];\n                break;\n            case 'text':\n            default:\n                $item = [\n                    'method' => self::CONST_STR,\n                    'value' => '1',\n                ];\n                break;\n        }\n\n        //自增ID\n        $autoInc = stripos($others, \"AUTO_INCREMENT\");\n        if ($autoInc !== false) {\n            $item = [\n                'method' => self::IGNORE,\n                'desc' => '自增ID，忽略',\n            ];\n        }\n\n        //注释\n        $expComment = explode(\"COMMENT \", $others);\n        if (!empty($expComment) && count($expComment) >= 2 ) {\n            $item['comment'] = trim($expComment[1], \"',\");\n        }\n\n        //通用配置\n        $commonItem = $this->parseFileForAttribute('conf/common.ini',$key);\n        if(!empty($commonItem)){\n            $item = $commonItem;\n        }\n        //个性化配置\n        //针对你、你公司 常用的字段设置默认值，存放不可告人的数据秘密，配置文件在.gitignore里\n        $localItem = $this->parseFileForAttribute('conf/local.ini',$key);\n        if(!empty($localItem)){\n            $item = $localItem;\n        }\n        return $item;\n    }\n\n    /**\n     * 解析ini文件拿到自定义默认值，根据字段名猜测用户想要的是哪个类型，配置文件样例如下，目前只支持 模糊查找 和 精确匹配\n            [0]\n            key = avatar\n            method = RAND_PIC_URL\n            value = 300,400\n            way = search  跟key的匹配方式，search为模糊搜索，输入key包含 avatar 就走这个匹配\n\n            [1]\n            key = index_day\n            method = INCR_DAY\n            value = 20180301\n            way = match  match为精确匹配，输入key 等于 index_day 就走这个匹配\n     *\n     *\n     * @param $fileName string 配置文件路径\n     * @param $key string 字段名\n     * @return array\n     */\n    private function parseFileForAttribute($fileName,$key)\n    {\n        if(!file_exists($fileName)){\n            return [];\n        }\n        $item = [];\n        $matchArray = parse_ini_file($fileName,true);\n\n        foreach ($matchArray as $match) {\n            if(empty($match['way'])){   //默认key规则为相等\n                $match['way'] = 'match';\n            }\n            if($match['way'] == 'search' && false !== stripos($key, $match['key'])){\n                $item = $match;\n                $item['method'] = strtolower($item['method']);\n            }else if ($match['way'] == 'match' && $key == $match['key']) {\n                $item = $match;\n                $item['method'] = strtolower($item['method']);\n            }\n        }\n        unset($item['way']);\n        return $item;\n    }\n\n\n}\ndate_default_timezone_set(\"Asia/Shanghai\");\n\n\n$defaultSql = \"\nCREATE TABLE `im_feed` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `parent_id` int(11) NOT NULL DEFAULT '0',\n  `user_id` bigint(11) NOT NULL DEFAULT '0' COMMENT '学号或者老师工号',\n  `content` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '',\n  `is_deleted` tinyint(4) NOT NULL DEFAULT '0',\n  `photos` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '',\n  `create_time` int(11) NOT NULL DEFAULT '0',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\";\n\n$defaultSql2 = \"\nCREATE TABLE t_supplier_product\n(\nid INT AUTO_INCREMENT\nPRIMARY KEY,\nsupplier_id INT NOT NULL\nCOMMENT '供应商id',\nproduct_detail_id INT NOT NULL\nCOMMENT '单品id',\nprice DOUBLE NOT NULL\nCOMMENT '采购价',\nKEY (`supplier_id`)\n)\nCOMMENT '供应商货品'\nENGINE = InnoDB\nCHARSET = utf8;\n\";\n\n$parser = new CreateSqlParser();\nif(empty($_POST['sql'])){\n    $sql = $defaultSql;\n}else{\n    $sql = $_POST['sql'];\n}\n$ret = $parser->execute($sql);\necho json_encode($ret);exit();\n"
  }
]