├── phppoem ├── core │ ├── session │ │ ├── memcache.php │ │ ├── redis.php │ │ └── db.php │ ├── tpl │ │ ├── exception.php │ │ ├── jump.php │ │ └── trace.php │ ├── cache.php │ ├── controller.php │ ├── cache │ │ ├── storage.php │ │ ├── file.php │ │ └── redis.php │ ├── more │ │ ├── upload.php │ │ ├── page.php │ │ └── build.php │ ├── load.php │ ├── route.php │ ├── view.php │ ├── app.php │ ├── log.php │ ├── db.php │ └── model.php ├── license.txt ├── start.php ├── config.php └── function.php ├── .gitignore ├── .htaccess ├── public └── index.php ├── composer.json └── README.md /phppoem/core/session/memcache.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .editorconfig 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine on 3 | RewriteCond %{REQUEST_FILENAME} !-d 4 | RewriteCond %{REQUEST_FILENAME} !-f 5 | RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] 6 | 7 | -------------------------------------------------------------------------------- /phppoem/license.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ----- || ----\ ----\ || || 6 | / ---/ || / - \ / - \ \\ // 7 | | | || | -- / | -- / | | 8 | \ ---\ || \ \ | 9 | ----- ||===== ----/ ----/ | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | 2 | .tpl-e{ max-width:600px;margin:0 auto;padding: 80px 10px;font-family: '微软雅黑'; color: #333; font-size: 14px;} 3 | .tpl-e h1{ font-size: 40px; font-weight: normal;margin: 0;} 4 | .tpl-e .jump{margin: 10px 0;} 5 | .tpl-e .success,.tpl-e .error{font-size: 18px;margin-bottom: 20px;} 6 | hr{margin: 20px 0;} 7 | 8 |
9 |

>_<

10 |

11 |

12 |

13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cleey/phppoem", 3 | "type": "library", 4 | "description": "如诗一般简洁、优美的PHP框架,PhpPoem is a poetic , simple and beautiful php framework, write php as a poem.", 5 | "keywords": ["phppoem","framework","poem"], 6 | "homepage": "https://github.com/cleey/phppoem", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Cleey", 11 | "email": "cleeytest@163.com", 12 | "homepage": "http://phppoem.com", 13 | "role": "Developer" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=5.3.0" 18 | }, 19 | "autoload": { 20 | "psr-0": { 21 | "Monolog": "src" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /phppoem/core/cache.php: -------------------------------------------------------------------------------- 1 | 2 | .tpl-e{ max-width:600px;margin:0 auto;padding: 80px 10px;font-family: '微软雅黑'; color: #333; font-size: 14px;} 3 | .tpl-e h1{ font-size: 40px; font-weight: normal;margin: 0;} 4 | .tpl-e .jump{margin: 10px 0;} 5 | .tpl-e .success,.tpl-e .error{font-size: 18px;margin: 10px 0;} 6 | 7 | 8 |
9 | 10 |

^_^

11 |

12 | 13 |

>_<

14 |

15 | 16 |

17 | 页面自动 跳转 18 | 等待时间: 19 |

