From c06966ca8ee201abbcd1e3bd1aebb03c4346e8b9 Mon Sep 17 00:00:00 2001 From: xcenweb Date: Sat, 18 Jun 2022 17:37:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=87=B31.1.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 +- app/__begin.php | 8 +- app/index.php | 2 +- app/test.php | 7 +- app/test/.gitignore | 2 + app/test/api.php | 25 - app/test/compiling.php | 42 - app/test/encrypt.php | 0 app/test/extend_include.php | 5 - app/test/extend_namespace.php | 5 - app/test/lib/cache/filecache.php | 31 - app/test/lib/cache/memcache.php | 1 - app/test/lib/config.php | 17 - app/test/lib/db/medoo.php | 5 - app/test/lib/log.php | 13 - app/test/lib/tool/arr.php | 30 - app/test/lib/tool/array2xml.php | 20 - app/test/lib/tool/file.php | 7 - app/test/lib/tool/filter.php | 27 - app/test/lib/tool/str.php | 5 - app/test/lib/tool/user.php | 5 - app/test/test.php | 2 - composer.json | 36 + config/AccessControl.php | 21 +- config/api.php | 15 +- config/dove.php | 2 +- config/filter.php | 1 - data/cache/file/.gitignore | 2 + data/cache/file/a.data | 1 - dove/DoveAPI.php | 48 - dove/helper.php | 142 -- dove/lib/App.php | 78 -- dove/lib/Config.php | 47 - dove/lib/Debug.php | 108 -- dove/lib/Log.php | 36 - dove/lib/Plugin.php | 28 - dove/lib/Route.php | 39 - dove/lib/cache/Filecache.php | 88 -- dove/lib/cache/Memcache.php | 60 - dove/lib/db/Medoo.php | 2205 ------------------------------ dove/lib/db/Tdb.php | 43 - dove/lib/tool/Arr.php | 22 - dove/lib/tool/ArrToxml.php | 81 -- dove/lib/tool/Encrypt.php | 150 -- dove/lib/tool/File.php | 104 -- dove/lib/tool/Filter.php | 65 - dove/lib/tool/Ftp.php | 351 ----- dove/lib/tool/Str.php | 85 -- dove/lib/tool/User.php | 20 - dove/tpl/debug/json.php | 14 - dove/tpl/debug/page.tpl | 67 - dove/tpl/debug/pe_json.php | 5 - dove/tpl/debug/pe_page.tpl | 22 - public/index.php | 17 +- public/nginx.conf | 9 + public/static/.gitignore | 2 + runtime/.gitignore | 2 + runtime/log/test.log | 1 - vendor/.gitignore | 2 + 59 files changed, 113 insertions(+), 4183 deletions(-) create mode 100644 app/test/.gitignore delete mode 100644 app/test/api.php delete mode 100644 app/test/compiling.php delete mode 100644 app/test/encrypt.php delete mode 100644 app/test/extend_include.php delete mode 100644 app/test/extend_namespace.php delete mode 100644 app/test/lib/cache/filecache.php delete mode 100644 app/test/lib/cache/memcache.php delete mode 100644 app/test/lib/config.php delete mode 100644 app/test/lib/db/medoo.php delete mode 100644 app/test/lib/log.php delete mode 100644 app/test/lib/tool/arr.php delete mode 100644 app/test/lib/tool/array2xml.php delete mode 100644 app/test/lib/tool/file.php delete mode 100644 app/test/lib/tool/filter.php delete mode 100644 app/test/lib/tool/str.php delete mode 100644 app/test/lib/tool/user.php delete mode 100644 app/test/test.php create mode 100644 composer.json create mode 100644 data/cache/file/.gitignore delete mode 100644 data/cache/file/a.data delete mode 100644 dove/DoveAPI.php delete mode 100644 dove/helper.php delete mode 100644 dove/lib/App.php delete mode 100644 dove/lib/Config.php delete mode 100644 dove/lib/Debug.php delete mode 100644 dove/lib/Log.php delete mode 100644 dove/lib/Plugin.php delete mode 100644 dove/lib/Route.php delete mode 100644 dove/lib/cache/Filecache.php delete mode 100644 dove/lib/cache/Memcache.php delete mode 100644 dove/lib/db/Medoo.php delete mode 100644 dove/lib/db/Tdb.php delete mode 100644 dove/lib/tool/Arr.php delete mode 100644 dove/lib/tool/ArrToxml.php delete mode 100644 dove/lib/tool/Encrypt.php delete mode 100644 dove/lib/tool/File.php delete mode 100644 dove/lib/tool/Filter.php delete mode 100644 dove/lib/tool/Ftp.php delete mode 100644 dove/lib/tool/Str.php delete mode 100644 dove/lib/tool/User.php delete mode 100644 dove/tpl/debug/json.php delete mode 100644 dove/tpl/debug/page.tpl delete mode 100644 dove/tpl/debug/pe_json.php delete mode 100644 dove/tpl/debug/pe_page.tpl create mode 100644 public/nginx.conf create mode 100644 public/static/.gitignore create mode 100644 runtime/.gitignore delete mode 100644 runtime/log/test.log create mode 100644 vendor/.gitignore diff --git a/README.md b/README.md index 8e8f00d..d602da6 100644 --- a/README.md +++ b/README.md @@ -4,18 +4,28 @@ 快速、简洁、不同于MVC -### 运行快 +# 安装 -- 虚拟主机上最快`0.0015`秒,在一台2核服务器上运行速度最快`0.000998`秒 +使用composer安装框架(到test目录中): -![dove](http://dove.xcenadmin.top/run_time.png) +```composer +composer create-project xcenweb/doveapi test +``` + +使用composer更新框架: + +```composer +composer update xcenweb/doveapi +``` ### 可扩展 -- 框架的extend目录可通过include或者namespace等(自动)加载 +- 框架已支持通过composer自由安装使用更多的包,框架的extend目录可通过include或者namespace等(自动)加载 ### 完美支持一个API所需的操作 +- 通过`$this->xx()`的方式完成接口的post、get接收和json、xml、void的(统一)返回。 + ### 不同于MVC的架构 - 持续探索中... diff --git a/app/__begin.php b/app/__begin.php index e3ebe23..3f94baf 100644 --- a/app/__begin.php +++ b/app/__begin.php @@ -1,5 +1,8 @@ start([ + // 跨域设置 + 'origin' => '*', + // 设置声明的请求方法 'methods' => '*', @@ -10,4 +13,5 @@ // 设置phpini 'ini' => [], -]); \ No newline at end of file +]); +?> \ No newline at end of file diff --git a/app/index.php b/app/index.php index 3b9bf14..2c115d5 100644 --- a/app/index.php +++ b/app/index.php @@ -1 +1 @@ -DoveAPI v<?php echo DOVE_VERSION ?>

DoveAPI

Hello World! 感谢你使用本框架!你可以通过以下途径学习DoveAPI的使用方法:

📃框架文档📺️视频教程👍️体验一波报错页面👍 体验JSON返回👍 体验XML返回
当前DoveApi框架版本: v
本次加载时长:s
\ No newline at end of file +DoveAPI v<?php echo DOVE_VERSION ?>

DoveAPI

Hello World! 感谢你使用本框架!你可以通过以下途径学习DoveAPI的使用方法:

📃框架文档📺️视频教程👍️体验一波报错页面👍 体验JSON返回👍 体验XML返回
当前DoveAPI框架版本: v
本次加载时长:s
\ No newline at end of file diff --git a/app/test.php b/app/test.php index 0ca334a..312c7e8 100644 --- a/app/test.php +++ b/app/test.php @@ -1,17 +1,14 @@ response->mxml(true,666,'this is DoveAPI',[ 'version'=>DOVE_VERSION, 'runtime'=>round(microtime(true)-DOVE_START_TIME,8).'s', ]); } else { // 输出模板json,输出后后面的不会被运行 - API::mjson(true,666,'this is DoveAPI',[ + $this->response->mjson(true,666,'this is DoveAPI',[ 'version'=>DOVE_VERSION, 'runtime'=>round(microtime(true)-DOVE_START_TIME,8).'s', ]); diff --git a/app/test/.gitignore b/app/test/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/app/test/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/app/test/api.php b/app/test/api.php deleted file mode 100644 index cea0ffc..0000000 --- a/app/test/api.php +++ /dev/null @@ -1,25 +0,0 @@ -'POST,GET', - - // header批量设置 - 'header'=>[ - // 'Location: https://m.baidu.com' - ], - - // ini批量设置 - 'ini'=>[ - 'max_execution_time'=>60, - ], -]); - -// 模板输出json,输出后停止运行 -API::mjson(true,500,'失败'); - -// 模板输出xml,输出后停止运行 -API::mxml(true,500,'失败'); \ No newline at end of file diff --git a/app/test/compiling.php b/app/test/compiling.php deleted file mode 100644 index 35580ea..0000000 --- a/app/test/compiling.php +++ /dev/null @@ -1,42 +0,0 @@ -

DoveApi中文语法编译调试

- -//编译测试编 编译测试 编译测试 编译测试编 -// 译 编 编 译 -// 编 编译测试 编译测试 编 -// 编 编 编 编 -// 编 编译测试 编试测译 编 - - -something - - -/** - * use xxx; - * use dove\xxx; - * use dove\plugin\xxx; - */ -使用类 xxx;
-使用框架类 xxx;
-使用插件类 xxx 且重命名为 vvv;
- -/** - * 待开发 - * Api::initial([ - * 'header'=>[ - * 'xxx', - * 'xxxx'=>'abc', - * ], - * 'ini'=>[ - * 'xxx'=>'abc', - * ] - * ]); - */ -初始化接口类 { - header设置{ - 设置xxx; - 设置xxxx的且值为abc; - } - ini设置 { - 将xxx的值设为abc; - } -} diff --git a/app/test/encrypt.php b/app/test/encrypt.php deleted file mode 100644 index e69de29..0000000 diff --git a/app/test/extend_include.php b/app/test/extend_include.php deleted file mode 100644 index c204a47..0000000 --- a/app/test/extend_include.php +++ /dev/null @@ -1,5 +0,0 @@ -set('a','xxxxx',0); -$fc->set('b',['test'=>12345690],0); -$fc->set('c',2,0); - -// 获取缓存,即使缓存过期也要在输出后删除 -echo '缓存a 内容:'.$fc->get('a',true).'
'; -echo '缓存b 内容:'; -print_r($fc->get('b',true)); -echo '
缓存c 内容:'.$fc->get('c',true); - -// 缓存自增(仅支持数值缓存 -echo '

缓存c 自增前数值:'.$fc->get('c',true); -echo '
缓存c 自增后数值:'.$fc->inc('c',1); -// 缓存自减(仅支持数值缓存 -echo '

缓存c 自减前数值:'.$fc->get('c',true); -echo '
缓存c 自减后数值:'.$fc->dec('c',1); - -// 删除缓存 -// a.data b.data c.data -echo '

删除缓存 a,b,c 返回:'; -var_dump($fc->del('a','b','c')); - -// 删除所有缓存,包括子目录 -// $fc->clean(); \ No newline at end of file diff --git a/app/test/lib/cache/memcache.php b/app/test/lib/cache/memcache.php deleted file mode 100644 index b3d9bbc..0000000 --- a/app/test/lib/cache/memcache.php +++ /dev/null @@ -1 +0,0 @@ - 'abc的内容', -]); -// 同上,注意第2行的使用 -Config::set('配置名',[ - 'abc' => 'abc的内容', -]); diff --git a/app/test/lib/db/medoo.php b/app/test/lib/db/medoo.php deleted file mode 100644 index b58c6fe..0000000 --- a/app/test/lib/db/medoo.php +++ /dev/null @@ -1,5 +0,0 @@ ->mysql_config'); \ No newline at end of file diff --git a/app/test/lib/log.php b/app/test/lib/log.php deleted file mode 100644 index 5fa104e..0000000 --- a/app/test/lib/log.php +++ /dev/null @@ -1,13 +0,0 @@ -'值'],log/{$path}/,log文件名,log类型 -Log::save(['日期'=>date("Y年m月d日 H时i分s秒")],'test','test_log','test'); - -// ['关键字'=>'值'],config/dove.php中配置名 -Log::save(['日期'=>date("Y年m月d日 H时i分s秒")],'eg'); \ No newline at end of file diff --git a/app/test/lib/tool/arr.php b/app/test/lib/tool/arr.php deleted file mode 100644 index 9070d6f..0000000 --- a/app/test/lib/tool/arr.php +++ /dev/null @@ -1,30 +0,0 @@ - [ - 'abcd' => 556677, - 'ABCD' => ['五五','六六',], - ], - 'doveapi' => [45,25,36,80], -]; - -$arrayB = ['4566z1','doveapi','gugu','fff','0o0O0oO']; - -echo '拆分数组
'; - -// 将一个多维数组拆分成 键=>值 -print_r(Arr::divide($arrayA)); - -echo '

