├── MIT-License ├── README.md ├── example ├── config.inc.php ├── require_file.php ├── template │ └── test.html └── test.php └── leiphp.php /MIT-License: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Lei Zongmin(雷宗民) 2 | http://ucdok.com 3 | 4 | The MIT License 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LeiPHP 微型的PHP框架 2 | ============== 3 | 4 | 此框架仅有一个文件,其中包含了MySQL数据库、上传文件、调试信息、导入依赖文件、 5 | 模板和REST路由等一系列常用操作。适合用来快速写一些对性能要求不高的程序。 6 | 7 | 8 | 初始化 9 | ============= 10 | 11 | 首先新建一个`config.inc.php`文件,所有程序通过加载该文件来进行配置及初始化: 12 | 13 | ```php 14 | 41 | ``` 42 | 43 | 在所有php程序中,均可载入`config.inc.php`文件唉实现初始化leiphp: 44 | 45 | ```php 46 | 50 | ``` 51 | 52 | 53 | REST路由 54 | =========== 55 | 56 | leiphp可以根据不同的请求方法来调用相应的处理函数完成请求,比如: 57 | 58 | ```php 59 | 86 | ``` 87 | 88 | 89 | 操作MySQL数据库 90 | =============== 91 | 92 | leiphp中提供了一个静态类 __SQL__ 来操作MySQL数据库: 93 | 94 | * `SQL::connect($server = 'localhost:3306', $username = 'root', $password = '',$database = '');` 95 | 连接到数据库,当配置了数据库连接时,leiapp会自动执行此方法来连接到数据库,若你的 96 | 程序中已经通过`mysql_connect`来创建了一个数据库连接,可以不用再执行此方法连接数 97 | 据库; 98 | 99 | * `SQL::getAll($sql)` 或 `SQL::getData($sql)` 查询SQL,并返回数组格式的结果, 100 | 失败返回`FALSE`; 101 | 102 | * `SQL::getOne($sql)` 或 `SQL::getLine($sql)` 查询SQL,仅返回第一条结果, 103 | 失败返回`FALSE`; 104 | 105 | * `SQL::update($sql)` 或 `SQL::runSql($sql)` 查询SQL,返回受影响的记录数,一般 106 | 用于执行插入或更新操作; 107 | 108 | * `SQL::id()` 或 `SQL::lastId()` 返回最后插入的一条记录的ID; 109 | 110 | * `SQL::errno()` 返回最后执行的一条SQL语句的出错号 111 | 112 | * `SQL::errmsg()` 返回最后执行的一条SQL语句的出错信息 113 | 114 | * `SQL::escape($str)` 返回安全的SQL字符串 115 | 116 | 更简便的数据库操作: 117 | 118 | * `SQL::getAll($table, $where)` 查询所有记录,其中$table是表名,$where是一个条件 119 | 数组,如:array('id' => 1) 120 | 121 | * `SQL::getOne($table, $where)` 查询一条记录 122 | 123 | * `SQL::update($table, $where, $update)` 更新记录并返回受影响的记录数,其中 124 | $update是要更新的数据数组,如:array('name' => 'haha') 125 | 126 | * `SQL::insert($table, $data)` 插入一条记录并返回其ID,其中$data是一个数组, 127 | 如:array('name' => 'haha', 'age' => 20) 128 | 129 | * `SQL::delete($table, $where)` 删除记录 130 | 131 | 条件格式: 132 | 133 | * 普通:`array('a' => 1, 'b' => 2)` 相当于 `a=1 AND b=2` 134 | 135 | * 指定连接操作符:`array('link' => 'OR', 'a' => 1, 'b' => 2)` 相当于 `a=1 OR b=2` 136 | 137 | * 指定比较操作符:`array('a' => array('>' => 2))` 相当于 `a>2` 138 | 139 | * 同一个字段多个条件:`array('a' => array('>' => 2, '<' => 5))` 相当于 140 | `(a>2 AND a < 5)` 141 | 142 | * 指定多个条件的连接操作符:`array('a' => array('link' => 'OR', '>' => 2, '<' => 5))` 143 | 相当于 `(a>2 OR a < 5)` 144 | 145 | 146 | 上传文件操作 147 | =============== 148 | 149 | leiphp中提供了一个静态类 __UPLOAD__ 来操作上传文件: 150 | 151 | * `UPLOAD::get($filename)` 返回指定名称的上传文件信息,该名称为`
`表单中的 152 | ``中的**name**值,该返回值为一个数组,包含以下项: __name__ 153 | (名称), __type__ (MIME类型), __size__ (大小), 154 | __tmp_name__ (临时文件名); 155 | 156 | * `UPLOAD::move($file, $target)` 移动上传的文件到指定位置,第一个参数为 157 | `UPLOAD::get($filename)`的返回值,第二个参数为目标文件名; 158 | 159 | 160 | 调试信息操作 161 | ============= 162 | 163 | leiphp中提供了一个静态类 __DEBUG__ 来操作调试信息,当定义了常量`APP_DEBUG`时, 164 | 会在页面底部输出调试信息: 165 | 166 | * `DEBUG::put($msg = '', $title = '')` 输出调试信息 167 | 168 | * `DEBUG::get()` 取调试信息 169 | 170 | * `DEBUG::clear()` 清除所有调试信息 171 | 172 | 173 | 应用相关操作 174 | ============= 175 | 176 | leiphp中提供了一个静态类 __APP__ 来进行应用相关的操作,及一些公共函数: 177 | 178 | * `APP::encryptPassword ($password)` 加密密码,返回一个加盐处理后的MD5字符串, 179 | 如:`FF:15855D447208A6AB4BD2CC88D4B91732:83`; 180 | 181 | * `APP::validatePassword ($password, $encrypted)` 验证密码,第一个参数为待验证的 182 | 密码,第二个参数为`APP::encryptPassword ($password)`返回的字符串, 183 | 返回`TRUE`或`FALSE`; 184 | 185 | * `APP::dump($var)` 打印变量结构,一般用于调试; 186 | 187 | * `APP::showError($msg)` 显示出错信息 188 | 189 | * `APP::load($filename)` 载入依赖的php文件,若不指定后缀名,会自动加上`.php`, 190 | 默认以当前php文件为根目录,若文件名以`/`开头,则以常量`APP_ROOT`定义的应用目录 191 | 作为根目录; 192 | 193 | * `APP::sendJSON($data)` 返回JSON格式数据 194 | 195 | * `APP::sendError($msg, $data = array())` 返回JSON格式的出错信息:`{"error":"msg"}` 196 | 197 | * `APP::authEncode($string, $key, $expirey)` 加密账户验证信息,可指定过期时间 198 | 199 | * `APP::authDecode($string, $key)` 加密账户验证信息 200 | 201 | * `APP::getTemplate($name, $locals)` 载入模板文件,若不指定后缀名,会 202 | 自动加上`.html`,以常量`APP_TEMPLATE_ROOT`定义的模板目录作为根目录,模板文件实际 203 | 上为php程序文件,第二个参数为模板中可用的变量,在模板中通过`$locals`来读取(若 204 | 无命名冲突也可以直接使用键名),返回渲染后的内容 205 | 206 | * `APP::setLocals($name, $value)` 设置模板变量 207 | 208 | * `APP::getLocals($name)` 取模板变量值 209 | 210 | * `APP::render($name, $locals, $layout = '')` 自动为`$locals`加上用 211 | `APP::setLocals()`设置的变量,并渲染模板。如果指定了视图模板`$layout`,则需要在 212 | 视图模板中通过`$body`变量来获取模板内容。 213 | 214 | * `APP::init()` 初始化leiphp; 215 | 216 | * `APP::end()` 提前退出 217 | 218 | 自动路由 219 | =========== 220 | 221 | leiphp中提供了一个静态类 __ROUTER__ 来进行路由相关的操作: 222 | 223 | * `ROUTER::register($path, $function, $is_preg = false)` 注册中间件,其中`$path` 224 | 为路径前缀,`$function`为要执行的函数,如果`$is_preg`为`true`表示`$path`是一个 225 | 正则表达式 226 | 227 | * `ROUTER::run($dir, $path)` 执行自动路由。其中`$dir`是要自动加载的PHP文件所在 228 | 的目录,以应用目录`APP_ROOT`中定义的目录为根目录,默认为`action`目录,`$path`是 229 | 当前请求的路径,默认为`$_GET['__path__']` 230 | 231 | 示例: 232 | 233 | 应用统一入口文件:index.php 234 | 235 | ```php 236 | 240 | ``` 241 | 242 | 需要配置服务器的URL Rewrite,比如将 `/app/(.*)` 的所有请求转到 243 | `/app/index.php?__path__=$1` 244 | 245 | Apache的配置示例: 246 | 247 | ``` 248 | RewriteCond %{REQUEST_FILENAME} !-f 249 | RewriteCond %{REQUEST_FILENAME} !-d 250 | RewriteRule ^app/(.*)$ /app/index.php?%{QUERY_STRING}&__path__=$1 [L] 251 | ``` 252 | 253 | Nginx的配置示例: 254 | 255 | ```lua 256 | if (!-e $request_filename) { 257 | rewrite "^/app/(.*)" "/app/index.php?%{QUERY_STRING}&__path__=$1" last; 258 | } 259 | ``` 260 | 261 | SAE的配置示例: 262 | 263 | ```yaml 264 | handle: 265 | - rewrite: if(!is_dir() && !is_file() && path~"^app/(.*)") goto "app/index.php?%{QUERY_STRING}&__path__=$1" 266 | ``` 267 | 268 | 当请求 `/app/my/action` 时,会自动执行文件 `/action/my/action.php` 269 | 270 | 如请求 `/app/my/action/` ,则自动执行文件 `/action/my/action/index.php` 271 | 272 | 273 | 授权 274 | ============ 275 | 276 | 基于MIT协议发布。 277 | 278 | ``` 279 | Copyright (c) 2012 Lei Zongmin(雷宗民) 280 | http://ucdok.com 281 | 282 | The MIT License 283 | 284 | Permission is hereby granted, free of charge, to any person obtaining 285 | a copy of this software and associated documentation files (the 286 | "Software"), to deal in the Software without restriction, including 287 | without limitation the rights to use, copy, modify, merge, publish, 288 | distribute, sublicense, and/or sell copies of the Software, and to 289 | permit persons to whom the Software is furnished to do so, subject to 290 | the following conditions: 291 | 292 | The above copyright notice and this permission notice shall be 293 | included in all copies or substantial portions of the Software. 294 | 295 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 296 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 297 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 298 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 299 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 300 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 301 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 302 | ``` 303 | -------------------------------------------------------------------------------- /example/config.inc.php: -------------------------------------------------------------------------------- 1 | Hello, world 2 | -------------------------------------------------------------------------------- /example/test.php: -------------------------------------------------------------------------------- 1 | '; 12 | 13 | APP::load('require_file'); 14 | 15 | 16 | // 处理GET请求 17 | function method_get () { 18 | echo 'GET请求'; 19 | APP::dump($_GET); 20 | APP::dump(SQL::getAll('show tables')); 21 | APP::dump(SQL::getAll('show databases')); 22 | APP::dump(SQL::getAll('select 1+1 as a')); 23 | // 渲染模板 24 | APP::template('test.html'); 25 | } 26 | 27 | // 处理POST请求 28 | function method_post () { 29 | echo 'POST请求'; 30 | APP::dump($_POST); 31 | } 32 | 33 | // 以下函数用于处理任意请求(当没有定义method_get或method_post时) 34 | function method_all () { 35 | echo '处理所有请求'; 36 | } 37 | -------------------------------------------------------------------------------- /leiphp.php: -------------------------------------------------------------------------------- 1 | 6 | * @version 0.3 7 | */ 8 | 9 | /* 处理不同的请求方法 */ 10 | function _leiphp_request_method_router () { 11 | // 如果已调用APP::end(),则不再执行此函数,因为在die后仍然会执行register_shutdown_function注册的函数 12 | if (APP::$is_exit) return; 13 | 14 | // 执行相应的请求方法 15 | $method = strtolower($_SERVER['REQUEST_METHOD']); 16 | $funcname = "method_$method"; 17 | define('APP_TIMESTAMP_ROUTE', microtime(true)); 18 | if (function_exists($funcname)) { 19 | $funcname(); 20 | } elseif (function_exists('method_all')) { 21 | $funcname = 'method_all'; 22 | method_all(); 23 | } else { 24 | $funcname = 'method_undefine'; 25 | } 26 | 27 | // 关闭数据库连接 28 | @SQL::close(); 29 | 30 | // 显示调试信息 31 | $accept_type = strtolower(trim($_SERVER['HTTP_ACCEPT'])); 32 | if (APP::$is_debug && substr($accept_type, 0, 9) == 'text/html') { 33 | $spent2 = round((microtime(true) - APP_TIMESTAMP_ROUTE) * 1000, 3); 34 | $spent = round((microtime(true) - APP_TIMESTAMP_START) * 1000, 3); 35 | $debug = DEBUG::clear(); 36 | echo "
Debug
Function $funcname spent: {$spent2}ms
Total spent: {$spent}ms
47 |
$debug
50 |
"; 51 | } 52 | } 53 | 54 | /** 55 | * 调试信息流操作 56 | */ 57 | if (!class_exists('DEBUG', false)) { 58 | class DEBUG { 59 | public static $stack = ''; 60 | 61 | /** 62 | * 添加到DEBUG流 63 | * 64 | * @param string $msg 65 | * @param string $title 66 | */ 67 | public static function put ($msg = '', $title = '') { 68 | if (APP::$is_debug) { 69 | if (!empty($title)) { 70 | $msg = "[$title] $msg"; 71 | } 72 | $timestamp = round((microtime(true) - APP_TIMESTAMP_START) * 1000, 3).'ms'; 73 | DEBUG::$stack .= "[$timestamp] $msg\r\n"; 74 | } 75 | } 76 | 77 | /** 78 | * 获取DEBUG流 79 | * 80 | * @return string 81 | */ 82 | public static function get () { 83 | return DEBUG::$stack; 84 | } 85 | 86 | /** 87 | * 清空DEBUG流,并返回之前的信息 88 | * 89 | * @return string 90 | */ 91 | public static function clear () { 92 | $ret = DEBUG::$stack; 93 | DEBUG::$stack = ''; 94 | return $ret; 95 | } 96 | } 97 | } 98 | 99 | /** 100 | * MySQL 数据库操作 101 | */ 102 | if (!class_exists('SQL', false)) { 103 | class SQL { 104 | 105 | // 当前数据库连接 106 | public static $connection = null; 107 | 108 | /** 109 | * 连接到数据库 110 | * 成功返回true, 失败返回false 111 | * 112 | * @param string $server 113 | * @param string $username 114 | * @param string $password 115 | * @param string $database 116 | * @return bool 117 | */ 118 | public static function connect ($server = 'localhost:3306', $username = 'root', $password = '', $database = '') { 119 | $timestamp = microtime(true); 120 | 121 | SQL::$connection = mysqli_connect($server, $username, $password, $database); 122 | 123 | DEBUG::put('Connected: '.$username.'@'.$server.' spent: '.round((microtime(true) - $timestamp) * 1000, 3).'ms', 'MySQL'); 124 | 125 | $err = SQL::error(); 126 | if ($err['id'] > 0) { 127 | DEBUG::put(' - Error: #'.$err['id'].' '.$err['error'], 'MySQL'); 128 | } 129 | 130 | return SQL::$connection; 131 | } 132 | 133 | /** 134 | * 获取出错信息 135 | * 返回数据格式: {id:出错ID, error:出错描述} 136 | * 137 | * @return array 138 | */ 139 | public static function error () { 140 | return array( 141 | 'id' => SQL::errno(), 142 | 'error' => SQL::errmsg() 143 | ); 144 | } 145 | 146 | /** 147 | * 返回出错代码 148 | * 149 | * @return int 150 | */ 151 | public static function errno () { 152 | return mysqli_errno(SQL::$connection); 153 | } 154 | 155 | /** 156 | * 返回出错描述信息 157 | * 158 | * @return string 159 | */ 160 | public static function errmsg () { 161 | return mysqli_error(SQL::$connection); 162 | } 163 | 164 | /** 165 | * 设置字符编码 166 | * 167 | * @param {String} $encoding 168 | * @return {String} 169 | */ 170 | public static function charset ($encoding = '') { 171 | if (!empty($encoding)) { 172 | mysqli_set_charset(SQL::$connection, $encoding); 173 | DEBUG::put('charset='.$encoding, 'MySQL'); 174 | } 175 | return mysqli_get_charset(SQL::$connection); 176 | } 177 | 178 | /** 179 | * 执行SQL语句 180 | * 181 | * @param string $sql 182 | * @return resource 183 | */ 184 | public static function query ($sql) { 185 | $timestamp = microtime(true); 186 | $r = mysqli_query(SQL::$connection, $sql); 187 | $spent = round((microtime(true) - $timestamp) * 1000, 3); 188 | if ($r) { 189 | DEBUG::put('Query: '.$sql.' spent: '.$spent.'ms', 'MySQL'); 190 | } else { 191 | DEBUG::put('Query: '.$sql.' fail: #'.SQL::errno().' '.SQL::errmsg().' spent: '.$spent, 'MySQL'); 192 | } 193 | return $r; 194 | } 195 | 196 | /** 197 | * 查询并返回所有数据 198 | * 格式为: [{字段名:值, 字段名:值 ...}, ...],返回false表示失败 199 | * 200 | * @param string $sql 201 | * @return array 202 | */ 203 | public static function getAll ($sql, $where = null) { 204 | if (is_array($where)) return SQL::getAll2($sql, $where); 205 | 206 | $r = SQL::query($sql); 207 | if (!$r) return false; 208 | $data = array(); 209 | while ($row = mysqli_fetch_array($r, MYSQL_ASSOC)) { 210 | $data[] = $row; 211 | } 212 | return count($data) < 1 ? false : $data; 213 | } 214 | public static function getData ($sql) { 215 | return SQL::getAll($sql); 216 | } 217 | 218 | /** 219 | * 查询并返回一行数据 格式为 {字段名:值, 字段名:值 ...},返回false表示失败 220 | * 221 | * @param string $sql 222 | * @return array 223 | */ 224 | public static function getOne ($sql, $where = null) { 225 | if (is_array($where)) return SQL::getOne2($sql, $where); 226 | 227 | $sql .= ' LIMIT 1'; 228 | $data = SQL::getAll($sql); 229 | return $data == false ? false : $data[0]; 230 | } 231 | public static function getLine ($sql) { 232 | return SQL::getOne($sql); 233 | } 234 | 235 | /** 236 | * 执行SQL命令 返回影响的记录行数 237 | * 238 | * @param string $sql 239 | * @return int 240 | */ 241 | public static function update ($sql, $where = null, $update = null) { 242 | if (is_array($where) && is_array($update)) return SQL::update2($sql, $where, $update); 243 | 244 | $r = SQL::query($sql); 245 | if (!$r) return false; 246 | return mysqli_affected_rows(SQL::$connection); 247 | } 248 | public static function runSql ($sql) { 249 | return SQL::update($sql); 250 | } 251 | 252 | /** 253 | * 插入记录 254 | * 255 | * @param string $table 256 | * @param array $data 257 | * @return int 258 | */ 259 | public static function insert ($table, $data) { 260 | if (!(is_array($data) && count($data) > 0)) return false; 261 | 262 | $table = SQL::escape($table); 263 | $fields = array(); 264 | $values = array(); 265 | foreach ($data as $f => $v) { 266 | $fields[] = '`'.SQL::escape($f).'`'; 267 | $values[] = '\''.SQL::escape($v).'\''; 268 | } 269 | $fields = implode(', ', $fields); 270 | $values = implode(', ', $values); 271 | $sql = "INSERT INTO `$table` ($fields) VALUES ($values)"; 272 | return SQL::update($sql) > 0 ? SQL::id() : false; 273 | } 274 | 275 | /** 276 | * 解析where条件 277 | * 278 | * @param array $where 例如: array( 279 | * 'field' => 'values', 280 | * 'link' => 'OR' // 可省略,默认为AND 281 | * ) 282 | * array('field' => array( 283 | * 'link' => 'OR', // 可省略,默认为AND 284 | * '>' => 1200, 285 | * '<=' => 555 286 | * )) 287 | * @return string 288 | */ 289 | public static function _parseWhere ($where) { 290 | if (count($where) < 1) return '1'; 291 | 292 | $items = array(); 293 | $link = 'AND'; 294 | foreach ($where as $f => $v) { 295 | if (strtolower($f) == 'link') { 296 | $link = strtoupper($v); 297 | continue; 298 | } 299 | $f = SQL::escape($f); 300 | if (is_array($v)) { 301 | $items2 = array(); 302 | $link2 = 'AND'; 303 | foreach ($v as $op1 => $op2) { 304 | if (strtolower($op1) == 'link') { 305 | $link2 = strtoupper($op2); 306 | continue; 307 | } 308 | $op2 = SQL::escape($op2); 309 | $items2[] = "`$f`$op1'$op2'"; 310 | } 311 | $items[] = '('.implode(" $link2 ", $items2).')'; 312 | } else { 313 | $v = SQL::escape($v); 314 | $items[] = "`$f`='$v'"; 315 | } 316 | } 317 | return implode(" $link ", $items); 318 | } 319 | 320 | /** 321 | * 更新记录 322 | * 323 | * @param string $table 324 | * @param array $where 325 | * @param array $update 326 | * @return int 327 | */ 328 | public static function update2 ($table, $where, $update) { 329 | if (!(is_array($where) && count($where) > 0 && is_array($update) && count($update) > 0)) return false; 330 | 331 | $table = SQL::escape($table); 332 | $set = array(); 333 | foreach ($update as $f => $v) { 334 | $f = SQL::escape($f); 335 | $v = SQL::escape($v); 336 | $set[] = "`$f`='$v'"; 337 | } 338 | $set = implode(', ', $set); 339 | $where = SQL::_parseWhere($where); 340 | $sql = "UPDATE `$table` SET $set WHERE $where"; 341 | return SQL::update($sql); 342 | } 343 | 344 | /** 345 | * 删除记录 346 | * 347 | * @param string $table 348 | * @param array $where 349 | * @return int 350 | */ 351 | public static function delete ($table, $where) { 352 | if (!is_array($where) && count($where) > 0) return false; 353 | 354 | $table = SQL::escape($table); 355 | $where = SQL::_parseWhere($where); 356 | $sql = "DELETE FROM `$table` WHERE $where"; 357 | return SQL::update($sql); 358 | } 359 | 360 | /** 361 | * 查询一条记录 362 | * 363 | * string $table 364 | * @param array $where 365 | * @return array 366 | */ 367 | public static function getOne2 ($table, $where) { 368 | if (!is_array($where) && count($where) > 0) return false; 369 | 370 | $table = SQL::escape($table); 371 | $where = SQL::_parseWhere($where); 372 | $sql = "SELECT * FROM `$table` WHERE $where"; 373 | return SQL::getOne($sql); 374 | } 375 | 376 | /** 377 | * 查询记录 378 | * 379 | * string $table 380 | * @param array $where 381 | * @return array 382 | */ 383 | public static function getAll2 ($table, $where) { 384 | if (!is_array($where) && count($where) > 0) return false; 385 | 386 | $table = SQL::escape($table); 387 | $where = SQL::_parseWhere($where); 388 | $sql = "SELECT * FROM `$table` WHERE $where"; 389 | return SQL::getAll($sql); 390 | } 391 | 392 | /** 393 | * 取最后插入ID 394 | * 395 | * @return int 396 | */ 397 | public static function id () { 398 | return mysqli_insert_id(SQL::$connection); 399 | } 400 | public static function lastId () { 401 | return SQL::id(); 402 | } 403 | 404 | /** 405 | * 转换为SQL安全字符串 406 | * 407 | * @param string $str 408 | * @return string 409 | */ 410 | public static function escape ($str) { 411 | return mysqli_real_escape_string(SQL::$connection, $str); 412 | } 413 | 414 | /** 415 | * 关闭SQL连接 416 | */ 417 | public static function close () { 418 | DEBUG::put('Close connection.', 'MySQL'); 419 | return @mysqli_close(SQL::$connection); 420 | } 421 | } 422 | } else { 423 | DEBUG::put('Class SQL is already exists!', 'Warning'); 424 | } 425 | 426 | /** 427 | * 上传文件管理 428 | */ 429 | if (!class_exists('UPLOAD', false)) { 430 | class UPLOAD { 431 | /** 432 | * 获取上传文件 433 | * 返回格式:{name: 名称, type: 文件MIME类型, size: 大小, tmp_name: 临时文件名} 434 | * 435 | * @param string $filename 436 | * @return array 437 | */ 438 | public static function get ($filename) { 439 | if (isset($_FILES[$filename])) { 440 | $uploaded_file = array( 441 | 'name' => $_FILES[$filename]['name'], 442 | 'type' => $_FILES[$filename]['type'], 443 | 'size' => $_FILES[$filename]['size'], 444 | 'tmp_name' => $_FILES[$filename]['tmp_name'] 445 | ); 446 | } elseif (isset($_POST[$filename])) { 447 | $uploaded_file = array( 448 | 'name' => $_POST[$filename], 449 | ); 450 | } elseif (isset($GLOBALS['HTTP_POST_FILES'][$filename])) { 451 | global $HTTP_POST_FILES; 452 | $uploaded_file = array( 453 | 'name' => $HTTP_POST_FILES[$filename]['name'], 454 | 'type' => $HTTP_POST_FILES[$filename]['type'], 455 | 'size' => $HTTP_POST_FILES[$filename]['size'], 456 | 'tmp_name' => $HTTP_POST_FILES[$filename]['tmp_name'] 457 | ); 458 | } elseif (isset($GLOBALS['HTTP_POST_VARS'][$filename])) { 459 | global $HTTP_POST_VARS; 460 | $uploaded_file = array( 461 | 'name' => $HTTP_POST_VARS[$filename], 462 | ); 463 | } else { 464 | $uploaded_file = array( 465 | 'name' => $GLOBALS[$filename . '_name'], 466 | 'type' => $GLOBALS[$filename . '_type'], 467 | 'size' => $GLOBALS[$filename . '_size'], 468 | 'tmp_name' => $GLOBALS[$filename] 469 | ); 470 | } 471 | return $uploaded_file; 472 | } 473 | 474 | /** 475 | * 移动临时文件 476 | * 477 | * @param {array} $file 478 | * @param {string} $target 479 | * @return {string} 480 | */ 481 | public static function move ($file, $target) { 482 | $timestamp = microtime(true); 483 | $source = is_array($file) ? $file['tmp_name'] : $file; 484 | move_uploaded_file($source, $target); 485 | DEBUG::put('Move '.$source.' to '.$target.' spent: '.round((microtime(true) - $timestamp) * 1000, 3).'ms', 'Upload'); 486 | return $target; 487 | } 488 | } 489 | } else { 490 | DEBUG::put('Class UPLOAD is already exists!', 'Warning'); 491 | } 492 | 493 | if (!class_exists('ROUTER', false)) { 494 | class ROUTER { 495 | 496 | // 中间件列表 497 | public static $_list = array(); 498 | 499 | /** 500 | * 开始自动路由 501 | * 502 | * @param string $dir 目录,默认为 action 表示应用目录下的action目录 503 | * @param string $path 路径,默认使用 $_GET['__path__'],如为空则为 / 504 | */ 505 | public static function run ($dir = 'action', $path = '_____NORMAL_____') { 506 | // 目录不能以/开头及结尾 507 | if (substr($dir, 0, 1) == '/') $dir = substr($dir, 1); 508 | if (substr($dir, -1) == '/') $dir = substr($dir, strlen($dir) - 1); 509 | // 路径必须以/开头,但不能以/结尾 510 | if ($path == '_____NORMAL_____') $path = @$_GET['__path__']; 511 | if (empty($path)) $path = '/'; 512 | if (substr($path, 0, 1) != '/') $path = '/'.$path; 513 | if ($path != '/' && substr($path, -1) == '/') $path = substr($path, strlen($path) - 1); 514 | 515 | // 中间件处理 516 | ROUTER::runMiddleware($path); 517 | 518 | $filename = APP_ROOT.$dir.$path.(substr($path, -1) == '/' ? '/index' : '').'.php'; 519 | DEBUG::put("path=$path, file=$filename", 'Router'); 520 | if (file_exists($filename)) { 521 | require($filename); 522 | } else { 523 | ROUTER::notFound($path, $filename); 524 | } 525 | } 526 | 527 | /** 528 | * 路由未找到 529 | */ 530 | public static function notFound ($path, $filename) { 531 | @header("HTTP/1.1 404 Not Found"); 532 | APP::showError("Path \"$path\" Not Found."); 533 | DEBUG::put("Not found: path=$path, file=$filename", 'Router'); 534 | } 535 | 536 | /** 537 | * 注册中间件 538 | * 539 | * @param string $path 路径 540 | * @param callback $function 要执行的函数 541 | * @param bool $is_preg 路径是否为正则表达式,默认为false 542 | */ 543 | public static function register ($path, $function, $is_preg = false) { 544 | ROUTER::$_list[] = array($path, $function, $is_preg); 545 | DEBUG::put("Use: $path => $function", 'Router'); 546 | } 547 | 548 | /** 549 | * 执行中间件 550 | * 551 | * @param string $path 552 | */ 553 | public static function runMiddleware ($path) { 554 | $pathlen = strlen($path); 555 | foreach (ROUTER::$_list as $i => $v) { 556 | $p = $v[0]; 557 | $f = $v[1]; 558 | $is_preg = $v[2]; 559 | if ($is_preg) { 560 | if (!preg_match($p, $path)) continue; 561 | } else { 562 | $pl = strlen($p); 563 | if ($pl > $pathlen) continue; 564 | if (substr($path, 0, $pl) != $p) continue; 565 | } 566 | @call_user_func($f, $path); 567 | } 568 | } 569 | } 570 | } else { 571 | DEBUG::put('Class ROUTER is already exists!', 'Warning'); 572 | } 573 | 574 | class APP { 575 | 576 | // 版本号 577 | public static $version = 1; 578 | 579 | // 是否提前退出 580 | public static $is_exit = false; 581 | 582 | // 是否为调试状态 583 | public static $is_debug = false; 584 | 585 | // 模板变量 586 | public static $locals = array(); 587 | 588 | /** 589 | * 账户验证加密解密函数 (来自Discuz!的authcode函数) 590 | * 591 | * @param string $string 明文 或 密文 592 | * @param string $operation DECODE表示解密,其它表示加密 593 | * @param string $key 密匙 594 | * @param int $expiry 密文有效期 595 | * 596 | * @return string 597 | */ 598 | public static function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) { 599 | $ckey_length = 4; 600 | $key = md5($key ? $key : 'leiphp-default-key'); 601 | $keya = md5(substr($key, 0, 16)); 602 | $keyb = md5(substr($key, 16, 16)); 603 | $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : ''; 604 | $cryptkey = $keya.md5($keya.$keyc); 605 | $key_length = strlen($cryptkey); 606 | $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string; 607 | $string_length = strlen($string); 608 | $result = ''; 609 | $box = range(0, 255); 610 | $rndkey = array(); 611 | for($i = 0; $i <= 255; $i++) { 612 | $rndkey[$i] = ord($cryptkey[$i % $key_length]); 613 | } 614 | for($j = $i = 0; $i < 256; $i++) { 615 | $j = ($j + $box[$i] + $rndkey[$i]) % 256; 616 | $tmp = $box[$i]; 617 | $box[$i] = $box[$j]; 618 | $box[$j] = $tmp; 619 | } 620 | for($a = $j = $i = 0; $i < $string_length; $i++) { 621 | $a = ($a + 1) % 256; 622 | $j = ($j + $box[$a]) % 256; 623 | $tmp = $box[$a]; 624 | $box[$a] = $box[$j]; 625 | $box[$j] = $tmp; 626 | $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256])); 627 | } 628 | if($operation == 'DECODE') { 629 | if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) { 630 | return substr($result, 26); 631 | } else { 632 | return ''; 633 | } 634 | } else { 635 | return $keyc.str_replace('=', '', base64_encode($result)); 636 | } 637 | } 638 | 639 | /** 640 | * 加密账户验证信息 641 | * 642 | * @param string $string 要加密的字符串 643 | * @param string $key 密匙 644 | * @param int $expiry 从现在起的有效时间(秒) 645 | * @return string 646 | */ 647 | public static function authEncode ($string, $key, $expiry = 0) { 648 | return APP::authcode($string, 'ENCODE', $key, $expiry); 649 | } 650 | 651 | /** 652 | * 解密账户验证信息 653 | * 654 | * @param string $string 密文 655 | * @param string $key 密匙 656 | * @return string 657 | */ 658 | public static function authDecode ($string, $key) { 659 | return APP::authcode($string, 'DECODE', $key); 660 | } 661 | 662 | /** 663 | * 加密密码 664 | * 665 | * @param string $password 666 | * @return string 667 | */ 668 | public static function encryptPassword ($password) { 669 | $random = strtoupper(md5(rand().rand())); 670 | $left = substr($random, 0, 2); 671 | $right = substr($random, -2); 672 | $newpassword = strtoupper(md5($left.$password.$right)); 673 | return $left.':'.$newpassword.':'.$right; 674 | } 675 | 676 | /** 677 | * 验证密码 678 | * 679 | * @param string $password 待验证的密码 680 | * @param string $encrypted 密码加密字符串 681 | * @return bool 682 | */ 683 | public static function validatePassword ($password, $encrypted) { 684 | $random = explode(':', strtoupper($encrypted)); 685 | if (count($random) < 3) return false; 686 | $left = $random[0]; 687 | $right = $random[2]; 688 | $main = $random[1]; 689 | $newpassword = strtoupper(md5($left.$password.$right)); 690 | return $newpassword == $main; 691 | } 692 | 693 | /** 694 | * 显示出错信息 695 | * 696 | * @param string $msg 697 | */ 698 | public static function showError ($msg) { 699 | $accept_type = strtolower(trim($_SERVER['HTTP_ACCEPT'])); 700 | if (strpos($accept_type, 'json') !== false) { 701 | APP::sendJSON(array('error' => $msg)); 702 | } else { 703 | echo "
$msg
"; 709 | } 710 | } 711 | 712 | /** 713 | * 载入模板 714 | * 如果指定了参数$layout,则会嵌套一个layout模板 715 | * 716 | * @param string $name 模板名 717 | * @param array $locals 变量 718 | * @return string 719 | */ 720 | public static function getTemplate ($name, $locals = array()) { 721 | if (!pathinfo($name, PATHINFO_EXTENSION)) $name = $name.'.html'; 722 | $filename = APP_TEMPLATE_ROOT.$name; 723 | $timestamp = microtime(true); 724 | ob_start(); 725 | extract($locals, EXTR_SKIP); 726 | include($filename); 727 | $html = ob_get_clean(); 728 | DEBUG::put('Render '.$filename.' spent: '.round((microtime(true) - $timestamp) * 1000, 3).'ms', 'Template'); 729 | return $html; 730 | } 731 | 732 | /** 733 | * 渲染模板,自动使用APP::$locals中的数据 734 | * 如果指定了参数$layout,则会嵌套一个layout模板 735 | * 736 | * Examples: 737 | * APP::render('template'); 738 | * APP::render('template', $locals); 739 | * APP::render('template', 'layout'); 740 | * APP::render('template', $locals, 'layout'); 741 | * 742 | * @param string $name 743 | * @param array $locals 744 | * @param string $layout 745 | */ 746 | public static function render ($name, $locals = array(), $layout = '') { 747 | if (!is_array($locals)) { 748 | $layout = $locals; 749 | $locals = array(); 750 | } 751 | 752 | foreach (APP::$locals as $i => $v) { 753 | if (!isset($locals[$i])) $locals[$i] = $v; 754 | } 755 | 756 | $body = APP::getTemplate($name, $locals); 757 | if (empty($layout)) { 758 | echo $body; 759 | } else { 760 | $locals['body'] = $body; 761 | echo APP::getTemplate($layout, $locals); 762 | } 763 | } 764 | 765 | public static function template ($name, $locals = array(), $layout = '') { 766 | APP::render($name, $locals, $layout); 767 | } 768 | 769 | /** 770 | * 设置模板变量 771 | * 772 | * @param string $name 773 | * @param mixed $value 774 | * @return mixed 775 | */ 776 | public static function setLocals ($name, $value = null) { 777 | APP::$locals[$name] = $value; 778 | return @APP::$locals[$name]; 779 | } 780 | 781 | /** 782 | * 取模板变量 783 | * 784 | * @param string $name 785 | * @return mixed 786 | */ 787 | public static function getLocals ($name) { 788 | return @APP::$locals[$name]; 789 | } 790 | 791 | /** 792 | * 返回JSON格式数据 793 | * 794 | * @param mixed $data 795 | */ 796 | public static function sendJSON ($data = null) { 797 | @header('content-type: application/json'); 798 | if (is_array($data) && APP::$is_debug) $data['debug'] = DEBUG::get(); 799 | echo json_encode($data); 800 | APP::end(); 801 | } 802 | 803 | /** 804 | * 返回JSON格式的出错信息 805 | * 806 | * @param string $msg 出错信息 807 | * @param array $data 其他数据 808 | */ 809 | public static function sendError ($msg, $data = array()) { 810 | $data['error'] = $msg; 811 | APP::sendJSON($data); 812 | } 813 | 814 | /** 815 | * 加载文件 816 | * 文件名如果不指定扩展名,则自动加上.php再加载 817 | * 如果以 / 开头,则从应用根目录开始查找 818 | * 819 | * @param string $filename 820 | * @return mixed 821 | */ 822 | public static function load ($filename) { 823 | if (!pathinfo($filename, PATHINFO_EXTENSION)) { 824 | $filename = $filename.'.php'; 825 | } 826 | if (substr($filename, 0, 1) == '/') { 827 | $filename = APP_ROOT.substr($filename, 1); 828 | } else { 829 | $filename = dirname($_SERVER["SCRIPT_FILENAME"]).'/'.$filename; 830 | } 831 | return require($filename); 832 | } 833 | 834 | /** 835 | * 调试输出 836 | * 837 | * @param mixed $var 838 | */ 839 | public static function dump ($var) { 840 | echo '
';
841 |     print_r($var);
842 |     echo '
'; 843 | } 844 | 845 | /** 846 | * 初始化 847 | */ 848 | public static function init () { 849 | // 是否关闭出错显示 850 | if (defined('APP_DEBUG') && APP_DEBUG) { 851 | APP::$is_debug = true; 852 | error_reporting(E_ALL); 853 | ini_set('display_errors', '1'); 854 | } else { 855 | error_reporting(0); 856 | ini_set('display_errors', '0'); 857 | } 858 | 859 | // 开始时间 860 | define('APP_TIMESTAMP_START', microtime(true)); 861 | 862 | // 只要定义了数据库配置中的任一项均自动连接数据库 863 | if (defined('CONF_MYSQL_SERVER') || defined('CONF_MYSQL_USER') || 864 | defined('CONF_MYSQL_PASSWD') || defined('CONF_MYSQL_DBNAME')) { 865 | $server = defined('CONF_MYSQL_SERVER') ? CONF_MYSQL_SERVER : 'localhost:3306'; 866 | $user = defined('CONF_MYSQL_USER') ? CONF_MYSQL_USER : 'root'; 867 | $passwd = defined('CONF_MYSQL_PASSWD') ? CONF_MYSQL_PASSWD : ''; 868 | $dbname = defined('CONF_MYSQL_DBNAME') ? CONF_MYSQL_DBNAME : ''; 869 | $permanent = defined('CONF_MYSQL_PERMANENT') ? CONF_MYSQL_PERMANENT : false; 870 | SQL::connect($server, $user, $passwd, $dbname, $permanent); 871 | if (defined('CONF_MYSQL_CHARSET')) SQL::charset(CONF_MYSQL_CHARSET); 872 | } 873 | 874 | // 自动执行 method_VERB 875 | register_shutdown_function('_leiphp_request_method_router'); 876 | } 877 | 878 | /** 879 | * 提前退出 880 | */ 881 | public static function end () { 882 | APP::$is_exit = true; 883 | die; 884 | } 885 | } 886 | --------------------------------------------------------------------------------