20 |
21 | 22 | 35 | -------------------------------------------------------------------------------- /phppoem/core/controller.php: -------------------------------------------------------------------------------- 1 | view_obj = new \poem\view(); 11 | } 12 | 13 | /** 14 | * 展示页面 15 | * @param 参考 v() 16 | * @return void 17 | */ 18 | public function view($tpl = '') { 19 | $this->view_obj->display($tpl); 20 | } 21 | 22 | /** 23 | * 执行页面并返回执行结果 24 | * @param 参考 fetch() 25 | * @return string 26 | */ 27 | public function fetch($tpl = '') { 28 | return $this->view_obj->fetch($tpl); 29 | } 30 | 31 | /** 32 | * 用户变量 33 | * @param 参考 assign() 34 | * @return void 35 | */ 36 | public function assign($key, $value = '') { 37 | $this->view_obj->assign($key, $value); 38 | } 39 | 40 | /** 41 | * 返回成功跳转 42 | * @param 参考 err_jump() 43 | * @return void 44 | */ 45 | public function ok_jump($info, $url = '', $params = '', $second = false) { 46 | $this->view_obj->auto_jump($info, $url, $params, $second, 1); 47 | } 48 | 49 | /** 50 | * 返回失败跳转 51 | * @param 参考 ok_jump(); 52 | * @return void 53 | */ 54 | public function err_jump($info, $url = '', $params = '', $second = false) { 55 | $this->view_obj->auto_jump($info, $url, $params, $second, 0); 56 | } 57 | } -------------------------------------------------------------------------------- /phppoem/config.php: -------------------------------------------------------------------------------- 1 | true, 4 | 'layout' => false, 5 | 6 | // 数据库配置 7 | 'db_type' => 'mysql', 8 | 'db_host' => 'localhost', 9 | 'db_name' => '', 10 | 'db_user' => '', 11 | 'db_pass' => '', 12 | 'db_prefix' => '', 13 | 'db_port' => '3306', 14 | 'db_charset' => 'utf8', 15 | 'db_dsn' => '', 16 | 'db_deploy' => false, // 部署方式: false 集中式(单一服务器),true 分布式(主从服务器) 17 | 'db_rw_separate' => false, // 数据库读写是否分离 主从式有效 18 | 'db_master_num' => 1, // 读写分离后 主服务器数量 19 | 'db_slave_no' => '', // 指定从服务器序号 20 | 21 | 'runtime_path' => APP_PATH . '/runtime/', // 运行目录 22 | 23 | 'session_type' => '', 24 | 'session_prefix' => '', 25 | 'default_module' => 'home', // 默认模块 26 | 'default_errcode' => 1, // 默认ajax 错误码,gp()使用 27 | 28 | 'storage_type' => 'file', 29 | 'storage_expire'=> 86400, // 1day 30 | 'storage_compress'=> false, // 缓存压缩 31 | 32 | 'cookie_expire' => 0, 33 | 'cookie_domain' => '', 34 | 'cookie_path' => '/', 35 | 'cookie_prefix' => '', 36 | 'cookie_secure' => false, // cookie 启用安全传输 true 在https下会传输,http不会传输 37 | 'cookie_httponly' => false, // true 无法通过程序读取如 JS脚本、Applet等 38 | 39 | 'log_path' => '', // 日志路径 40 | 'log_level' => 5, 41 | 'log_remain_days' => 1, // 日志保留天数 42 | 'log_relative_path' => true, // 相对路径日志还是全路径日志 43 | 44 | 'debug_trace' => false, // 页面右下角展示调试信息 45 | ); -------------------------------------------------------------------------------- /phppoem/core/session/redis.php: -------------------------------------------------------------------------------- 1 | maxtime = ini_get('session.gc_maxlifetime'); 15 | $this->table = config('session_table') ?: "session"; 16 | 17 | return true; 18 | } 19 | 20 | /** 21 | * 关闭session 22 | * @return bool 23 | */ 24 | public function close() { 25 | return true; 26 | } 27 | 28 | /** 29 | * 获取sesseion_id 30 | * @param string $session_id 31 | * @return array $session 32 | */ 33 | public function read($session_id) { 34 | return redis()->get($this->table . $session_id); 35 | } 36 | 37 | /** 38 | * 存储session 39 | * @param string $session_id 40 | * @param array $session_data 41 | * @return bool 42 | */ 43 | public function write($session_id, $session_data) { 44 | return redis()->setex($this->table . $session_id, $this->maxtime, $session_data); 45 | } 46 | 47 | /** 48 | * 销毁session 49 | * @param string $session_id 50 | * @return bool 51 | */ 52 | public function destroy($session_id) { 53 | return redis()->del($this->table . $session_id); 54 | } 55 | 56 | /** 57 | * 销毁session 58 | * @param int $maxlifetime 59 | * @return bool 60 | */ 61 | public function gc($maxlifetime) { 62 | return true; 63 | } 64 | } -------------------------------------------------------------------------------- /phppoem/core/cache/storage.php: -------------------------------------------------------------------------------- 1 | config('storage_type'), // redis or storage 19 | 'expire' => config('storage_expire'), 20 | 'compress' => config('storage_compress'), 21 | ), $option); 22 | 23 | $this->_option = $option; 24 | $type = '\\poem\\cache\\'.$option['type']; 25 | $this->_ins = new $type(); 26 | } 27 | 28 | /** 29 | * 获取键key 30 | * @param string $key 键 31 | * @return string 数据 32 | */ 33 | public function get($key) { 34 | $data = $this->_ins->get($key); 35 | if($this->_option['compress'] && function_exists('gzdeflate')){ 36 | return gzinflate($data); 37 | } 38 | return $data; 39 | } 40 | 41 | /** 42 | * 设置键值并含过期时间 43 | * @param string $key 键 44 | * @param string $value 值 45 | * @param string $expire 过期时间 46 | * @return string 文件路径 47 | */ 48 | public function set_expire($key, $value, $expire = null) { 49 | if (is_null($expire)) { 50 | $expire = $this->_option['expire']; 51 | } 52 | if($this->_option['compress'] && function_exists('gzdeflate')){ 53 | $value = gzdeflate($value); 54 | } 55 | 56 | return $this->_ins->set_expire($key, $value, $expire); 57 | } 58 | 59 | /** 60 | * 删除键值 61 | * @param string $key 键 62 | * @return bool 63 | */ 64 | public function del($key) { 65 | return $this->_ins->del($key); 66 | } 67 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PhpPoem 2 | ================= 3 | >PhpPoem, 如诗一般简洁优美的PHP框架 4 | >PhpPoem, a simple and beautiful php framework, php will be like poet. 5 | 6 | Home: [http://phppoem.com](http://phppoem.com) 7 | Author: [Cleey](http://www.cleey.com) 8 | QQ Group: 137951449 9 | 10 | 快速安装(类unix) 11 | ----------------- 12 | >站点根目录shell输入`git clone https://github.com/cleey/phppoem`部署框架代码 13 | >配置Hosts如下,并添加本地测试域 14 | ~~~ 15 | echo "127.0.0.1 dev.phppoem.com" >> /etc/hosts 16 | ~~~ 17 | >配置Nginx rewrite及path,如下 18 | ~~~ 19 | server { 20 | listen 80; 21 | server_name dev.phppoem.com; 22 | index index.php index.html index.shtml; 23 | 24 | #默认路径指向phppoem项目的public目录下 25 | root /path/www/phppoem/public; 26 | 27 | #phppoem Url Rewrite 28 | location /{ 29 | if (!-e $request_filename) { 30 | rewrite ^/(.*)$ /index.php/$1 last; 31 | break; 32 | } 33 | } 34 | 35 | #phppoem Path Info 36 | location ~ \.php($|/) { 37 | fastcgi_pass 127.0.0.1:9000; 38 | fastcgi_index index.php; 39 | fastcgi_split_path_info ^(.+\.php)(/.*)$; 40 | fastcgi_param PATH_INFO $fastcgi_path_info; 41 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 42 | include fastcgi.conf; 43 | } 44 | } 45 | ~~~ 46 | >重载Nginx, 输入以下命令使配置文件生效 47 | ~~~ 48 | nginx -s reload 49 | ~~~ 50 | >浏览器输入`http://dev.phppoem.com`, 框架会自动为您在目部录下构建测试项目`/app`,并在浏览器为您呈现Success! 51 | >现在! 开始感受PhpPoem诗般的优雅吧! 52 | 53 | 54 | 压力测试 55 | ----------------- 56 | 服务器使用配置为 CPU:16核, RAM:16G测试 ,php5.3.3开启opcache,使用压测工具ab,结果如下: 57 | ~~~ 58 | ab -c7500 -t10 test.com 59 | Requests per second: 7836.84 [#/sec] (mean) 60 | Time per request: 957.019 [ms] (mean) 61 | Time per request: 0.128 [ms] (mean, across all concurrent requests) 62 | Transfer rate: 1642.15 [Kbytes/sec] received 63 | ~~~ 64 | PhpPoem 2.0 并发 7500 持续10s,结果 7836.84 req/s 65 | 66 | 如何成为PhpPoem贡献者 67 | ----------------- 68 | PhpPoem 的源码托管在 GitHub,你可以进行派生(fork)、修改,最后发起 Pull Request 请求。我们会在最短的时间内对您提交的代码进行 Review,并反馈给您。 69 | -------------------------------------------------------------------------------- /phppoem/core/more/upload.php: -------------------------------------------------------------------------------- 1 | $v) { 14 | if (empty($v)) {return array('code' => 0, 'info' => '参数不能为空:' . $k);} 15 | } 16 | if (!is_dir($data['url'])) {return array('code' => 0, 'info' => '路径错误:' . $data['url']);} 17 | $fileField = $data['fileField'] ?: 'file'; 18 | $file = $_FILES[$fileField]; 19 | 20 | $ext = pathinfo($file["name"], PATHINFO_EXTENSION); 21 | // 文件过大 22 | if ($file["size"] > $data['size']) { 23 | $return = array('code' => 0, 'info' => '文件过大:' . $file["size"] . ',请上传小于:' . $data['size']); 24 | } 25 | // 不允许后缀 26 | elseif (!in_array($ext, $data['allow'])) { 27 | $return = array('code' => 0, 'info' => '不允许后缀:' . $ext . '请上传:' . implode(',', $data['allow'])); 28 | } else { 29 | if ($file["error"] > 0) { 30 | $return = array('code' => 0, 'info' => "Return Code: " . $file["error"]); 31 | } else { 32 | if (!$data['filename']) { 33 | // $data['filename'] = Date('YmdHis').'_'.$file["name"]; 34 | $data['filename'] = date('YmdHis') . '_' . uniqid() . '.' . $ext; 35 | } 36 | $newfile_url = $data['url'] . $data['filename']; 37 | move_uploaded_file($file["tmp_name"], $newfile_url); 38 | $return = array( 39 | 'code' => 1, 40 | 'origin' => $_FILES[$fileField]["name"], 41 | 'size' => $_FILES[$fileField]["size"], 42 | 'name' => $data['filename'], 43 | 'type' => $ext, 44 | 'info' => '/' . $newfile_url); 45 | } 46 | } 47 | return $return; 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /phppoem/core/cache/file.php: -------------------------------------------------------------------------------- 1 | del($file); 33 | $this->del($file_timeout); 34 | return false; 35 | } 36 | } 37 | 38 | return file_get_contents($file); 39 | } 40 | 41 | /** 42 | * 设置键值 43 | * @param string $key 键 44 | * @param string $value 值 45 | * @return 返回设置的文件路径 46 | */ 47 | public function set($key, $value) { 48 | $key = config('runtime_path') . '/' . $key; 49 | $dir = dirname($key); 50 | if (!is_dir($dir)) { 51 | mkdir($dir, 0775, true); 52 | } 53 | 54 | $re = file_put_contents($key, $value); 55 | 56 | if (!$re) { 57 | $str = 'storage cache write failed:' . $key; 58 | l($str, \poem\log::INFO, \poem\log::DEPTH_FILTER_POEM); 59 | throw new \exception($str); 60 | } 61 | return $key; 62 | } 63 | 64 | public function set_expire($key, $value, $expire) { 65 | $this->set($key, $value); 66 | $this->set($key.'.t',time()+$expire); 67 | } 68 | 69 | /** 70 | * 删除key 71 | * @param string $key 键 72 | * @return null 73 | */ 74 | public function del($key) { 75 | $file = config('runtime_path') . '/' . $key; 76 | $file_tiemout = config('runtime_path') . '/' . $key; 77 | if (is_file($file)) { 78 | unlink($file); 79 | } 80 | if (is_file($file_tiemout)) { 81 | unlink($file_tiemout); 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /phppoem/core/load.php: -------------------------------------------------------------------------------- 1 | maxtime = ini_get('session.gc_maxlifetime'); 29 | $this->table = config('session_table') ?: "session"; 30 | return true; 31 | } 32 | 33 | /** 34 | * 关闭session 进行gc 35 | * @return bool 36 | */ 37 | public function close() { 38 | $this->gc($this->maxtime); 39 | return true; 40 | } 41 | 42 | /** 43 | * 获取sesseion_id 44 | * @param string $session_id 45 | * @return array $session 46 | */ 47 | public function read($session_id) { 48 | $re = m($this->table)->field('session_data')->where(['session_id' => $session_id, 'session_expire' => ['>', time()]])->find(); 49 | return $re['session_data']; 50 | } 51 | 52 | /** 53 | * 存储session 54 | * @param string $session_id 55 | * @param array $session_data 56 | * @return bool 57 | */ 58 | public function write($session_id, $session_data) { 59 | $map = array('session_id' => $session_id); 60 | $data = array( 61 | 'session_data' => $session_data, 62 | 'session_expire' => time() + $this->maxtime, 63 | ); 64 | if (m($this->table)->where($map)->find()) { 65 | $re = m($this->table)->where($map)->update($data); 66 | } else { 67 | $re = m($this->table)->insert(array_merge($map, $data)); 68 | } 69 | return $re ? true : false; 70 | } 71 | 72 | /** 73 | * 销毁session 74 | * @param string $session_id 75 | * @return bool 76 | */ 77 | public function destroy($session_id) { 78 | $re = m($this->table)->where(['session_id' => $session_id])->delete(); 79 | return $re ? true : false; 80 | } 81 | 82 | /** 83 | * 销毁session 84 | * @param int $maxlifetime 85 | * @return bool 86 | */ 87 | public function gc($maxlifetime) { 88 | $maxtime = time() + $maxlifetime; 89 | $re = m($this->table)->where(['session_expire' => ['<', $maxtime]])->delete(); 90 | return $re ? true : false; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /phppoem/core/cache/redis.php: -------------------------------------------------------------------------------- 1 | config('redis_host') ?: '127.0.0.1', 16 | 'port' => config('redis_port') ?: 6379, 17 | 'expire' => config('redis_expire') ?: null, 18 | 'auth' => config('redis_auth') ?: 0, 19 | 'timeout' => config('redis_timeout') ?: 0, 20 | ), $option); 21 | $this->_option = $option; 22 | $this->_ins = new \redis(); 23 | $re = $this->_ins->connect($option['host'], $option['port'], $option['timeout']); 24 | if (!$re) { 25 | throw new \Exception("Connect Redis Failed", 1); 26 | } 27 | 28 | if ($option['auth']) { 29 | $this->_ins->auth($option['auth']); 30 | } 31 | } 32 | 33 | /** 34 | * 魔术方法,调用redis函数 35 | * @param string $name 方法名 36 | * @param array $args 参数 37 | * @return mixed redis return 38 | */ 39 | public function __call($name, $args){ 40 | return call_user_func(array($this->_ins, $name), $args); 41 | } 42 | 43 | /** 44 | * 获取键key 45 | * @param string $key 键 46 | * @return string 数据 47 | */ 48 | public function get($key) { 49 | return $this->_ins->get($key); 50 | } 51 | 52 | /** 53 | * 设置键值 54 | * @param string $key 键 55 | * @param string $value 值 56 | * @param integer $option 选项 0:序列化写文件 -1:直接写文件 -2:追加文件 否则序列化追加文件 57 | * @return 返回设置的文件路径 58 | */ 59 | public function set($key, $value, $expire = null) { 60 | if (is_null($expire)) { 61 | $expire = $this->_option['expire']; 62 | } 63 | 64 | $value = is_object($value) || is_array($value) ? json_encode($value) : $value; 65 | return is_int($expire) ? $this->_ins->set($key, $value) : $this->_ins->setex($key, $expire, $value); 66 | } 67 | 68 | /** 69 | * 设置过期时间 70 | * 71 | * @param string $key 72 | * @param string $value 73 | * @param int $expire 74 | * @return void 75 | */ 76 | public function set_expire($key, $value, $expire) { 77 | return $this->_ins->setex($key, $expire, $value); 78 | } 79 | 80 | /** 81 | * 删除键值 82 | * @param string $key 键 83 | * @return bool 84 | */ 85 | public function del($key) { 86 | return $this->_ins->del($key); 87 | } 88 | 89 | /** 90 | * 析构函数,删除资源 91 | * @return null 92 | */ 93 | function __destruct() { 94 | $this->_ins->close(); 95 | } 96 | } -------------------------------------------------------------------------------- /phppoem/core/more/page.php: -------------------------------------------------------------------------------- 1 | no_clear()->count(); // 总记录数 16 | $list = $m->limit(($page - 1) * $page_size, $page_size)->select(); // 结果列表 17 | $info['list'] = $list; 18 | $info['total'] = $total; // 总记录数 19 | $info['url'] = $url ? $url : POEM_FUNC_URL; //url 20 | $info['page'] = $page; // 当前页 21 | $info['page_count'] = ceil((int) $info['total'] / (int) $page_size); //总页数 22 | $info['html'] = self::pagehtml($page, $info['page_count'], $info['url'], $show_nums, $total); 23 | return $info; 24 | } 25 | 26 | /** 27 | * 分页页码HTML 28 | * @param int $np 当前页 29 | * @param int $tp 总页数 30 | * @param string $url 31 | * @param int $num 页码展示数量 32 | * @param int $total 总条数 33 | * @return string 34 | */ 35 | static function pagehtml($np, $tp, $url, $num = 5, $total) { 36 | $up = $np - 1; // 上一页 37 | $dp = $np + 1; // 下一页 38 | $f = ($np == 1) ? 'disabled' : ''; // 是否为首页 39 | $e = ($np == $tp) ? 'disabled' : ''; // 是否问尾页 40 | $html = ''; 41 | if ($tp > 0) { 42 | $html .= '"; 67 | } 68 | return $html; 69 | } 70 | 71 | /** 72 | * [geturl description] 73 | * @param [type] $url [description] 74 | * @param [type] $page [description] 75 | * @return [type] [description] 76 | */ 77 | static function geturl($url, $page) { 78 | static $var; 79 | if (!$var) { 80 | $var = array_merge($_GET, $_POST); 81 | } 82 | 83 | $var['p'] = $page; 84 | return $url . '?' . http_build_query($var); 85 | } 86 | } -------------------------------------------------------------------------------- /phppoem/core/more/build.php: -------------------------------------------------------------------------------- 1 | {$varname}'; 17 | 18 | // 控制器文件 19 | protected static $controller = ''value'\n);\n"; 69 | $route = "'value'\n);\n"; 70 | $m_path = APP_PATH . '/' . $module . '/model'; 71 | $v_path = APP_PATH . '/' . $module . '/view'; 72 | $c_path = APP_PATH . '/' . $module . '/controller'; 73 | $app = array( 74 | APP_PATH => array( 75 | 'config.php' => $cfg, 76 | 'function.php' => ' $route, 78 | ), 79 | $v_path => array(), 80 | $c_path => array(), 81 | ); 82 | if (!empty($models)) { 83 | $app[$m_path] = array(); 84 | } 85 | 86 | foreach ($app as $dir => $v) { 87 | if (!is_dir($dir)) { 88 | mkdir($dir, 0755, true); 89 | } 90 | 91 | foreach ($v as $file => $data) { 92 | if (!is_file("$dir/$file")) { 93 | file_put_contents("$dir/$file", $data); 94 | } 95 | } 96 | 97 | } 98 | foreach ($ctrls as $ctrl) { 99 | $ctrl = strtolower($ctrl); 100 | mkdir("$v_path/$ctrl", 0755, true); 101 | $html_demo = "$v_path/$ctrl/viewtest.html"; 102 | !is_file($html_demo) && file_put_contents($html_demo, self::$view); 103 | $data = str_replace(array('[MODULE]', '[CTRL]'), array($module, $ctrl), self::$controller); 104 | $ctrl_fn = "$c_path/$ctrl.php"; 105 | !is_file($ctrl_fn) && file_put_contents($ctrl_fn, $data); 106 | } 107 | foreach ($models as $model) { 108 | $model = strtolower($model); 109 | $data = str_replace(array('[MODULE]', '[MODEL]'), array($module, $model), self::$model); 110 | $model_demo = "$m_path/$model.php"; 111 | !is_file($model_demo) && file_put_contents($model_demo, self::$model); 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /phppoem/core/route.php: -------------------------------------------------------------------------------- 1 | $path) { 84 | // 匹配带{id}的参数 85 | preg_match_all('/{(\w+)}/', $pattern, $matchs, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); 86 | 87 | $tmp = ''; 88 | $pos = 0; 89 | $vars = array(); 90 | foreach ($matchs as $match) { 91 | $tmp .= preg_quote(substr($pattern, $pos, $match[0][1] - $pos), '/') . '([,\w]+)'; 92 | $pos = $match[0][1] + strlen($match[0][0]); // offset + var_len 93 | $vars[] = $match[1][0]; // varname 94 | } 95 | $tmp .= preg_quote(substr($pattern, $pos), '/'); 96 | $flag = preg_match_all('#^' . $tmp . '$#', $url, $values); // 匹配url 97 | if ($flag) { 98 | foreach ($vars as $k => $name) { 99 | $_GET[$name] = $values[$k + 1][0]; 100 | $_REQUEST[$name] = $_GET[$name]; 101 | } 102 | 103 | $url = $path; 104 | break; 105 | } 106 | } 107 | return $url; 108 | } 109 | 110 | /** 111 | * 参数解析 112 | * @param string $param 113 | * @return void 写入$GET 114 | */ 115 | private static function parse_param($param) { 116 | $len = floor(count($param) / 2); 117 | $i = 0; 118 | while ($len--) { 119 | $_GET[$param[$i]] = $param[$i + 1]; 120 | $_REQUEST[$param[$i]] = $param[$i + 1]; 121 | $i += 2; 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /phppoem/core/tpl/trace.php: -------------------------------------------------------------------------------- 1 |
2 | 24 | 25 |
26 |
27 | 69 | -------------------------------------------------------------------------------- /phppoem/core/view.php: -------------------------------------------------------------------------------- 1 | fetch($tpl); 14 | } 15 | 16 | /** 17 | * 赋值 这里assign('name','phppoem'),可以在 v()或fetch()当作变量使用 $name 18 | * @param string/array $key 数组会直接merge合并 19 | * @param string $value 20 | * @return void 21 | */ 22 | function assign($key, $value = '') { 23 | if (is_array($key)) { 24 | $this->html_vars = array_merge($this->html_vars, $key); 25 | } else { 26 | $this->html_vars[$key] = $value; 27 | } 28 | 29 | } 30 | 31 | /** 32 | * 获取视图信息 相对于 v('index')函数会渲染并输出echo,而 fetch('index')会获取'index'渲染的结果 33 | * @param string $tpl 模板名 34 | * @return string $html 35 | */ 36 | function fetch($tpl = '') { 37 | // 模板文件 38 | T('POEM_COMPILE_TIME'); 39 | $tpl = $this->parse_tpl($tpl); 40 | 41 | $filekey = str_replace(APP_PATH, '', $tpl); // 文件名 home/index/index.html 42 | $filekey = str_replace(POEM_PATH, 'poem', $filekey); // 文件名 系统页面 43 | $filekey = 'view/'.$filekey; 44 | 45 | $c_w_v_tpl = poem_ins('\poem\cache\file')->has($filekey); // 判断是否存在 46 | if (APP_DEBUG || $c_w_v_tpl === false || config('view_debug')) { 47 | $content = file_get_contents($tpl); 48 | // 开启页面布局 49 | if (($layfile = config('layout')) && config('layout_on') === true) { 50 | $layfile = $this->parse_tpl($layfile); 51 | $content = str_replace('{__LAYOUT__}', $content, file_get_contents($layfile)); 52 | } 53 | $content = $this->compiler($content); // 模板编译 54 | $c_w_v_tpl = poem_ins('\poem\cache\file')->set($filekey, $content); 55 | } 56 | T('POEM_COMPILE_TIME', 0); 57 | 58 | // 模板变量 59 | if (!empty($this->html_vars)) { 60 | extract($this->html_vars); 61 | } 62 | 63 | // 缓冲区 64 | ob_start(); 65 | ob_implicit_flush(0); 66 | include $c_w_v_tpl; 67 | 68 | // 获取并清空缓存 69 | return ob_get_clean(); 70 | } 71 | 72 | 73 | /** 74 | * 获取指定页面文件绝对路径 75 | * @param string $tpl 模板uri 76 | * @return string $file_path 77 | */ 78 | protected function parse_tpl($tpl = '') { 79 | if (is_file($tpl)) { 80 | return $tpl; 81 | } 82 | $module = POEM_MODULE; 83 | $tpl = $tpl != '' ? $tpl : POEM_FUNC; 84 | 85 | if (strpos($tpl, '@') !== false) { 86 | // 模块 Home@Index/index 87 | list($module, $tpl) = explode('@', $tpl); 88 | } elseif (strpos($tpl, ':') !== false) { 89 | // 指定文件夹 Index:index 90 | $tpl = str_replace(':', '/', $tpl); 91 | } else { 92 | $tpl = POEM_CTRL. "/{$tpl}"; 93 | } 94 | 95 | $file = APP_PATH . "/$module/view/{$tpl}.html"; // html文件路径 96 | $view_path = config('view_path'); 97 | if($view_path) { 98 | $file = $view_path . "/$module/{$tpl}.html"; // html文件路径 99 | } 100 | 101 | if (!is_file($file)) { 102 | \poem\app::halt('文件不存在' . $file); 103 | } 104 | 105 | return $file; 106 | } 107 | 108 | /** 109 | * 编译渲染文件 110 | * @param string $content 111 | * @return string $html 112 | */ 113 | protected function compiler($content) { 114 | // 添加安全代码 代表入口文件进入的 115 | $content = '' . $content; 116 | $content = preg_replace( 117 | array( 118 | '/{\$([\w\[\]\'"\$]+)}/s', // 匹配 {$vo['info']} 119 | '/{\:([^\}]+)}/s', // 匹配 {:func($vo['info'])} 120 | '//', // 匹配 121 | '//', // 匹配 122 | '//', 123 | '//', 124 | ), 125 | array( 126 | '', 127 | '', 128 | '', 129 | '', 130 | '', 131 | '', 132 | ), 133 | $content); 134 | $content = str_replace(array('', '', 'POEM_URL', 'POEM_MODULE_URL', 'POEM_CTRL_URL', 'POEM_FUNC_URL'), array('','', POEM_URL, POEM_MODULE_URL, POEM_CTRL_URL, POEM_FUNC_URL), $content); 135 | // 匹配 136 | $content = preg_replace_callback( 137 | '//', 138 | function ($matches) {return $this->compiler(file_get_contents($this->parse_tpl($matches[1])));}, 139 | $content); 140 | 141 | return $content; 142 | } 143 | 144 | /** 145 | * 模板解析 include 146 | * @param string $content 147 | * @return string $content 148 | */ 149 | protected function compile_include($content) { 150 | // 匹配 151 | $flag = preg_match_all('//', $content, $matches); 152 | foreach ($matches[1] as $v) { 153 | $tmp = $this->compiler(file_get_contents($this->parse_tpl($matches[1]))); 154 | preg_replace('//', $tmp, $content); 155 | } 156 | return $content; 157 | } 158 | 159 | /** 160 | * 页面跳转 161 | * @param string $info 页面展示内容 162 | * @param string $url 展示后跳转至uri 163 | * @param string $param url 参数,如: ?type=1 164 | * @param int $second 页面停留时间 165 | * @param int $status 状态 0成功 1失败 166 | * @return void 167 | */ 168 | function auto_jump($info, $url = '', $param = '', $second = 0, $status = 1) { 169 | $key = $status == 1 ? 'message' : 'error'; 170 | if ($url != '') { 171 | $url = poem_url($url); 172 | } 173 | 174 | if ($param) { 175 | $url .= $param; 176 | } 177 | 178 | $url = $url ? $url : ($status == 1 ? $_SERVER["HTTP_REFERER"] : 'javascript:history.back(-1);'); 179 | if (!$second) { 180 | $second = $status == 1 ? 1 : 3; 181 | } 182 | 183 | $this->assign($key, $info); 184 | $this->assign('jumpUrl', $url); 185 | $this->assign('waitSecond', $second); 186 | 187 | $file = config('poem_jump_tpl') ? config('poem_jump_tpl') : CORE_PATH . 'tpl/jump.php'; 188 | 189 | $this->display($file); 190 | exit; 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /phppoem/core/app.php: -------------------------------------------------------------------------------- 1 | clean_log(); 43 | 44 | t('POEM_EXEC_TIME', 0); 45 | t('POEM_TIME', 0); // 计时结束 46 | if ($show_log && config('debug_trace') && !IS_AJAX && !IS_CLI) { 47 | log::show(); 48 | } 49 | $time = number_format(t('POEM_TIME', -1), 5); 50 | l($_SERVER['REQUEST_URI'] . ' '. $time. 's'); 51 | exit; 52 | } 53 | 54 | /** 55 | * 加载公共函数,配置 56 | * @return null 57 | */ 58 | static function boot() { 59 | // 加载方法 60 | $time = microtime(1); 61 | include CORE_FUNC; // 核心库 62 | if (is_file(APP_FUNC)) { 63 | include APP_FUNC; 64 | } 65 | // App公共 66 | t('POEM_FUNC_TIME', '', microtime(1) - $time); 67 | 68 | // 加载配置 69 | t('POEM_CONF_TIME'); 70 | config(include CORE_CONF); // 核心库 71 | if (is_file(APP_CONF)) { 72 | config(include APP_CONF); 73 | } 74 | // App公共 75 | t('POEM_CONF_TIME', 0); 76 | } 77 | 78 | /** 79 | * 执行用户代码 80 | * @return null 81 | */ 82 | static function exec() { 83 | try { 84 | // 非法操作 85 | if (!preg_match('/^[A-Za-z](\w)*$/', POEM_FUNC)) { 86 | throw new \reflectionException('function: [' . htmlspecialchars(POEM_FUNC) . '] not exists'); 87 | } 88 | 89 | if (is_file($file = APP_PATH . '/' . POEM_MODULE . '/boot/function.php')) { 90 | include $file; 91 | } 92 | // 请求模块 93 | if (is_file($file = APP_PATH . '/' . POEM_MODULE . '/boot/config.php')) { 94 | config(include $file); 95 | } 96 | // 请求模块 97 | // load::instance(POEM_MODULE.'\\controller\\'.POEM_CTRL, POEM_FUNC); 98 | $ctrl = load::controller(POEM_CTRL); // 执行操作 99 | $method = new \reflectionMethod($ctrl, POEM_FUNC); 100 | if ($method->isPublic()) { 101 | $method->invoke($ctrl); 102 | } else { 103 | throw new \reflectionException('module('.POEM_MODULE.') controller('.POEM_CTRL.') func('.POEM_FUNC.') not found'); 104 | } 105 | } catch (\ReflectionException $e) { 106 | log::get_instance()->set_switch(false); 107 | // 操作不存在 108 | if (function_exists('_app_empty_call')) { 109 | _app_empty_call($e); 110 | } else { 111 | // 不存在的ctrl/func抛出异常, 不写日志 112 | self::app_exception($e); 113 | } 114 | } 115 | } 116 | 117 | /** 118 | * 异常Exception处理 119 | * @param class $e Exception 120 | * @param bool $is_write_log 是否写日志,默认写 121 | * @return null 122 | */ 123 | static function app_exception($e) { 124 | $err = array(); 125 | $err['message'] = $e->getMessage(); 126 | $trace = $e->getTrace(); 127 | if ('E' == $trace[0]['function']) { 128 | $err['file'] = $trace[0]['file']; 129 | $err['line'] = $trace[0]['line']; 130 | } else { 131 | $err['file'] = $e->getFile(); 132 | $err['line'] = $e->getLine(); 133 | } 134 | $err['trace'] = $e->getTraceAsString(); 135 | 136 | self::halt($err); 137 | } 138 | 139 | /** 140 | * 自定义错误处理 141 | * @param int $errno 错误代码 142 | * @param string $errstr 错误信息 143 | * @param string $errfile 错误文件 144 | * @param int $errline 文件错误行号 145 | * @return null 146 | */ 147 | static function app_error($errno, $errstr, $errfile, $errline) { 148 | $errStr = "$errstr $errfile 第 $errline 行."; 149 | 150 | $haltArr = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR); 151 | if (in_array($errno, $haltArr)) { 152 | self::halt($errStr); 153 | } 154 | } 155 | 156 | /** 157 | * 致命错误Fatal捕获 158 | * @return null 159 | */ 160 | static function app_fatal() { 161 | $e = error_get_last(); 162 | $haltArr = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR); 163 | if ($e && in_array($e['type'], $haltArr)) { 164 | self::halt($e); 165 | } 166 | } 167 | 168 | /** 169 | * 异常处理并结束 170 | * @param array/string $err 异常信息 171 | * @param bool $is_write_log 是否写日志,默认写 172 | * @return null 173 | */ 174 | static function halt($err) { 175 | $e = array(); 176 | if (APP_DEBUG || IS_CLI) { 177 | if (!is_array($err)) { 178 | $trace = debug_backtrace(); 179 | $e['message'] = $err; 180 | $e['file'] = $trace[0]['file']; 181 | $e['line'] = $trace[0]['line']; 182 | ob_start(); 183 | debug_print_backtrace(); 184 | $e['trace'] = ob_get_clean(); 185 | } else { 186 | $e = $err; 187 | } 188 | 189 | } else { 190 | $err = is_array($err) ? $err['message'] : $err; 191 | $e['message'] = config('sys_error_msg') ?: $err; 192 | } 193 | 194 | l("${e['file']}:${e['line']} ${e['message']}", log::FATAL, 2); 195 | if(function_exists('_app_halt')){ 196 | _app_halt($e); 197 | exit; 198 | } 199 | 200 | if (IS_CLI || IS_AJAX) { 201 | $log_id = log::get_instance()->get_log_id(); 202 | $log_str = iconv('UTF-8', 'gbk', $e['message']) . PHP_EOL . 203 | 'File: ' . $e['file'] . ':' . $e['line'] . PHP_EOL . 204 | 'LogID:'. $log_id . PHP_EOL . 205 | $e['trace']; 206 | exit($log_str); 207 | } 208 | 209 | include CORE_PATH . 'tpl/exception.php'; 210 | exit; 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /phppoem/core/log.php: -------------------------------------------------------------------------------- 1 | 'FATAL', 13 | self::ERR => 'ERR', 14 | self::WARN => 'WARN', 15 | self::INFO => 'INFO', 16 | self::DEBUG => 'DEBUG', 17 | ); 18 | 19 | private static $instance; 20 | 21 | protected $log_level; 22 | protected $log_remain_days; 23 | protected $log_id; 24 | protected $log_dir; 25 | protected $log_file; 26 | private $log_relative_path; // 相对地址则需要清理项目路径前缀 27 | private $project_path; 28 | 29 | protected $log_switch = true; // 日志开关 30 | 31 | private static $trace = array(); // 页面展示日志信息 32 | 33 | /** 34 | * 构造文件 35 | * @param array $cfg log_* 配置 36 | */ 37 | function __construct($cfg) { 38 | $this->log_level = $cfg['log_level']; 39 | $this->log_remain_days = $cfg['log_remain_days']; 40 | $this->set_log_file($cfg['log_path']); 41 | $this->log_relative_path = $cfg['log_relative_path']; 42 | if($this->log_relative_path){ 43 | $this->project_path = realpath(APP_PATH.'/../').'/'; 44 | } 45 | 46 | $arr = gettimeofday(); 47 | $log_id = ($arr['sec']*100000 + $arr['usec']/10) & 0x7FFFFFFF; 48 | $this->log_id = $log_id; 49 | } 50 | 51 | public function get_log_id(){ 52 | return $this->log_id; 53 | } 54 | 55 | public function set_switch($flag){ 56 | $this->log_switch = $flag; 57 | } 58 | 59 | /** 60 | * 单例模式使用log 61 | * @return self 62 | */ 63 | static function get_instance() { 64 | if (is_null(self::$instance)) { 65 | self::$instance = new \poem\log(config()); 66 | } 67 | return self::$instance; 68 | } 69 | 70 | /** 71 | * 写入日志 72 | * @param string $str 日志信息 73 | * @param string $lvl 日志级别 74 | * @param string $depth 深度,用于反查哪个文件打的日志 75 | * @return null 76 | */ 77 | public function write($str, $lvl, $depth = 0) { 78 | if(!$this->log_switch) return; 79 | 80 | if ($lvl > $this->log_level) return; 81 | list($cur_file, $cur_line) = $this->get_trace_info($depth); 82 | 83 | $time = date('Y-m-d H:i:s'); 84 | $log = "$time {$this->log_id} $cur_file:$cur_line $str" . PHP_EOL; 85 | 86 | self::trace('LOG', $log); 87 | 88 | if (!is_dir($this->log_dir)) { 89 | mkdir($this->log_dir, 0755, true); 90 | } 91 | $lvl_name = $this->levels[$lvl]; 92 | $lvl_info = $this->levels[self::INFO]; 93 | file_put_contents($this->log_file.'.'.$lvl_info, $lvl_name.' '.$log, FILE_APPEND); 94 | if($lvl != self::INFO){ 95 | file_put_contents($this->log_file.'.'.$lvl_name, $lvl_name.' '.$log, FILE_APPEND); 96 | } 97 | } 98 | 99 | /** 100 | * Undocumented function 101 | * 102 | * @param int $depth 103 | * @param bool $filter_poem_path 104 | * @return array 105 | */ 106 | private function get_trace_info($depth){ 107 | if($depth == self::DEPTH_FILTER_POEM){ 108 | $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10); 109 | foreach($trace as $v) 110 | if(stripos($v['file'], POEM_PATH)===false){ 111 | $cur_trace = $v; 112 | break; 113 | } 114 | }else{ 115 | $depth ++; 116 | $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, $depth + 1); 117 | $cur_trace = isset($trace[$depth]) ? $trace[$depth] : array('file'=>'','line'=>''); 118 | } 119 | 120 | $cur_file = $cur_trace['file']; 121 | $cur_line = $cur_trace['line']; 122 | if($this->log_relative_path){ 123 | $cur_file = str_replace($this->project_path, '', $cur_file); 124 | } 125 | 126 | return array($cur_file, $cur_line); 127 | } 128 | 129 | /** 130 | * 设置日志文件 131 | * 通过 config.php 'log_path' 设置日志路径 132 | * 日志是按小时为文件名切割的,保留时间见 $this->clean_log() 133 | * @param string $log_dir 日志保存目录 134 | * @return void 135 | */ 136 | private function set_log_file($log_dir) { 137 | if (empty($log_dir)) { 138 | $log_dir = config('runtime_path') . '/' . 'log'; 139 | } 140 | $log_dir .= '/' . POEM_MODULE; 141 | 142 | $this->log_dir = $log_dir; 143 | $filename = date('YmdH') . '.log'; 144 | $this->log_file = $log_dir . '/' . $filename; 145 | } 146 | 147 | /** 148 | * 清理日志,默认保留 1 天 149 | * 通过 config.php 'log_remain_days' 设置日志保留天数 150 | * @return void 151 | */ 152 | public function clean_log() { 153 | $dh = opendir($this->log_dir); 154 | if (!$dh) { 155 | return; 156 | } 157 | while (false !== ($file = readdir($dh))) { 158 | if ($file == '.' || $file == '..') { 159 | continue; 160 | } 161 | $full_path = $this->log_dir . '/' . $file; 162 | if (is_dir($full_path)) { 163 | continue; 164 | } 165 | $file_date = substr($file, 0, 8); // date('YmdH').log 166 | $cur_date = date('Ymd'); 167 | 168 | $days = $cur_date - $file_date; 169 | if ($this->log_remain_days <= $days) { 170 | unlink($full_path); ////删除文件 171 | } 172 | } 173 | closedir($dh); 174 | } 175 | 176 | /** 177 | * 日志追踪,页面查看 178 | * @param string $key 键 179 | * @param string $value 值 180 | * @return null 181 | */ 182 | static function trace($key, $value) { 183 | if (!config('debug_trace')) { 184 | return; 185 | } 186 | 187 | if (isset(self::$trace[$key]) && count(self::$trace[$key]) > 50) { 188 | return; 189 | } 190 | 191 | self::$trace[$key][] = $value; 192 | } 193 | 194 | /** 195 | * 请求结束,由框架保存 196 | * @return null 197 | */ 198 | static function show() { 199 | $trace_tmp = self::$trace; 200 | $files = get_included_files(); 201 | foreach ($files as $key => $file) { 202 | $files[$key] = $file . ' ( ' . number_format(filesize($file) / 1024, 2) . ' KB )'; 203 | } 204 | $cltime = T('POEM_TIME', -1); 205 | $trace_tmp['SYS'] = array( 206 | '请求信息' => $_SERVER['REQUEST_METHOD'] . ' ' . strip_tags($_SERVER['REQUEST_URI']) . ' ' . $_SERVER['SERVER_PROTOCOL'] . ' ' . date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']), 207 | '总吞吐量' => number_format(1 / $cltime, 2) . ' req/s', 208 | '总共时间' => number_format($cltime, 5) . ' s', 209 | '框架加载' => number_format(($cltime - T('POEM_EXEC_TIME', -1)), 5) . ' s (func:' . number_format(T('POEM_FUNC_TIME', -1) * 1000, 2) . 'ms conf:' . number_format(T('POEM_CONF_TIME', -1) * 1000, 2) . 'ms route:' . number_format(T('POEM_ROUTE_TIME', -1) * 1000, 2) . 'ms)', 210 | 'App时间' => number_format(T('POEM_EXEC_TIME', -1), 5) . ' s (compile:' . number_format(T('POEM_COMPILE_TIME', -1) * 1000, 2) . ' ms)', 211 | '内存使用' => number_format(memory_get_usage() / 1024 / 1024, 5) . ' MB', 212 | '文件加载' => count($files), 213 | '会话信息' => 'SESSION_ID=' . session_id(), 214 | ); 215 | 216 | $trace_tmp['FILE'] = $files; 217 | 218 | $arr = array( 219 | 'SYS' => '基本', 220 | 'FILE' => '文件', 221 | 'SQL' => '数据库', 222 | 'LOG' => '日志', 223 | ); 224 | foreach ($arr as $key => $value) { 225 | $num = 50; 226 | $len = 0; 227 | if (is_array($trace_tmp[$key]) && ($len = count($trace_tmp[$key])) > $num) { 228 | $trace_tmp[$key] = array_slice($trace_tmp[$key], 0, $num); 229 | } 230 | $trace[$value] = $trace_tmp[$key]; 231 | if ($len > $num) { 232 | $trace[$value][] = "...... 共 $len 条"; 233 | } 234 | 235 | } 236 | $totalTime = number_format($cltime, 3); 237 | include CORE_PATH . 'tpl/trace.php'; 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /phppoem/core/db.php: -------------------------------------------------------------------------------- 1 | \PDO::ERRMODE_EXCEPTION, \PDO::MYSQL_ATTR_MULTI_STATEMENTS => false); 13 | // 统计事务次数,用于支持嵌套事务 14 | protected $transaction_times = 0; 15 | 16 | /** 17 | * 获取db实例 18 | * @param array $config db配置 19 | * @return object $instance db实例 20 | */ 21 | static function get_instance($config) { 22 | $key = md5(is_array($config) ? serialize($config) : $config); 23 | 24 | if (!isset(self::$_ins[$key]) || !(self::$_ins[$key] instanceof self)) { 25 | $db_obj = new self(); 26 | $db_obj->_cfg = $config; 27 | if (!is_string($config) && isset($config['db_deploy']) && !empty($config['db_deploy'])) { 28 | $db_obj->parse_cfg(); 29 | } 30 | self::$_ins[$key] = $db_obj; 31 | } 32 | 33 | return self::$_ins[$key]; 34 | } 35 | 36 | /** 37 | * 初始化连接 38 | * @param bool $master 是否连接主节点 39 | * @return void 40 | */ 41 | public function init_connect($master = true) { 42 | if (!is_string($this->_cfg) && isset($this->_cfg['db_deploy']) && !empty($this->_cfg['db_deploy'])) { 43 | // 采用分布式数据库, 存在主从的区别 44 | $this->_conn = $this->deploy_connect($master); 45 | } else { 46 | // 默认单数据库 47 | $this->_conn = $this->connect(); 48 | } 49 | } 50 | 51 | /** 52 | * 解析配置 53 | * 数据配置文件,多数据库配置通过 "," 分割 54 | * @return void 55 | */ 56 | private function parse_cfg() { 57 | $this->_cfg['db_user'] = explode(',', $this->_cfg['db_user']); 58 | $this->_cfg['db_pass'] = explode(',', $this->_cfg['db_pass']); 59 | $this->_cfg['db_host'] = explode(',', $this->_cfg['db_host']); 60 | $this->_cfg['db_port'] = explode(',', $this->_cfg['db_port']); 61 | $this->_cfg['db_name'] = explode(',', $this->_cfg['db_name']); 62 | $this->_cfg['db_charset'] = explode(',', $this->_cfg['db_charset']); 63 | } 64 | 65 | /** 66 | * 分布式数据配置 67 | * @param bool $master 是否连接主节点 68 | * @return [type] [description] 69 | */ 70 | protected function deploy_connect($master = false) { 71 | // 分布式数据库配置解析 72 | $conf = $this->_cfg; 73 | 74 | // 数据库读写是否分离 75 | if ($conf['db_rw_separate']) { 76 | if ($master) { 77 | $id = mt_rand(0, $this->_cfg['db_master_num'] - 1); 78 | } else { 79 | if (is_numeric($conf['db_slave_no'])) { 80 | $id = $conf['db_slave_no']; 81 | } else { 82 | $id = mt_rand($conf['db_master_num'], count($conf['db_host']) - 1); 83 | } 84 | } 85 | } else { // 读写操作不区分服务器 86 | $id = mt_rand(0, count($conf['db_host']) - 1); // 每次随机连接的数据库 87 | } 88 | 89 | $id_config = array( 90 | 'db_type' => $conf['db_type'], 91 | 'db_user' => isset($conf['db_user'][$id]) ? $conf['db_user'][$id] : $conf['db_user'][0], 92 | 'db_pass' => isset($conf['db_pass'][$id]) ? $conf['db_pass'][$id] : $conf['db_pass'][0], 93 | 'db_host' => isset($conf['db_host'][$id]) ? $conf['db_host'][$id] : $conf['db_host'][0], 94 | 'db_port' => isset($conf['db_port'][$id]) ? $conf['db_port'][$id] : $conf['db_port'][0], 95 | 'db_name' => isset($conf['db_name'][$id]) ? $conf['db_name'][$id] : $conf['db_name'][0], 96 | 'db_charset' => isset($conf['db_charset'][$id]) ? $conf['db_charset'][$id] : $conf['db_charset'][0], 97 | ); 98 | return $this->connect($id_config, $id, $master); 99 | } 100 | 101 | /** 102 | * 连接数据库 103 | * @param string $config 配置dsn信息 104 | * @param int $linkid 连接ID,分布式数据库时不同数据库标识 105 | * @param bool $reconnect 是否为重试 106 | * @return resource PDO类资源 107 | */ 108 | private function connect($config = '', $linkid = 0, $reconnect = false) { 109 | if (!isset($this->_linkid[$linkid])) { 110 | $dsn = $this->parse_dsn($config); 111 | if ($dsn['char']) { 112 | $this->options[\pdo::MYSQL_ATTR_INIT_COMMAND] = "SET NAMES '" . $dsn['char'] . "'"; 113 | } 114 | 115 | t('poem_db_exec'); 116 | try { 117 | $this->_linkid[$linkid] = new \pdo($dsn['dsn'], $dsn['user'], $dsn['pass'], $this->options); 118 | $time = number_format(T('poem_db_exec', 1) * 1000, 2); 119 | } catch (\PDOException $e) { 120 | if ($reconnect) { 121 | l($e->getMessage(), \poem\log::FATAL); 122 | $this->connect($config, $linkid); 123 | } else { 124 | throw new \Exception($e->getMessage()); 125 | } 126 | } 127 | Log::trace('SQL', "PDO连接 [{$time}ms]"); 128 | } 129 | return $this->_linkid[$linkid]; 130 | } 131 | 132 | /** 133 | * 解析数据源名称 Data Source Name, 字符串转换为数组 134 | * @param string $config dsn配置信息 135 | * @return array $config 136 | */ 137 | private function parse_dsn($config = '') { 138 | if ($config == '') { 139 | $config = $this->_cfg; 140 | } 141 | 142 | $char = ''; 143 | if (is_array($config)) { 144 | $type = $config['db_type']; 145 | $host = $config['db_host']; 146 | $port = $config['db_port']; 147 | $name = $config['db_name']; 148 | $user = $config['db_user']; 149 | $pass = $config['db_pass']; 150 | $char = $config['db_charset']; 151 | $dsn = "{$type}:host={$host};port={$port};dbname={$name};charset={$char}"; 152 | } else { 153 | $tmp = explode('@', $config); 154 | if (count($tmp) == 2) { 155 | list($user, $pass) = explode(':', $tmp[0]); 156 | $dsn = $tmp[1]; 157 | } else { 158 | $dsn = $config; 159 | } 160 | } 161 | return array('user' => $user, 'pass' => $pass, 'char' => $char, 'dsn' => $dsn); 162 | } 163 | 164 | /** 165 | * 关闭所有连接 166 | * @return void 167 | */ 168 | public function close() { 169 | $this->_conn = null; 170 | } 171 | 172 | /** 173 | * 开启事务 174 | * @return bool 成功/失败 175 | */ 176 | public function begintransaction($name = '') { 177 | $this->transaction_times ++; 178 | $name && l($name.' begintransaction: '.$this->transaction_times, \poem\log::INFO, 2); 179 | if($this->transaction_times > 1){ 180 | return true; 181 | } 182 | 183 | return $this->_conn->begintransaction(); 184 | } 185 | 186 | /** 187 | * 回滚 188 | * @return void 189 | */ 190 | public function rollback($name = '') { 191 | $name && l($name . ' rollback success: ' . $this->transaction_times, \poem\log::INFO, 2); 192 | $this->transaction_times --; 193 | return $this->_conn->rollback(); 194 | } 195 | 196 | /** 197 | * 提交事务 198 | * @return void 199 | */ 200 | public function commit($name = '') { 201 | $name && l($name.' commit: '.$this->transaction_times, \poem\log::INFO, 2); 202 | $this->transaction_times --; 203 | if($this->transaction_times == 0) { 204 | $name && l($name.' commit success: '.$this->transaction_times, \poem\log::INFO, 2); 205 | return $this->_conn->commit(); 206 | } else if($this->transaction_times < 0) { 207 | throw new \exception('commit count > begintransaction count'); 208 | } 209 | } 210 | 211 | /** 212 | * 执行sql语句 213 | * @param string $sql 214 | * @return bool 215 | */ 216 | public function exec($sql) { 217 | T('poem_db_exec'); 218 | try { 219 | $re = $this->_conn->exec($sql); 220 | T('poem_db_exec', 0); 221 | return $re; 222 | } catch (\PDOException $e) { 223 | $this->error($e, $sql); 224 | } 225 | } 226 | 227 | /** 228 | * 执行sql查询 229 | * @param string $sql 230 | * @param array $bind 参数绑定 231 | * @return array $ret 二维查询结果 232 | */ 233 | public function select($sql, $bind) { 234 | return $this->execute($sql, $bind, 'select'); 235 | } 236 | 237 | /** 238 | * 执行sql插入 239 | * @param string $sql 240 | * @param array $bind 参数绑定 241 | * @return int $id 主键ID 242 | */ 243 | public function insert($sql, $bind) { 244 | return $this->execute($sql, $bind, 'insert'); 245 | } 246 | 247 | /** 248 | * 执行sql更新 249 | * @param string $sql 250 | * @param array $bind 参数绑定 251 | * @return int $count 影响行数 252 | */ 253 | public function update($sql, $bind) { 254 | return $this->execute($sql, $bind, 'update'); 255 | } 256 | 257 | /** 258 | * 执行sql删除 259 | * @param string $sql 260 | * @param array $bind 参数绑定 261 | * @return int $count 影响行数 262 | */ 263 | public function delete($sql, $bind) { 264 | return $this->execute($sql, $bind, 'delete'); 265 | } 266 | 267 | /** 268 | * 执行sql语句 269 | * @param string $sql 270 | * @param array $bind 参数绑定 271 | * @param string $flag 增删改查标记 272 | * @return mixed 查为数组/增为bool/删该为影响行数 273 | */ 274 | private function execute($sql, $bind, $flag = '') { 275 | T('poem_db_exec'); 276 | try { 277 | $pre = $this->_conn->prepare($sql); 278 | if (!$pre) { 279 | $this->error($this->_conn, $sql, $bind); 280 | } 281 | 282 | foreach ($bind as $k => $v) { 283 | $pre->bindValue($k, $v); 284 | } 285 | 286 | $re = $pre->execute(); 287 | if (!$re) { 288 | $this->error($pre, $sql, $bind); 289 | } 290 | 291 | T('poem_db_exec', 0); 292 | switch ($flag) { 293 | case 'insert':return $this->_conn->lastInsertId(); 294 | break; 295 | case 'update':return $pre->rowCount(); 296 | break; 297 | case 'delete':return $pre->rowCount(); 298 | break; 299 | case 'select':return $pre->fetchAll(\PDO::FETCH_ASSOC); 300 | break; 301 | default:break; 302 | } 303 | } catch (\PDOException $e) { 304 | $this->error($e, $sql, $bind); 305 | } 306 | } 307 | 308 | /** 309 | * 抛出异常 310 | * @param class $e PDOException 311 | * @param string $sql 312 | * @return class $Exception 313 | */ 314 | private function error($e, $sql, $bind=array()) { 315 | $info = implode(', ', $e->errorInfo) . "\n [SQL 语句]:" . $sql; 316 | if(config('db_debug')) $info .= ', [Bind Data]: ' . json_encode($bind); 317 | l($info, \poem\log::FATAL, \poem\log::DEPTH_FILTER_POEM); 318 | throw new \exception($info); 319 | } 320 | 321 | /** 322 | * 析构函数 323 | * @return void 324 | */ 325 | public function __destruct() { 326 | $this->_linkid = null; 327 | $this->_conn = null; 328 | } 329 | } -------------------------------------------------------------------------------- /phppoem/function.php: -------------------------------------------------------------------------------- 1 | i('name'), 31 | * 'age' => i('age'), 32 | * ) 33 | * 34 | * “,” 分割多个字段 35 | * “|” 后面是提示内容,如果参数为name空,则会提示 "姓名, 不能为空." 36 | */ 37 | function gp($param, $allow_null = false) { 38 | $arr = explode(',', $param); 39 | // 分解 | key和val 40 | foreach ($arr as $value) { 41 | $k = explode('|', $value); 42 | $v = i($k[0]); 43 | if ($allow_null == false && (is_null($v) || $v === '')) { 44 | $more = isset($k[1]) ? $k[1] : $k[0]; 45 | $tmp = "{$more} , 不能为空"; 46 | if (IS_AJAX) {ajax(config('default_errcode'), $tmp, 'param cannot be null');} 47 | err_jump($tmp); 48 | } 49 | $args[$k[0]] = $v; 50 | } 51 | return count($args) == 1 ? current($args) : $args; 52 | } 53 | 54 | /** 55 | * 配置管理 56 | * @param string $name 57 | * @param mixed $value 58 | * @return mixed 59 | * 使用方法 60 | * 1. config($key); 获取 config $key 61 | * 2. config($key,$value); 设置config $key值为$value 62 | * 3. config(); 获取所有config 63 | */ 64 | function config($name = null, $value = null) { 65 | static $config = array(); 66 | if (empty($name)) { 67 | return $config; 68 | } 69 | 70 | if (is_string($name)) { 71 | if (!is_null($value)) { 72 | $config[$name] = $value; 73 | } else { 74 | return isset($config[$name]) ? $config[$name] : null; 75 | } 76 | 77 | } 78 | if (is_array($name)) { 79 | $config = array_merge($config, $name); 80 | } 81 | return null; 82 | } 83 | 84 | /** 85 | * 调试输出 86 | * @param mixed multi 任意类型/数目 87 | * @return void 88 | */ 89 | function cc() { 90 | $vars = func_get_args(); 91 | foreach ($vars as $var) { 92 | if(IS_CLI){ 93 | var_export($var); 94 | echo PHP_EOL.'----------------------'.PHP_EOL; 95 | }else{ 96 | highlight_string("'; 98 | } 99 | } 100 | } 101 | 102 | 103 | /** 104 | * 调试输出 105 | * @param mixed multi 任意类型/数目 106 | * @return void 107 | */ 108 | function co() { 109 | $vars = func_get_args(); 110 | foreach ($vars as $var) { 111 | if(IS_CLI){ 112 | var_export($var); 113 | echo PHP_EOL.'----------------------'.PHP_EOL; 114 | }else{ 115 | highlight_string("'; 117 | } 118 | } 119 | \poem\app::end(false); 120 | } 121 | 122 | /** 123 | * ajax返回值 124 | * @param string $code 提示码 125 | * @param string $info 提示信息 126 | * @param string $data 数据信息 127 | * @return void echo json 128 | */ 129 | function ajax($code, $info = '', $data = '') { 130 | $re = ['code' => $code, 'info' => $info]; 131 | 132 | if ($data !== '') { 133 | $re['data'] = $data; 134 | } 135 | echo json_encode($re); 136 | \poem\app::end(); 137 | } 138 | 139 | /** 140 | * ajax返回值 141 | * @param string $code 提示码 142 | * @param string $info 提示信息 143 | * @param string $data 数据信息 144 | * @return void echo json 145 | */ 146 | function json($arr) { 147 | header('Content-Type: application/json'); 148 | echo json_encode($arr); 149 | \poem\app::end(); 150 | } 151 | 152 | /** 153 | * 封装返回值 154 | * @param string $code 提示码 155 | * @param string $info 提示信息 156 | * @param string $data 数据信息 157 | * @return array 158 | */ 159 | function ret($code, $info = '', $data = '') { 160 | return ['code' => $code, 'info' => $info, 'data' => $data]; 161 | } 162 | 163 | function ajax_obj($ret){ 164 | echo json_encode($ret); 165 | \pome\app::end(); 166 | } 167 | 168 | /** 169 | * l 日志log缩写, 日志信息 170 | * @param string $info 日志内容 171 | * @param string $level 日志级别 172 | * @param int $depth 调用 173 | * @return void 174 | */ 175 | function l($info, $level = \poem\log::INFO, $depth = 0) { 176 | if($depth != \poem\log::DEPTH_FILTER_POEM) $depth = $depth + 1; 177 | \poem\log::get_instance()->write($info, $level, $depth); 178 | } 179 | 180 | /** 181 | * m 模型model缩写,数据库表模型 182 | * @param string $tb 表名 183 | * @param string/array $config 配置信息 184 | * @return \poem\model 185 | */ 186 | function m($tb = '', $config = '') { 187 | static $model; 188 | $uniq_str = is_array($config) ? $tb.join(',', $config) : $tb.$config; 189 | $key = md5($uniq_str); 190 | if (!isset($model[$key])) { 191 | $class = 'poem\\model'; 192 | if ($tb && is_file($file = MODULE_MODEL . strtolower($tb) . '.php')) { 193 | include $file; 194 | $class = POEM_MODULE . '\\model\\' . $tb; 195 | } 196 | $model[$key] = new $class($tb, $config); 197 | } 198 | return $model[$key]; 199 | } 200 | 201 | /** 202 | * lib service 203 | * @param array $name 204 | * @return service 205 | */ 206 | function service($name){ 207 | static $service; 208 | if (!isset($service[$name])) { 209 | $class = '\\lib\\service\\'.$name; 210 | $service[$name] = new $class(); 211 | } 212 | return $service[$name]; 213 | } 214 | 215 | /** 216 | * v 视图view缩写,渲染并输出 217 | * @param string $tpl 模板名 218 | * @return void 219 | */ 220 | function v($tpl = '') { 221 | \poem\load::instance('poem\view')->display($tpl); 222 | 223 | \poem\app::end(); 224 | } 225 | 226 | /** 227 | * 获取视图信息 相对于 v('index')函数会渲染并输出echo,而 fetch('index')会获取'index'渲染的结果 228 | * @param string $tpl 模板名 229 | * @return string $html 230 | */ 231 | function fetch($tpl = '') { 232 | return \poem\load::instance('poem\view')->fetch($tpl); 233 | } 234 | 235 | /** 236 | * 赋值 这里assign('name','phppoem'),可以在 v()或fetch()当作变量使用 $name 237 | * @param string/array $key 数组会直接merge合并 238 | * @param string $value 239 | * @return void 240 | */ 241 | function assign($key, $value = '') { 242 | \poem\load::instance('poem\view')->assign($key, $value); 243 | } 244 | 245 | /** 246 | * 成功跳转页面 247 | * @param string $info 页面展示内容 248 | * @param string $uri 展示后跳转至uri 249 | * @param string $param url 参数,如: ?type=1 250 | * @param int $second 页面停留时间 251 | * @return void 252 | */ 253 | function ok_jump($info, $uri = '', $param = '', $second = false) { 254 | \poem\load::instance('poem\view')->auto_jump($info, $uri, $param, $second, 1); 255 | } 256 | 257 | /** 258 | * 失败成功跳转页面 259 | * @param string $info 页面展示内容 260 | * @param string $uri 展示后跳转至uri 261 | * @param string $param url 参数,如: ?type=1 262 | * @param int $second 页面停留时间 263 | * @return void 264 | */ 265 | function err_jump($info, $uri = '', $param = '', $second = false) { 266 | \poem\load::instance('poem\view')->auto_jump($info, $uri, $param, $second, 0); 267 | } 268 | 269 | /** 270 | * 缓存设置 271 | * @param string $cache_type 缓存类型 redis/memcache/file 272 | * @param string $key 健 273 | * @param string $value 值 274 | * @param array $options 配置选项 275 | * @return mixed 276 | */ 277 | function cache($cache_type = '', $key = '', $value = '', $options = null) { 278 | // option array为配置信息, int为超时 279 | $obj = \poem\cache::get_instance($cache_type, is_array($options) ? $options : null); 280 | if ($key === '') {return $obj->_ins;} // 返回实例 281 | 282 | if ($value === '') { 283 | return $obj->get($key); 284 | } elseif (is_null($value)) { 285 | return $obj->del($key); 286 | } else { 287 | return $obj->set($key, $value, is_numeric($options) ? $options : null); 288 | } 289 | } 290 | 291 | /** 292 | * 文件缓存 293 | * @param string $key 健 294 | * @param string $value 值 295 | * @param int $expire 值 296 | * @return mixed 297 | * 使用方法 298 | * 1. s($key); 获取文件 $key 299 | * 2. s($key,$value); 设置文件 $key值为$value 300 | */ 301 | function storage($key = '', $value = '', $expire = null) { 302 | $obj = poem_ins('\poem\cache\storage'); 303 | if ($value === '') { 304 | return $obj->get($key); 305 | } elseif (is_null($value)) { 306 | return $obj->del($key); 307 | } else { 308 | return $obj->set_expire($key, $value, $expire); 309 | } 310 | } 311 | 312 | /** 313 | * 使用 redis 314 | * @param string $key 315 | * @param string $value 316 | * @param array $options redis配置信息 317 | * @return mixed 318 | * 使用方法 319 | * 1. redis($key) 获取 $key 320 | * 2. redis($key,$value) 设置$key为$value 321 | * 3. redis($key,'',$option) 获取 $key,按$option配置 322 | * 4. redis($key,$value,$option) 设置$key为$value,按$option配置 323 | */ 324 | function redis($key = '', $value = '', $options = null) {return cache('redis', $key, $value, $options);} 325 | 326 | /** 327 | * 使用memcache 328 | * @param string $k [description] 329 | * @param string $v [description] 330 | * @param [type] $opt [description] 331 | * @return [type] [description] 332 | */ 333 | function memcache($key = '', $value = '', $options = null) {return cache('memcache', $key, $value, $options);} 334 | 335 | /** 336 | * 加载扩展文件 337 | * @param string $require_class /vendor 目录下的路径名称 338 | * @param string $ext 后缀 339 | * @return void 340 | */ 341 | function vendor($require_class, $ext = '.php') { 342 | \poem\load::vendor($require_class, $ext); 343 | } 344 | 345 | /** 346 | * cookie的使用 347 | * @param string $name cookie 健 348 | * @param string $value cookie 值 349 | * @return mixed 350 | * 使用方法: 351 | * 1. cookie($key); 获取 cookie $key 352 | * 2. cookie($key,$value); 设置cookie $key值为$value 353 | * 3. cookie($key,$value,time()+10); 设置cookie $key值为$value 并只保存10s 354 | * 4. cookie($key,$value,$option_arr); 设置cookie $key值为$value 并按以下option_arr条件 355 | * $option_arr = array( 356 | * 'prefix' => 10, // cookie前缀 357 | * 'expire' => '', // 过期时间, 默认浏览器关闭过期 358 | * 'path' => '/', // cookie可使用url路径 359 | * 'domain' => 'phppoem.com', // cookie可使用域名 360 | * 'secure' => false, // true 在https下会传输,http不会传输 361 | * 'httponly' => false, // true 无法通过程序读取如 JS脚本、Applet等 362 | * ); 363 | */ 364 | function cookie($name = '', $value = '', $option = null) { 365 | if (empty($name)) { 366 | return $_COOKIE; 367 | } 368 | 369 | $cfg = array( 370 | 'prefix' => config('cookie_prefix'), // cookie 名称前缀 371 | 'expire' => config('cookie_expire'), // cookie 保存时间 372 | 'path' => config('cookie_path'), // cookie 保存路径 373 | 'domain' => config('cookie_domain'), // cookie 有效域名 374 | 'secure' => config('cookie_secure'), // cookie 启用安全传输 375 | 'httponly' => config('cookie_httponly'), // httponly设置 376 | ); 377 | $name = $cfg['prefix'] . $name; 378 | if ($value === '') { 379 | return $_COOKIE[$name]; 380 | } 381 | 382 | if (!is_null($option)) { 383 | if (is_numeric($option)) { 384 | $cfg['expire'] = $option; 385 | } elseif (is_string($option)) { 386 | parse_str($option, $option); 387 | $cfg = array_merge($cfg, $option); 388 | } 389 | } 390 | 391 | if (is_null($value)) { 392 | $cfg['expire'] = time() - 3600; 393 | unset($_COOKIE[$name]); 394 | } 395 | setcookie($name, $value, $cfg['expire'], $cfg['path'], $cfg['domain'], $cfg['secure'], $cfg['httponly']); 396 | $_COOKIE[$name] = $value; 397 | } 398 | 399 | /** 400 | * session的使用 401 | * @param string $name session_Id 402 | * @param string $value session_data 403 | * @return mixed 404 | * 使用方法 405 | * 1. session($key); 获取 session $key 406 | * 2. session($key,$value); 设置session $key值为$value 407 | * 3. session(); 获取所有session 408 | */ 409 | function session($name = '', $value = '') { 410 | static $flag = 0; 411 | if ($flag == 0) { 412 | // 自定义session存储介质 413 | if (config('session_type')) { 414 | if (config('session_expire')) { 415 | ini_set('session.gc_maxlifetime', config('session_expire')); 416 | } 417 | 418 | $class = '\\poem\\session\\' . config('session_type'); 419 | if (!session_set_save_handler(new $class())) { 420 | throw new \Exception('error session handler'); 421 | } 422 | 423 | } 424 | session_start(); 425 | $flag = 1; 426 | } 427 | if ($name === '') { 428 | return $_SESSION; 429 | } 430 | 431 | if (is_null($name)) { 432 | unset($_SESSION); 433 | } 434 | 435 | if ($value === '') { 436 | return $_SESSION[$name]; 437 | } elseif (is_null($value)) { 438 | unset($_SESSION[$name]); 439 | } else { 440 | $_SESSION[$name] = $value; 441 | } 442 | } 443 | 444 | /** 445 | * layout布局设置 446 | * @param false/string $flag false代表关闭布局 string代表开启,并设置布局文件 447 | * @return void 448 | */ 449 | function layout($flag) { 450 | if ($flag !== false) { 451 | config('layout_on', true); 452 | if (is_string($flag)) { 453 | config('layout', $flag); 454 | } 455 | } else { 456 | config('layout_on', false); 457 | } 458 | } 459 | 460 | /** 461 | * 计时函数 462 | * @param string $key 计时的标记 463 | * @param string $end 是否结束 464 | * @param int $settime 为key设置的时间 465 | * @return mixed 466 | */ 467 | function t($key, $end = '', $settime = null) { 468 | static $time = array(); // 计时 469 | if (empty($key)) { 470 | return $time; 471 | } 472 | 473 | if (!is_null($settime)) { 474 | $time[$key] = $settime; 475 | return; 476 | } 477 | 478 | if ($end === -1) { 479 | return $time[$key]; // 返回 key 480 | } elseif ($end === 1) { 481 | return microtime(1) - $time[$key]; // 返回 上次key到现在的时间 482 | } elseif ($end === 0) { 483 | $time[$key] = microtime(1) - $time[$key]; // 记录 上次key现在的时间 484 | } elseif (!empty($end)) { 485 | if (!isset($time[$end])) { 486 | $time[$end] = microtime(1); 487 | } 488 | 489 | return $time[$end] - $time[$key]; // 返回 两个key的差值 490 | } else { 491 | $time[$key] = microtime(1); // 记录 当前key 492 | } 493 | } 494 | 495 | /** 496 | * 跳转 497 | * @param string $uri 498 | * @return void 499 | */ 500 | function jump($uri) { 501 | $url = poem_url($uri); 502 | header("Location: $url"); 503 | \poem\app::end(false); 504 | } 505 | 506 | /** 507 | * 解析uri为url 508 | * @param string $uri 资源定位 509 | * @return string $url 510 | */ 511 | function poem_url($uri) { 512 | if (strpos($uri, '//') !== false) { 513 | return $uri; 514 | } 515 | 516 | if (strpos($uri, '/') === 0) { 517 | return $uri; 518 | } 519 | 520 | $module = strtolower(POEM_MODULE); 521 | $class = strtolower(POEM_CTRL); 522 | $func = POEM_FUNC; 523 | $tmp = explode('/', trim($uri, '/')); 524 | switch (count($tmp)) { 525 | case 1:$func = $tmp[0]; 526 | break; 527 | case 2:$class = $tmp[0]; 528 | $func = $tmp[1]; 529 | break; 530 | case 3:$module = $tmp[0]; 531 | $class = $tmp[1]; 532 | $func = $tmp[2]; 533 | break; 534 | } 535 | return POEM_URL . "/$module/$class/$func"; // html文件路径 536 | } 537 | 538 | /** 539 | * 单例模式 540 | * 541 | * @param string $class_name 542 | * @return class 543 | */ 544 | function poem_ins($class_name){ 545 | static $singleton; 546 | if(!isset($singleton[$class_name])){ 547 | $singleton[$class_name] = new $class_name; 548 | } 549 | return $singleton[$class_name]; 550 | } 551 | -------------------------------------------------------------------------------- /phppoem/core/model.php: -------------------------------------------------------------------------------- 1 | db_cfg = array( 43 | 'db_type' => $config['db_type'], 44 | 'db_host' => $config['db_host'], 45 | 'db_name' => $config['db_name'], 46 | 'db_user' => $config['db_user'], 47 | 'db_pass' => $config['db_pass'], 48 | 'db_port' => $config['db_port'], 49 | 'db_charset' => $config['db_charset'], 50 | 'db_deploy' => $config['db_deploy'], 51 | 'db_rw_separate' => $config['db_rw_separate'], 52 | 'db_master_num' => $config['db_master_num'], 53 | 'db_slave_no' => $config['db_slave_no'], 54 | ); 55 | 56 | if ($tb_name != '') { 57 | $tb_name = $config['db_prefix'] . $tb_name; 58 | $this->_table = $this->parse_tbname($tb_name); 59 | } 60 | } 61 | 62 | /** 63 | * 关闭数据 64 | * @return void 65 | */ 66 | public function close() { 67 | db::get_instance($this->db_cfg)->_linkid = null; 68 | } 69 | 70 | /** 71 | * 获取当前sql 72 | * @return string $sql 73 | */ 74 | public function sql() { 75 | return $this->_sql; 76 | } 77 | 78 | /** 79 | * 开始事务 80 | * @return void 81 | */ 82 | public function begintransaction($name = '') { 83 | db::get_instance($this->db_cfg)->init_connect(true); 84 | 85 | return db::get_instance($this->db_cfg)->begintransaction($name); 86 | } 87 | 88 | /** 89 | * 回滚 90 | * @return void 91 | */ 92 | public function rollback($name = '') { 93 | db::get_instance($this->db_cfg)->rollback($name); 94 | } 95 | 96 | /** 97 | * 提交事务 98 | * @return void 99 | */ 100 | public function commit($name = '') { 101 | db::get_instance($this->db_cfg)->commit($name); 102 | } 103 | 104 | /** 105 | * 使用master 106 | * @return self 107 | */ 108 | public function use_master() { 109 | $this->_ismaster = true; 110 | return $this; 111 | } 112 | 113 | /** 114 | * 不清理查询数据 115 | * @return self 116 | */ 117 | public function no_clear() { 118 | $this->_enable_clear = false; 119 | return $this; 120 | } 121 | 122 | /** 123 | * 开启/关闭抛出异常,当select/find没有选出数据的时候 124 | * @return empty 125 | */ 126 | public function empty_exception($enable=true) { 127 | $this->_empty_exception = $enable; 128 | } 129 | 130 | /** 131 | * 开启sql缓存 132 | * 133 | * @param integer $cache_time 缓存时间默认 3600s 134 | * @return self 135 | */ 136 | public function cache($cache_time=3600){ 137 | $this->_enable_cache_time = $cache_time; 138 | return $this; 139 | } 140 | 141 | /** 142 | * 执行sql查询 143 | * @param string $sql 144 | * @param array $bind 参数绑定 145 | * @return array $ret 二维查询结果 146 | */ 147 | public function query($sql, $bind = array()) { 148 | return $this->query_with_cache($sql, $bind); 149 | } 150 | 151 | /** 152 | * query with cache check 153 | * 154 | * @param string $sql 155 | * @param array $bind 156 | * @return void 157 | */ 158 | private function query_with_cache($sql, $bind) { 159 | // 1. check cache 160 | if($this->_enable_cache_time){ 161 | $data = $this->get_db_cache($sql, $bind); 162 | if(!empty($data)){ 163 | $this->after_sql('Cached'); 164 | return $data; 165 | } 166 | } 167 | 168 | // 2. query db 169 | db::get_instance($this->db_cfg)->init_connect($this->_ismaster); 170 | $this->_sql = $sql; 171 | $info = db::get_instance($this->db_cfg)->select($sql, $bind); 172 | 173 | $this->after_sql(); 174 | if (empty($info) && $this->_empty_exception) { 175 | throw new \exception('empty data: '.$this->_sql); 176 | } 177 | 178 | // 3. cache data 179 | if($this->_enable_cache_time){ 180 | $this->set_db_cache($sql, $bind, $info); 181 | } 182 | 183 | return $info; 184 | } 185 | 186 | /** 187 | * get db cache 188 | * 189 | * @param string $sql 190 | * @param mixed $bind 191 | * @return mixed 192 | */ 193 | private function get_db_cache($sql, $bind){ 194 | $real_uniq = $sql . serialize($bind); 195 | $cache_file = $this->get_db_cache_key($real_uniq); 196 | $cache_str = storage($cache_file); 197 | if(empty($cache_str)){ return false; } 198 | 199 | $split_pos = strrpos($cache_str,PHP_EOL); 200 | $cache_real_uniq = substr($cache_str, 0, $split_pos); 201 | if($real_uniq == $cache_real_uniq){ 202 | $data_str = substr($cache_str, $split_pos+1); 203 | $data = unserialize($data_str); 204 | if(!empty($data)){ 205 | return $data; 206 | } 207 | l('dbcache empty: '.$real_uniq); 208 | }else{ 209 | l('dbcache key conflict: '.$real_uniq.' | '.$cache_real_uniq); 210 | } 211 | return false; 212 | } 213 | 214 | private function get_db_cache_key($real_uniq){ 215 | if(config('storage_type') == \poem\cache\storage::TYPE_REDIS){ 216 | return 'dbcache'.md5($real_uniq); 217 | }else{ 218 | return '/dbcache/'.date('YmdH').'/'.md5($real_uniq); 219 | } 220 | } 221 | 222 | /** 223 | * set db cache 224 | * 225 | * @param strin $sql 226 | * @param mixed $bind 227 | * @param mixed $info 228 | * @return void 229 | */ 230 | private function set_db_cache($sql, $bind, $info){ 231 | $real_uniq = $sql . serialize($bind); 232 | $cache_file = $this->get_db_cache_key($real_uniq); 233 | 234 | $data = $real_uniq.PHP_EOL.serialize($info); 235 | storage($cache_file, $data, $this->_enable_cache_time ); 236 | $this->_enable_cache_time = false; 237 | } 238 | 239 | /** 240 | * 执行sql语句 241 | * @param string $sql 242 | * @return bool 243 | */ 244 | public function exec($sql) { 245 | db::get_instance($this->db_cfg)->init_connect(true); 246 | 247 | $this->_sql = $sql; 248 | $info = db::get_instance($this->db_cfg)->exec($sql); 249 | $this->after_sql(); 250 | return $info; 251 | } 252 | 253 | /** 254 | * 设置自增 255 | * @param string $field 数据表字段 256 | * @param int $num 自增数 257 | * @return int $count 返回影响的函数 258 | */ 259 | public function set_increase($field, $num = 1) { 260 | return $this->update("`{$field}`=`{$field}`+" . intval($num)); 261 | } 262 | 263 | /** 264 | * 设置自减 265 | * @param string $field 数据表字段 266 | * @param int $num 自增数 267 | * @return int $count 返回影响的函数 268 | */ 269 | public function set_decrease($field, $num = 1) { 270 | return $this->update("`{$field}`=`{$field}`-" . intval($num)); 271 | } 272 | 273 | /** 274 | * sql distinct 275 | * @param boolean $flag 是否开启distinct 276 | * @return self 277 | */ 278 | public function distinct($flag = true) { 279 | $this->_distinct = $flag ? 'DISTINCT ' : ''; 280 | return $this; 281 | } 282 | 283 | /** 284 | * sql select field 285 | * @param string $str 表字段 多个使用逗号隔开 'id,name,old' 286 | * @return self 287 | */ 288 | public function field($str) { 289 | $this->_field = $str; 290 | return $this; 291 | } 292 | 293 | /** 294 | * get select field 295 | * @param string $str 返回 field() 设置的值 296 | * @return string select field 297 | */ 298 | public function get_field() { 299 | return $this->_field; 300 | } 301 | 302 | /** 303 | * sql join 304 | * @param string $str 表名 305 | * @param string $type join类型 306 | * @return self 307 | */ 308 | public function join($str, $type = 'INNER') { 309 | $this->_join[] = stristr($str, 'JOIN') ? $str : $type . ' JOIN ' . $str; 310 | return $this; 311 | } 312 | 313 | /** 314 | * sql where 315 | * @param array/string $arr where条件 316 | * @return self 317 | */ 318 | public function where($arr) { 319 | if (is_string($arr)) { 320 | $this->_where['_string'] = $arr; 321 | } else { 322 | $this->_where = array_merge($this->_where, $arr); 323 | } 324 | 325 | return $this; 326 | } 327 | 328 | /** 329 | * sql set where 330 | * @param array/string $arr where条件 331 | * @return self 332 | */ 333 | public function set_where($where) { 334 | $this->_where = $where; 335 | return $this; 336 | } 337 | 338 | /** 339 | * sql get where 340 | * @return array/string $arr where条件 341 | */ 342 | public function get_where() { 343 | return $this->_where; 344 | } 345 | 346 | /** 347 | * sql having 348 | * @param string $str 字符串 349 | * @return self 350 | */ 351 | public function having($str) { 352 | $this->_having = $str; 353 | return $this; 354 | } 355 | 356 | /** 357 | * sql limit 358 | * @param int $begin 开始 359 | * @param int $end 结束 360 | * @return self 361 | */ 362 | public function limit($begin = 0, $end = 0) { 363 | if ($end == 0) { 364 | $end = $begin; 365 | $begin = 0; 366 | } 367 | $this->_limit = $begin; 368 | if ($end) { 369 | $this->_limit .= ",$end"; 370 | } 371 | 372 | return $this; 373 | } 374 | 375 | /** 376 | * sql order 377 | * @param string $str 表字段 378 | * @return self 379 | */ 380 | public function order($str) { 381 | $this->_order = $str; 382 | return $this; 383 | } 384 | 385 | /** 386 | * sql group 387 | * @param string $str 表字段 388 | * @return self 389 | */ 390 | public function group($str) { 391 | $this->_group = $str; 392 | return $this; 393 | } 394 | 395 | /** 396 | * sql insert 397 | * @param array $data 插入的表字段键值一维数组 398 | * @return bool $ret 成功/失败 399 | */ 400 | public function insert($data = null) { 401 | if ($data == null) {return;} 402 | 403 | db::get_instance($this->db_cfg)->init_connect(true); 404 | // INSERT INTO more (id, NaMe) values (?, ?) 405 | $keys = ''; 406 | $vals = ''; 407 | foreach ($data as $k => $v) { 408 | if (is_null($v)) { 409 | continue; 410 | } 411 | 412 | $keys .= "`$k`,"; 413 | $vals .= ":$k,"; 414 | $this->_bind[":$k"] = $v; 415 | } 416 | $keys = substr($keys, 0, -1); 417 | $vals = substr($vals, 0, -1); 418 | $this->_sql = 'INSERT INTO ' . $this->_table . " ($keys) VALUES ($vals)"; 419 | $ret = db::get_instance($this->db_cfg)->insert($this->_sql, $this->_bind); 420 | $this->after_sql(); 421 | return $ret; 422 | } 423 | 424 | /** 425 | * 多组插入 426 | * @param array $data 插入的表字段键值二维数组 427 | * @param int $num 多少为一组插入,将$data分块 428 | * @return int $id 最后一次插入id 429 | */ 430 | public function insert_multi($data = null, $num = 1000) { 431 | $first_row = current($data); 432 | if (!is_array($first_row)) {throw new \exception('insert_multi data must be array');} 433 | db::get_instance($this->db_cfg)->init_connect(true); 434 | 435 | $keys = implode(',', array_keys($first_row)); 436 | $sql = "insert into " . $this->_table . " ($keys) values"; 437 | $vals = array(); 438 | foreach ($data as $v) { 439 | $vals[] = '(' . implode(',', $this->parse_value($v)) . ')'; 440 | if (count($vals) >= $num) { 441 | $this->_sql = 'INSERT INTO ' . $this->_table . " ($keys) VALUES " . implode(',', $vals); 442 | $info = db::get_instance($this->db_cfg)->insert($this->_sql, $this->_bind); 443 | $vals = array(); 444 | } 445 | } 446 | if (count($vals)) { 447 | $this->_sql = 'INSERT INTO ' . $this->_table . " ($keys) VALUES " . implode(',', $vals); 448 | $info = db::get_instance($this->db_cfg)->insert($this->_sql, $this->_bind); 449 | } 450 | $this->after_sql(); 451 | return $info; 452 | } 453 | 454 | /** 455 | * 更新 456 | * @param array $data 更新的表字段键值一维数组 457 | * @param array $pk 主键字段 458 | * @return int $count 影响的行 459 | */ 460 | public function update($data = null, $pk='id') { 461 | if ($data == null) {return;} 462 | db::get_instance($this->db_cfg)->init_connect(true); 463 | 464 | if (isset($data[$pk])) { 465 | $this->where(array($pk => $data[$pk])); 466 | unset($data[$pk]); 467 | } 468 | if (empty($this->_where)) { 469 | return false; 470 | } 471 | 472 | $keys = ''; 473 | if (is_array($data)) { 474 | foreach ($data as $k => $v) { 475 | $kt = $this->parse_key($k); 476 | $keys .= "$kt=:$k,"; 477 | $bind[":$k"] = $v; 478 | } 479 | $keys = substr($keys, 0, -1); 480 | $this->_bind = array_merge($this->_bind, $bind); 481 | } elseif (is_string($data)) { 482 | $keys = $data; 483 | } else { 484 | throw new \exception('update params must be array or string'); 485 | } 486 | 487 | $this->_sql = 'UPDATE ' . $this->_table . " SET {$keys}"; 488 | $this->parse_where($this->_where); 489 | $info = db::get_instance($this->db_cfg)->update($this->_sql, $this->_bind); 490 | $this->after_sql(); 491 | return $info; 492 | } 493 | 494 | /** 495 | * 删除 496 | * @return int $count 影响的行 497 | */ 498 | public function delete() { 499 | db::get_instance($this->db_cfg)->init_connect(true); 500 | 501 | $this->_sql = 'DELETE FROM ' . $this->_table; 502 | 503 | // 防止误删 504 | if (empty($this->_where)) { 505 | throw new \Exception('delete sql need where:' . $this->_sql); 506 | } 507 | $this->parse_where($this->_where); 508 | $ret = db::get_instance($this->db_cfg)->delete($this->_sql, $this->_bind); 509 | $this->after_sql(); 510 | return $ret; 511 | } 512 | 513 | /** 514 | * 查询 515 | * @return array $data 二维数组对应表行 516 | */ 517 | public function select() { 518 | // $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%LOCK%%COMMENT%'; 519 | $this->_sql = 'SELECT ' . $this->_distinct . $this->_field . ' FROM ' . $this->_table; 520 | $this->set_join($this->_join); 521 | $this->parse_where($this->_where); 522 | $this->set_group($this->_group); 523 | $this->set_having($this->_having); 524 | $this->set_order($this->_order); 525 | $this->set_limit($this->_limit); 526 | $this->set_union($this->_union); 527 | $this->set_lock($this->_lock); 528 | $this->set_comment($this->_comment); 529 | $this->set_force($this->_force); 530 | 531 | return $this->query_with_cache($this->_sql, $this->_bind); 532 | } 533 | 534 | /** 535 | * 统计行数 536 | * @return int $count 537 | */ 538 | public function count() { 539 | db::get_instance($this->db_cfg)->init_connect(true); 540 | 541 | $this->_sql = 'SELECT count(*) as num FROM ' . $this->_table; 542 | $this->set_join($this->_join); 543 | $this->parse_where($this->_where); 544 | $this->set_group($this->_group); 545 | $this->set_order($this->_order); 546 | $this->set_limit($this->_limit); 547 | $info = db::get_instance($this->db_cfg)->select($this->_sql, $this->_bind); 548 | $this->after_sql(); 549 | return $info[0]['num']; 550 | } 551 | 552 | /** 553 | * 查询一行 554 | * @return array $data 一维数组表字段键值对 555 | */ 556 | public function find() { 557 | $info = $this->limit(1)->select(); 558 | return $info[0]; 559 | } 560 | 561 | /** 562 | * 根据ID查询 563 | * @param int $id 564 | * @return array $data 一维数组表字段键值对 565 | */ 566 | public function id($id) { 567 | return $this->where(array('id' => $id))->find(); 568 | } 569 | 570 | /** 571 | * 执行sql后,记录sql 并清理所有条件 572 | * @return void 573 | */ 574 | protected function after_sql($prefix='') { 575 | foreach ($this->_bind as $key => $value) { 576 | $this->_sql = str_replace($key, db::get_instance($this->db_cfg)->_conn->quote($value), $this->_sql); 577 | } 578 | $time = number_format(T('poem_db_exec', -1) * 1000, 2); 579 | Log::trace('SQL', $this->_sql . "[{$time}ms]"); 580 | l($prefix.'SQL: '. $this->_sql . "[{$time}ms]", \poem\log::INFO, \poem\log::DEPTH_FILTER_POEM); 581 | $this->_bind = array(); 582 | if (!$this->_enable_clear) { 583 | $this->_enable_clear = true; 584 | return; 585 | } 586 | $this->clear(); 587 | } 588 | 589 | /** 590 | * 清理所有条件 591 | * @return void 592 | */ 593 | public function clear(){ 594 | $this->_distinct = ''; 595 | $this->_field = '*'; 596 | $this->_join = array(); 597 | $this->_where = array(); 598 | $this->_group = ''; 599 | $this->_having = ''; 600 | $this->_order = ''; 601 | $this->_limit = ''; 602 | $this->_union = ''; 603 | $this->_lock = ''; 604 | $this->_comment = ''; 605 | $this->_force = ''; 606 | $this->_ismaster = false; 607 | } 608 | 609 | /** 610 | * sql设置where 611 | * @param string $_where 612 | * @param bool $return_flag 是否换位 613 | * @return mixed 614 | */ 615 | protected function parse_where($_where = null, $return_flag = false) { 616 | if ($_where == null) { 617 | return ''; 618 | } 619 | 620 | $logic = 'AND'; 621 | if (isset($_where['_logic'])) { 622 | $logic = strtoupper($_where['_logic']); 623 | unset($_where['_logic']); 624 | } 625 | 626 | $item = array(); 627 | foreach ($_where as $k => $v) { 628 | if ($k == '_complex') { 629 | $item[] = substr($this->parse_where($v, true), 7); 630 | } elseif (is_array($v)) { 631 | $k = $this->parse_key($k); 632 | $exp = strtoupper($v[0]); // in like 633 | if (preg_match('/^(NOT IN|IN)$/', $exp)) { 634 | if (is_string($v[1])) { 635 | $v[1] = explode(',', $v[1]); 636 | } 637 | 638 | $vals = implode(',', $this->parse_value($v[1])); 639 | $item[] = "$k $exp ($vals)"; 640 | } elseif (preg_match('/^(=|!=|<|<>|<=|>|>=)$/', $exp)) { 641 | $k1 = count($this->_bind); 642 | $item[] = "$k $exp :$k1"; 643 | $this->_bind[":$k1"] = $v[1]; 644 | } elseif (preg_match('/^(BETWEEN|NOT BETWEEN)$/', $exp)) { 645 | $tmp = is_string($v[1]) ? explode(',', $v[1]) : $v[1]; 646 | $k1 = count($this->_bind); 647 | $k2 = $k1 + 1; 648 | $item[] = "$k $exp :$k1 AND :$k2"; 649 | $this->_bind[":$k1"] = $tmp[0]; 650 | $this->_bind[":$k2"] = $tmp[1]; 651 | } elseif (preg_match('/^(LIKE|NOT LIKE)$/', $exp)) { 652 | if (is_array($v[1])) { 653 | $likeLogic = isset($v[2]) ? strtoupper($v[2]) : 'OR'; 654 | $like = []; 655 | foreach ($v[1] as $like_item) { 656 | $like[] = "$k $exp " . $this->parse_value($like_item); 657 | } 658 | 659 | $str = implode($likeLogic, $like); 660 | $item[] = "($str)"; 661 | } else { 662 | $wyk = ':' . count($this->_bind); 663 | $item[] = "$k $exp $wyk"; 664 | $this->_bind[$wyk] = $v[1]; 665 | } 666 | } else { 667 | throw new \Exception("exp error", 1); 668 | } 669 | } elseif ($k == '_string') { 670 | $item[] = $v; 671 | } else { 672 | $k = $this->parse_key($k); 673 | $wyk = ':' . count($this->_bind); 674 | $item[] = "$k=$wyk"; 675 | $this->_bind[$wyk] = $v; 676 | } 677 | } 678 | 679 | $str = ' WHERE (' . implode(" $logic ", $item) . ')'; 680 | if ($return_flag == true) { 681 | return $str; 682 | } 683 | 684 | $this->_sql .= $str; 685 | } 686 | 687 | /** 688 | * sql设置join 689 | * @param array $_join 690 | * @return void 691 | */ 692 | public function set_join($_join) { 693 | if (empty($_join)) { 694 | return false; 695 | } 696 | 697 | $this->_sql .= ' ' . implode(' ', $_join); 698 | } 699 | 700 | /** 701 | * sql设置group 702 | * @param string $_group 703 | * @return void 704 | */ 705 | public function set_group($_group) { 706 | if (empty($_group)) { 707 | return false; 708 | } 709 | 710 | $this->_sql .= ' GROUP BY ' . $_group; 711 | } 712 | 713 | /** 714 | * sql设置having 715 | * @param string $_having 716 | * @return void 717 | */ 718 | public function set_having($_having) { 719 | if (empty($_having)) { 720 | return false; 721 | } 722 | 723 | $this->_sql .= ' HAVING ' . $_having; 724 | } 725 | 726 | /** 727 | * sql设置order 728 | * @param string $_order 729 | * @return void 730 | */ 731 | public function set_order($_order) { 732 | if (empty($_order)) { 733 | return false; 734 | } 735 | 736 | $this->_sql .= ' ORDER BY ' . $_order; 737 | } 738 | 739 | /** 740 | * sql设置limit 741 | * @param string $_limit 742 | * @return void 743 | */ 744 | public function set_limit($_limit) { 745 | if (empty($_limit)) { 746 | return false; 747 | } 748 | 749 | $this->_sql .= ' LIMIT ' . $_limit; 750 | } 751 | 752 | /** 753 | * todo sql 设置union 754 | * @param string $_union; 755 | * @return void 756 | */ 757 | public function set_union($_union) { 758 | } 759 | 760 | /** 761 | * todo sql 设置lock 762 | * @param string $_lock; 763 | * @return void 764 | */ 765 | public function set_lock($_lock) { 766 | } 767 | 768 | /** 769 | * todo sql 设置comment 770 | * @param string $_comment; 771 | * @return void 772 | */ 773 | public function set_comment($_comment) { 774 | } 775 | 776 | /** 777 | * todo sql 设置force 778 | * @param string $_force; 779 | * @return void 780 | */ 781 | public function set_force($_force) { 782 | } 783 | 784 | /** 785 | * sql格式化字段值 786 | * @param mixed $val 字段 787 | * @return mixed $val 字段 788 | */ 789 | protected function parse_value($val) { 790 | $type = gettype($val); 791 | switch ($type) { 792 | case 'string':return db::get_instance($this->db_cfg)->_conn->quote($val); 793 | case 'array':return array_map([$this, 'parse_value'], $val); 794 | case 'boolean':return $val ? 1 : 0; 795 | case 'NULL':return 'null'; 796 | case 'integer':return $val; 797 | default:throw new \exception('sql parse_value not allow:' . $type); 798 | } 799 | } 800 | 801 | /** 802 | * sql格式化字段 803 | * @param string $key 字段 804 | * @return string $key 字段 805 | */ 806 | protected function parse_key($key) { 807 | if ($key[0] == '`') {return;} 808 | if ($pos = strpos($key, '.')) { 809 | $key = '`' . substr_replace($key, '`.`', $pos, 1) . '`'; 810 | } else { 811 | $key = "`$key`"; 812 | } 813 | return $key; 814 | } 815 | 816 | /** 817 | * sql格式化表名 818 | * @param string $tb 表名 819 | * @return string $tb sql格式表名 820 | */ 821 | private function parse_tbname($tb) { 822 | if ($tb[0] == '`') {return;} 823 | if ($pos = strpos($tb, ' ')) { 824 | $tb = '`' . substr_replace($tb, '` ', $pos, 1); 825 | } elseif ($pos = strpos($tb, '.')) { 826 | $tb = '`' . substr_replace($tb, '`.', $pos, 1); 827 | } else { 828 | $tb = "`$tb`"; 829 | } 830 | return $tb; 831 | } 832 | } 833 | --------------------------------------------------------------------------------