打乱数组
'; - -// 打乱数组或输出被打乱的数组的第一个值 -// 第二个值为true(默认)时输出被打乱的数组,为false时输出打乱数组的第一个的值 -print_r(Arr::random($arrayB)); -echo '
'; -var_dump(Arr::random($arrayB,false)); -echo '
'; -var_dump(Arr::random($arrayB,false,3)); - -echo '
'; \ No newline at end of file diff --git a/app/test/lib/tool/array2xml.php b/app/test/lib/tool/array2xml.php deleted file mode 100644 index 56e5a86..0000000 --- a/app/test/lib/tool/array2xml.php +++ /dev/null @@ -1,20 +0,0 @@ -200, - 'user'=>[ - 'item1'=>[ - 'username'=>'abc', - 'point'=>123, - ], - 'item2'=>[ - 'username'=>'abc', - 'point'=>123, - ], - ], -]; - -echo ArrToxml::build($array,'data'); \ No newline at end of file diff --git a/app/test/lib/tool/file.php b/app/test/lib/tool/file.php deleted file mode 100644 index f949342..0000000 --- a/app/test/lib/tool/file.php +++ /dev/null @@ -1,7 +0,0 @@ -- 规则内容:{$b}
- 导致错误的字符串内容:{$c}"; -}); - -echo '
加载时间:'.round(microtime(true)-DOVE_START_TIME,8).''; -echo '


'; - -// 检验多字符 bug -// Filter::validate(['规则1'=>[0=>'字符1',1=>'字符2',...],'规则2'=>[0=>'字符']],允许空字符串内容,闭包函数); - -Filter::validate([ - '数字'=>[0=>10,1=>134,2=>66], - '邮箱'=>null, -],false,function($a,$b,$c,$d,$e){ - echo "第{$a}个规则下发生了错误
- 发生错误的规则名称:{$c}
- 发生错误的规则:{$d}
- 规则下第{$b}个字符串
- 导致错误的字符串内容:{$e}"; -}); - -echo '
加载时间:'.round(microtime(true)-DOVE_START_TIME,8).''; -echo '


'; \ No newline at end of file diff --git a/app/test/lib/tool/str.php b/app/test/lib/tool/str.php deleted file mode 100644 index 88b3877..0000000 --- a/app/test/lib/tool/str.php +++ /dev/null @@ -1,5 +0,0 @@ -=7.4.3", + "xcenweb/doveapi-core": "@dev" + }, + "autoload": { + "psr-0": { + "": "extend\\" + } + }, + "config": { + "preferred-install": "dist", + "sort-packages": true + } +} \ No newline at end of file diff --git a/config/AccessControl.php b/config/AccessControl.php index 98df30a..d548dae 100644 --- a/config/AccessControl.php +++ b/config/AccessControl.php @@ -1,15 +1,26 @@ 'index.php', // 禁止被访问的目录名,被禁止后访问会返回框架404错误 'padlock' => [], - // 框架管理器设置 - 'FrameworkManage' => [ - 'entry' => 'gu-fme', //入口,为 false 时不开启 + // 指定路径绑定域名(计划中 + 'band' => [ + // test.xcenadmin.top => app/abc/index.php + // test.xcenadmin.top/abc => app/abc/abc.php + 'test' => 'abc', + + // xcenadmin.top => app/index.php + // www.xcenadmin.top => app/index.php + '__INDEX__' => '', + ], + + // 框架管理器设置(计划中 + 'fme_setting' => [ + 'switch' => false, //管理器功能开关 'username' => 'admin',//用户名 'password' => 'admin',//密码 'ip' => false, //ip限制 diff --git a/config/api.php b/config/api.php index b657505..7cec84f 100644 --- a/config/api.php +++ b/config/api.php @@ -1,23 +1,26 @@ start();全局自动运行 + 'autoload' => false, + + // 默认的声明的请求方法,多个用半角逗号隔开 'methods' => '*', // 默认跨域设置 'origin' => '*', // 默认自动加载的header设置 - 'def_header' => [ + 'header' => [ 'X-Powered-By: DoveAPI-Framework', ], // 默认自动修改的ini设置 - 'def_ini' => [], + 'ini' => [], - // 统一执返 API::m(); - // 可选 json/xml/void - 'uni_response' => 'json', + // 统一执返 $response->uni(); + // 可选 json/xml/void/mjson/mxml + 'response_uni' => 'json', // 模板 mjson、mxml方法通用 'response_temps' => [ diff --git a/config/dove.php b/config/dove.php index 9d78ca8..0557864 100644 --- a/config/dove.php +++ b/config/dove.php @@ -10,7 +10,7 @@ 'debug_mode' => 'page', // 非debug模式(生产环境)下错误返回形式 json/page - 'pe_debug_mode' => 'page', + 'pe_debug_mode' => 'json', // 是否开启debug-log记录 'error_log'=> true, diff --git a/config/filter.php b/config/filter.php index 5e92ab0..222c3d2 100644 --- a/config/filter.php +++ b/config/filter.php @@ -1,7 +1,6 @@ 'Strict',E_NOTICE=>'Notice',E_WARNING=>'Warning',E_DEPRECATED=>'Deprecated',E_USER_ERROR=>'User Error',E_USER_NOTICE=>'User Notice',E_USER_WARNING=>'User Warning',E_USER_DEPRECATED=>'User Deprecated']; - $level = isset($levels[$level])?$levels[$level]:'Unkonw error'; - Debug::e(500,"{$level}: {$msg} in {$file} on {$line}",$file); -}); -spl_autoload_register(function ($className){ - $clist = ['extend\\'=>DOVE_EXTEND_DIR,'dove\\plugin\\'=>DOVE_PLUGIN_DIR,'dove\\'=>DOVE_LIBRARY]; - foreach($clist as $namespace => $dir){ - if(mb_substr($className, 0, mb_strlen($namespace)) != $namespace) continue; - $file = str_replace("\\", "/", $dir . mb_str_right($className, $namespace)) . ".php"; - if(!file_exists($file)) Debug::e(500,"未找到类{$className}[{$file}]"); - require $file; - return true; - } -}); -set_include_path(get_include_path().PATH_SEPARATOR.DOVE_EXTEND_DIR.'/'); -date_default_timezone_set('PRC'); -Config::get('dove','debug')?error_reporting(E_ALL):error_reporting(0); -ob_start(); \ No newline at end of file diff --git a/dove/helper.php b/dove/helper.php deleted file mode 100644 index df0462b..0000000 --- a/dove/helper.php +++ /dev/null @@ -1,142 +0,0 @@ -getMessage()); - return; - } - $start = $func->getStartLine() - 1; - $end = $func->getEndLine() - 1; - $filename= $func->getFileName(); - die("已找到函数 $funcname 在文件 [$filename]:$start-$end行)"); -} - -// = = = = = = = = = = = = = = = = = = - -/** - * DoveApi - 使您快速编写Api接口的PHP框架 - * - * 这里是框架的函数库 - * - * @github: https://github.com/xcenweb/DoveApi - * @author: guge - * @qqgroup:489921607 - * - */ - -/** - * 获取真实ip - * @return string - */ -function get_ip() -{ - if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) { - $ip = $_SERVER['REMOTE_ADDR']; - } elseif (getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) { - $ip = getenv('HTTP_CLIENT_IP'); - } elseif (getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) { - $ip = getenv('HTTP_X_FORWARDED_FOR'); - } elseif (getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) { - $ip = getenv('REMOTE_ADDR'); - } - preg_match("/[\\d\\.]{7,15}/", isset($ip) ? $ip : NULL, $match); - return isset($match[0]) ? $match[0] : 'unknown'; -} - -/** - * 截取字符串右边的内容 - * @param string $str 原字符 - * @param string $q 左边的字符 - * @param integer $offset 查询偏移量 - * @return string - */ -function mb_str_right($str, $q, $offset = 0) -{ - return mb_substr($str, mb_strpos($str, $q, $offset) + mb_strlen($q), mb_strlen($str)); -} - -/** - * http method - * @param string $m_n 使用的方法、方法名 - * @param string $def 默认的内容 - * @return string - */ -function M($m_n,$def='') -{ - $m_n = explode('.',$m_n); - $m = isset($m_n[0])?$m_n[0]:'r'; - $n = isset($m_n[1])?$m_n[1]:'*'; - if($m=="get"||$m=="g"){ - if($n=='*') return $_GET; - return isset($_GET[$n])?$_GET[$n]:$def; - } elseif ($m=="post"||$m=="p"){ - if($n=='*') return $_POST; - return isset($_POST[$n])?$_POST[$n]:$def; - } elseif ($m=="request"||$m=="r"){ - if($n=='*') return $_REQUEST; - return isset($_REQUEST[$n])?$_REQUEST[$n]:$def; - } - return false; -} - -/** - * 计算存储大小单位 - * @param string $total 字节数 - * @return string 返回空间大小 - */ -function space_total($total=0) { - $rule = ['GB'=>1073741824,'MB'=>1048576,'KB'=>1024]; - foreach($rule as $unit=>$byte){ - if($total>$byte) return round($total/$byte).$unit; - } - return $total.'B'; -} \ No newline at end of file diff --git a/dove/lib/App.php b/dove/lib/App.php deleted file mode 100644 index 8f59ee5..0000000 --- a/dove/lib/App.php +++ /dev/null @@ -1,78 +0,0 @@ - $val){ - $stack.= "{$line}. is "; - if(isset($val['class'])) $stack.=''.$val['class'].''.$val['type']; - if(isset($val['function'])) $stack.= ($val['function']=='{closure}') ? '{closure}' : ''.$val['function'].'()'; - if(isset($val['file']) && isset($val['line'])) $stack.='在文件 ['.$val['file'].'] 第'.$val['line'].'行'; - $stack.= "\r\n"; - $line++; - } - $array = [ - 'domain'=>Route::domain(), - 'err_code'=>self::$code, - 'err_info'=>self::$info, - 'err_file'=>self::$file, - 'call_stack'=>str_replace('\\','/',$stack), - 'get_array_list' => static::array_list($_GET), - 'post_array_list' => static::array_list($_POST), - 'version'=>DOVE_VERSION, - 'exitTime'=>round(microtime(true)-DOVE_START_TIME,8), - ]; - if (Plugin::exists('Compiling')) { - $uncf_content = file_exists(App::$file)?htmlspecialchars(file_get_contents(App::$file)):'[File Not Found]'; - $cf_content = file_exists(App::$cachePath)?htmlspecialchars(file_get_contents(App::$cachePath)):'[File Not Found]'; - $array['mistake_file'] = '

未编译文件

'.str_replace(ROOT_DIR,'',App::$file).'
'.$uncf_content.'

编译后文件

'.str_replace(ROOT_DIR,'',App::$cachePath).'
'.$cf_content.'
'; - } else { - $content = file_exists(App::$file)?htmlspecialchars(file_get_contents(App::$file)):'[File Not Found]'; - $array['mistake_file'] = '

发生错误的文件

'.str_replace(ROOT_DIR,'',App::$file).'
'.$content.'
'; - } - $value = []; - $string= []; - foreach($array as $val=>$str){ - $value[] = '{$'.$val.'}'; - $string[]= $str; - } - ob_clean(); - die(str_replace($value,$string,file_get_contents(self::$tplDir.$tpl.'.tpl'))); - } - - // 输出json - private static function json($tpl) - { - $stack = []; - $line = 1; - foreach(self::$backtrace as $key => $val){ - if(isset($val['class'])) $stack['#'.$line]['do']=$val['class'].$val['type']; - if(isset($val['function'])) $stack['#'.$line]['do']= ($val['function']=='{closure}') ? '{closure}' : $val['function'].'()'; - if(isset($val['file']) && isset($val['line'])) $stack['#'.$line]['in']=$val['file'].':'.$val['line']; - $line++; - } - $code = self::$code; - $info = self::$info; - $file = self::$file; - $array = require(self::$tplDir.$tpl.'.php'); - ob_clean(); - header('Content-type: application/json;charset=utf-8'); - die(json_encode($array,JSON_UNESCAPED_UNICODE)); - } - - public static function array_list($array){ - $return = ''; - foreach($array as $k=>$v){ - if($v=='') $v = 'NULL'; - $return.= "$k = $v
"; - } - return empty($return)?'--Empty--':$return; - } -} \ No newline at end of file diff --git a/dove/lib/Log.php b/dove/lib/Log.php deleted file mode 100644 index 7b53729..0000000 --- a/dove/lib/Log.php +++ /dev/null @@ -1,36 +0,0 @@ -$v) $text.="-{$k}:{$v}\r\n"; - return static::write($text,DOVE_RUNTIME_DIR.'log/'.$path.'/',$logname.'.log'); - } - - // 错误log - public static function saveErr($errFile='未知',$errInfo='未知',$remarks='无') - { - if(!Config::get('dove','error_log')) return true; - return static::write("\r\n[ERROR][".date('Y-m-d H:i:s')."][".get_ip()."]\r\n-请求链接:".Route::url(true)."\r\n-文件路径:{$errFile}\r\n-报错内容:{$errInfo}\r\n-加载时间:".round(microtime(true)-DOVE_START_TIME,8)."(s)\r\n-报错备注:{$remarks}\r\n",DOVE_RUNTIME_DIR.'log/'.date('Ym'),date('d').'.log'); - } - - public static function write($text,$path,$file) - { - if(!file_exists($path)) mkdir($path,0777,true); - return file_put_contents($path.'/'.$file,$text,FILE_APPEND|LOCK_EX); - } -} \ No newline at end of file diff --git a/dove/lib/Plugin.php b/dove/lib/Plugin.php deleted file mode 100644 index e2018c6..0000000 --- a/dove/lib/Plugin.php +++ /dev/null @@ -1,28 +0,0 @@ -path = $c['path']; - $this->suffix = $c['suffix']; - $this->compress_level = $c['compress_level']; - return true; - } - - // 设置缓存,数组自动序列化 - public function set($key,$value='',$exp=0){ - $f = $this->path.$key.$this->suffix; - file_put_contents($f,gzcompress(strval(is_array($value)?serialize($value):$value),$this->compress_level)); - return touch($f,time()+$exp); - } - - // 读取缓存,数组自动去序列化 - // get(名称,true是删除后返回内容,默认删除后返回false) - public function get($key,$delp=false){ - $f = $this->path.$key.$this->suffix; - if(!file_exists($f)) return false; - $data = gzuncompress(file_get_contents($f)); - if(filemtime($f) < time()){ - unlink($f); - if(!$delp) return false; - } - return (preg_match("/^a\:[0-9]\:\{.*\}/",$data)==1)?unserialize($data):$data; - } - - // 数值自增,返回自增后的值 - public function inc($key,$int=1){ - $f = $this->path.$key.$this->suffix; - if(!file_exists($f)) return false; - $int = intval(gzuncompress(file_get_contents($f)))+$int; - file_put_contents($f,gzcompress(strval($int),$this->compress_level)); - return $int; - } - - // 数值自减,返回自减后的值 - public function dec($key,$int=1){ - $f = $this->path.$key.$this->suffix; - if(!file_exists($f)) return false; - $int = intval(gzuncompress(file_get_contents($f)))-$int; - file_put_contents($f,gzcompress(strval($int),$this->compress_level)); - return $int; - } - - // 删除缓存,可进行多个删除 - public function del(){ - $keys = func_get_args(); - foreach($keys as $key){ - $f = $this->path.$key.$this->suffix; - if(!file_exists($f)) continue; - unlink($f); - } - return true; - } - - // 删除所有缓存,包括子目录 - public function clean(){ - $dirs = scandir($this->path); - foreach ($dirs as $dir) { - if ($dir != '.' && $dir != '..') { - $sonDir = $this->path.'/'.$dir; - if(is_dir($sonDir)){ - $this->clear($sonDir); - @rmdir($sonDir); - } else { - @unlink($sonDir); - } - } - } - return true; - } - - // 保存缓存 - public function save() - { - - } -} \ No newline at end of file diff --git a/dove/lib/cache/Memcache.php b/dove/lib/cache/Memcache.php deleted file mode 100644 index ad391f3..0000000 --- a/dove/lib/cache/Memcache.php +++ /dev/null @@ -1,60 +0,0 @@ -conn = ($keep)?memcache_pconnect(self::$config['host'],self::$config['port']):memcache_connect(self::$config['host'],self::$config['port']); - if(self::$config['auto_compress']) memcache_set_compress_threshold($this->conn,self::$config['threshold'],self::$config['min_saving']); - } - - // 设置 - public function set($key='',$value='',$flag=0,$expire=0){ - return memcache_set($this->conn,$key,$value,$flag,$expire); - } - - // 获取 - public function get($key=''){ - return memcache_get($this->conn,$key); - } - - // 自增 - public function inc($key='',$value=''){ - return memcache_increment($this->conn,$key,$value); - } - - // 自减 - public function dec($key='',$value=''){ - return memcache_decrement($this->conn,$key,$value); - } - - // 删除全部 - public function clear(){ - return memcache_flush($this->conn); - } - - // 删除指定 - public function del($key='',$timeout=0){ - return memcache_delete($this->conn,$key,$timeout); - } - - // 服务状态 - public function server_status(){ - return memcache_get_server_status($this->conn,self::$config['host'],self::$config['port']); - } - - // 版本 - public function get_version(){ - return memcache_get_version($this->conn); - } - - public function close() - { - return memcache_close($this->conn); - } -} \ No newline at end of file diff --git a/dove/lib/db/Medoo.php b/dove/lib/db/Medoo.php deleted file mode 100644 index c2fc2e4..0000000 --- a/dove/lib/db/Medoo.php +++ /dev/null @@ -1,2205 +0,0 @@ ->')); - if (isset($options['prefix'])) $this->prefix = $options['prefix']; - if (isset($options['testMode']) && $options['testMode'] == true) { - $this->testMode = true; - return; - } - $options['type'] = $options['type'] ?? $options['database_type']; - if (!isset($options['pdo'])) { - $options['database'] = $options['database'] ?? $options['database_name']; - if (!isset($options['socket'])) $options['host'] = $options['host'] ?? $options['server'] ?? false; - } - - if (isset($options['type'])) { - $this->type = strtolower($options['type']); - if ($this->type === 'mariadb') $this->type = 'mysql'; - } - - if (isset($options['logging']) && is_bool($options['logging'])) $this->logging = $options['logging']; - $option = $options['option'] ?? []; - $commands = (isset($options['command']) && is_array($options['command']))?$options['command']:[]; - - switch ($this->type) { - - case 'mysql': - // Make MySQL using standard quoted identifier. - $commands[] = 'SET SQL_MODE=ANSI_QUOTES'; - - break; - - case 'mssql': - // Keep MSSQL QUOTED_IDENTIFIER is ON for standard quoting. - $commands[] = 'SET QUOTED_IDENTIFIER ON'; - - // Make ANSI_NULLS is ON for NULL value. - $commands[] = 'SET ANSI_NULLS ON'; - - break; - } - - if (isset($options['pdo'])) { - if (!$options['pdo'] instanceof PDO) { - throw new InvalidArgumentException('Invalid PDO object supplied.'); - } - - $this->pdo = $options['pdo']; - - foreach ($commands as $value) { - $this->pdo->exec($value); - } - - return; - } - - if (isset($options['dsn'])) { - if (is_array($options['dsn']) && isset($options['dsn']['driver'])) { - $attr = $options['dsn']; - } else { - throw new InvalidArgumentException('Invalid DSN option supplied.'); - } - } else { - if ( - isset($options['port']) && - is_int($options['port'] * 1) - ) { - $port = $options['port']; - } - - $isPort = isset($port); - - switch ($this->type) { - - case 'mysql': - $attr = [ - 'driver' => 'mysql', - 'dbname' => $options['database'] - ]; - - if (isset($options['socket'])) { - $attr['unix_socket'] = $options['socket']; - } else { - $attr['host'] = $options['host']; - - if ($isPort) { - $attr['port'] = $port; - } - } - - break; - - case 'pgsql': - $attr = [ - 'driver' => 'pgsql', - 'host' => $options['host'], - 'dbname' => $options['database'] - ]; - - if ($isPort) { - $attr['port'] = $port; - } - - break; - - case 'sybase': - $attr = [ - 'driver' => 'dblib', - 'host' => $options['host'], - 'dbname' => $options['database'] - ]; - - if ($isPort) { - $attr['port'] = $port; - } - - break; - - case 'oracle': - $attr = [ - 'driver' => 'oci', - 'dbname' => $options['host'] ? - '//' . $options['host'] . ($isPort ? ':' . $port : ':1521') . '/' . $options['database'] : - $options['database'] - ]; - - if (isset($options['charset'])) { - $attr['charset'] = $options['charset']; - } - - break; - - case 'mssql': - if (isset($options['driver']) && $options['driver'] === 'dblib') { - $attr = [ - 'driver' => 'dblib', - 'host' => $options['host'] . ($isPort ? ':' . $port : ''), - 'dbname' => $options['database'] - ]; - - if (isset($options['appname'])) { - $attr['appname'] = $options['appname']; - } - - if (isset($options['charset'])) { - $attr['charset'] = $options['charset']; - } - } else { - $attr = [ - 'driver' => 'sqlsrv', - 'Server' => $options['host'] . ($isPort ? ',' . $port : ''), - 'Database' => $options['database'] - ]; - - if (isset($options['appname'])) { - $attr['APP'] = $options['appname']; - } - - $config = [ - 'ApplicationIntent', - 'AttachDBFileName', - 'Authentication', - 'ColumnEncryption', - 'ConnectionPooling', - 'Encrypt', - 'Failover_Partner', - 'KeyStoreAuthentication', - 'KeyStorePrincipalId', - 'KeyStoreSecret', - 'LoginTimeout', - 'MultipleActiveResultSets', - 'MultiSubnetFailover', - 'Scrollable', - 'TraceFile', - 'TraceOn', - 'TransactionIsolation', - 'TransparentNetworkIPResolution', - 'TrustServerCertificate', - 'WSID', - ]; - - foreach ($config as $value) { - $keyname = strtolower(preg_replace(['/([a-z\d])([A-Z])/', '/([^_])([A-Z][a-z])/'], '$1_$2', $value)); - - if (isset($options[$keyname])) { - $attr[$value] = $options[$keyname]; - } - } - } - - break; - - case 'sqlite': - $attr = [ - 'driver' => 'sqlite', - $options['database'] - ]; - - break; - } - } - - if (!isset($attr)) { - throw new InvalidArgumentException('Incorrect connection options.'); - } - - $driver = $attr['driver']; - - if (!in_array($driver, PDO::getAvailableDrivers())) { - throw new InvalidArgumentException("Unsupported PDO driver: {$driver}."); - } - - unset($attr['driver']); - - $stack = []; - - foreach ($attr as $key => $value) { - $stack[] = is_int($key) ? $value : $key . '=' . $value; - } - - $dsn = $driver . ':' . implode(';', $stack); - - if ( - in_array($this->type, ['mysql', 'pgsql', 'sybase', 'mssql']) && - isset($options['charset']) - ) { - $commands[] = "SET NAMES '{$options['charset']}'" . ( - $this->type === 'mysql' && isset($options['collation']) ? - " COLLATE '{$options['collation']}'" : '' - ); - } - - $this->dsn = $dsn; - - try { - $this->pdo = new PDO( - $dsn, - $options['username'] ?? null, - $options['password'] ?? null, - $option - ); - - if (isset($options['error'])) { - $this->pdo->setAttribute( - PDO::ATTR_ERRMODE, - in_array($options['error'], [ - PDO::ERRMODE_SILENT, - PDO::ERRMODE_WARNING, - PDO::ERRMODE_EXCEPTION - ]) ? - $options['error'] : - PDO::ERRMODE_SILENT - ); - } - - foreach ($commands as $value) { - $this->pdo->exec($value); - } - } catch (PDOException $e) { - throw new PDOException($e->getMessage()); - } - } - - /** - * Generate a new map key for placeholder. - * - * @return string - */ - protected function mapKey(): string - { - return ':MeD' . $this->guid++ . '_mK'; - } - - /** - * Execute customized raw statement. - * - * @param string $statement The raw SQL statement. - * @param array $map The array of input parameters value for prepared statement. - * @return \PDOStatement|null - */ - public function query(string $statement, array $map = []): ?PDOStatement - { - $raw = $this->raw($statement, $map); - $statement = $this->buildRaw($raw, $map); - - return $this->exec($statement, $map); - } - - /** - * Execute the raw statement. - * - * @param string $statement The SQL statement. - * @param array $map The array of input parameters value for prepared statement. - * @codeCoverageIgnore - * @return \PDOStatement|null - */ - public function exec(string $statement, array $map = [], callable $callback = null): ?PDOStatement - { - $this->statement = null; - $this->errorInfo = null; - $this->error = null; - - if ($this->testMode) { - $this->queryString = $this->generate($statement, $map); - return null; - } - - if ($this->debugMode) { - if ($this->debugLogging) { - $this->debugLogs[] = $this->generate($statement, $map); - return null; - } - - echo $this->generate($statement, $map); - - $this->debugMode = false; - - return null; - } - - if ($this->logging) { - $this->logs[] = [$statement, $map]; - } else { - $this->logs = [[$statement, $map]]; - } - - $statement = $this->pdo->prepare($statement); - $errorInfo = $this->pdo->errorInfo(); - - if ($errorInfo[0] !== '00000') { - $this->errorInfo = $errorInfo; - $this->error = $errorInfo[2]; - - return null; - } - - foreach ($map as $key => $value) { - $statement->bindValue($key, $value[0], $value[1]); - } - - if (is_callable($callback)) { - $this->pdo->beginTransaction(); - $callback($statement); - $execute = $statement->execute(); - $this->pdo->commit(); - } else { - $execute = $statement->execute(); - } - - $errorInfo = $statement->errorInfo(); - - if ($errorInfo[0] !== '00000') { - $this->errorInfo = $errorInfo; - $this->error = $errorInfo[2]; - - return null; - } - - if ($execute) { - $this->statement = $statement; - } - - return $statement; - } - - /** - * Generate readable statement. - * - * @param string $statement - * @param array $map - * @codeCoverageIgnore - * @return string - */ - protected function generate(string $statement, array $map): string - { - $identifier = [ - 'mysql' => '`$1`', - 'mssql' => '[$1]' - ]; - - $statement = preg_replace( - '/(?!\'[^\s]+\s?)"([\p{L}_][\p{L}\p{N}@$#\-_]*)"(?!\s?[^\s]+\')/u', - $identifier[$this->type] ?? '"$1"', - $statement - ); - - foreach ($map as $key => $value) { - if ($value[1] === PDO::PARAM_STR) { - $replace = $this->quote($value[0]); - } elseif ($value[1] === PDO::PARAM_NULL) { - $replace = 'NULL'; - } elseif ($value[1] === PDO::PARAM_LOB) { - $replace = '{LOB_DATA}'; - } else { - $replace = $value[0] . ''; - } - - $statement = str_replace($key, $replace, $statement); - } - - return $statement; - } - - /** - * Build a raw object. - * - * @param string $string The raw string. - * @param array $map The array of mapping data for the raw string. - * @return Medoo::raw - */ - public static function raw(string $string, array $map = []): Raw - { - $raw = new Raw(); - - $raw->map = $map; - $raw->value = $string; - - return $raw; - } - - /** - * Finds whether the object is raw. - * - * @param object $object - * @return bool - */ - protected function isRaw($object): bool - { - return $object instanceof Raw; - } - - /** - * Generate the actual query from the raw object. - * - * @param mixed $raw - * @param array $map - * @return string|null - */ - protected function buildRaw($raw, array &$map): ?string - { - if (!$this->isRaw($raw)) { - return null; - } - - $query = preg_replace_callback( - '/(([`\']).*?)?((FROM|TABLE|INTO|UPDATE|JOIN)\s*)?\<(([\p{L}_][\p{L}\p{N}@$#\-_]*)(\.[\p{L}_][\p{L}\p{N}@$#\-_]*)?)\>([^,]*?\2)?/u', - function ($matches) { - if (!empty($matches[2]) && isset($matches[8])) { - return $matches[0]; - } - - if (!empty($matches[4])) { - return $matches[1] . $matches[4] . ' ' . $this->tableQuote($matches[5]); - } - - return $matches[1] . $this->columnQuote($matches[5]); - }, - $raw->value - ); - - $rawMap = $raw->map; - - if (!empty($rawMap)) { - foreach ($rawMap as $key => $value) { - $map[$key] = $this->typeMap($value, gettype($value)); - } - } - - return $query; - } - - /** - * Quote a string for use in a query. - * - * @param string $string - * @return string - */ - public function quote(string $string): string - { - if ($this->type === 'mysql') { - return "'" . preg_replace(['/([\'"])/', '/(\\\\\\\")/'], ["\\\\\${1}", '\\\${1}'], $string) . "'"; - } - - return "'" . preg_replace('/\'/', '\'\'', $string) . "'"; - } - - /** - * Quote table name for use in a query. - * - * @param string $table - * @return string - */ - public function tableQuote(string $table): string - { - if (preg_match('/^[\p{L}_][\p{L}\p{N}@$#\-_]*$/u', $table)) { - return '"' . $this->prefix . $table . '"'; - } - - throw new InvalidArgumentException("Incorrect table name: {$table}."); - } - - /** - * Quote column name for use in a query. - * - * @param string $column - * @return string - */ - public function columnQuote(string $column): string - { - if (preg_match('/^[\p{L}_][\p{L}\p{N}@$#\-_]*(\.?[\p{L}_][\p{L}\p{N}@$#\-_]*)?$/u', $column)) { - return strpos($column, '.') !== false ? - '"' . $this->prefix . str_replace('.', '"."', $column) . '"' : - '"' . $column . '"'; - } - - throw new InvalidArgumentException("Incorrect column name: {$column}."); - } - - /** - * Mapping the type name as PDO data type. - * - * @param mixed $value - * @param string $type - * @return array - */ - protected function typeMap($value, string $type): array - { - $map = [ - 'NULL' => PDO::PARAM_NULL, - 'integer' => PDO::PARAM_INT, - 'double' => PDO::PARAM_STR, - 'boolean' => PDO::PARAM_BOOL, - 'string' => PDO::PARAM_STR, - 'object' => PDO::PARAM_STR, - 'resource' => PDO::PARAM_LOB - ]; - - if ($type === 'boolean') { - $value = ($value ? '1' : '0'); - } elseif ($type === 'NULL') { - $value = null; - } - - return [$value, $map[$type]]; - } - - /** - * Build the statement part for the column stack. - * - * @param array|string $columns - * @param array $map - * @param bool $root - * @param bool $isJoin - * @return string - */ - protected function columnPush(&$columns, array &$map, bool $root, bool $isJoin = false): string - { - if ($columns === '*') { - return $columns; - } - - $stack = []; - $hasDistinct = false; - - if (is_string($columns)) { - $columns = [$columns]; - } - - foreach ($columns as $key => $value) { - $isIntKey = is_int($key); - $isArrayValue = is_array($value); - - if (!$isIntKey && $isArrayValue && $root && count(array_keys($columns)) === 1) { - $stack[] = $this->columnQuote($key); - $stack[] = $this->columnPush($value, $map, false, $isJoin); - } elseif ($isArrayValue) { - $stack[] = $this->columnPush($value, $map, false, $isJoin); - } elseif (!$isIntKey && $raw = $this->buildRaw($value, $map)) { - preg_match('/(?[\p{L}_][\p{L}\p{N}@$#\-_\.]*)(\s*\[(?(String|Bool|Int|Number))\])?/u', $key, $match); - $stack[] = "{$raw} AS {$this->columnQuote($match['column'])}"; - } elseif ($isIntKey && is_string($value)) { - if ($isJoin && strpos($value, '*') !== false) { - throw new InvalidArgumentException('Cannot use table.* to select all columns while joining table.'); - } - - preg_match('/(?[\p{L}_][\p{L}\p{N}@$#\-_\.]*)(?:\s*\((?[\p{L}_][\p{L}\p{N}@$#\-_]*)\))?(?:\s*\[(?(?:String|Bool|Int|Number|Object|JSON))\])?/u', $value, $match); - - $columnString = ''; - - if (!empty($match['alias'])) { - $columnString = "{$this->columnQuote($match['column'])} AS {$this->columnQuote($match['alias'])}"; - $columns[$key] = $match['alias']; - - if (!empty($match['type'])) { - $columns[$key] .= ' [' . $match['type'] . ']'; - } - } else { - $columnString = $this->columnQuote($match['column']); - } - - if (!$hasDistinct && strpos($value, '@') === 0) { - $columnString = 'DISTINCT ' . $columnString; - $hasDistinct = true; - array_unshift($stack, $columnString); - - continue; - } - - $stack[] = $columnString; - } - } - - return implode(',', $stack); - } - - /** - * Implode where conditions. - * - * @param array $data - * @param array $map - * @param string $conjunctor - * @return string - */ - protected function dataImplode(array $data, array &$map, string $conjunctor): string - { - $stack = []; - - foreach ($data as $key => $value) { - $type = gettype($value); - - if ( - $type === 'array' && - preg_match("/^(AND|OR)(\s+#.*)?$/", $key, $relationMatch) - ) { - $stack[] = '(' . $this->dataImplode($value, $map, ' ' . $relationMatch[1]) . ')'; - continue; - } - - $mapKey = $this->mapKey(); - $isIndex = is_int($key); - - preg_match( - '/([\p{L}_][\p{L}\p{N}@$#\-_\.]*)(\[(?.*)\])?([\p{L}_][\p{L}\p{N}@$#\-_\.]*)?/u', - $isIndex ? $value : $key, - $match - ); - - $column = $this->columnQuote($match[1]); - $operator = $match['operator'] ?? null; - - if ($isIndex && isset($match[4]) && in_array($operator, ['>', '>=', '<', '<=', '=', '!='])) { - $stack[] = "${column} ${operator} " . $this->columnQuote($match[4]); - continue; - } - - if ($operator && $operator != '=') { - if (in_array($operator, ['>', '>=', '<', '<='])) { - $condition = "{$column} {$operator} "; - - if (is_numeric($value)) { - $condition .= $mapKey; - $map[$mapKey] = [$value, is_float($value) ? PDO::PARAM_STR : PDO::PARAM_INT]; - } elseif ($raw = $this->buildRaw($value, $map)) { - $condition .= $raw; - } else { - $condition .= $mapKey; - $map[$mapKey] = [$value, PDO::PARAM_STR]; - } - - $stack[] = $condition; - } elseif ($operator === '!') { - switch ($type) { - - case 'NULL': - $stack[] = $column . ' IS NOT NULL'; - break; - - case 'array': - $placeholders = []; - - foreach ($value as $index => $item) { - $stackKey = $mapKey . $index . '_i'; - $placeholders[] = $stackKey; - $map[$stackKey] = $this->typeMap($item, gettype($item)); - } - - $stack[] = $column . ' NOT IN (' . implode(', ', $placeholders) . ')'; - break; - - case 'object': - if ($raw = $this->buildRaw($value, $map)) { - $stack[] = "{$column} != {$raw}"; - } - break; - - case 'integer': - case 'double': - case 'boolean': - case 'string': - $stack[] = "{$column} != {$mapKey}"; - $map[$mapKey] = $this->typeMap($value, $type); - break; - } - } elseif ($operator === '~' || $operator === '!~') { - if ($type !== 'array') { - $value = [$value]; - } - - $connector = ' OR '; - $data = array_values($value); - - if (is_array($data[0])) { - if (isset($value['AND']) || isset($value['OR'])) { - $connector = ' ' . array_keys($value)[0] . ' '; - $value = $data[0]; - } - } - - $likeClauses = []; - - foreach ($value as $index => $item) { - $item = strval($item); - - if (!preg_match('/((?' || $operator === '><') { - if ($type === 'array') { - if ($operator === '><') { - $column .= ' NOT'; - } - - if ($this->isRaw($value[0]) && $this->isRaw($value[1])) { - $stack[] = "({$column} BETWEEN {$this->buildRaw($value[0], $map)} AND {$this->buildRaw($value[1], $map)})"; - } else { - $stack[] = "({$column} BETWEEN {$mapKey}a AND {$mapKey}b)"; - $dataType = (is_numeric($value[0]) && is_numeric($value[1])) ? PDO::PARAM_INT : PDO::PARAM_STR; - - $map[$mapKey . 'a'] = [$value[0], $dataType]; - $map[$mapKey . 'b'] = [$value[1], $dataType]; - } - } - } elseif ($operator === 'REGEXP') { - $stack[] = "{$column} REGEXP {$mapKey}"; - $map[$mapKey] = [$value, PDO::PARAM_STR]; - } else { - throw new InvalidArgumentException("Invalid operator [{$operator}] for column {$column} supplied."); - } - - continue; - } - - switch ($type) { - - case 'NULL': - $stack[] = $column . ' IS NULL'; - break; - - case 'array': - $placeholders = []; - - foreach ($value as $index => $item) { - $stackKey = $mapKey . $index . '_i'; - - $placeholders[] = $stackKey; - $map[$stackKey] = $this->typeMap($item, gettype($item)); - } - - $stack[] = $column . ' IN (' . implode(', ', $placeholders) . ')'; - break; - - case 'object': - if ($raw = $this->buildRaw($value, $map)) { - $stack[] = "{$column} = {$raw}"; - } - break; - - case 'integer': - case 'double': - case 'boolean': - case 'string': - $stack[] = "{$column} = {$mapKey}"; - $map[$mapKey] = $this->typeMap($value, $type); - break; - } - } - - return implode($conjunctor . ' ', $stack); - } - - /** - * Build the where clause. - * - * @param array|null $where - * @param array $map - * @return string - */ - protected function whereClause($where, array &$map): string - { - $clause = ''; - - if (is_array($where)) { - $conditions = array_diff_key($where, array_flip( - ['GROUP', 'ORDER', 'HAVING', 'LIMIT', 'LIKE', 'MATCH'] - )); - - if (!empty($conditions)) { - $clause = ' WHERE ' . $this->dataImplode($conditions, $map, ' AND'); - } - - if (isset($where['MATCH']) && $this->type === 'mysql') { - $match = $where['MATCH']; - - if (is_array($match) && isset($match['columns'], $match['keyword'])) { - $mode = ''; - - $options = [ - 'natural' => 'IN NATURAL LANGUAGE MODE', - 'natural+query' => 'IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION', - 'boolean' => 'IN BOOLEAN MODE', - 'query' => 'WITH QUERY EXPANSION' - ]; - - if (isset($match['mode'], $options[$match['mode']])) { - $mode = ' ' . $options[$match['mode']]; - } - - $columns = implode(', ', array_map([$this, 'columnQuote'], $match['columns'])); - $mapKey = $this->mapKey(); - $map[$mapKey] = [$match['keyword'], PDO::PARAM_STR]; - $clause .= ($clause !== '' ? ' AND ' : ' WHERE') . ' MATCH (' . $columns . ') AGAINST (' . $mapKey . $mode . ')'; - } - } - - if (isset($where['GROUP'])) { - $group = $where['GROUP']; - - if (is_array($group)) { - $stack = []; - - foreach ($group as $column => $value) { - $stack[] = $this->columnQuote($value); - } - - $clause .= ' GROUP BY ' . implode(',', $stack); - } elseif ($raw = $this->buildRaw($group, $map)) { - $clause .= ' GROUP BY ' . $raw; - } else { - $clause .= ' GROUP BY ' . $this->columnQuote($group); - } - } - - if (isset($where['HAVING'])) { - $having = $where['HAVING']; - - if ($raw = $this->buildRaw($having, $map)) { - $clause .= ' HAVING ' . $raw; - } else { - $clause .= ' HAVING ' . $this->dataImplode($having, $map, ' AND'); - } - } - - if (isset($where['ORDER'])) { - $order = $where['ORDER']; - - if (is_array($order)) { - $stack = []; - - foreach ($order as $column => $value) { - if (is_array($value)) { - $valueStack = []; - - foreach ($value as $item) { - $valueStack[] = is_int($item) ? $item : $this->quote($item); - } - - $valueString = implode(',', $valueStack); - $stack[] = "FIELD({$this->columnQuote($column)}, {$valueString})"; - } elseif ($value === 'ASC' || $value === 'DESC') { - $stack[] = $this->columnQuote($column) . ' ' . $value; - } elseif (is_int($column)) { - $stack[] = $this->columnQuote($value); - } - } - - $clause .= ' ORDER BY ' . implode(',', $stack); - } elseif ($raw = $this->buildRaw($order, $map)) { - $clause .= ' ORDER BY ' . $raw; - } else { - $clause .= ' ORDER BY ' . $this->columnQuote($order); - } - } - - if (isset($where['LIMIT'])) { - $limit = $where['LIMIT']; - - if (in_array($this->type, ['oracle', 'mssql'])) { - if ($this->type === 'mssql' && !isset($where['ORDER'])) { - $clause .= ' ORDER BY (SELECT 0)'; - } - - if (is_numeric($limit)) { - $limit = [0, $limit]; - } - - if ( - is_array($limit) && - is_numeric($limit[0]) && - is_numeric($limit[1]) - ) { - $clause .= " OFFSET {$limit[0]} ROWS FETCH NEXT {$limit[1]} ROWS ONLY"; - } - } else { - if (is_numeric($limit)) { - $clause .= ' LIMIT ' . $limit; - } elseif ( - is_array($limit) && - is_numeric($limit[0]) && - is_numeric($limit[1]) - ) { - $clause .= " LIMIT {$limit[1]} OFFSET {$limit[0]}"; - } - } - } - } elseif ($raw = $this->buildRaw($where, $map)) { - $clause .= ' ' . $raw; - } - - return $clause; - } - - /** - * Build statement for the select query. - * - * @param string $table - * @param array $map - * @param array|string $join - * @param array|string $columns - * @param array $where - * @param string $columnFn - * @return string - */ - protected function selectContext( - string $table, - array &$map, - $join, - &$columns = null, - $where = null, - $columnFn = null - ): string { - preg_match('/(?[\p{L}_][\p{L}\p{N}@$#\-_]*)\s*\((?[\p{L}_][\p{L}\p{N}@$#\-_]*)\)/u', $table, $tableMatch); - - if (isset($tableMatch['table'], $tableMatch['alias'])) { - $table = $this->tableQuote($tableMatch['table']); - $tableAlias = $this->tableQuote($tableMatch['alias']); - $tableQuery = "{$table} AS {$tableAlias}"; - } else { - $table = $this->tableQuote($table); - $tableQuery = $table; - } - - $isJoin = $this->isJoin($join); - - if ($isJoin) { - $tableQuery .= ' ' . $this->buildJoin($tableAlias ?? $table, $join, $map); - } else { - if (is_null($columns)) { - if ( - !is_null($where) || - (is_array($join) && isset($columnFn)) - ) { - $where = $join; - $columns = null; - } else { - $where = null; - $columns = $join; - } - } else { - $where = $columns; - $columns = $join; - } - } - - if (isset($columnFn)) { - if ($columnFn === 1) { - $column = '1'; - - if (is_null($where)) { - $where = $columns; - } - } elseif ($raw = $this->buildRaw($columnFn, $map)) { - $column = $raw; - } else { - if (empty($columns) || $this->isRaw($columns)) { - $columns = '*'; - $where = $join; - } - - $column = $columnFn . '(' . $this->columnPush($columns, $map, true) . ')'; - } - } else { - $column = $this->columnPush($columns, $map, true, $isJoin); - } - - return 'SELECT ' . $column . ' FROM ' . $tableQuery . $this->whereClause($where, $map); - } - - /** - * Determine the array is with join syntax. - * - * @param mixed $join - * @return bool - */ - protected function isJoin($join): bool - { - if (!is_array($join)) { - return false; - } - - $keys = array_keys($join); - - if ( - isset($keys[0]) && - is_string($keys[0]) && - strpos($keys[0], '[') === 0 - ) { - return true; - } - - return false; - } - - /** - * Build the join statement. - * - * @param string $table - * @param array $join - * @param array $map - * @return string - */ - protected function buildJoin(string $table, array $join, array &$map): string - { - $tableJoin = []; - $type = [ - '>' => 'LEFT', - '<' => 'RIGHT', - '<>' => 'FULL', - '><' => 'INNER' - ]; - - foreach ($join as $subtable => $relation) { - preg_match('/(\[(?\<\>?|\>\[\p{L}_][\p{L}\p{N}@$#\-_]*)\s?(\((?[\p{L}_][\p{L}\p{N}@$#\-_]*)\))?/u', $subtable, $match); - - if ($match['join'] === '' || $match['table'] === '') { - continue; - } - - if (is_string($relation)) { - $relation = 'USING ("' . $relation . '")'; - } elseif (is_array($relation)) { - // For ['column1', 'column2'] - if (isset($relation[0])) { - $relation = 'USING ("' . implode('", "', $relation) . '")'; - } else { - $joins = []; - - foreach ($relation as $key => $value) { - if ($key === 'AND' && is_array($value)) { - $joins[] = $this->dataImplode($value, $map, ' AND'); - continue; - } - - $joins[] = ( - strpos($key, '.') > 0 ? - // For ['tableB.column' => 'column'] - $this->columnQuote($key) : - - // For ['column1' => 'column2'] - $table . '.' . $this->columnQuote($key) - ) . - ' = ' . - $this->tableQuote($match['alias'] ?? $match['table']) . '.' . $this->columnQuote($value); - } - - $relation = 'ON ' . implode(' AND ', $joins); - } - } elseif ($raw = $this->buildRaw($relation, $map)) { - $relation = $raw; - } - - $tableName = $this->tableQuote($match['table']); - - if (isset($match['alias'])) { - $tableName .= ' AS ' . $this->tableQuote($match['alias']); - } - - $tableJoin[] = $type[$match['join']] . " JOIN ${tableName} ${relation}"; - } - - return implode(' ', $tableJoin); - } - - /** - * Mapping columns for the stack. - * - * @param array|string $columns - * @param array $stack - * @param bool $root - * @return array - */ - protected function columnMap($columns, array &$stack, bool $root): array - { - if ($columns === '*') { - return $stack; - } - - foreach ($columns as $key => $value) { - if (is_int($key)) { - preg_match('/([\p{L}_][\p{L}\p{N}@$#\-_]*\.)?(?[\p{L}_][\p{L}\p{N}@$#\-_]*)(?:\s*\((?[\p{L}_][\p{L}\p{N}@$#\-_]*)\))?(?:\s*\[(?(?:String|Bool|Int|Number|Object|JSON))\])?/u', $value, $keyMatch); - - $columnKey = !empty($keyMatch['alias']) ? - $keyMatch['alias'] : - $keyMatch['column']; - - $stack[$value] = isset($keyMatch['type']) ? - [$columnKey, $keyMatch['type']] : - [$columnKey, 'String']; - } elseif ($this->isRaw($value)) { - preg_match('/([\p{L}_][\p{L}\p{N}@$#\-_]*\.)?(?[\p{L}_][\p{L}\p{N}@$#\-_]*)(\s*\[(?(String|Bool|Int|Number))\])?/u', $key, $keyMatch); - $columnKey = $keyMatch['column']; - - $stack[$key] = isset($keyMatch['type']) ? - [$columnKey, $keyMatch['type']] : - [$columnKey, 'String']; - } elseif (!is_int($key) && is_array($value)) { - if ($root && count(array_keys($columns)) === 1) { - $stack[$key] = [$key, 'String']; - } - - $this->columnMap($value, $stack, false); - } - } - - return $stack; - } - - /** - * Mapping the data from the table. - * - * @param array $data - * @param array $columns - * @param array $columnMap - * @param array $stack - * @param bool $root - * @param array $result - * @codeCoverageIgnore - * @return void - */ - protected function dataMap( - array $data, - array $columns, - array $columnMap, - array &$stack, - bool $root, - array &$result = null - ): void { - if ($root) { - $columnsKey = array_keys($columns); - - if (count($columnsKey) === 1 && is_array($columns[$columnsKey[0]])) { - $indexKey = array_keys($columns)[0]; - $dataKey = preg_replace("/^[\p{L}_][\p{L}\p{N}@$#\-_]*\./u", '', $indexKey); - $currentStack = []; - - foreach ($data as $item) { - $this->dataMap($data, $columns[$indexKey], $columnMap, $currentStack, false, $result); - $index = $data[$dataKey]; - - if (isset($result)) { - $result[$index] = $currentStack; - } else { - $stack[$index] = $currentStack; - } - } - } else { - $currentStack = []; - $this->dataMap($data, $columns, $columnMap, $currentStack, false, $result); - - if (isset($result)) { - $result[] = $currentStack; - } else { - $stack = $currentStack; - } - } - - return; - } - - foreach ($columns as $key => $value) { - $isRaw = $this->isRaw($value); - - if (is_int($key) || $isRaw) { - $map = $columnMap[$isRaw ? $key : $value]; - $columnKey = $map[0]; - $item = $data[$columnKey]; - - if (isset($map[1])) { - if ($isRaw && in_array($map[1], ['Object', 'JSON'])) { - continue; - } - - if (is_null($item)) { - $stack[$columnKey] = null; - continue; - } - - switch ($map[1]) { - - case 'Number': - $stack[$columnKey] = (float) $item; - break; - - case 'Int': - $stack[$columnKey] = (int) $item; - break; - - case 'Bool': - $stack[$columnKey] = (bool) $item; - break; - - case 'Object': - $stack[$columnKey] = unserialize($item); - break; - - case 'JSON': - $stack[$columnKey] = json_decode($item, true); - break; - - case 'String': - $stack[$columnKey] = $item; - break; - } - } else { - $stack[$columnKey] = $item; - } - } else { - $currentStack = []; - $this->dataMap($data, $value, $columnMap, $currentStack, false, $result); - - $stack[$key] = $currentStack; - } - } - } - - /** - * Build and execute returning query. - * - * @param string $query - * @param array $map - * @param array $data - * @return \PDOStatement|null - */ - private function returningQuery($query, &$map, &$data): ?PDOStatement - { - $returnColumns = array_map( - function ($value) { - return $value[0]; - }, - $data - ); - - $query .= ' RETURNING ' . - implode(', ', array_map([$this, 'columnQuote'], $returnColumns)) . - ' INTO ' . - implode(', ', array_keys($data)); - - return $this->exec($query, $map, function ($statement) use (&$data) { - // @codeCoverageIgnoreStart - foreach ($data as $key => $return) { - if (isset($return[3])) { - $statement->bindParam($key, $data[$key][1], $return[2], $return[3]); - } else { - $statement->bindParam($key, $data[$key][1], $return[2]); - } - } - // @codeCoverageIgnoreEnd - }); - } - - /** - * Create a table. - * - * @param string $table - * @param array $columns Columns definition. - * @param array $options Additional table options for creating a table. - * @return \PDOStatement|null - */ - public function create(string $table, $columns, $options = null): ?PDOStatement - { - $stack = []; - $tableOption = ''; - $tableName = $this->tableQuote($table); - - foreach ($columns as $name => $definition) { - if (is_int($name)) { - $stack[] = preg_replace('/\<([\p{L}_][\p{L}\p{N}@$#\-_]*)\>/u', '"$1"', $definition); - } elseif (is_array($definition)) { - $stack[] = $this->columnQuote($name) . ' ' . implode(' ', $definition); - } elseif (is_string($definition)) { - $stack[] = $this->columnQuote($name) . ' ' . $definition; - } - } - - if (is_array($options)) { - $optionStack = []; - - foreach ($options as $key => $value) { - if (is_string($value) || is_int($value)) { - $optionStack[] = "{$key} = {$value}"; - } - } - - $tableOption = ' ' . implode(', ', $optionStack); - } elseif (is_string($options)) { - $tableOption = ' ' . $options; - } - - $command = 'CREATE TABLE'; - - if (in_array($this->type, ['mysql', 'pgsql', 'sqlite'])) { - $command .= ' IF NOT EXISTS'; - } - - return $this->exec("{$command} {$tableName} (" . implode(', ', $stack) . "){$tableOption}"); - } - - /** - * Drop a table. - * - * @param string $table - * @return \PDOStatement|null - */ - public function drop(string $table): ?PDOStatement - { - return $this->exec('DROP TABLE IF EXISTS ' . $this->tableQuote($this->prefix . $table)); - } - - /** - * Select data from the table. - * - * @param string $table - * @param array $join - * @param array|string $columns - * @param array $where - * @return array|null - */ - public function select(string $table, $join, $columns = null, $where = null): ?array - { - $map = []; - $result = []; - $columnMap = []; - - $args = func_get_args(); - $lastArgs = $args[array_key_last($args)]; - $callback = is_callable($lastArgs) ? $lastArgs : null; - - $where = is_callable($where) ? null : $where; - $columns = is_callable($columns) ? null : $columns; - - $column = $where === null ? $join : $columns; - $isSingle = (is_string($column) && $column !== '*'); - - $statement = $this->exec($this->selectContext($table, $map, $join, $columns, $where), $map); - - $this->columnMap($columns, $columnMap, true); - - if (!$this->statement) { - return $result; - } - - // @codeCoverageIgnoreStart - if ($columns === '*') { - if (isset($callback)) { - while ($data = $statement->fetch(PDO::FETCH_ASSOC)) { - $callback($data); - } - - return null; - } - - return $statement->fetchAll(PDO::FETCH_ASSOC); - } - - while ($data = $statement->fetch(PDO::FETCH_ASSOC)) { - $currentStack = []; - - if (isset($callback)) { - $this->dataMap($data, $columns, $columnMap, $currentStack, true); - - $callback( - $isSingle ? - $currentStack[$columnMap[$column][0]] : - $currentStack - ); - } else { - $this->dataMap($data, $columns, $columnMap, $currentStack, true, $result); - } - } - - if (isset($callback)) { - return null; - } - - if ($isSingle) { - $singleResult = []; - $resultKey = $columnMap[$column][0]; - - foreach ($result as $item) { - $singleResult[] = $item[$resultKey]; - } - - return $singleResult; - } - - return $result; - } - // @codeCoverageIgnoreEnd - - /** - * Insert one or more records into the table. - * - * @param string $table - * @param array $values - * @param string $primaryKey - * @return \PDOStatement|null - */ - public function insert(string $table, array $values, string $primaryKey = null): ?PDOStatement - { - $stack = []; - $columns = []; - $fields = []; - $map = []; - $returnings = []; - - if (!isset($values[0])) { - $values = [$values]; - } - - foreach ($values as $data) { - foreach ($data as $key => $value) { - $columns[] = $key; - } - } - - $columns = array_unique($columns); - - foreach ($values as $data) { - $values = []; - - foreach ($columns as $key) { - $value = $data[$key]; - $type = gettype($value); - - if ($this->type === 'oracle' && $type === 'resource') { - $values[] = 'EMPTY_BLOB()'; - $returnings[$this->mapKey()] = [$key, $value, PDO::PARAM_LOB]; - continue; - } - - if ($raw = $this->buildRaw($data[$key], $map)) { - $values[] = $raw; - continue; - } - - $mapKey = $this->mapKey(); - $values[] = $mapKey; - - switch ($type) { - - case 'array': - $map[$mapKey] = [ - strpos($key, '[JSON]') === strlen($key) - 6 ? - json_encode($value) : - serialize($value), - PDO::PARAM_STR - ]; - break; - - case 'object': - $value = serialize($value); - break; - - case 'NULL': - case 'resource': - case 'boolean': - case 'integer': - case 'double': - case 'string': - $map[$mapKey] = $this->typeMap($value, $type); - break; - } - } - - $stack[] = '(' . implode(', ', $values) . ')'; - } - - foreach ($columns as $key) { - $fields[] = $this->columnQuote(preg_replace("/(\s*\[JSON\]$)/i", '', $key)); - } - - $query = 'INSERT INTO ' . $this->tableQuote($table) . ' (' . implode(', ', $fields) . ') VALUES ' . implode(', ', $stack); - - if ( - $this->type === 'oracle' && (!empty($returnings) || isset($primaryKey)) - ) { - if ($primaryKey) { - $returnings[':RETURNID'] = [$primaryKey, '', PDO::PARAM_INT, 8]; - } - - $statement = $this->returningQuery($query, $map, $returnings); - - if ($primaryKey) { - $this->returnId = $returnings[':RETURNID'][1]; - } - - return $statement; - } - - return $this->exec($query, $map); - } - - /** - * Modify data from the table. - * - * @param string $table - * @param array $data - * @param array $where - * @return \PDOStatement|null - */ - public function update(string $table, $data, $where = null): ?PDOStatement - { - $fields = []; - $map = []; - $returnings = []; - - foreach ($data as $key => $value) { - $column = $this->columnQuote(preg_replace("/(\s*\[(JSON|\+|\-|\*|\/)\]$)/", '', $key)); - $type = gettype($value); - - if ($this->type === 'oracle' && $type === 'resource') { - $fields[] = "{$column} = EMPTY_BLOB()"; - $returnings[$this->mapKey()] = [$key, $value, PDO::PARAM_LOB]; - continue; - } - - if ($raw = $this->buildRaw($value, $map)) { - $fields[] = "{$column} = {$raw}"; - continue; - } - - preg_match('/(?[\p{L}_][\p{L}\p{N}@$#\-_]*)(\[(?\+|\-|\*|\/)\])?/u', $key, $match); - - if (isset($match['operator'])) { - if (is_numeric($value)) { - $fields[] = "{$column} = {$column} {$match['operator']} {$value}"; - } - } else { - $mapKey = $this->mapKey(); - $fields[] = "{$column} = {$mapKey}"; - - switch ($type) { - - case 'array': - $map[$mapKey] = [ - strpos($key, '[JSON]') === strlen($key) - 6 ? - json_encode($value) : - serialize($value), - PDO::PARAM_STR - ]; - break; - - case 'object': - $value = serialize($value); - - break; - case 'NULL': - case 'resource': - case 'boolean': - case 'integer': - case 'double': - case 'string': - $map[$mapKey] = $this->typeMap($value, $type); - break; - } - } - } - - $query = 'UPDATE ' . $this->tableQuote($table) . ' SET ' . implode(', ', $fields) . $this->whereClause($where, $map); - - if ($this->type === 'oracle' && !empty($returnings)) { - return $this->returningQuery($query, $map, $returnings); - } - - return $this->exec($query, $map); - } - - /** - * Delete data from the table. - * - * @param string $table - * @param array|Raw $where - * @return \PDOStatement|null - */ - public function delete(string $table, $where): ?PDOStatement - { - $map = []; - - return $this->exec('DELETE FROM ' . $this->tableQuote($table) . $this->whereClause($where, $map), $map); - } - - /** - * Replace old data with a new one. - * - * @param string $table - * @param array $columns - * @param array $where - * @return \PDOStatement|null - */ - public function replace(string $table, array $columns, $where = null): ?PDOStatement - { - $map = []; - $stack = []; - - foreach ($columns as $column => $replacements) { - if (is_array($replacements)) { - foreach ($replacements as $old => $new) { - $mapKey = $this->mapKey(); - $columnName = $this->columnQuote($column); - $stack[] = "{$columnName} = REPLACE({$columnName}, {$mapKey}a, {$mapKey}b)"; - - $map[$mapKey . 'a'] = [$old, PDO::PARAM_STR]; - $map[$mapKey . 'b'] = [$new, PDO::PARAM_STR]; - } - } - } - - if (empty($stack)) { - throw new InvalidArgumentException('Invalid columns supplied.'); - } - - return $this->exec('UPDATE ' . $this->tableQuote($table) . ' SET ' . implode(', ', $stack) . $this->whereClause($where, $map), $map); - } - - /** - * Get only one record from the table. - * - * @param string $table - * @param array $join - * @param array|string $columns - * @param array $where - * @return mixed - */ - public function get(string $table, $join = null, $columns = null, $where = null) - { - $map = []; - $result = []; - $columnMap = []; - $currentStack = []; - - if ($where === null) { - if ($this->isJoin($join)) { - $where['LIMIT'] = 1; - } else { - $columns['LIMIT'] = 1; - } - - $column = $join; - } else { - $column = $columns; - $where['LIMIT'] = 1; - } - - $isSingle = (is_string($column) && $column !== '*'); - $query = $this->exec($this->selectContext($table, $map, $join, $columns, $where), $map); - - if (!$this->statement) { - return false; - } - - // @codeCoverageIgnoreStart - $data = $query->fetchAll(PDO::FETCH_ASSOC); - - if (isset($data[0])) { - if ($column === '*') { - return $data[0]; - } - - $this->columnMap($columns, $columnMap, true); - $this->dataMap($data[0], $columns, $columnMap, $currentStack, true, $result); - - if ($isSingle) { - return $result[0][$columnMap[$column][0]]; - } - - return $result[0]; - } - } - // @codeCoverageIgnoreEnd - - /** - * Determine whether the target data existed from the table. - * - * @param string $table - * @param array $join - * @param array $where - * @return bool - */ - public function has(string $table, $join, $where = null): bool - { - $map = []; - $column = null; - - $query = $this->exec( - $this->type === 'mssql' ? - $this->selectContext($table, $map, $join, $column, $where, Medoo::raw('TOP 1 1')) : - 'SELECT EXISTS(' . $this->selectContext($table, $map, $join, $column, $where, 1) . ')', - $map - ); - - if (!$this->statement) { - return false; - } - - // @codeCoverageIgnoreStart - $result = $query->fetchColumn(); - - return $result === '1' || $result === 1 || $result === true; - } - // @codeCoverageIgnoreEnd - - /** - * Randomly fetch data from the table. - * - * @param string $table - * @param array $join - * @param array|string $columns - * @param array $where - * @return array - */ - public function rand(string $table, $join = null, $columns = null, $where = null): array - { - $orderRaw = $this->raw( - $this->type === 'mysql' ? 'RAND()' - : ($this->type === 'mssql' ? 'NEWID()' - : 'RANDOM()') - ); - - if ($where === null) { - if ($this->isJoin($join)) { - $where['ORDER'] = $orderRaw; - } else { - $columns['ORDER'] = $orderRaw; - } - } else { - $where['ORDER'] = $orderRaw; - } - - return $this->select($table, $join, $columns, $where); - } - - /** - * Build for the aggregate function. - * - * @param string $type - * @param string $table - * @param array $join - * @param string $column - * @param array $where - * @return string|null - */ - private function aggregate(string $type, string $table, $join = null, $column = null, $where = null): ?string - { - $map = []; - - $query = $this->exec($this->selectContext($table, $map, $join, $column, $where, $type), $map); - - if (!$this->statement) { - return null; - } - - // @codeCoverageIgnoreStart - return (string) $query->fetchColumn(); - } - // @codeCoverageIgnoreEnd - - /** - * Count the number of rows from the table. - * - * @param string $table - * @param array $join - * @param string $column - * @param array $where - * @return int|null - */ - public function count(string $table, $join = null, $column = null, $where = null): ?int - { - return (int) $this->aggregate('COUNT', $table, $join, $column, $where); - } - - /** - * Calculate the average value of the column. - * - * @param string $table - * @param array $join - * @param string $column - * @param array $where - * @return string|null - */ - public function avg(string $table, $join, $column = null, $where = null): ?string - { - return $this->aggregate('AVG', $table, $join, $column, $where); - } - - /** - * Get the maximum value of the column. - * - * @param string $table - * @param array $join - * @param string $column - * @param array $where - * @return string|null - */ - public function max(string $table, $join, $column = null, $where = null): ?string - { - return $this->aggregate('MAX', $table, $join, $column, $where); - } - - /** - * Get the minimum value of the column. - * - * @param string $table - * @param array $join - * @param string $column - * @param array $where - * @return string|null - */ - public function min(string $table, $join, $column = null, $where = null): ?string - { - return $this->aggregate('MIN', $table, $join, $column, $where); - } - - /** - * Calculate the total value of the column. - * - * @param string $table - * @param array $join - * @param string $column - * @param array $where - * @return string|null - */ - public function sum(string $table, $join, $column = null, $where = null): ?string - { - return $this->aggregate('SUM', $table, $join, $column, $where); - } - - /** - * Start a transaction. - * - * @param callable $actions - * @codeCoverageIgnore - * @return void - */ - public function action(callable $actions): void - { - if (is_callable($actions)) { - $this->pdo->beginTransaction(); - - try { - $result = $actions($this); - - if ($result === false) { - $this->pdo->rollBack(); - } else { - $this->pdo->commit(); - } - } catch (Exception $e) { - $this->pdo->rollBack(); - throw $e; - } - } - } - - /** - * Return the ID for the last inserted row. - * - * @param string $name - * @codeCoverageIgnore - * @return string|null - */ - public function id(string $name = null): ?string - { - $type = $this->type; - - if ($type === 'oracle') { - return $this->returnId; - } elseif ($type === 'pgsql') { - $id = $this->pdo->query('SELECT LASTVAL()')->fetchColumn(); - - return (string) $id ?: null; - } - - return $this->pdo->lastInsertId($name); - } - - /** - * Enable debug mode and output readable statement string. - * - * @codeCoverageIgnore - * @return Medoo - */ - public function debug(): self - { - $this->debugMode = true; - - return $this; - } - - /** - * Enable debug logging mode. - * - * @codeCoverageIgnore - * @return void - */ - public function beginDebug(): void - { - $this->debugMode = true; - $this->debugLogging = true; - } - - /** - * Disable debug logging and return all readable statements. - * - * @codeCoverageIgnore - * @return void - */ - public function debugLog(): array - { - $this->debugMode = false; - $this->debugLogging = false; - - return $this->debugLogs; - } - - /** - * Return the last performed statement. - * - * @codeCoverageIgnore - * @return string|null - */ - public function last(): ?string - { - if (empty($this->logs)) { - return null; - } - - $log = $this->logs[array_key_last($this->logs)]; - - return $this->generate($log[0], $log[1]); - } - - /** - * Return all executed statements. - * - * @codeCoverageIgnore - * @return string[] - */ - public function log(): array - { - return array_map( - function ($log) { - return $this->generate($log[0], $log[1]); - }, - $this->logs - ); - } - - /** - * Get information about the database connection. - * - * @codeCoverageIgnore - * @return array - */ - public function info(): array - { - $output = [ - 'server' => 'SERVER_INFO', - 'driver' => 'DRIVER_NAME', - 'client' => 'CLIENT_VERSION', - 'version' => 'SERVER_VERSION', - 'connection' => 'CONNECTION_STATUS' - ]; - - foreach ($output as $key => $value) { - $output[$key] = @$this->pdo->getAttribute(constant('PDO::ATTR_' . $value)); - } - - $output['dsn'] = $this->dsn; - - return $output; - } -} diff --git a/dove/lib/db/Tdb.php b/dove/lib/db/Tdb.php deleted file mode 100644 index 53b7dd2..0000000 --- a/dove/lib/db/Tdb.php +++ /dev/null @@ -1,43 +0,0 @@ -dbf = $options['dbpath'].$option['dbname'].'.tdb'; - if(file_exist($this->dbf)) $this->debug('数据库不存在'); - $dataFlow = unserialize(gzuncompress(file_get_contents($this->dbf))); - } - - // 建造一个数据库 - public function mk_db($name) - { - - } - - // 格式化一个数据库 - public function re_db($name) - { - - } - - // 删除数据库 - public function del_db($name) - { - - } - - // 使用框架debug - public function debug($msg,$c=500) - { - Debug::e($c,'dove\db\Tdb::'.$msg,__FILE__); - } -} \ No newline at end of file diff --git a/dove/lib/tool/Arr.php b/dove/lib/tool/Arr.php deleted file mode 100644 index 3a2944c..0000000 --- a/dove/lib/tool/Arr.php +++ /dev/null @@ -1,22 +0,0 @@ -值 - public static function divide($array) - { - return [array_keys($array), array_values($array)]; - } - - // 输出被打乱的$array或输出被打乱的$array的第$count个值 - // $print值为true(默认)时输出被打乱的数组,为false时输出打乱数组的第一个的值 - public static function random($array,$print=true,$count=0) - { - shuffle($array); - return $print?$array:$array[$count]; - } -} \ No newline at end of file diff --git a/dove/lib/tool/ArrToxml.php b/dove/lib/tool/ArrToxml.php deleted file mode 100644 index 8329f51..0000000 --- a/dove/lib/tool/ArrToxml.php +++ /dev/null @@ -1,81 +0,0 @@ -openMemory(); - $xml->startDocument(self::$version,self::$encoding); - $xml->startElement($startElement); - $data = static::writeAttr($xml,$data); - static::writeEl($xml,$data); - $xml->endElement(); - return $xml->outputMemory(true); - } - - public static function writeAttr(\XMLWriter $xml, $data) - { - if (is_array($data)) { - $nonAttributes = []; - foreach ($data as $key => $val) { - if ($key[0] == '@') { - $xml->writeAttribute(substr($key, 1), $val); - } else if ($key[0] == '%') { - if (is_array($val)) $nonAttributes = $val; - else $xml->text($val); - } elseif ($key[0] == '#') { - if (is_array($val)) $nonAttributes = $val; - else { - $xml->startElement(substr($key, 1)); - $xml->writeCData($val); - $xml->endElement(); - } - }else if($key[0] == "!"){ - if (is_array($val)) $nonAttributes = $val; - else $xml->writeCData($val); - } - else $nonAttributes[$key] = $val; - } - return $nonAttributes; - } else return $data; - } - - public static function writeEl(\XMLWriter $xml, $data) - { - foreach ($data as $key => $value) { - if (is_array($value) && !static::isAssoc($value)) { //numeric array - foreach ($value as $itemValue) { - if (is_array($itemValue)) { - $xml->startElement($key); - $itemValue = static::writeAttr($xml, $itemValue); - static::writeEl($xml, $itemValue); - $xml->endElement(); - } else { - $itemValue = static::writeAttr($xml, $itemValue); - $xml->writeElement($key, "$itemValue"); - } - } - } else if (is_array($value)) { - $xml->startElement($key); - $value = static::writeAttr($xml, $value); - static::writeEl($xml, $value); - $xml->endElement(); - } else { - $value = static::writeAttr($xml, $value); - $xml->writeElement($key, "$value"); - } - } - } - - public static function isAssoc($array) - { - return (bool)count(array_filter(array_keys($array), 'is_string')); - } -} \ No newline at end of file diff --git a/dove/lib/tool/Encrypt.php b/dove/lib/tool/Encrypt.php deleted file mode 100644 index 0674047..0000000 --- a/dove/lib/tool/Encrypt.php +++ /dev/null @@ -1,150 +0,0 @@ - 0) - && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16) - ) { - return substr($result, 26); - } else { - return ''; - } - } - - // 字符串两端增加盐值 - public function addSalt($str='',$salt=null) - { - if(is_array($salt)){ - if(isset($salt[0])) $str = $salt[0].$str; - if(isset($salt[1])) $str.= $salt[1]; - } - return static::saltTag($str); - } - - // 替换盐值中的标签 - public static function saltTag($v) - { - $v = str_replace([ - '{datetime}','{date}','{time}','{safecode}', - ],[ - date('YmdHis'),date('Ymd'),date('His'),Config::get('dove','safecode'), - ],$v); - return $v; - } -} \ No newline at end of file diff --git a/dove/lib/tool/File.php b/dove/lib/tool/File.php deleted file mode 100644 index 9f2ff5c..0000000 --- a/dove/lib/tool/File.php +++ /dev/null @@ -1,104 +0,0 @@ -[0=>'字符1',1=>'字符2',...],'规则2'=>[0=>'字符']],允许空字符串内容,闭包函数); - public static function validate($array=[],$empty=true,$func=null){ - self::initial(); - $n_rule = 1; - foreach($array as $rule=>$strs){ - if(!isset(self::$RULES[$rule])) Debug::e(500,"Filter:规则不存在!"); - if(is_array($strs)){ - // 一规则多字符验证 - foreach($strs as $n_str=>$str){ - if(!preg_match(self::$RULES[$rule],strval($str)) || $empty && $str=="" || $str==[]){ - if($func) $func($n_rule,$n_str+1,$rule,self::$RULES[$rule],$str); - return false; - } - } - } else { - // 一规则一字符验证 - if(!preg_match(self::$RULES[$rule],strval($strs)) || $empty && $str=="" || $str==[]){ - if($func) $func($n_rule,1,$rule,self::$RULES[$rule],$strs); - return false; - } - } - $n_rule++; - } - return true; - } - - // 过滤(替换掉)字符 - // 'rule'=>'to' - public static function filter($str,$ruleTo=[]){ - $rules = []; - $tos = []; - foreach($ruleTo as $rule=>$to){ - $rule[] = $rule; - $tos[] = $to; - } - return preg_replace($rules,$tos,$str); - } - - // 初始化 - private static function initial(){ - if(self::$RULES==[]) self::$RULES = Config::get('filter','*'); - return true; - } -} \ No newline at end of file diff --git a/dove/lib/tool/Ftp.php b/dove/lib/tool/Ftp.php deleted file mode 100644 index d998694..0000000 --- a/dove/lib/tool/Ftp.php +++ /dev/null @@ -1,351 +0,0 @@ -'','username'=>'','password'=>'','port'=>''...); - */ - public function __construct($config = []) { - if(count($config) > 0) { - $this->_init($config); - } - } - - /** - * FTP连接 - * - * @access public - * @param array 配置数组 - * @return boolean - */ - public function connect($config = []){ - if(count($config) > 0) $this->_init($config); - if(false === ($this->conn_id = @ftp_connect($this->hostname,$this->port))){ - Debug::e(500,'ftp:连接失败'); - return false; - } - if(!$this->_login()){ - Debug::e(500,'ftp:登录失败'); - return false; - } - if($this->passive === true) ftp_pasv($this->conn_id,true); - return true; - } - - - /** - * 目录改变 - * - * @access public - * @param string 目录标识(ftp) - * @param boolean - * @return boolean - */ - public function chgdir($path = '') { - if($path == '' OR ! $this->_isconn()) return false; - $result = @ftp_chdir($this->conn_id,$path); - if($result === false) { - Debug::e(500,'ftp:目录改变失败['.$path.']'); - return false; - } - return true; - } - - /** - * 目录生成 - * - * @access public - * @param string 目录标识(ftp) - * @param int 文件权限列表 - * @return boolean - */ - public function mkdir($path = '') { - if($path == '' OR ! $this->_isconn()) return false; - $parts = explode('/',$path); // 2013/06/11/username - $cd = ""; - foreach($parts as $part){ - if(!@ftp_chdir($this->conn_id, $part)){ - @ftp_mkdir($this->conn_id, $part); - @ftp_chdir($this->conn_id, $part); - @ftp_chmod($this->conn_id, 0777, $part); - } - $cd .= "../"; - } - @ftp_chdir($this->conn_id, $cd); - return true; - } - - /** - * 上传 - * - * @access public - * @param string 本地目录标识 - * @param string 远程目录标识(ftp) - * @param string 上传模式 auto || ascii - * @param int 上传后的文件权限列表 - * @return boolean - */ - public function upload($localpath, $remotepath, $mode = 'auto', $permissions = null) { - if(!$this->_isconn()) return false; - if(!file_exists($localpath)){ - Debug::e(500,'ftp:没有文件来源['.$localpath.']'); - return false; - } - if($mode == 'auto'){ - $ext = $this->_getext($localpath); - $mode = $this->_settype($ext); - } - $mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY; - $result = @ftp_put($this->conn_id, $remotepath, $localpath, $mode); - if($result === false) { - Debug::e(500,'ftp:文件上传异常!本地路径['.$localpath.'];服务器端路径['.$remotepath.']'); - return false; - } - if(!is_null($permissions)) { - $this->chmod($remotepath,(int)$permissions); - } - return true; - } - - /** - * 下载 - * - * @access public - * @param string 远程目录标识(ftp) - * @param string 本地目录标识 - * @param string 下载模式 auto || ascii - * @return boolean - */ - public function download($remotepath, $localpath, $mode = 'auto') { - if(!$this->_isconn()) return false; - if($mode == 'auto'){ - $ext = $this->_getext($remotepath); - $mode = $this->_settype($ext); - } - $mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY; - $result = @ftp_get($this->conn_id, $localpath, $remotepath, $mode); - if($result === false){ - Debug::e(500,'ftp:文件下载失败!本地路径['.$localpath.'];服务器端路径['.$remotepath.']'); - return false; - } - return true; - } - - /** - * 重命名/移动 - * - * @access public - * @param string 远程目录标识(ftp) - * @param string 新目录标识 - * @param boolean 判断是重命名(FALSE)还是移动(TRUE) - * @return boolean - */ - public function rename($oldname, $newname, $move = false) { - if(!$this->_isconn()) return false; - $result = @ftp_rename($this->conn_id, $oldname, $newname); - if($result === false) { - if($this->debug === true) { - $msg = ($move == false) ? "ftp:文件重命名失败" : "ftp:文件移动失败"; - Debug::e(500,$msg); - } - return false; - } - return true; - } - - /** - * 删除文件 - * - * @access public - * @param string 文件标识(ftp) - * @return boolean - */ - public function delete_file($file) { - if(!$this->_isconn()) return false; - $result = @ftp_delete($this->conn_id, $file); - if($result === false) { - if($this->debug === true) { - Debug::e(500,'ftp:删除文件失败['.$file.']'); - } - return false; - } - return true; - } - - /** - * 删除文件夹 - * - * @access public - * @param string 目录标识(ftp) - * @return boolean - */ - public function delete_dir($path) { - if(!$this->_isconn()) return false; - $path = preg_replace("/(.+?)\/*$/", "\\1/", $path);//对目录宏的'/'字符添加反斜杠'\' - $filelist = $this->filelist($path);//获取目录文件列表 - if($filelist !== false AND count($filelist) > 0) { - foreach($filelist as $item) { - //如果我们无法删除,那么就可能是一个文件夹 - //所以我们递归调用delete_dir() - if(!@delete_file($item)) { - $this->delete_dir($item); - } - } - } - //删除文件夹(空文件夹) - $result = @ftp_rmdir($this->conn_id, $path); - if($result === false) { - Debug::e(500,'ftp:删除文件夹失败['.$path.']'); - return false; - } - return true; - } - - /** - * 修改文件权限 - * - * @access public - * @param string 目录标识(ftp) - * @return boolean - */ - public function chmod($path, $perm) { - if(!$this->_isconn()) return false; - //只有在PHP5中才定义了修改权限的函数(ftp) - if(!function_exists('ftp_chmod')){ - Debug::e(500,'ftp:文件权限修改失败!(function)'); - return false; - } - $result = @ftp_chmod($this->conn_id, $perm, $path); - if($result === false){ - Debug::e(500,'ftp:文件权限修改失败!被操作路径['.$path.'];修改权限['.$perm.']'); - return false; - } - return true; - } - - /** - * 获取目录文件列表 - * - * @access public - * @param string 目录标识(ftp) - * @return array - */ - public function filelist($path = '.') { - if(!$this->_isconn()) return false; - return ftp_nlist($this->conn_id, $path); - } - - /** - * 关闭FTP - * - * @access public - * @return boolean - */ - public function close() { - if(!$this->_isconn()) return false; - return @ftp_close($this->conn_id); - } - - /** - * FTP成员变量初始化 - * - * @access private - * @param array 配置数组 - * @return void - */ - private function _init($config = array()) { - foreach($config as $key => $val) { - if(isset($this->$key)) { - $this->$key = $val; - } - } - //特殊字符过滤 - $this->hostname = preg_replace('|.+?://|','',$this->hostname); - } - - /** - * FTP登陆 - * - * @access private - * @return boolean - */ - private function _login() { - return @ftp_login($this->conn_id, $this->username, $this->password); - } - - /** - * 判断con_id - * - * @access private - * @return boolean - */ - private function _isconn() { - if(!is_resource($this->conn_id)) { - Debug::e(500,'ftp:连接失败!'); - return false; - } - return true; - } - - /** - * 从文件名中获取后缀扩展 - * - * @access private - * @param string 目录标识 - * @return string - */ - private function _getext($filename) { - if(false === strpos($filename, '.')) return 'txt'; - $extarr = explode('.', $filename); - return end($extarr); - } - - /** - * 从后缀扩展定义FTP传输模式 ascii 或 binary - * - * @access private - * @param string 后缀扩展 - * @return string - */ - private function _settype($ext) { - $text_type = [ - 'txt', - 'text', - 'php', - 'phps', - 'php4', - 'js', - 'css', - 'htm', - 'html', - 'phtml', - 'shtml', - 'log', - 'xml' - ]; - return (in_array($ext, $text_type)) ? 'ascii' : 'binary'; - } -} \ No newline at end of file diff --git a/dove/lib/tool/Str.php b/dove/lib/tool/Str.php deleted file mode 100644 index 6adde81..0000000 --- a/dove/lib/tool/Str.php +++ /dev/null @@ -1,85 +0,0 @@ - strtotime($dueTime)) ? (strtotime($dueTime)==strtotime($regTime)) ? [false,$regTime] : [false,$dueTime,$regTime] : [true,$dueTime,$regTime]; - } -} \ No newline at end of file diff --git a/dove/tpl/debug/json.php b/dove/tpl/debug/json.php deleted file mode 100644 index 0e3a134..0000000 --- a/dove/tpl/debug/json.php +++ /dev/null @@ -1,14 +0,0 @@ - $code, - 'message' => '程序运行时发生错误', - 'debug' => [ - 'file' => $file, - 'info' => $info, - 'call_stack' => $stack, - 'get' => $_GET, - 'post' => $POST, - ], - 'DoveAPI' => DOVE_VERSION, -]; \ No newline at end of file diff --git a/dove/tpl/debug/page.tpl b/dove/tpl/debug/page.tpl deleted file mode 100644 index 8ba243c..0000000 --- a/dove/tpl/debug/page.tpl +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - 哦嚯,出错咯 - DoveAPI - - - -
-
-
- 这里是框架的错误页面,出现此页面时说明程序运行可能出现问题,请阅读以下回调信息了解错误详情。
-
- 注意:生产环境中请关闭调试模式! -
-
-
-

Error Info 报错内容

-
{$err_info}
-
- - -

-
-
-

Call Stack 执行回溯

-
{$call_stack}
-
- {$mistake_file} -

GET

{$get_array_list}
- -

POST

{$post_array_list}
- -
-
-

DoveAPI V{$version} - 极速上手,快速开发!

- 运行耗时 {$exitTime}s -
-
- - - - \ No newline at end of file diff --git a/dove/tpl/debug/pe_json.php b/dove/tpl/debug/pe_json.php deleted file mode 100644 index ff056e8..0000000 --- a/dove/tpl/debug/pe_json.php +++ /dev/null @@ -1,5 +0,0 @@ - $code, - "message" => "发生了亿点点小错误" -]; \ No newline at end of file diff --git a/dove/tpl/debug/pe_page.tpl b/dove/tpl/debug/pe_page.tpl deleted file mode 100644 index a7921fa..0000000 --- a/dove/tpl/debug/pe_page.tpl +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - 错误::{$err_code} - doveapi - - -
-

该页面发生{$err_code}错误

-
-
-
- 请及时联系站长!感谢! -
- - \ No newline at end of file diff --git a/public/index.php b/public/index.php index 221666e..ea82a1a 100644 --- a/public/index.php +++ b/public/index.php @@ -1,15 +1,16 @@ run(); + +// 这后面不要有任何代码哟!除非是最后要执行的,咕~ \ No newline at end of file diff --git a/public/nginx.conf b/public/nginx.conf new file mode 100644 index 0000000..7c184cb --- /dev/null +++ b/public/nginx.conf @@ -0,0 +1,9 @@ +if (!-f $request_filename){ + set $rule_0 1$rule_0; +} +if (!-d $request_filename){ + set $rule_0 2$rule_0; +} +if ($rule_0 = "21"){ + rewrite ^/(.*)$ /index.php/$1 last; +} \ No newline at end of file diff --git a/public/static/.gitignore b/public/static/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/public/static/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/runtime/.gitignore b/runtime/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/runtime/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/runtime/log/test.log b/runtime/log/test.log deleted file mode 100644 index 30d74d2..0000000 --- a/runtime/log/test.log +++ /dev/null @@ -1 +0,0 @@ -test \ No newline at end of file diff --git a/vendor/.gitignore b/vendor/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/vendor/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file