├── code_test ├── a.php ├── undefined.php ├── heredoc.php ├── 10.php ├── 11.php ├── 9.php ├── class_child_call.php ├── namespace.php ├── 13.php ├── encode.php ├── 12.php ├── 15.php ├── 4.php ├── log.php ├── static.php ├── class.php ├── close_tag.php ├── namespace2.php ├── 8.php ├── 1.php ├── util.php ├── 5.php ├── 7.php └── 6.php ├── encode.png ├── logo.png ├── qrcode.png ├── code_test.php ├── README.md └── LICENSE /code_test/a.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /encode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubs/enphp/master/encode.png -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubs/enphp/master/logo.png -------------------------------------------------------------------------------- /qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hubs/enphp/master/qrcode.png -------------------------------------------------------------------------------- /code_test/undefined.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /code_test/heredoc.php: -------------------------------------------------------------------------------- 1 | --------------------分割线------------------
'; 12 | echo <<<'nowdoc' 13 | 14 | nowdoc_start 15 | 16 | enphp好像有个bug,heredoc结束符后未换行,报错。 17 | 18 | nowdoc_end 19 | 20 | nowdoc; 21 | echo '
--------------------分割线------------------
'; 22 | 23 | ?> -------------------------------------------------------------------------------- /code_test/10.php: -------------------------------------------------------------------------------- 1 | 5.3.0 !'); 5 | //超时时间 6 | @set_time_limit(120); 7 | //内存限制 取消内存限制 8 | @ini_set("memory_limit", '-1'); 9 | // 开启调试模式 建议开发阶段开启 部署阶段注释或者设为false 10 | //define('APP_DEBUG',True); 11 | // 定义应用目录 12 | define('APP_PATH', './Lib/'); 13 | 14 | // 引入ThinkPHP入口文件 15 | //require 'inc.php'; 16 | // 亲^_^ 后面不需要任何代码了 就是如此简单 17 | echo "123"; 18 | ?> -------------------------------------------------------------------------------- /code_test/11.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /code_test/9.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /code_test/class_child_call.php: -------------------------------------------------------------------------------- 1 | child = new child_class(); 19 | $this->child->call(child_class::a, child_class::b); 20 | } 21 | } 22 | 23 | 24 | $conf = array(); 25 | $a = new parent_class($conf); 26 | ?> -------------------------------------------------------------------------------- /code_test/namespace.php: -------------------------------------------------------------------------------- 1 | = self::$MINIMUM_DIMENSION && $height >= self::$MINIMUM_DIMENSION) { 11 | 12 | } 13 | } 14 | } 15 | 16 | class a { 17 | public $g; 18 | 19 | function b($g) { 20 | $this->$g = $g; 21 | } 22 | } 23 | 24 | class_alias('\z\a', '\z\b'); 25 | 26 | //echo "{$_SERVER[HTTP_HOST]}{$_SERVER[REQUEST_URI]}"; 27 | //echo $_SERVER['REQUEST_TIME']; 28 | $s = new \z\a(); 29 | $s->b('g'); 30 | echo $s->g; 31 | } 32 | 33 | 34 | ?> -------------------------------------------------------------------------------- /code_test/13.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /code_test/encode.php: -------------------------------------------------------------------------------- 1 | &$v) { 16 | core::addslashes($v); 17 | } 18 | } else { 19 | $var = addslashes($var); 20 | } 21 | return $var; 22 | } 23 | } 24 | 25 | $array = array('1'); 26 | core::addslashes($array); 27 | core::$aaa = 2; 28 | echo core::$aaa; 29 | 30 | $a = /**/ 31 | 'format_code'/**/ 32 | ; 33 | $b = /**/str_rot13('1')/**/ 34 | ; 35 | $c = /**/ 36 | 1/**/ 37 | ; 38 | $d = /**/ 39 | 3/**/ 40 | ; 41 | assert($c == 1); 42 | assert($d == 3); 43 | assert($a == 'format_code'); 44 | echo $a; 45 | 46 | 47 | ?> -------------------------------------------------------------------------------- /code_test/12.php: -------------------------------------------------------------------------------- 1 | 2 |
3 | 19 |
20 | 21 | -------------------------------------------------------------------------------- /code_test/15.php: -------------------------------------------------------------------------------- 1 | = self::$MINIMUM_DIMENSION && $height >= self::$MINIMUM_DIMENSION) { 10 | 11 | } 12 | } 13 | } 14 | 15 | class a { 16 | public $g; 17 | 18 | function b($g) { 19 | $this->$g = $g; 20 | } 21 | } 22 | 23 | final class MathUtils { 24 | private function __construct() { 25 | } 26 | 27 | public static function round($d) { 28 | return (int)($d + ($d < 0.0 ? -0.5 : 0.5)); 29 | } 30 | 31 | public static function distance($aX, $aY, $bX, $bY) { 32 | $xDiff = $aX - $bX; 33 | $yDiff = $aY - $bY; 34 | return (float)sqrt($xDiff * $xDiff + $yDiff * $yDiff); 35 | } 36 | } 37 | 38 | class Reader { 39 | 40 | } 41 | 42 | interface sReader { 43 | } 44 | 45 | abstract class mzReader extends Reader { 46 | } 47 | $_SERVER['HTTP_HOST'] = 1; 48 | $_SERVER['REQUEST_URI'] = 1; 49 | 50 | function main() { 51 | echo "{$_SERVER[HTTP_HOST]}{$_SERVER[REQUEST_URI]}"; 52 | //echo $_SERVER['REQUEST_TIME']; 53 | } 54 | define('ROOT_PATH', __DIR__); 55 | !defined('ROOT_PATH') && exit('123'); 56 | 57 | $s = new a(); 58 | $s->b('g'); 59 | echo $s->g; 60 | echo main(); 61 | ?> -------------------------------------------------------------------------------- /code_test/4.php: -------------------------------------------------------------------------------- 1 | $val) { 12 | $key = is_string($key) ? '\'' . addcslashes($key, '\'\\') . '\'' : $key; 13 | $val = !is_array($val) && (!preg_match("/^\-?\d+$/", $val) || strlen($val) > 12 || substr($val, 0, 1) == '0') ? '\'' . addcslashes($val, '\'\\') . '\'' : $val; 14 | if (is_array($val)) { 15 | $evaluate .= "$comma$key => " . self::array_eval($val, $space_str, $level + 1); 16 | } else { 17 | $evaluate .= "$comma$key => $val"; 18 | } 19 | $comma = ",{$space_line}$space_str"; 20 | } 21 | $evaluate .= "{$space_line}$space_str)"; 22 | return $evaluate; 23 | } 24 | 25 | public function set($key, $value, $life = 0) { 26 | $file_path = $this->get_file($key); 27 | $life = $life == 0 ? 600 : $life; 28 | $res = array('expire' => $this->get_time() + $life, 'body' => &$value,); 29 | if (file_put_contents($file_path, $this->gen_file_body($res))) { 30 | return true; 31 | } else { 32 | return false; 33 | } 34 | } 35 | } 36 | 37 | 38 | echo 'success'; 39 | ?> -------------------------------------------------------------------------------- /code_test/log.php: -------------------------------------------------------------------------------- 1 | $v) { 45 | if (is_array($v)) { 46 | $str .= '[' . $k . '=' . self::dump_var($v) . ']'; 47 | } else { 48 | $str .= '[' . $k . '=' . $v . ']'; 49 | } 50 | } 51 | return $str; 52 | } else { 53 | return '[' . $data . ']'; 54 | } 55 | } 56 | 57 | /** 58 | * log::info($arg1,$arg2....$argn); 59 | * 60 | * @param mixed 61 | */ 62 | public static function info() { 63 | self::add_log('info', func_get_args(), func_num_args()); 64 | } 65 | 66 | /** 67 | * log::error($arg1,$arg2....$argn); 68 | * 69 | * @param mixed 70 | */ 71 | public static function error() { 72 | self::add_log('error', func_get_args(), func_num_args()); 73 | throw new Exception('error'); 74 | } 75 | 76 | /** 77 | * add log 78 | * 79 | * @param $type 80 | * @param $arg_list 81 | * @param $arg_count 82 | */ 83 | private static function add_log($type, $arg_list, $arg_count) { 84 | $log = ''; 85 | for ($i = 0, $l = $arg_count; $i < $l; $i++) { 86 | $log .= self::dump_var($arg_list[$i]); 87 | } 88 | $log .= '[' . usedtime() . "ms]"; 89 | $log = "[" . date('H:i:s') . "]" . $log . "\r\n"; 90 | if (self::$log_fp) { 91 | fputs(self::$log_fp, $log); 92 | } 93 | if (php_sapi_name() == 'cli') { 94 | echo $log; 95 | } else { 96 | if (isset($_SERVER['log'])) { 97 | $_SERVER['log'] = array( 98 | 'info' => array(), 99 | 'error' => array(), 100 | ); 101 | } 102 | $_SERVER['log'][$type][] = $log; 103 | } 104 | } 105 | } 106 | 107 | ?> -------------------------------------------------------------------------------- /code_test/static.php: -------------------------------------------------------------------------------- 1 | pri = 1; 35 | if ($this->pri) { 36 | 37 | } 38 | if ($global) { 39 | 40 | } 41 | } 42 | 43 | 44 | } 45 | 46 | 47 | interface QrReader 48 | { 49 | public function decode($image); 50 | 51 | public function reset(); 52 | } 53 | 54 | 55 | abstract class Binarizer 56 | { 57 | 58 | public abstract function getBlackRow($y, $row); 59 | 60 | public abstract function getBlackMatrix(); 61 | 62 | public abstract function createBinarizer($source); 63 | 64 | } 65 | 66 | final class GenericGF 67 | { 68 | public static $AZTEC_DATA_12; 69 | public static $AZTEC_DATA_10; 70 | public static $AZTEC_DATA_6; 71 | public static $AZTEC_PARAM=6; 72 | public static $QR_CODE_FIELD_256; 73 | public static $DATA_MATRIX_FIELD_256; 74 | public static $AZTEC_DATA_8; 75 | public static $MAXICODE_FIELD_64; 76 | private $expTable; 77 | private $logTable; 78 | private $zero; 79 | private $one; 80 | private $size; 81 | private $primitive; 82 | private $generatorBase; 83 | 84 | 85 | public static function Init() { 86 | self::$AZTEC_DATA_12 = new a(0x1069, 4096, 1); 87 | self::$AZTEC_DATA_10 = new a(0x409, 1024, 1); 88 | self::$AZTEC_DATA_6 = new a(0x43, 64, 1); 89 | self::$AZTEC_PARAM = new a(0x13, 16, 1); 90 | self::$QR_CODE_FIELD_256 = new a(0x011D, 256, 0); 91 | self::$DATA_MATRIX_FIELD_256 = new a(0x012D, 256, 1); 92 | self::$AZTEC_DATA_8 = self::$DATA_MATRIX_FIELD_256; 93 | self::$MAXICODE_FIELD_64 = self::$AZTEC_DATA_6; 94 | } 95 | 96 | } 97 | 98 | if (PHP_EOL) { 99 | } 100 | $array = array( 101 | 'a' => $b, 102 | ); 103 | $func = 'load_static'; 104 | $b = (new a()); 105 | $b->{$func}(); 106 | $i = new ii(); 107 | $i->init('a', 'b'); 108 | $_SERVER['s'][0] = 'AZTEC_PARAM'; 109 | echo GenericGF::${$_SERVER['s'][0]}; 110 | } 111 | 112 | ?> -------------------------------------------------------------------------------- /code_test/class.php: -------------------------------------------------------------------------------- 1 | 'a', 10 | ); 11 | $func = 'load_static'; 12 | $b = (new $array['a']()); 13 | $b->{$func}(); 14 | $b->$func(); 15 | GenericGF::init(); 16 | print_r(GenericGF::$AZTEC_DATA_12); 17 | /* 18 | if (PHP_EOL) { 19 | } 20 | $aaa[0] = 'a'; 21 | $aaa[2] = 'init'; 22 | 23 | echo $func, PHP_EOL; 24 | $i = new ii(); 25 | $i->init('a', 'b'); 26 | $_SERVER['s'][0] = 'AZTEC_PARAM'; 27 | echo GenericGF::${$_SERVER['s'][0]};*/ 28 | } 29 | 30 | class f 31 | { 32 | public static $var; 33 | 34 | public static function load_static() { 35 | if (self::$var) { 36 | echo self::$var, PHP_EOL; 37 | } 38 | } 39 | } 40 | 41 | 42 | interface i 43 | { 44 | function init($a, $b); 45 | } 46 | 47 | class ii implements i 48 | { 49 | 50 | function init($b, $c) { 51 | echo $b, $c; 52 | } 53 | } 54 | 55 | class a 56 | { 57 | public static $var; 58 | private $pri; 59 | 60 | 61 | public function load_static() { 62 | global $global; 63 | static $conf; 64 | echo $global; 65 | $str = 'pri'; 66 | if ($conf) { 67 | } 68 | if (self::$var) { 69 | 70 | } 71 | $this->pri = 1; 72 | if ($this->pri) { 73 | 74 | } 75 | if ($global) { 76 | 77 | } 78 | } 79 | 80 | 81 | } 82 | 83 | 84 | interface QrReader 85 | { 86 | public function decode($image); 87 | 88 | public function reset(); 89 | } 90 | 91 | 92 | abstract class Binarizer 93 | { 94 | 95 | public abstract function getBlackRow($y, $row); 96 | 97 | public abstract function getBlackMatrix(); 98 | 99 | public abstract function createBinarizer($source); 100 | 101 | } 102 | 103 | final class GenericGF 104 | { 105 | public static $AZTEC_DATA_12; 106 | public static $AZTEC_DATA_10; 107 | public static $AZTEC_DATA_6; 108 | public static $AZTEC_PARAM = 6; 109 | public static $QR_CODE_FIELD_256; 110 | public static $DATA_MATRIX_FIELD_256; 111 | public static $AZTEC_DATA_8; 112 | public static $MAXICODE_FIELD_64; 113 | private $expTable; 114 | private $logTable; 115 | private $zero; 116 | private $one; 117 | private $size; 118 | private $primitive; 119 | private $generatorBase; 120 | 121 | 122 | public static function Init() { 123 | self::$AZTEC_DATA_12 = new a(0x1069, 4096, 1); 124 | self::$AZTEC_DATA_10 = new a(0x409, 1024, 1); 125 | self::$AZTEC_DATA_6 = new a(0x43, 64, 1); 126 | self::$AZTEC_PARAM = new a(0x13, 16, 1); 127 | self::$QR_CODE_FIELD_256 = new a(0x011D, 256, 0); 128 | self::$DATA_MATRIX_FIELD_256 = new a(0x012D, 256, 1); 129 | self::$AZTEC_DATA_8 = self::$DATA_MATRIX_FIELD_256; 130 | self::$MAXICODE_FIELD_64 = self::$AZTEC_DATA_6; 131 | } 132 | 133 | } 134 | 135 | test(); 136 | ?> -------------------------------------------------------------------------------- /code_test.php: -------------------------------------------------------------------------------- 1 | 2, 21 | //混淆函数产生变量最大长度 22 | 'ob_function_length' => 3, 23 | //混淆函数调用 1=混淆 0=不混淆 或者 array('eval', 'strpos') 为混淆指定方法 24 | 'ob_call' => 1, 25 | //随机插入乱码 26 | 'insert_mess' => 0, 27 | //混淆函数调用变量产生模式 1=字母混淆 2=乱码混淆 28 | 'encode_call' => 2, 29 | //混淆class 30 | 'ob_class' => 0, 31 | //混淆变量 方法参数 1=字母混淆 2=乱码混淆 32 | 'encode_var' => 2, 33 | //混淆变量最大长度 34 | 'encode_var_length' => 5, 35 | //混淆字符串常量 1=字母混淆 2=乱码混淆 36 | 'encode_str' => 2, 37 | //混淆字符串常量变量最大长度 38 | 'encode_str_length' => 3, 39 | // 混淆html 1=混淆 0=不混淆 40 | 'encode_html' => 2, 41 | // 混淆数字 1=混淆为0x00a 0=不混淆 42 | 'encode_number' => 1, 43 | // 混淆的字符串 以 gzencode 形式压缩 1=压缩 0=不压缩 44 | 'encode_gz' => 1, 45 | // 加换行(增加可阅读性) 46 | 'new_line' => 0, 47 | // 移除注释 1=移除 0=保留 48 | 'remove_comment' => 1, 49 | // debug 50 | 'debug' => 1, 51 | // 重复加密次数,加密次数越多反编译可能性越小,但性能会成倍降低 52 | 'deep' => 1, 53 | // PHP 版本 54 | 'php' => 7, 55 | ); 56 | // encode target 57 | enphp_file($file, $target_file, $options); 58 | log::info('encoded', $target_file); 59 | 60 | $old_output = $output = array(); 61 | // run encoded & old script 62 | exec('php -d error_reporting=0 "' . $target_file . '"', $output); 63 | 64 | exec('php -d error_reporting=0 "' . $file . '"', $old_output); 65 | 66 | $output = implode("\n", $output); 67 | $old_output = implode("\n", $old_output); 68 | $old_output = strtr($old_output, [realpath($file) => realpath($target_file)]); 69 | // compare result 70 | if ($old_output == $output) { 71 | log::info('SUCCESS_TEST'); 72 | } else { 73 | log::info('FAILURE_TEST'); 74 | echo str_repeat('===', 5); 75 | echo "\r\nold=", trim($old_output), "\r\n"; 76 | echo str_repeat('===', 5); 77 | echo "\r\nnew=", trim($output), "\r\n"; 78 | break; 79 | } 80 | // 81 | /* 82 | // php 5 83 | log::info('exec5', $target_file, $file); 84 | $options['php'] = 5; 85 | enphp_file($file, $target_file, $options); 86 | exec('php5_path "' . $target_file . '"', $output); 87 | exec('php5_path "' . $file . '"', $old_output); 88 | if ($old_output == $output) { 89 | log::info('SUCCESS_PHP5'); 90 | } else { 91 | log::info('FAILURE_PHP5'); 92 | echo "\r\n", trim(implode("\r\n", $output)), "\r\n"; 93 | break; 94 | } 95 | */ 96 | } 97 | ?> -------------------------------------------------------------------------------- /code_test/close_tag.php: -------------------------------------------------------------------------------- 1 | condition = 'uid = ? and pre_time > 0 and pre_time < ? and pre_ok = 0'; 19 | $search->join = 'left join t_client as client on client.id = t.cli_id'; 20 | $search->params = array(User::getCuruid(), time()); 21 | $search->select = 't.*,client.name cli_name'; 22 | return $this->queryAllSearch($search); 23 | } 24 | 25 | public function searchReback($s_params) { 26 | $search = new CSearch(); 27 | $condition = 'cli_id = ? and is_reback = 1'; 28 | $params = array($s_params['cli_id']); 29 | if ($s_params['start_time']) { 30 | $condition .= ' and t.time >= ? '; 31 | $params[] = strtotime($s_params['start_time']); 32 | } 33 | if ($s_params['end_time']) { 34 | $condition .= ' and t.time <= ? '; 35 | $params[] = strtotime($s_params['end_time']) + 86400; 36 | } 37 | if ($s_params['reback_type_id']) { 38 | $condition .= ' and t.fid = ? '; 39 | $params[] = $s_params['reback_type_id']; 40 | } 41 | if ($s_params['uid']) { 42 | $condition .= ' and t.uid = ? '; 43 | $params[] = $s_params['uid']; 44 | } 45 | $s_sort = Common::getSortStr($params, array( 46 | 'rebacktime' => 't.reback_time' 47 | )); 48 | $search->order = $s_sort; 49 | $search->condition = $condition; 50 | $search->params = $params; 51 | return $this->queryAllSearch($search); 52 | } 53 | 54 | public static function hasNotice() { 55 | return self::model()->count('uid = ? and pre_time > 0 and pre_time < ? and pre_ok = 0', array(User::getCuruid(), time())); 56 | } 57 | 58 | public function getOneById($id) { 59 | return $this->query('id=?', $id); 60 | } 61 | 62 | public function add($param) { 63 | $param['com_id'] = User::getCurcomid(); 64 | $param['c_time'] = time(); 65 | $param['e_time'] = time(); 66 | return $this->insert($param); 67 | } 68 | 69 | public function edit($param, $id) { 70 | $param['com_id'] = User::getCurcomid(); 71 | $param['e_time'] = time(); 72 | return $this->update($param, 'id = ?', $id); 73 | } 74 | 75 | /** 76 | * 通过ID查询记录 77 | * 78 | * @param int $com_id 公司ID 79 | * @param int $id 主键ID 80 | * 81 | * @return array 82 | */ 83 | public static function findById($com_id, $id) { 84 | $res = null; 85 | if ($id > 0) { 86 | $res = self::model()->query('com_id = ? and id = ?', array($com_id, $id)); 87 | } 88 | return $res; 89 | } 90 | 91 | public function getTodyFollow() { 92 | return $this->query('com_id = ? and c_time >' . mktime(0, 0, 0, date('m'), date('d'), date('Y')) . ' and c_time <' . (mktime(0, 0, 0, date('m'), date('d') + 1, date('Y')) - 1), array(User::getCurcomid()), 'count(*) as number'); 93 | 94 | } 95 | 96 | 97 | } 98 | 99 | ?> -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # EnPHP 5 | 6 | ![LOGO](https://github.com/djunny/enphp/raw/master/logo.png) 7 | 8 | 9 | ``` 10 | // 一个开源加密混淆 PHP 代码项目 11 | // a Open Source PHP Code Confusion + Encryption Project 12 | ``` 13 | 14 | 15 | ## 项目地址 16 | 17 | GITHUB:https://github.com/djunny/enphp 18 | 19 | GITEE:https://gitee.com/mz/enphp_opensource 20 | 21 | 22 | ## 背景 23 | 24 | ``` 25 | 曾经,作者也是商业软件开发者中小将一名,软件总是被人破解,于是花了几个月研究了 EnPHP。 26 | 这套项目也有偿提供过给很多人,不过,应该网上存在不少破解了。 27 | 项目主要贵在为大家提供一个加密混淆和还原的思路。 28 | // 严禁用于非法用途。 29 | ``` 30 | 31 | ## 加密效果 32 | 33 | ![LOGO](https://github.com/djunny/enphp/raw/master/encode.png) 34 | 35 | 36 | ## 使用方法 37 | ``` 38 | include './func_v2.php'; 39 | $options = array( 40 | //混淆方法名 1=字母混淆 2=乱码混淆 41 | 'ob_function' => 2, 42 | //混淆函数产生变量最大长度 43 | 'ob_function_length' => 3, 44 | //混淆函数调用 1=混淆 0=不混淆 或者 array('eval', 'strpos') 为混淆指定方法 45 | 'ob_call' => 1, 46 | //随机插入乱码 47 | 'insert_mess' => 0, 48 | //混淆函数调用变量产生模式 1=字母混淆 2=乱码混淆 49 | 'encode_call' => 2, 50 | //混淆class 51 | 'ob_class' => 0, 52 | //混淆变量 方法参数 1=字母混淆 2=乱码混淆 53 | 'encode_var' => 2, 54 | //混淆变量最大长度 55 | 'encode_var_length' => 5, 56 | //混淆字符串常量 1=字母混淆 2=乱码混淆 57 | 'encode_str' => 2, 58 | //混淆字符串常量变量最大长度 59 | 'encode_str_length' => 3, 60 | // 混淆html 1=混淆 0=不混淆 61 | 'encode_html' => 2, 62 | // 混淆数字 1=混淆为0x00a 0=不混淆 63 | 'encode_number' => 1, 64 | // 混淆的字符串 以 gzencode 形式压缩 1=压缩 0=不压缩 65 | 'encode_gz' => 0, 66 | // 加换行(增加可阅读性) 67 | 'new_line' => 1, 68 | // 移除注释 1=移除 0=保留 69 | 'remove_comment' => 1, 70 | // debug 71 | 'debug' => 1, 72 | // 重复加密次数,加密次数越多反编译可能性越小,但性能会成倍降低 73 | 'deep' => 1, 74 | // PHP 版本 75 | 'php' => 7, 76 | ); 77 | $file = 'code_test/1.php'; 78 | $target_file = 'encoded/2.php'; 79 | enphp_file($file, $target_file, $options); 80 | ``` 81 | 82 | ## 回归测试脚本: 83 | 可以将你要测试的代码放至 code_test 中,运行命令: 84 | ``` 85 | php code_test.php 86 | ``` 87 | 程序会自动进行回归测试,我也放了一些之前要测试的脚本在里边 88 | 89 | P.S. 90 | 91 | ``` 92 | 本来,还实现了 goto + xor 变种,不过兼容性和性能有点差,等有时间精力的时候再研究罢... 93 | ``` 94 | 95 | # 一些注意事项 96 | 97 | ## 如何让 EnPHP 加密强度更高? 98 | 99 | 1. 将全局逻辑尽量变成类方法,EnPHP 对类加密会有更好的加密混淆效果 100 | 2. 对于 class 的变量初始化请放至析构(__construct)方法中 101 | 3. 对于多维数组能用数字下标尽量用数字 102 | 4. 使用注释加密加强混淆强度 103 | 104 | 105 | ## 在混淆类名时,代码一定要有先后顺序: 106 | ``` 107 | interface i { 108 | function init($a, $b); 109 | } 110 | 111 | class ii implements i { 112 | // PHP 中继承的参数名可以不一样 113 | function init($b, $c) { 114 | echo $b, $c; 115 | } 116 | } 117 | ``` 118 | 119 | ``` 120 | namespace a{ 121 | class b{ 122 | } 123 | # 正确 124 | $b = new \a\b(); 125 | # 错误 126 | #$b = new b(): 127 | } 128 | ``` 129 | 130 | ## 使用注释语法加密字符串(支持字符串+数字): 131 | ``` 132 | //格式:/**/要二次混淆的内容/**/ 133 | $a = /**/"明文数据1"/**/; 134 | echo /**/2/**/; 135 | print(/**/"明文数据3"/**/); 136 | ``` 137 | 138 | 139 | 140 | ## 使用注释语法去除代码: 141 | ``` 142 | echo 1; 143 | /**/ 144 | echo 2; 145 | /**/ 146 | echo 3; 147 | //格式:/**/要隐藏的代码/**/ 148 | ``` 149 | 150 | 151 | -------------------------------------------------------------------------------- /code_test/namespace2.php: -------------------------------------------------------------------------------- 1 | header('分销商品'); 29 | $content->description('分销商品列表'); 30 | 31 | $content->body($this->grid()); 32 | }); 33 | } 34 | 35 | 36 | /** 37 | * Make a grid builder. 38 | * 39 | * @return Grid 40 | */ 41 | protected function grid() { 42 | return Admin::grid(Goods::class, function (Grid $grid) { 43 | 44 | $grid->model()->addConditions([ 45 | [ 46 | 'orderBy' => [ 47 | 'goods_id', 48 | 'desc', 49 | ], 50 | ], 51 | [ 52 | 'where' => [ 53 | 'commission', 54 | '>', 55 | '0', 56 | ], 57 | ], 58 | ]); 59 | 60 | $grid->disableCreation(); 61 | 62 | $grid->actions(function (Grid\Displayers\Actions $actions) { 63 | $actions->disableDelete(); 64 | $actions->disableEdit(); 65 | $goods_id = $actions->row->goods_id; 66 | $actions->append(''); 67 | $goods = Goods::find($goods_id); 68 | if (count($goods->spec_values) > 0) { 69 | $actions->append(' | '); 70 | } 71 | }); 72 | 73 | $grid->tools(function (Grid\Tools $tools) { 74 | $tools->batch(function (Grid\Tools\BatchActions $actions) { 75 | $actions->disableDelete(); 76 | }); 77 | }); 78 | 79 | $grid->goods_id('商品ID')->sortable(); 80 | $grid->goods_sku('商品SKU'); 81 | 82 | $grid->gc_id('商品分类')->display(function ($gc_id) { 83 | return Category::find($gc_id)->gc_name; 84 | })->label('primary'); 85 | 86 | $grid->brand_id('商品品牌')->display(function ($brand_id) { 87 | return Brand::find($brand_id)->brand_name; 88 | })->label(); 89 | 90 | $grid->goods_name('商品名称'); 91 | $grid->commission('商品佣金')->display(function ($commission) { 92 | return $commission . ' %'; 93 | }); 94 | $grid->is_hot('是否推荐')->switch(); 95 | $grid->is_new('是否新品')->switch(); 96 | $grid->status('商品状态')->switch(); 97 | 98 | $grid->filter(function ($filter) { 99 | $filter->disableIdFilter(); 100 | $filter->is('gc_id', '商品分类')->select(Category::selectOptions()); 101 | $filter->like('goods_name', '商品名称'); 102 | $filter->like('goods_sku', '商品SKU'); 103 | }); 104 | 105 | }); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /code_test/8.php: -------------------------------------------------------------------------------- 1 | 10 | // +---------------------------------------------------------------------- 11 | 12 | //---------------------------------- 13 | // ThinkPHP公共入口文件 14 | //---------------------------------- 15 | 16 | // 记录开始运行时间 17 | $GLOBALS['_beginTime'] = microtime(TRUE); 18 | // 记录内存初始使用 19 | define('MEMORY_LIMIT_ON',function_exists('memory_get_usage')); 20 | if(MEMORY_LIMIT_ON) $GLOBALS['_startUseMems'] = memory_get_usage(); 21 | 22 | // 版本信息 23 | const THINK_VERSION = '3.2.2'; 24 | 25 | // URL 模式定义 26 | const URL_COMMON = 0; //普通模式 27 | const URL_PATHINFO = 1; //PATHINFO模式 28 | const URL_REWRITE = 2; //REWRITE模式 29 | const URL_COMPAT = 3; // 兼容模式 30 | 31 | // 类文件后缀 32 | const EXT = '.class.php'; 33 | 34 | // 系统常量定义 35 | defined('THINK_PATH') or define('THINK_PATH', __DIR__.'/'); 36 | defined('APP_PATH') or define('APP_PATH', dirname($_SERVER['SCRIPT_FILENAME']).'/'); 37 | defined('APP_STATUS') or define('APP_STATUS', ''); // 应用状态 加载对应的配置文件 38 | defined('APP_DEBUG') or define('APP_DEBUG', false); // 是否调试模式 39 | 40 | if(function_exists('saeAutoLoader')){// 自动识别SAE环境 41 | defined('APP_MODE') or define('APP_MODE', 'sae'); 42 | defined('STORAGE_TYPE') or define('STORAGE_TYPE', 'Sae'); 43 | }else{ 44 | defined('APP_MODE') or define('APP_MODE', 'common'); // 应用模式 默认为普通模式 45 | defined('STORAGE_TYPE') or define('STORAGE_TYPE', 'File'); // 存储类型 默认为File 46 | } 47 | //define('TMPL_PATH','./Tpl/'); 48 | defined('RUNTIME_PATH') or define('RUNTIME_PATH', './Runtime/'); // 系统运行时目录 49 | defined('LIB_PATH') or define('LIB_PATH', realpath(THINK_PATH.'Library').'/'); // 系统核心类库目录 50 | defined('CORE_PATH') or define('CORE_PATH', LIB_PATH.'Think/'); // Think类库目录 51 | defined('BEHAVIOR_PATH')or define('BEHAVIOR_PATH', LIB_PATH.'Behavior/'); // 行为类库目录 52 | defined('MODE_PATH') or define('MODE_PATH', THINK_PATH.'Mode/'); // 系统应用模式目录 53 | defined('VENDOR_PATH') or define('VENDOR_PATH', LIB_PATH.'Vendor/'); // 第三方类库目录 54 | defined('COMMON_PATH') or define('COMMON_PATH', APP_PATH.'Common/'); // 应用公共目录 55 | defined('CONF_PATH') or define('CONF_PATH', APP_PATH.'Conf/'); // 应用配置目录 56 | defined('LANG_PATH') or define('LANG_PATH', APP_PATH.'Lang/'); // 应用语言目录 57 | defined('HTML_PATH') or define('HTML_PATH', RUNTIME_PATH.'Html/'); // 应用静态目录 58 | defined('LOG_PATH') or define('LOG_PATH', RUNTIME_PATH.'Logs/'); // 应用日志目录 59 | defined('TEMP_PATH') or define('TEMP_PATH', RUNTIME_PATH.'Temp/'); // 应用缓存目录 60 | defined('DATA_PATH') or define('DATA_PATH', RUNTIME_PATH.'Data/'); // 应用数据目录 61 | defined('CACHE_PATH') or define('CACHE_PATH', RUNTIME_PATH.'Cache/'); // 应用模板缓存目录 62 | defined('CONF_EXT') or define('CONF_EXT', '.php'); // 配置文件后缀 63 | defined('CONF_PARSE') or define('CONF_PARSE', ''); // 配置文件解析方法 64 | 65 | // 系统信息 66 | if(version_compare(PHP_VERSION,'5.4.0','<')) { 67 | ini_set('magic_quotes_runtime',0); 68 | define('MAGIC_QUOTES_GPC',get_magic_quotes_gpc()?True:False); 69 | }else{ 70 | define('MAGIC_QUOTES_GPC',false); 71 | } 72 | define('IS_CGI',(0 === strpos(PHP_SAPI,'cgi') || false !== strpos(PHP_SAPI,'fcgi')) ? 1 : 0 ); 73 | define('IS_WIN',strstr(PHP_OS, 'WIN') ? 1 : 0 ); 74 | define('IS_CLI',PHP_SAPI=='cli'? 1 : 0); 75 | 76 | if(!IS_CLI) { 77 | // 当前文件名 78 | if(!defined('_PHP_FILE_')) { 79 | if(IS_CGI) { 80 | //CGI/FASTCGI模式下 81 | $_temp = explode('.php',$_SERVER['PHP_SELF']); 82 | define('_PHP_FILE_', rtrim(str_replace($_SERVER['HTTP_HOST'],'',$_temp[0].'.php'),'/')); 83 | }else { 84 | define('_PHP_FILE_', rtrim($_SERVER['SCRIPT_NAME'],'/')); 85 | } 86 | } 87 | if(!defined('__ROOT__')) { 88 | $_root = rtrim(dirname(_PHP_FILE_),'/'); 89 | define('__ROOT__', (($_root=='/' || $_root=='\\')?'':$_root)); 90 | } 91 | } 92 | 93 | echo 'success'; 94 | -------------------------------------------------------------------------------- /code_test/1.php: -------------------------------------------------------------------------------- 1 | 1]; 77 | // 78 | //preg_replace_callback('/(\w+)\/([^\/]+)/', function ($match) use (&$var) { 79 | // $a = ''; 80 | // $b = '321'; 81 | // $var[$match[1]] = strip_tags($match[2]); 82 | //}, $path) xor assert($var['a'] === 'b'); 83 | //assert($var['c'] === 'd'); 84 | //print_r($b . $c . $a) xor print_r($var); 85 | //assert(count($var) == 3); 86 | //echo 'CASE_UPPER=', CASE_UPPER, "\r\n"; 87 | //// for test DEFINE 88 | //$name = array( 89 | // 'k' => 1, 90 | // 'b' => 2, 91 | //); 92 | //$name = array_change_key_case($name, CASE_UPPER); 93 | //print_R($name); 94 | // 95 | //// for test static method 96 | //class core 97 | //{ 98 | // private static $conf; 99 | // 100 | // static function _init(&$conf) { 101 | // // 得兼容 102 | // self::$conf = $conf; 103 | // } 104 | // 105 | // static function init(&$conf, $a, $b) { 106 | // // 这里容易被忽略 107 | // self::_init($conf); 108 | // } 109 | //} 110 | // 111 | //$a = '1'; 112 | //$b = 0; 113 | //core::init($a, array(), $b); 114 | // 115 | //exit; 116 | // 117 | //$a = array('print_r'); 118 | //if (false !== ($_val = $a[0](1, 1))) { 119 | // echo $_val; 120 | //} 121 | // 122 | //include 'inc.php'; 123 | //echo PHP_VERSION ? 1 : 0, "\r\n"; 124 | //echo num_hex(1, 8), "\r\n"; 125 | //E(); 126 | // 127 | //function E() { 128 | // return new A\E(array(1)); 129 | //} 130 | // 131 | //function num_hex($encode, $num) { 132 | // 133 | // if ($encode == 1) { 134 | // if (strpos($num, '0x') === 0) { 135 | // $num = base_convert($num, 16, 10); 136 | // } 137 | // $repeat = ($num % 5) + 1; 138 | // $str = '0x' . str_repeat('0', $repeat) . base_convert($num, 10, 16); 139 | // return $str; 140 | // } else { 141 | // return $num; 142 | // } 143 | //} 144 | // 145 | //error_reporting(E_ERROR); 146 | // 147 | // 148 | //$func[1] = 'microtime'; 149 | //$_SERVER['starttime'] = $func[1](1); 150 | //$starttime = explode(' ', $_SERVER['starttime']); 151 | //$_SERVER['time'] = isset($_SERVER['REQUEST_TIME']) ? $_SERVER['REQUEST_TIME'] : $starttime[1]; 152 | // 153 | //try { 154 | // echo a(2, 2), "\r\n"; 155 | // echo 1 / 0; 156 | //} catch (Exception $e) { 157 | // print_r($e); 158 | //} 159 | //function a($a = array(array(1, 3, array())), $b) { 160 | // return $a; 161 | //} 162 | // 163 | //function array_eval($space_line = "\n", $level = 0) { 164 | // $space_str = ''; 165 | // for ($i = 0; $i <= $level; $i++) { 166 | // $space_str .= "\t"; 167 | // } 168 | // $evaluate = "Array{$space_line}$space_str{$space_line}("; 169 | // return $evaluate; 170 | //} 171 | // 172 | //$ins = 'html'; 173 | //echo $ins::encode('我123123mf' . 'msd' . 'faj' . 'sf' . 'j'); 174 | //echo(html_encode('我的')); 175 | // 176 | //echo "\r\n", number_format(microtime(1) - $_SERVER['starttime'], 6) * 1000, "\r\n"; 177 | // 178 | //function html_encode($s) { 179 | // $s = json_encode($s); 180 | // $s = substr($s, 1, -1); 181 | // $p = '/\\\u([0-9a-z]{4})/is'; 182 | // $s = preg_replace($p, '&#x$1;', $s); 183 | // echo 'pattern=', $p, "\r\n"; 184 | // echo $s; 185 | //} 186 | // 187 | //class html 188 | //{ 189 | // public static function encode($s, $type = 'dec'/*hex*/) { 190 | // if ($type == 'dex') { 191 | // $s = json_encode($s); 192 | // $s = substr($s, 1, -1); 193 | // 194 | // return preg_replace('/\\\u([0-9a-z]{4})/', '&#x$1;', $s); 195 | // } else { 196 | // return preg_replace_callback('|[^\x00-\x7F]+|', array(__CLASS__, '_covert'), $s); 197 | // } 198 | // } 199 | // 200 | // public static function _covert($data) { 201 | // if (is_array($data)) { 202 | // $chars = str_split(iconv('UTF-8', "UCS-2BE", $data[0]), 2); 203 | // $chars = array_map(array(__CLASS__, __FUNCTION__), $chars); 204 | // return join("", $chars); 205 | // } else { 206 | // $code = hexdec(sprintf("%02s%02s;", dechex(ord($data[0])), dechex(ord($data[1])))); 207 | // return sprintf("&#%s;", $code); 208 | // } 209 | // } 210 | //} 211 | // 212 | // 213 | //function _gzdecode($data) { 214 | // $flags = ord(substr($data, 3, 1)); 215 | // $headerlen = 10; 216 | // $extralen = 0; 217 | // $filenamelen = 0; 218 | // if ($flags & 4) { 219 | // $extralen = unpack('v', substr($data, 10, 2)); 220 | // $extralen = $extralen[1]; 221 | // $headerlen += 2 + $extralen; 222 | // } 223 | // if ($flags & 8) $headerlen = strpos($data, chr(0), $headerlen) + 1; 224 | // if ($flags & 16) $headerlen = strpos($data, chr(0), $headerlen) + 1; 225 | // if ($flags & 2) $headerlen += 2; 226 | // $unpacked = @gzinflate(substr($data, $headerlen)); 227 | // if ($unpacked === FALSE) $unpacked = $data; 228 | // return $unpacked; 229 | //}//gzdecode end 230 | // 231 | //?> 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | -------------------------------------------------------------------------------- /code_test/util.php: -------------------------------------------------------------------------------- 1 | $value) { 44 | $arr['filelist'][$key]['path'] = pre_clear($value['path']); 45 | } 46 | foreach ($arr['folderlist'] as $key => $value) { 47 | $arr['folderlist'][$key]['path'] = pre_clear($value['path']); 48 | } 49 | }else{ 50 | $arr = pre_clear($arr); 51 | } 52 | } 53 | //前缀处理 非root用户目录/从HOME开始 54 | function pre_clear($path){ 55 | if (ST=='share') { 56 | return str_replace(HOME,'',$path); 57 | } 58 | if (substr($path,0,strlen(PUBLIC_PATH)) == PUBLIC_PATH) { 59 | return '*public*/'.str_replace(PUBLIC_PATH,'',$path); 60 | } 61 | if (substr($path,0,strlen(USER_RECYCLE)) == USER_RECYCLE) { 62 | return '*recycle*/'.str_replace(USER_RECYCLE,'',$path); 63 | } 64 | return str_replace(HOME,'',$path); 65 | } 66 | function xxsClear(&$list){ 67 | if (is_array($list)) { 68 | foreach ($list['filelist'] as $key => $value) { 69 | $list['filelist'][$key]['ext'] = htmlspecial($value['ext']); 70 | $list['filelist'][$key]['path'] = htmlspecial($value['path']); 71 | $list['filelist'][$key]['name'] = htmlspecial($value['name']); 72 | } 73 | foreach ($list['folderlist'] as $key => $value) { 74 | $list['folderlist'][$key]['path'] = htmlspecial($value['path']); 75 | $list['folderlist'][$key]['name'] = htmlspecial($value['name']); 76 | } 77 | }else{ 78 | $list = htmlspecial($list); 79 | } 80 | } 81 | function htmlspecial($str){ 82 | return str_replace( 83 | array('<','>','"',"'"), 84 | array('<','>','"',''','&'), 85 | $str 86 | ); 87 | } 88 | function htmlspecial_decode($str){ 89 | return str_replace( 90 | array('<','>','"','''), 91 | array('<','>','"',"'"), 92 | $str 93 | ); 94 | } 95 | 96 | //扩展名权限判断 97 | function checkExtUnzip($s,$info){ 98 | return checkExt($info['stored_filename']); 99 | } 100 | //扩展名权限判断 有权限则返回1 不是true 101 | function checkExt($file,$changExt=false){ 102 | if (strstr($file,'<') || strstr($file,'>') || $file=='') { 103 | return 0; 104 | } 105 | if ($GLOBALS['is_root'] == 1) return 1; 106 | $not_allow = $GLOBALS['auth']['ext_not_allow']; 107 | $ext_arr = explode('|',$not_allow); 108 | foreach ($ext_arr as $current) { 109 | if ($current !== '' && stristr($file,'.'.$current)){//含有扩展名 110 | return 0; 111 | } 112 | } 113 | return 1; 114 | } 115 | 116 | 117 | function get_charset(&$str) { 118 | if ($str == '') return 'utf-8'; 119 | //前面检测成功则,自动忽略后面 120 | $charset=strtolower(mb_detect_encoding($str,$GLOBALS['config']['check_charset'])); 121 | if (substr($str,0,3)==chr(0xEF).chr(0xBB).chr(0xBF)){ 122 | $charset='utf-8'; 123 | }else if($charset=='cp936'){ 124 | $charset='gbk'; 125 | } 126 | if ($charset == 'ascii') $charset = 'utf-8'; 127 | return strtolower($charset); 128 | } 129 | 130 | function php_env_check(){ 131 | $L = $GLOBALS['L']; 132 | $error = ''; 133 | $base_path = get_path_this(BASIC_PATH).'/'; 134 | if(!function_exists('iconv')) $error.= '
  • '.$L['php_env_error_iconv'].'
  • '; 135 | if(!function_exists('mb_convert_encoding')) $error.= '
  • '.$L['php_env_error_mb_string'].'
  • '; 136 | if(!version_compare(PHP_VERSION,'5.0','>=')) $error.= '
  • '.$L['php_env_error_version'].'
  • '; 137 | if(!function_exists('file_get_contents')) $error.='
  • '.$L['php_env_error_file'].'
  • '; 138 | if(!path_writable(BASIC_PATH)) $error.= '
  • '.$base_path.' '.$L['php_env_error_path'].'
  • '; 139 | if(!path_writable(BASIC_PATH.'data')) $error.= '
  • '.$base_path.'data '.$L['php_env_error_path'].'
  • '; 140 | 141 | $parent = get_path_father(BASIC_PATH); 142 | $arr_check = array( 143 | BASIC_PATH, 144 | BASIC_PATH.'data', 145 | BASIC_PATH.'data/system', 146 | BASIC_PATH.'data/User', 147 | BASIC_PATH.'data/thumb', 148 | ); 149 | foreach ($arr_check as $value) { 150 | if(!path_writable($value)){ 151 | $error.= '
  • '.str_replace($parent,'',$value).'/ '.$L['php_env_error_path'].'
  • '; 152 | } 153 | } 154 | if( !function_exists('imagecreatefromjpeg')|| 155 | !function_exists('imagecreatefromgif')|| 156 | !function_exists('imagecreatefrompng')|| 157 | !function_exists('imagecolorallocate')){ 158 | $error.= '
  • '.$L['php_env_error_gd'].'
  • '; 159 | } 160 | return $error; 161 | } 162 | 163 | //语言包加载:优先级:cookie获取>自动识别 164 | //首次没有cookie则自动识别——存入cookie,过期时间无限 165 | function init_lang(){ 166 | if (isset($_COOKIE['kod_user_language'])) { 167 | $lang = $_COOKIE['kod_user_language']; 168 | }else{//没有cookie 169 | preg_match('/^([a-z\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches); 170 | $lang = $matches[1]; 171 | switch (substr($lang,0,2)) { 172 | case 'zh': 173 | if ($lang != 'zn-TW'){ 174 | $lang = 'zh-CN'; 175 | } 176 | break; 177 | case 'en':$lang = 'en';break; 178 | default:$lang = 'en';break; 179 | } 180 | $lang = str_replace('-', '_',$lang); 181 | setcookie('kod_user_language',$lang, time()+3600*24*365); 182 | } 183 | if ($lang == '') $lang = 'en'; 184 | 185 | $lang = str_replace(array('/','\\','..','.'),'',$lang); 186 | define('LANGUAGE_TYPE', $lang); 187 | include(LANGUAGE_PATH.$lang.'/main.php'); 188 | } 189 | 190 | function init_setting(){ 191 | $setting_file = USER_SYSTEM.'system_setting.php'; 192 | if (!file_exists($setting_file)){//不存在则建立 193 | $setting = $GLOBALS['config']['setting_system_default']; 194 | $setting['menu'] = $GLOBALS['config']['setting_menu_default']; 195 | fileCache::save($setting_file,$setting); 196 | }else{ 197 | $setting = fileCache::load($setting_file); 198 | } 199 | if (!is_array($setting)) { 200 | $setting = $GLOBALS['config']['setting_system_default']; 201 | } 202 | if (!is_array($setting['menu'])) { 203 | $setting['menu'] = $GLOBALS['config']['setting_menu_default']; 204 | } 205 | 206 | $GLOBALS['app']->setDefaultController($setting['first_in']);//设置默认控制器 207 | $GLOBALS['app']->setDefaultAction('index'); //设置默认控制器函数 208 | 209 | $GLOBALS['config']['setting_system'] = $setting;//全局 210 | $GLOBALS['L']['kod_name'] = $setting['system_name']; 211 | $GLOBALS['L']['kod_name_desc'] = $setting['system_desc']; 212 | if (isset($setting['powerby'])) { 213 | $GLOBALS['L']['kod_power_by'] = $setting['powerby']; 214 | } 215 | 216 | //加载用户自定义配置 217 | $setting_user = BASIC_PATH.'config/setting_user.php'; 218 | if (file_exists($setting_user)) { 219 | include($setting_user); 220 | } 221 | } 222 | //登陆是否需要验证码 223 | function need_check_code(){ 224 | if(!function_exists('imagecolorallocate')){ 225 | return false; 226 | }else{ 227 | return true; 228 | } 229 | } 230 | function is_wap(){ 231 | if(preg_match('/(up.browser|up.link|mmp|symbian|smartphone|midp|wap|phone|iphone|ipad|ipod|android|xoom)/i', 232 | strtolower($_SERVER['HTTP_USER_AGENT']))){ 233 | return true; 234 | } 235 | if((isset($_SERVER['HTTP_ACCEPT'])) && 236 | (strpos(strtolower($_SERVER['HTTP_ACCEPT']),'application/vnd.wap.xhtml+xml') !== false)){ 237 | return true; 238 | } 239 | return false; 240 | } 241 | function user_logout(){ 242 | setcookie('PHPSESSID', '', time()-3600,'/'); 243 | setcookie('kod_name', '', time()-3600); 244 | setcookie('kod_token', '', time()-3600); 245 | setcookie('kod_user_language', '', time()-3600); 246 | session_destroy(); 247 | header('location:./index.php?user/login'); 248 | exit; 249 | }?> -------------------------------------------------------------------------------- /code_test/5.php: -------------------------------------------------------------------------------- 1 | 10 | // +---------------------------------------------------------------------- 11 | namespace Think; 12 | /** 13 | * ThinkPHP内置模板引擎类 14 | * 支持XML标签和普通标签的模板解析 15 | * 编译型模板引擎 支持动态缓存 16 | */ 17 | class Template 18 | { 19 | 20 | // 模板页面中引入的标签库列表 21 | protected $tagLib = array(); 22 | // 当前模板文件 23 | protected $templateFile = array(); 24 | // 模板变量 25 | public $tVar = array(); 26 | public $config = array(); 27 | private $literal = array(); 28 | private $block = array(); 29 | 30 | /** 31 | * 架构函数 32 | * 33 | * @access public 34 | */ 35 | public function __construct() { 36 | $this->config['cache_path'] = C('CACHE_PATH'); 37 | $this->config['template_suffix'] = C('TMPL_TEMPLATE_SUFFIX'); 38 | $this->config['cache_suffix'] = C('TMPL_CACHFILE_SUFFIX'); 39 | $this->config['tmpl_cache'] = C('TMPL_CACHE_ON'); 40 | $this->config['cache_time'] = C('TMPL_CACHE_TIME'); 41 | $this->config['taglib_begin'] = $this->stripPreg(C('TAGLIB_BEGIN')); 42 | $this->config['taglib_end'] = $this->stripPreg(C('TAGLIB_END')); 43 | $this->config['tmpl_begin'] = $this->stripPreg(C('TMPL_L_DELIM')); 44 | $this->config['tmpl_end'] = $this->stripPreg(C('TMPL_R_DELIM')); 45 | $this->config['default_tmpl'] = C('TEMPLATE_NAME'); 46 | $this->config['layout_item'] = C('TMPL_LAYOUT_ITEM'); 47 | } 48 | 49 | private function stripPreg($str) { 50 | return str_replace( 51 | array('{', '}', '(', ')', '|', '[', ']', '-', '+', '*', '.', '^', '?'), 52 | array('\{', '\}', '\(', '\)', '\|', '\[', '\]', '\-', '\+', '\*', '\.', '\^', '\?'), 53 | $str); 54 | } 55 | 56 | // 模板变量获取和设置 57 | public function get($name) { 58 | if (isset($this->tVar[$name])) 59 | return $this->tVar[$name]; 60 | else 61 | return false; 62 | } 63 | 64 | public function set($name, $value) { 65 | $this->tVar[$name] = $value; 66 | } 67 | 68 | /** 69 | * 加载模板 70 | * 71 | * @access public 72 | * 73 | * @param string $tmplTemplateFile 模板文件 74 | * @param array $templateVar 模板变量 75 | * @param string $prefix 模板标识前缀 76 | * 77 | * @return void 78 | */ 79 | public function fetch($templateFile, $templateVar, $prefix = '') { 80 | $this->tVar = $templateVar; 81 | $templateCacheFile = $this->loadTemplate($templateFile, $prefix); 82 | Storage::load($templateCacheFile, $this->tVar, null, 'tpl'); 83 | } 84 | 85 | /** 86 | * 加载主模板并缓存 87 | * 88 | * @access public 89 | * 90 | * @param string $tmplTemplateFile 模板文件 91 | * @param string $prefix 模板标识前缀 92 | * 93 | * @return string 94 | * @throws ThinkExecption 95 | */ 96 | public function loadTemplate($tmplTemplateFile, $prefix = '') { 97 | if (is_file($tmplTemplateFile)) { 98 | $this->templateFile = $tmplTemplateFile; 99 | // 读取模板文件内容 100 | $tmplContent = file_get_contents($tmplTemplateFile); 101 | } else { 102 | $tmplContent = $tmplTemplateFile; 103 | } 104 | // 根据模版文件名定位缓存文件 105 | $tmplCacheFile = $this->config['cache_path'] . $prefix . md5($tmplTemplateFile) . $this->config['cache_suffix']; 106 | 107 | // 判断是否启用布局 108 | if (C('LAYOUT_ON')) { 109 | if (false !== strpos($tmplContent, '{__NOLAYOUT__}')) { // 可以单独定义不使用布局 110 | $tmplContent = str_replace('{__NOLAYOUT__}', '', $tmplContent); 111 | } else { // 替换布局的主体内容 112 | $layoutFile = THEME_PATH . C('LAYOUT_NAME') . $this->config['template_suffix']; 113 | $tmplContent = str_replace($this->config['layout_item'], $tmplContent, file_get_contents($layoutFile)); 114 | } 115 | } 116 | // 编译模板内容 117 | $tmplContent = $this->compiler($tmplContent); 118 | Storage::put($tmplCacheFile, trim($tmplContent), 'tpl'); 119 | return $tmplCacheFile; 120 | } 121 | 122 | /** 123 | * 编译模板文件内容 124 | * 125 | * @access protected 126 | * 127 | * @param mixed $tmplContent 模板内容 128 | * 129 | * @return string 130 | */ 131 | protected function compiler($tmplContent) { 132 | //模板解析 133 | $tmplContent = $this->parse($tmplContent); 134 | // 还原被替换的Literal标签 135 | $tmplContent = preg_replace_callback('//is', array($this, 'restoreLiteral'), $tmplContent); 136 | // 添加安全代码 137 | $tmplContent = '' . $tmplContent; 138 | // 优化生成的php代码 139 | $tmplContent = str_replace('?>config['taglib_begin']; 159 | $end = $this->config['taglib_end']; 160 | // 检查include语法 161 | $content = $this->parseInclude($content); 162 | // 检查PHP语法 163 | $content = $this->parsePhp($content); 164 | // 首先替换literal标签内容 165 | $content = preg_replace_callback('/' . $begin . 'literal' . $end . '(.*?)' . $begin . '\/literal' . $end . '/is', array($this, 'parseLiteral'), $content); 166 | 167 | // 获取需要引入的标签库列表 168 | // 标签库只需要定义一次,允许引入多个一次 169 | // 一般放在文件的最前面 170 | // 格式: 171 | // 当TAGLIB_LOAD配置为true时才会进行检测 172 | if (C('TAGLIB_LOAD')) { 173 | $this->getIncludeTagLib($content); 174 | if (!empty($this->tagLib)) { 175 | // 对导入的TagLib进行解析 176 | foreach ($this->tagLib as $tagLibName) { 177 | $this->parseTagLib($tagLibName, $content); 178 | } 179 | } 180 | } 181 | // 预先加载的标签库 无需在每个模板中使用taglib标签加载 但必须使用标签库XML前缀 182 | if (C('TAGLIB_PRE_LOAD')) { 183 | $tagLibs = explode(',', C('TAGLIB_PRE_LOAD')); 184 | foreach ($tagLibs as $tag) { 185 | $this->parseTagLib($tag, $content); 186 | } 187 | } 188 | // 内置标签库 无需使用taglib标签导入就可以使用 并且不需使用标签库XML前缀 189 | $tagLibs = explode(',', C('TAGLIB_BUILD_IN')); 190 | foreach ($tagLibs as $tag) { 191 | $this->parseTagLib($tag, $content, true); 192 | } 193 | //解析普通模板标签 {tagName} 194 | $content = preg_replace_callback('/(' . $this->config['tmpl_begin'] . ')([^\d\w\s' . $this->config['tmpl_begin'] . $this->config['tmpl_end'] . '].+?)(' . $this->config['tmpl_end'] . ')/is', array($this, 'parseTag'), $content); 195 | return $content; 196 | } 197 | 198 | // 检查PHP语法 199 | protected function parsePhp($content) { 200 | if (ini_get('short_open_tag')) { 201 | // 开启短标签的情况要将' . "\n", $content); 203 | } 204 | // PHP语法检查 205 | if (C('TMPL_DENY_PHP') && false !== strpos($content, 'config['taglib_begin'] . 'layout\s(.+?)\s*?\/' . $this->config['taglib_end'] . '/is', $content, $matches); 215 | if ($find) { 216 | //替换Layout标签 217 | $content = str_replace($matches[0], '', $content); 218 | //解析Layout标签 219 | $array = $this->parseXmlAttrs($matches[1]); 220 | if (!C('LAYOUT_ON') || C('LAYOUT_NAME') != $array['name']) { 221 | // 读取布局模板 222 | $layoutFile = THEME_PATH . $array['name'] . $this->config['template_suffix']; 223 | $replace = isset($array['replace']) ? $array['replace'] : $this->config['layout_item']; 224 | // 替换布局的主体内容 225 | $content = str_replace($replace, $content, file_get_contents($layoutFile)); 226 | } 227 | } else { 228 | $content = str_replace('{__NOLAYOUT__}', '', $content); 229 | } 230 | return $content; 231 | } 232 | 233 | // 解析模板中的include标签 234 | protected function parseInclude($content, $extend = true) { 235 | // 解析继承 236 | if ($extend) 237 | $content = $this->parseExtend($content); 238 | // 解析布局 239 | $content = $this->parseLayout($content); 240 | // 读取模板中的include标签 241 | $find = preg_match_all('/' . $this->config['taglib_begin'] . 'include\s(.+?)\s*?\/' . $this->config['taglib_end'] . '/is', $content, $matches); 242 | if ($find) { 243 | for ($i = 0; $i < $find; $i++) { 244 | $include = $matches[1][$i]; 245 | $array = $this->parseXmlAttrs($include); 246 | $file = $array['file']; 247 | unset($array['file']); 248 | $content = str_replace($matches[0][$i], $this->parseIncludeItem($file, $array, $extend), $content); 249 | } 250 | } 251 | return $content; 252 | } 253 | 254 | // 解析模板中的extend标签 255 | protected function parseExtend($content) { 256 | $begin = $this->config['taglib_begin']; 257 | $end = $this->config['taglib_end']; 258 | // 读取模板中的继承标签 259 | $find = preg_match('/' . $begin . 'extend\s(.+?)\s*?\/' . $end . '/is', $content, $matches); 260 | if ($find) { 261 | //替换extend标签 262 | $content = str_replace($matches[0], '', $content); 263 | // 记录页面中的block标签 264 | preg_replace_callback('/' . $begin . 'block\sname=[\'"](.+?)[\'"]\s*?' . $end . '(.*?)' . $begin . '\/block' . $end . '/is', array($this, 'parseBlock'), $content); 265 | // 读取继承模板 266 | $array = $this->parseXmlAttrs($matches[1]); 267 | $content = $this->parseTemplateName($array['name']); 268 | $content = $this->parseInclude($content, false); //对继承模板中的include进行分析 269 | // 替换block标签 270 | $content = $this->replaceBlock($content); 271 | } else { 272 | $content = preg_replace_callback('/' . $begin . 'block\sname=[\'"](.+?)[\'"]\s*?' . $end . '(.*?)' . $begin . '\/block' . $end . '/is', function ($match) { 273 | return stripslashes($match[2]); 274 | }, $content); 275 | } 276 | return $content; 277 | } 278 | 279 | /** 280 | * 分析XML属性 281 | * 282 | * @access private 283 | * 284 | * @param string $attrs XML属性字符串 285 | * 286 | * @return array 287 | */ 288 | private function parseXmlAttrs($attrs) { 289 | $xml = ''; 290 | $xml = simplexml_load_string($xml); 291 | if (!$xml) 292 | E(L('_XML_TAG_ERROR_')); 293 | $xml = (array)($xml->tag->attributes()); 294 | $array = array_change_key_case($xml['@attributes']); 295 | return $array; 296 | } 297 | 298 | /** 299 | * 替换页面中的literal标签 300 | * 301 | * @access private 302 | * 303 | * @param string $content 模板内容 304 | * 305 | * @return string|false 306 | */ 307 | private function parseLiteral($content) { 308 | if (is_array($content)) $content = $content[1]; 309 | if (trim($content) == '') return ''; 310 | //$content = stripslashes($content); 311 | $i = count($this->literal); 312 | $parseStr = ""; 313 | $this->literal[$i] = $content; 314 | return $parseStr; 315 | } 316 | 317 | /** 318 | * 还原被替换的literal标签 319 | * 320 | * @access private 321 | * 322 | * @param string $tag literal标签序号 323 | * 324 | * @return string|false 325 | */ 326 | private function restoreLiteral($tag) { 327 | if (is_array($tag)) $tag = $tag[1]; 328 | // 还原literal标签 329 | $parseStr = $this->literal[$tag]; 330 | // 销毁literal记录 331 | unset($this->literal[$tag]); 332 | return $parseStr; 333 | } 334 | 335 | /** 336 | * 记录当前页面中的block标签 337 | * 338 | * @access private 339 | * 340 | * @param string $name block名称 341 | * @param string $content 模板内容 342 | * 343 | * @return string 344 | */ 345 | private function parseBlock($name, $content = '') { 346 | if (is_array($name)) { 347 | $content = $name[2]; 348 | $name = $name[1]; 349 | } 350 | $this->block[$name] = $content; 351 | return ''; 352 | } 353 | 354 | /** 355 | * 替换继承模板中的block标签 356 | * 357 | * @access private 358 | * 359 | * @param string $content 模板内容 360 | * 361 | * @return string 362 | */ 363 | private function replaceBlock($content) { 364 | static $parse = 0; 365 | $begin = $this->config['taglib_begin']; 366 | $end = $this->config['taglib_end']; 367 | $reg = '/(' . $begin . 'block\sname=[\'"](.+?)[\'"]\s*?' . $end . ')(.*?)' . $begin . '\/block' . $end . '/is'; 368 | if (is_string($content)) { 369 | do { 370 | $content = preg_replace_callback($reg, array($this, 'replaceBlock'), $content); 371 | } while ($parse && $parse--); 372 | return $content; 373 | } elseif (is_array($content)) { 374 | if (preg_match('/' . $begin . 'block\sname=[\'"](.+?)[\'"]\s*?' . $end . '/is', $content[3])) { //存在嵌套,进一步解析 375 | $parse = 1; 376 | $content[3] = preg_replace_callback($reg, array($this, 'replaceBlock'), "{$content[3]}{$begin}/block{$end}"); 377 | return $content[1] . $content[3]; 378 | } else { 379 | $name = $content[2]; 380 | $content = $content[3]; 381 | $content = isset($this->block[$name]) ? $this->block[$name] : $content; 382 | return $content; 383 | } 384 | } 385 | } 386 | 387 | /** 388 | * 搜索模板页面中包含的TagLib库 389 | * 并返回列表 390 | * 391 | * @access public 392 | * 393 | * @param string $content 模板内容 394 | * 395 | * @return string|false 396 | */ 397 | public function getIncludeTagLib(& $content) { 398 | //搜索是否有TagLib标签 399 | $find = preg_match('/' . $this->config['taglib_begin'] . 'taglib\s(.+?)(\s*?)\/' . $this->config['taglib_end'] . '\W/is', $content, $matches); 400 | if ($find) { 401 | //替换TagLib标签 402 | $content = str_replace($matches[0], '', $content); 403 | //解析TagLib标签 404 | $array = $this->parseXmlAttrs($matches[1]); 405 | $this->tagLib = explode(',', $array['name']); 406 | } 407 | return; 408 | } 409 | 410 | /** 411 | * TagLib库解析 412 | * 413 | * @access public 414 | * 415 | * @param string $tagLib 要解析的标签库 416 | * @param string $content 要解析的模板内容 417 | * @param boolen $hide 是否隐藏标签库前缀 418 | * 419 | * @return string 420 | */ 421 | public function parseTagLib($tagLib, &$content, $hide = false) { 422 | $begin = $this->config['taglib_begin']; 423 | $end = $this->config['taglib_end']; 424 | if (strpos($tagLib, '\\')) { 425 | // 支持指定标签库的命名空间 426 | $className = $tagLib; 427 | $tagLib = substr($tagLib, strrpos($tagLib, '\\') + 1); 428 | } else { 429 | $className = 'Think\\Template\TagLib\\' . ucwords($tagLib); 430 | } 431 | $tLib = \Think\Think::instance($className); 432 | $that = $this; 433 | foreach ($tLib->getTags() as $name => $val) { 434 | $tags = array($name); 435 | if (isset($val['alias'])) {// 别名设置 436 | $tags = explode(',', $val['alias']); 437 | $tags[] = $name; 438 | } 439 | $level = isset($val['level']) ? $val['level'] : 1; 440 | $closeTag = isset($val['close']) ? $val['close'] : true; 441 | foreach ($tags as $tag) { 442 | $parseTag = !$hide ? $tagLib . ':' . $tag : $tag;// 实际要解析的标签名称 443 | if (!method_exists($tLib, '_' . $tag)) { 444 | // 别名可以无需定义解析方法 445 | $tag = $name; 446 | } 447 | $n1 = empty($val['attr']) ? '(\s*?)' : '\s([^' . $end . ']*)'; 448 | $this->tempVar = array($tagLib, $tag); 449 | 450 | if (!$closeTag) { 451 | $patterns = '/' . $begin . $parseTag . $n1 . '\/(\s*?)' . $end . '/is'; 452 | $content = preg_replace_callback($patterns, function ($matches) use ($tLib, $tag, $that) { 453 | return $that->parseXmlTag($tLib, $tag, $matches[1], $matches[2]); 454 | }, $content); 455 | } else { 456 | $patterns = '/' . $begin . $parseTag . $n1 . $end . '(.*?)' . $begin . '\/' . $parseTag . '(\s*?)' . $end . '/is'; 457 | for ($i = 0; $i < $level; $i++) { 458 | $content = preg_replace_callback($patterns, function ($matches) use ($tLib, $tag, $that) { 459 | return $that->parseXmlTag($tLib, $tag, $matches[1], $matches[2]); 460 | }, $content); 461 | } 462 | } 463 | } 464 | } 465 | } 466 | 467 | /** 468 | * 解析标签库的标签 469 | * 需要调用对应的标签库文件解析类 470 | * 471 | * @access public 472 | * 473 | * @param object $tagLib 标签库对象实例 474 | * @param string $tag 标签名 475 | * @param string $attr 标签属性 476 | * @param string $content 标签内容 477 | * 478 | * @return string|false 479 | */ 480 | public function parseXmlTag($tagLib, $tag, $attr, $content) { 481 | if (ini_get('magic_quotes_sybase')) 482 | $attr = str_replace('\"', '\'', $attr); 483 | $parse = '_' . $tag; 484 | $content = trim($content); 485 | $tags = $tagLib->parseXmlAttr($attr, $tag); 486 | return $tagLib->$parse($tags, $content); 487 | } 488 | 489 | /** 490 | * 模板标签解析 491 | * 格式: {TagName:args [|content] } 492 | * 493 | * @access public 494 | * 495 | * @param string $tagStr 标签内容 496 | * 497 | * @return string 498 | */ 499 | public function parseTag($tagStr) { 500 | if (is_array($tagStr)) $tagStr = $tagStr[2]; 501 | //if (MAGIC_QUOTES_GPC) { 502 | $tagStr = stripslashes($tagStr); 503 | //} 504 | $flag = substr($tagStr, 0, 1); 505 | $flag2 = substr($tagStr, 1, 1); 506 | $name = substr($tagStr, 1); 507 | if ('$' == $flag && '.' != $flag2 && '(' != $flag2) { //解析模板变量 格式 {$varName} 508 | return $this->parseVar($name); 509 | } elseif ('-' == $flag || '+' == $flag) { // 输出计算 510 | return ''; 511 | } elseif (':' == $flag) { // 输出某个函数的结果 512 | return ''; 513 | } elseif ('~' == $flag) { // 执行某个函数 514 | return ''; 515 | } elseif (substr($tagStr, 0, 2) == '//' || (substr($tagStr, 0, 2) == '/*' && substr(rtrim($tagStr), -2) == '*/')) { 516 | //注释标签 517 | return ''; 518 | } 519 | // 未识别的标签直接返回 520 | return C('TMPL_L_DELIM') . $tagStr . C('TMPL_R_DELIM'); 521 | } 522 | 523 | /** 524 | * 模板变量解析,支持使用函数 525 | * 格式: {$varname|function1|function2=arg1,arg2} 526 | * 527 | * @access public 528 | * 529 | * @param string $varStr 变量数据 530 | * 531 | * @return string 532 | */ 533 | public function parseVar($varStr) { 534 | $varStr = trim($varStr); 535 | static $_varParseList = array(); 536 | //如果已经解析过该变量字串,则直接返回变量值 537 | if (isset($_varParseList[$varStr])) return $_varParseList[$varStr]; 538 | $parseStr = ''; 539 | $varExists = true; 540 | if (!empty($varStr)) { 541 | $varArray = explode('|', $varStr); 542 | //取得变量名称 543 | $var = array_shift($varArray); 544 | if ('Think.' == substr($var, 0, 6)) { 545 | // 所有以Think.打头的以特殊变量对待 无需模板赋值就可以输出 546 | $name = $this->parseThinkVar($var); 547 | } elseif (false !== strpos($var, '.')) { 548 | //支持 {$var.property} 549 | $vars = explode('.', $var); 550 | $var = array_shift($vars); 551 | switch (strtolower(C('TMPL_VAR_IDENTIFY'))) { 552 | case 'array': // 识别为数组 553 | $name = '$' . $var; 554 | foreach ($vars as $key => $val) 555 | $name .= '["' . $val . '"]'; 556 | break; 557 | case 'obj': // 识别为对象 558 | $name = '$' . $var; 559 | foreach ($vars as $key => $val) 560 | $name .= '->' . $val; 561 | break; 562 | default: // 自动判断数组或对象 只支持二维 563 | $name = 'is_array($' . $var . ')?$' . $var . '["' . $vars[0] . '"]:$' . $var . '->' . $vars[0]; 564 | } 565 | } elseif (false !== strpos($var, '[')) { 566 | //支持 {$var['key']} 方式输出数组 567 | $name = "$" . $var; 568 | preg_match('/(.+?)\[(.+?)\]/is', $var, $match); 569 | $var = $match[1]; 570 | } elseif (false !== strpos($var, ':') && false === strpos($var, '(') && false === strpos($var, '::') && false === strpos($var, '?')) { 571 | //支持 {$var:property} 方式输出对象的属性 572 | $vars = explode(':', $var); 573 | $var = str_replace(':', '->', $var); 574 | $name = "$" . $var; 575 | $var = $vars[0]; 576 | } else { 577 | $name = "$$var"; 578 | } 579 | //对变量使用函数 580 | if (count($varArray) > 0) 581 | $name = $this->parseVarFunction($name, $varArray); 582 | $parseStr = ''; 583 | } 584 | $_varParseList[$varStr] = $parseStr; 585 | return $parseStr; 586 | } 587 | 588 | /** 589 | * 对模板变量使用函数 590 | * 格式 {$varname|function1|function2=arg1,arg2} 591 | * 592 | * @access public 593 | * 594 | * @param string $name 变量名 595 | * @param array $varArray 函数列表 596 | * 597 | * @return string 598 | */ 599 | public function parseVarFunction($name, $varArray) { 600 | //对变量使用函数 601 | $length = count($varArray); 602 | //取得模板禁止使用函数列表 603 | $template_deny_funs = explode(',', C('TMPL_DENY_FUNC_LIST')); 604 | for ($i = 0; $i < $length; $i++) { 605 | $args = explode('=', $varArray[$i], 2); 606 | //模板函数过滤 607 | $fun = strtolower(trim($args[0])); 608 | switch ($fun) { 609 | case 'default': // 特殊模板函数 610 | $name = '(isset(' . $name . ') && (' . $name . ' !== ""))?(' . $name . '):' . $args[1]; 611 | break; 612 | default: // 通用模板函数 613 | if (!in_array($fun, $template_deny_funs)) { 614 | if (isset($args[1])) { 615 | if (strstr($args[1], '###')) { 616 | $args[1] = str_replace('###', $name, $args[1]); 617 | $name = "$fun($args[1])"; 618 | } else { 619 | $name = "$fun($name,$args[1])"; 620 | } 621 | } else if (!empty($args[0])) { 622 | $name = "$fun($name)"; 623 | } 624 | } 625 | } 626 | } 627 | return $name; 628 | } 629 | 630 | /** 631 | * 特殊模板变量解析 632 | * 格式 以 $Think. 打头的变量属于特殊模板变量 633 | * 634 | * @access public 635 | * 636 | * @param string $varStr 变量字符串 637 | * 638 | * @return string 639 | */ 640 | public function parseThinkVar($varStr) { 641 | $vars = explode('.', $varStr); 642 | $vars[1] = strtoupper(trim($vars[1])); 643 | $parseStr = ''; 644 | if (count($vars) >= 3) { 645 | $vars[2] = trim($vars[2]); 646 | switch ($vars[1]) { 647 | case 'SERVER': 648 | $parseStr = '$_SERVER[\'' . strtoupper($vars[2]) . '\']'; 649 | break; 650 | case 'GET': 651 | $parseStr = '$_GET[\'' . $vars[2] . '\']'; 652 | break; 653 | case 'POST': 654 | $parseStr = '$_POST[\'' . $vars[2] . '\']'; 655 | break; 656 | case 'COOKIE': 657 | if (isset($vars[3])) { 658 | $parseStr = '$_COOKIE[\'' . $vars[2] . '\'][\'' . $vars[3] . '\']'; 659 | } else { 660 | $parseStr = 'cookie(\'' . $vars[2] . '\')'; 661 | } 662 | break; 663 | case 'SESSION': 664 | if (isset($vars[3])) { 665 | $parseStr = '$_SESSION[\'' . $vars[2] . '\'][\'' . $vars[3] . '\']'; 666 | } else { 667 | $parseStr = 'session(\'' . $vars[2] . '\')'; 668 | } 669 | break; 670 | case 'ENV': 671 | $parseStr = '$_ENV[\'' . strtoupper($vars[2]) . '\']'; 672 | break; 673 | case 'REQUEST': 674 | $parseStr = '$_REQUEST[\'' . $vars[2] . '\']'; 675 | break; 676 | case 'CONST': 677 | $parseStr = strtoupper($vars[2]); 678 | break; 679 | case 'LANG': 680 | $parseStr = 'L("' . $vars[2] . '")'; 681 | break; 682 | case 'CONFIG': 683 | if (isset($vars[3])) { 684 | $vars[2] .= '.' . $vars[3]; 685 | } 686 | $parseStr = 'C("' . $vars[2] . '")'; 687 | break; 688 | default: 689 | break; 690 | } 691 | } else if (count($vars) == 2) { 692 | switch ($vars[1]) { 693 | case 'NOW': 694 | $parseStr = "date('Y-m-d g:i a',time())"; 695 | break; 696 | case 'VERSION': 697 | $parseStr = 'THINK_VERSION'; 698 | break; 699 | case 'TEMPLATE': 700 | $parseStr = "'" . $this->templateFile . "'";//'C("TEMPLATE_NAME")'; 701 | break; 702 | case 'LDELIM': 703 | $parseStr = 'C("TMPL_L_DELIM")'; 704 | break; 705 | case 'RDELIM': 706 | $parseStr = 'C("TMPL_R_DELIM")'; 707 | break; 708 | default: 709 | if (defined($vars[1])) 710 | $parseStr = $vars[1]; 711 | } 712 | } 713 | return $parseStr; 714 | } 715 | 716 | /** 717 | * 加载公共模板并缓存 和当前模板在同一路径,否则使用相对路径 718 | * 719 | * @access private 720 | * 721 | * @param string $tmplPublicName 公共模板文件名 722 | * @param array $vars 要传递的变量列表 723 | * 724 | * @return string 725 | */ 726 | private function parseIncludeItem($tmplPublicName, $vars = array(), $extend) { 727 | // 分析模板文件名并读取内容 728 | $parseStr = $this->parseTemplateName($tmplPublicName); 729 | // 替换变量 730 | foreach ($vars as $key => $val) { 731 | $parseStr = str_replace('[' . $key . ']', $val, $parseStr); 732 | } 733 | // 再次对包含文件进行模板分析 734 | return $this->parseInclude($parseStr, $extend); 735 | } 736 | 737 | /** 738 | * 分析加载的模板文件并读取内容 支持多个模板文件读取 739 | * 740 | * @access private 741 | * 742 | * @param string $tmplPublicName 模板文件名 743 | * 744 | * @return string 745 | */ 746 | private function parseTemplateName($templateName) { 747 | if (substr($templateName, 0, 1) == '$') 748 | //支持加载变量文件名 749 | $templateName = $this->get(substr($templateName, 1)); 750 | $array = explode(',', $templateName); 751 | $parseStr = ''; 752 | foreach ($array as $templateName) { 753 | if (empty($templateName)) continue; 754 | if (false === strpos($templateName, $this->config['template_suffix'])) { 755 | // 解析规则为 模块@主题/控制器/操作 756 | $templateName = T($templateName); 757 | } 758 | // 获取模板文件内容 759 | $parseStr .= file_get_contents($templateName); 760 | } 761 | return $parseStr; 762 | } 763 | } 764 | 765 | echo 'success'; 766 | 767 | ?> -------------------------------------------------------------------------------- /code_test/7.php: -------------------------------------------------------------------------------- 1 | 10 | // +---------------------------------------------------------------------- 11 | namespace Think; 12 | /** 13 | * ThinkPHP 数据库中间层实现类 14 | */ 15 | class Db { 16 | // 数据库类型 17 | protected $dbType = null; 18 | // 是否自动释放查询结果 19 | protected $autoFree = false; 20 | // 当前操作所属的模型名 21 | protected $model = '_think_'; 22 | // 是否使用永久连接 23 | protected $pconnect = false; 24 | // 当前SQL指令 25 | protected $queryStr = ''; 26 | protected $modelSql = array(); 27 | // 最后插入ID 28 | protected $lastInsID = null; 29 | // 返回或者影响记录数 30 | protected $numRows = 0; 31 | // 返回字段数 32 | protected $numCols = 0; 33 | // 事务指令数 34 | protected $transTimes = 0; 35 | // 错误信息 36 | protected $error = ''; 37 | // 数据库连接ID 支持多个连接 38 | protected $linkID = array(); 39 | // 当前连接ID 40 | protected $_linkID = null; 41 | // 当前查询ID 42 | protected $queryID = null; 43 | // 数据库连接参数配置 44 | protected $config = ''; 45 | // 数据库表达式 46 | protected $exp = array('eq'=>'=','neq'=>'<>','gt'=>'>','egt'=>'>=','lt'=>'<','elt'=>'<=','notlike'=>'NOT LIKE','like'=>'LIKE','in'=>'IN','notin'=>'NOT IN','not in'=>'NOT IN','between'=>'BETWEEN','notbetween'=>'NOT BETWEEN','not between'=>'NOT BETWEEN'); 47 | // 查询表达式 48 | protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%COMMENT%'; 49 | // 参数绑定 50 | protected $bind = array(); 51 | 52 | /** 53 | * 取得数据库类实例 54 | * @static 55 | * @access public 56 | * @return mixed 返回数据库驱动类 57 | */ 58 | public static function getInstance($db_config='') { 59 | static $_instance = array(); 60 | $guid = to_guid_string($db_config); 61 | if(!isset($_instance[$guid])){ 62 | $obj = new Db(); 63 | $_instance[$guid] = $obj->factory($db_config); 64 | } 65 | return $_instance[$guid]; 66 | } 67 | 68 | /** 69 | * 加载数据库 支持配置文件或者 DSN 70 | * @access public 71 | * @param mixed $db_config 数据库配置信息 72 | * @return string 73 | */ 74 | public function factory($db_config='') { 75 | // 读取数据库配置 76 | $db_config = $this->parseConfig($db_config); 77 | if(empty($db_config['dbms'])) 78 | E(L('_NO_DB_CONFIG_')); 79 | // 数据库类型 80 | if(strpos($db_config['dbms'],'\\')){ 81 | $class = $db_config['dbms']; 82 | }else{ 83 | $dbType = ucwords(strtolower($db_config['dbms'])); 84 | $class = 'Think\\Db\\Driver\\'. $dbType; 85 | } 86 | // 检查驱动类 87 | if(class_exists($class)) { 88 | $db = new $class($db_config); 89 | }else { 90 | // 类没有定义 91 | E(L('_NO_DB_DRIVER_').': ' . $class); 92 | } 93 | return $db; 94 | } 95 | 96 | /** 97 | * 根据DSN获取数据库类型 返回大写 98 | * @access protected 99 | * @param string $dsn dsn字符串 100 | * @return string 101 | */ 102 | protected function _getDsnType($dsn) { 103 | $match = explode(':',$dsn); 104 | $dbType = strtoupper(trim($match[0])); 105 | return $dbType; 106 | } 107 | 108 | /** 109 | * 分析数据库配置信息,支持数组和DSN 110 | * @access private 111 | * @param mixed $db_config 数据库配置信息 112 | * @return string 113 | */ 114 | private function parseConfig($db_config='') { 115 | if ( !empty($db_config) && is_string($db_config)) { 116 | // 如果DSN字符串则进行解析 117 | $db_config = $this->parseDSN($db_config); 118 | }elseif(is_array($db_config)) { // 数组配置 119 | $db_config = array_change_key_case($db_config); 120 | $db_config = array( 121 | 'dbms' => $db_config['db_type'], 122 | 'username' => $db_config['db_user'], 123 | 'password' => $db_config['db_pwd'], 124 | 'hostname' => $db_config['db_host'], 125 | 'hostport' => $db_config['db_port'], 126 | 'database' => $db_config['db_name'], 127 | 'dsn' => isset($db_config['db_dsn'])?$db_config['db_dsn']:'', 128 | 'params' => isset($db_config['db_params'])?$db_config['db_params']:array(), 129 | 'charset' => isset($db_config['db_charset'])?$db_config['db_charset']:'utf8', 130 | ); 131 | }elseif(empty($db_config)) { 132 | // 如果配置为空,读取配置文件设置 133 | if( C('DB_DSN') && 'pdo' != strtolower(C('DB_TYPE')) ) { // 如果设置了DB_DSN 则优先 134 | $db_config = $this->parseDSN(C('DB_DSN')); 135 | }else{ 136 | $db_config = array ( 137 | 'dbms' => C('DB_TYPE'), 138 | 'username' => C('DB_USER'), 139 | 'password' => C('DB_PWD'), 140 | 'hostname' => C('DB_HOST'), 141 | 'hostport' => C('DB_PORT'), 142 | 'database' => C('DB_NAME'), 143 | 'dsn' => C('DB_DSN'), 144 | 'params' => C('DB_PARAMS'), 145 | 'charset' => C('DB_CHARSET'), 146 | ); 147 | } 148 | } 149 | return $db_config; 150 | } 151 | 152 | /** 153 | * 初始化数据库连接 154 | * @access protected 155 | * @param boolean $master 主服务器 156 | * @return void 157 | */ 158 | protected function initConnect($master=true) { 159 | if(1 == C('DB_DEPLOY_TYPE')) 160 | // 采用分布式数据库 161 | $this->_linkID = $this->multiConnect($master); 162 | else 163 | // 默认单数据库 164 | if ( !$this->_linkID ) $this->_linkID = $this->connect(); 165 | } 166 | 167 | /** 168 | * 连接分布式服务器 169 | * @access protected 170 | * @param boolean $master 主服务器 171 | * @return void 172 | */ 173 | protected function multiConnect($master=false) { 174 | foreach ($this->config as $key=>$val){ 175 | $_config[$key] = explode(',',$val); 176 | } 177 | // 数据库读写是否分离 178 | if(C('DB_RW_SEPARATE')){ 179 | // 主从式采用读写分离 180 | if($master) 181 | // 主服务器写入 182 | $r = floor(mt_rand(0,C('DB_MASTER_NUM')-1)); 183 | else{ 184 | if(is_numeric(C('DB_SLAVE_NO'))) {// 指定服务器读 185 | $r = C('DB_SLAVE_NO'); 186 | }else{ 187 | // 读操作连接从服务器 188 | $r = floor(mt_rand(C('DB_MASTER_NUM'),count($_config['hostname'])-1)); // 每次随机连接的数据库 189 | } 190 | } 191 | }else{ 192 | // 读写操作不区分服务器 193 | $r = floor(mt_rand(0,count($_config['hostname'])-1)); // 每次随机连接的数据库 194 | } 195 | $db_config = array( 196 | 'username' => isset($_config['username'][$r])?$_config['username'][$r]:$_config['username'][0], 197 | 'password' => isset($_config['password'][$r])?$_config['password'][$r]:$_config['password'][0], 198 | 'hostname' => isset($_config['hostname'][$r])?$_config['hostname'][$r]:$_config['hostname'][0], 199 | 'hostport' => isset($_config['hostport'][$r])?$_config['hostport'][$r]:$_config['hostport'][0], 200 | 'database' => isset($_config['database'][$r])?$_config['database'][$r]:$_config['database'][0], 201 | 'dsn' => isset($_config['dsn'][$r])?$_config['dsn'][$r]:$_config['dsn'][0], 202 | 'params' => isset($_config['params'][$r])?$_config['params'][$r]:$_config['params'][0], 203 | 'charset' => isset($_config['charset'][$r])?$_config['charset'][$r]:$_config['charset'][0], 204 | ); 205 | return $this->connect($db_config,$r); 206 | } 207 | 208 | /** 209 | * DSN解析 210 | * 格式: mysql://username:passwd@localhost:3306/DbName#charset 211 | * @static 212 | * @access public 213 | * @param string $dsnStr 214 | * @return array 215 | */ 216 | public function parseDSN($dsnStr) { 217 | if( empty($dsnStr) ){return false;} 218 | $info = parse_url($dsnStr); 219 | if($info['scheme']){ 220 | $dsn = array( 221 | 'dbms' => $info['scheme'], 222 | 'username' => isset($info['user']) ? $info['user'] : '', 223 | 'password' => isset($info['pass']) ? $info['pass'] : '', 224 | 'hostname' => isset($info['host']) ? $info['host'] : '', 225 | 'hostport' => isset($info['port']) ? $info['port'] : '', 226 | 'database' => isset($info['path']) ? substr($info['path'],1) : '', 227 | 'charset' => isset($info['fragment'])?$info['fragment']:'utf8', 228 | ); 229 | }else { 230 | preg_match('/^(.*?)\:\/\/(.*?)\:(.*?)\@(.*?)\:([0-9]{1, 6})\/(.*?)$/',trim($dsnStr),$matches); 231 | $dsn = array ( 232 | 'dbms' => $matches[1], 233 | 'username' => $matches[2], 234 | 'password' => $matches[3], 235 | 'hostname' => $matches[4], 236 | 'hostport' => $matches[5], 237 | 'database' => $matches[6] 238 | ); 239 | } 240 | $dsn['dsn'] = ''; // 兼容配置信息数组 241 | return $dsn; 242 | } 243 | 244 | /** 245 | * 数据库调试 记录当前SQL 246 | * @access protected 247 | */ 248 | protected function debug() { 249 | $this->modelSql[$this->model] = $this->queryStr; 250 | $this->model = '_think_'; 251 | // 记录操作结束时间 252 | if (C('DB_SQL_LOG')) { 253 | G('queryEndTime'); 254 | trace($this->queryStr.' [ RunTime:'.G('queryStartTime','queryEndTime',6).'s ]','','SQL'); 255 | } 256 | } 257 | 258 | /** 259 | * 设置锁机制 260 | * @access protected 261 | * @return string 262 | */ 263 | protected function parseLock($lock=false) { 264 | if(!$lock) return ''; 265 | if('ORACLE' == $this->dbType) { 266 | return ' FOR UPDATE NOWAIT '; 267 | } 268 | return ' FOR UPDATE '; 269 | } 270 | 271 | /** 272 | * set分析 273 | * @access protected 274 | * @param array $data 275 | * @return string 276 | */ 277 | protected function parseSet($data) { 278 | foreach ($data as $key=>$val){ 279 | if(is_array($val) && 'exp' == $val[0]){ 280 | $set[] = $this->parseKey($key).'='.$val[1]; 281 | }elseif(is_scalar($val) || is_null($val)) { // 过滤非标量数据 282 | if(C('DB_BIND_PARAM') && 0 !== strpos($val,':')){ 283 | $name = md5($key); 284 | $set[] = $this->parseKey($key).'=:'.$name; 285 | $this->bindParam($name,$val); 286 | }else{ 287 | $set[] = $this->parseKey($key).'='.$this->parseValue($val); 288 | } 289 | } 290 | } 291 | return ' SET '.implode(',',$set); 292 | } 293 | 294 | /** 295 | * 参数绑定 296 | * @access protected 297 | * @param string $name 绑定参数名 298 | * @param mixed $value 绑定值 299 | * @return void 300 | */ 301 | protected function bindParam($name,$value){ 302 | $this->bind[':'.$name] = $value; 303 | } 304 | 305 | /** 306 | * 参数绑定分析 307 | * @access protected 308 | * @param array $bind 309 | * @return array 310 | */ 311 | protected function parseBind($bind){ 312 | $bind = array_merge($this->bind,$bind); 313 | $this->bind = array(); 314 | return $bind; 315 | } 316 | 317 | /** 318 | * 字段名分析 319 | * @access protected 320 | * @param string $key 321 | * @return string 322 | */ 323 | protected function parseKey(&$key) { 324 | return $key; 325 | } 326 | 327 | /** 328 | * value分析 329 | * @access protected 330 | * @param mixed $value 331 | * @return string 332 | */ 333 | protected function parseValue($value) { 334 | if(is_string($value)) { 335 | $value = '\''.$this->escapeString($value).'\''; 336 | }elseif(isset($value[0]) && is_string($value[0]) && strtolower($value[0]) == 'exp'){ 337 | $value = $this->escapeString($value[1]); 338 | }elseif(is_array($value)) { 339 | $value = array_map(array($this, 'parseValue'),$value); 340 | }elseif(is_bool($value)){ 341 | $value = $value ? '1' : '0'; 342 | }elseif(is_null($value)){ 343 | $value = 'null'; 344 | } 345 | return $value; 346 | } 347 | 348 | /** 349 | * field分析 350 | * @access protected 351 | * @param mixed $fields 352 | * @return string 353 | */ 354 | protected function parseField($fields) { 355 | if(is_string($fields) && strpos($fields,',')) { 356 | $fields = explode(',',$fields); 357 | } 358 | if(is_array($fields)) { 359 | // 完善数组方式传字段名的支持 360 | // 支持 'field1'=>'field2' 这样的字段别名定义 361 | $array = array(); 362 | foreach ($fields as $key=>$field){ 363 | if(!is_numeric($key)) 364 | $array[] = $this->parseKey($key).' AS '.$this->parseKey($field); 365 | else 366 | $array[] = $this->parseKey($field); 367 | } 368 | $fieldsStr = implode(',', $array); 369 | }elseif(is_string($fields) && !empty($fields)) { 370 | $fieldsStr = $this->parseKey($fields); 371 | }else{ 372 | $fieldsStr = '*'; 373 | } 374 | //TODO 如果是查询全部字段,并且是join的方式,那么就把要查的表加个别名,以免字段被覆盖 375 | return $fieldsStr; 376 | } 377 | 378 | /** 379 | * table分析 380 | * @access protected 381 | * @param mixed $table 382 | * @return string 383 | */ 384 | protected function parseTable($tables) { 385 | if(is_array($tables)) {// 支持别名定义 386 | $array = array(); 387 | foreach ($tables as $table=>$alias){ 388 | if(!is_numeric($table)) 389 | $array[] = $this->parseKey($table).' '.$this->parseKey($alias); 390 | else 391 | $array[] = $this->parseKey($table); 392 | } 393 | $tables = $array; 394 | }elseif(is_string($tables)){ 395 | $tables = explode(',',$tables); 396 | array_walk($tables, array(&$this, 'parseKey')); 397 | } 398 | $tables = implode(',',$tables); 399 | return $tables; 400 | } 401 | 402 | /** 403 | * where分析 404 | * @access protected 405 | * @param mixed $where 406 | * @return string 407 | */ 408 | protected function parseWhere($where) { 409 | $whereStr = ''; 410 | if(is_string($where)) { 411 | // 直接使用字符串条件 412 | $whereStr = $where; 413 | }else{ // 使用数组表达式 414 | $operate = isset($where['_logic'])?strtoupper($where['_logic']):''; 415 | if(in_array($operate,array('AND','OR','XOR'))){ 416 | // 定义逻辑运算规则 例如 OR XOR AND NOT 417 | $operate = ' '.$operate.' '; 418 | unset($where['_logic']); 419 | }else{ 420 | // 默认进行 AND 运算 421 | $operate = ' AND '; 422 | } 423 | foreach ($where as $key=>$val){ 424 | if(is_numeric($key)){ 425 | $key = '_complex'; 426 | } 427 | if(0===strpos($key,'_')) { 428 | // 解析特殊条件表达式 429 | $whereStr .= $this->parseThinkWhere($key,$val); 430 | }else{ 431 | // 查询字段的安全过滤 432 | if(!preg_match('/^[A-Z_\|\&\-.a-z0-9\(\)\,]+$/',trim($key))){ 433 | E(L('_EXPRESS_ERROR_').':'.$key); 434 | } 435 | // 多条件支持 436 | $multi = is_array($val) && isset($val['_multi']); 437 | $key = trim($key); 438 | if(strpos($key,'|')) { // 支持 name|title|nickname 方式定义查询字段 439 | $array = explode('|',$key); 440 | $str = array(); 441 | foreach ($array as $m=>$k){ 442 | $v = $multi?$val[$m]:$val; 443 | $str[] = $this->parseWhereItem($this->parseKey($k),$v); 444 | } 445 | $whereStr .= '( '.implode(' OR ',$str).' )'; 446 | }elseif(strpos($key,'&')){ 447 | $array = explode('&',$key); 448 | $str = array(); 449 | foreach ($array as $m=>$k){ 450 | $v = $multi?$val[$m]:$val; 451 | $str[] = '('.$this->parseWhereItem($this->parseKey($k),$v).')'; 452 | } 453 | $whereStr .= '( '.implode(' AND ',$str).' )'; 454 | }else{ 455 | $whereStr .= $this->parseWhereItem($this->parseKey($key),$val); 456 | } 457 | } 458 | $whereStr .= $operate; 459 | } 460 | $whereStr = substr($whereStr,0,-strlen($operate)); 461 | } 462 | return empty($whereStr)?'':' WHERE '.$whereStr; 463 | } 464 | 465 | // where子单元分析 466 | protected function parseWhereItem($key,$val) { 467 | $whereStr = ''; 468 | if(is_array($val)) { 469 | if(is_string($val[0])) { 470 | $exp = strtolower($val[0]); 471 | if(preg_match('/^(EQ|NEQ|GT|EGT|LT|ELT)$/i',$val[0])) { // 比较运算 472 | $whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($val[1]); 473 | }elseif(preg_match('/^(NOTLIKE|LIKE)$/i',$val[0])){// 模糊查找 474 | if(is_array($val[1])) { 475 | $likeLogic = isset($val[2])?strtoupper($val[2]):'OR'; 476 | if(in_array($likeLogic,array('AND','OR','XOR'))){ 477 | $like = array(); 478 | foreach ($val[1] as $item){ 479 | $like[] = $key.' '.$this->exp[$exp].' '.$this->parseValue($item); 480 | } 481 | $whereStr .= '('.implode(' '.$likeLogic.' ',$like).')'; 482 | } 483 | }else{ 484 | $whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($val[1]); 485 | } 486 | }elseif('exp'==$exp){ // 使用表达式 487 | $whereStr .= $key.' '.$val[1]; 488 | }elseif(preg_match('/^(NOTIN|NOT IN|IN)$/i',$val[0])){ // IN 运算 489 | if(isset($val[2]) && 'exp'==$val[2]) { 490 | $whereStr .= $key.' '.$this->exp[$exp].' '.$val[1]; 491 | }else{ 492 | if(is_string($val[1])) { 493 | $val[1] = explode(',',$val[1]); 494 | } 495 | $zone = implode(',',$this->parseValue($val[1])); 496 | $whereStr .= $key.' '.$this->exp[$exp].' ('.$zone.')'; 497 | } 498 | }elseif(preg_match('/^(NOTBETWEEN|NOT BETWEEN|BETWEEN)$/i',$val[0])){ // BETWEEN运算 499 | $data = is_string($val[1])? explode(',',$val[1]):$val[1]; 500 | $whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($data[0]).' AND '.$this->parseValue($data[1]); 501 | }else{ 502 | E(L('_EXPRESS_ERROR_').':'.$val[0]); 503 | } 504 | }else { 505 | $count = count($val); 506 | $rule = isset($val[$count-1]) ? (is_array($val[$count-1]) ? strtoupper($val[$count-1][0]) : strtoupper($val[$count-1]) ) : '' ; 507 | if(in_array($rule,array('AND','OR','XOR'))) { 508 | $count = $count -1; 509 | }else{ 510 | $rule = 'AND'; 511 | } 512 | for($i=0;$i<$count;$i++) { 513 | $data = is_array($val[$i])?$val[$i][1]:$val[$i]; 514 | if('exp'==strtolower($val[$i][0])) { 515 | $whereStr .= $key.' '.$data.' '.$rule.' '; 516 | }else{ 517 | $whereStr .= $this->parseWhereItem($key,$val[$i]).' '.$rule.' '; 518 | } 519 | } 520 | $whereStr = '( '.substr($whereStr,0,-4).' )'; 521 | } 522 | }else { 523 | //对字符串类型字段采用模糊匹配 524 | if(C('DB_LIKE_FIELDS') && preg_match('/^('.C('DB_LIKE_FIELDS').')$/i',$key)) { 525 | $val = '%'.$val.'%'; 526 | $whereStr .= $key.' LIKE '.$this->parseValue($val); 527 | }else { 528 | $whereStr .= $key.' = '.$this->parseValue($val); 529 | } 530 | } 531 | return $whereStr; 532 | } 533 | 534 | /** 535 | * 特殊条件分析 536 | * @access protected 537 | * @param string $key 538 | * @param mixed $val 539 | * @return string 540 | */ 541 | protected function parseThinkWhere($key,$val) { 542 | $whereStr = ''; 543 | switch($key) { 544 | case '_string': 545 | // 字符串模式查询条件 546 | $whereStr = $val; 547 | break; 548 | case '_complex': 549 | // 复合查询条件 550 | $whereStr = is_string($val)? $val : substr($this->parseWhere($val),6); 551 | break; 552 | case '_query': 553 | // 字符串模式查询条件 554 | parse_str($val,$where); 555 | if(isset($where['_logic'])) { 556 | $op = ' '.strtoupper($where['_logic']).' '; 557 | unset($where['_logic']); 558 | }else{ 559 | $op = ' AND '; 560 | } 561 | $array = array(); 562 | foreach ($where as $field=>$data) 563 | $array[] = $this->parseKey($field).' = '.$this->parseValue($data); 564 | $whereStr = implode($op,$array); 565 | break; 566 | } 567 | return '( '.$whereStr.' )'; 568 | } 569 | 570 | /** 571 | * limit分析 572 | * @access protected 573 | * @param mixed $lmit 574 | * @return string 575 | */ 576 | protected function parseLimit($limit) { 577 | return !empty($limit)? ' LIMIT '.$limit.' ':''; 578 | } 579 | 580 | /** 581 | * join分析 582 | * @access protected 583 | * @param array $join 584 | * @return string 585 | */ 586 | protected function parseJoin($join) { 587 | $joinStr = ''; 588 | if(!empty($join)) { 589 | $joinStr = ' '.implode(' ',$join).' '; 590 | } 591 | return $joinStr; 592 | } 593 | 594 | /** 595 | * order分析 596 | * @access protected 597 | * @param mixed $order 598 | * @return string 599 | */ 600 | protected function parseOrder($order) { 601 | if(is_array($order)) { 602 | $array = array(); 603 | foreach ($order as $key=>$val){ 604 | if(is_numeric($key)) { 605 | $array[] = $this->parseKey($val); 606 | }else{ 607 | $array[] = $this->parseKey($key).' '.$val; 608 | } 609 | } 610 | $order = implode(',',$array); 611 | } 612 | return !empty($order)? ' ORDER BY '.$order:''; 613 | } 614 | 615 | /** 616 | * group分析 617 | * @access protected 618 | * @param mixed $group 619 | * @return string 620 | */ 621 | protected function parseGroup($group) { 622 | return !empty($group)? ' GROUP BY '.$group:''; 623 | } 624 | 625 | /** 626 | * having分析 627 | * @access protected 628 | * @param string $having 629 | * @return string 630 | */ 631 | protected function parseHaving($having) { 632 | return !empty($having)? ' HAVING '.$having:''; 633 | } 634 | 635 | /** 636 | * comment分析 637 | * @access protected 638 | * @param string $comment 639 | * @return string 640 | */ 641 | protected function parseComment($comment) { 642 | return !empty($comment)? ' /* '.$comment.' */':''; 643 | } 644 | 645 | /** 646 | * distinct分析 647 | * @access protected 648 | * @param mixed $distinct 649 | * @return string 650 | */ 651 | protected function parseDistinct($distinct) { 652 | return !empty($distinct)? ' DISTINCT ' :''; 653 | } 654 | 655 | /** 656 | * union分析 657 | * @access protected 658 | * @param mixed $union 659 | * @return string 660 | */ 661 | protected function parseUnion($union) { 662 | if(empty($union)) return ''; 663 | if(isset($union['_all'])) { 664 | $str = 'UNION ALL '; 665 | unset($union['_all']); 666 | }else{ 667 | $str = 'UNION '; 668 | } 669 | foreach ($union as $u){ 670 | $sql[] = $str.(is_array($u)?$this->buildSelectSql($u):$u); 671 | } 672 | return implode(' ',$sql); 673 | } 674 | 675 | /** 676 | * 插入记录 677 | * @access public 678 | * @param mixed $data 数据 679 | * @param array $options 参数表达式 680 | * @param boolean $replace 是否replace 681 | * @return false | integer 682 | */ 683 | public function insert($data,$options=array(),$replace=false) { 684 | $values = $fields = array(); 685 | $this->model = $options['model']; 686 | foreach ($data as $key=>$val){ 687 | if(is_array($val) && 'exp' == $val[0]){ 688 | $fields[] = $this->parseKey($key); 689 | $values[] = $val[1]; 690 | }elseif(is_scalar($val) || is_null($val)) { // 过滤非标量数据 691 | $fields[] = $this->parseKey($key); 692 | if(C('DB_BIND_PARAM') && 0 !== strpos($val,':')){ 693 | $name = md5($key); 694 | $values[] = ':'.$name; 695 | $this->bindParam($name,$val); 696 | }else{ 697 | $values[] = $this->parseValue($val); 698 | } 699 | } 700 | } 701 | $sql = ($replace?'REPLACE':'INSERT').' INTO '.$this->parseTable($options['table']).' ('.implode(',', $fields).') VALUES ('.implode(',', $values).')'; 702 | $sql .= $this->parseLock(isset($options['lock'])?$options['lock']:false); 703 | $sql .= $this->parseComment(!empty($options['comment'])?$options['comment']:''); 704 | return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array())); 705 | } 706 | 707 | /** 708 | * 通过Select方式插入记录 709 | * @access public 710 | * @param string $fields 要插入的数据表字段名 711 | * @param string $table 要插入的数据表名 712 | * @param array $option 查询数据参数 713 | * @return false | integer 714 | */ 715 | public function selectInsert($fields,$table,$options=array()) { 716 | $this->model = $options['model']; 717 | if(is_string($fields)) $fields = explode(',',$fields); 718 | array_walk($fields, array($this, 'parseKey')); 719 | $sql = 'INSERT INTO '.$this->parseTable($table).' ('.implode(',', $fields).') '; 720 | $sql .= $this->buildSelectSql($options); 721 | return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array())); 722 | } 723 | 724 | /** 725 | * 更新记录 726 | * @access public 727 | * @param mixed $data 数据 728 | * @param array $options 表达式 729 | * @return false | integer 730 | */ 731 | public function update($data,$options) { 732 | $this->model = $options['model']; 733 | $sql = 'UPDATE ' 734 | .$this->parseTable($options['table']) 735 | .$this->parseSet($data) 736 | .$this->parseWhere(!empty($options['where'])?$options['where']:'') 737 | .$this->parseOrder(!empty($options['order'])?$options['order']:'') 738 | .$this->parseLimit(!empty($options['limit'])?$options['limit']:'') 739 | .$this->parseLock(isset($options['lock'])?$options['lock']:false) 740 | .$this->parseComment(!empty($options['comment'])?$options['comment']:''); 741 | return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array())); 742 | } 743 | 744 | /** 745 | * 删除记录 746 | * @access public 747 | * @param array $options 表达式 748 | * @return false | integer 749 | */ 750 | public function delete($options=array()) { 751 | $this->model = $options['model']; 752 | $sql = 'DELETE FROM ' 753 | .$this->parseTable($options['table']) 754 | .$this->parseWhere(!empty($options['where'])?$options['where']:'') 755 | .$this->parseOrder(!empty($options['order'])?$options['order']:'') 756 | .$this->parseLimit(!empty($options['limit'])?$options['limit']:'') 757 | .$this->parseLock(isset($options['lock'])?$options['lock']:false) 758 | .$this->parseComment(!empty($options['comment'])?$options['comment']:''); 759 | return $this->execute($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array())); 760 | } 761 | 762 | /** 763 | * 查找记录 764 | * @access public 765 | * @param array $options 表达式 766 | * @return mixed 767 | */ 768 | public function select($options=array()) { 769 | $this->model = $options['model']; 770 | $sql = $this->buildSelectSql($options); 771 | $result = $this->query($sql,$this->parseBind(!empty($options['bind'])?$options['bind']:array())); 772 | return $result; 773 | } 774 | 775 | /** 776 | * 生成查询SQL 777 | * @access public 778 | * @param array $options 表达式 779 | * @return string 780 | */ 781 | public function buildSelectSql($options=array()) { 782 | if(isset($options['page'])) { 783 | // 根据页数计算limit 784 | list($page,$listRows) = $options['page']; 785 | $page = $page>0 ? $page : 1; 786 | $listRows= $listRows>0 ? $listRows : (is_numeric($options['limit'])?$options['limit']:20); 787 | $offset = $listRows*($page-1); 788 | $options['limit'] = $offset.','.$listRows; 789 | } 790 | if(C('DB_SQL_BUILD_CACHE')) { // SQL创建缓存 791 | $key = md5(serialize($options)); 792 | $value = S($key); 793 | if(false !== $value) { 794 | return $value; 795 | } 796 | } 797 | $sql = $this->parseSql($this->selectSql,$options); 798 | $sql .= $this->parseLock(isset($options['lock'])?$options['lock']:false); 799 | if(isset($key)) { // 写入SQL创建缓存 800 | S($key,$sql,array('expire'=>0,'length'=>C('DB_SQL_BUILD_LENGTH'),'queue'=>C('DB_SQL_BUILD_QUEUE'))); 801 | } 802 | return $sql; 803 | } 804 | 805 | /** 806 | * 替换SQL语句中表达式 807 | * @access public 808 | * @param array $options 表达式 809 | * @return string 810 | */ 811 | public function parseSql($sql,$options=array()){ 812 | $sql = str_replace( 813 | array('%TABLE%','%DISTINCT%','%FIELD%','%JOIN%','%WHERE%','%GROUP%','%HAVING%','%ORDER%','%LIMIT%','%UNION%','%COMMENT%'), 814 | array( 815 | $this->parseTable($options['table']), 816 | $this->parseDistinct(isset($options['distinct'])?$options['distinct']:false), 817 | $this->parseField(!empty($options['field'])?$options['field']:'*'), 818 | $this->parseJoin(!empty($options['join'])?$options['join']:''), 819 | $this->parseWhere(!empty($options['where'])?$options['where']:''), 820 | $this->parseGroup(!empty($options['group'])?$options['group']:''), 821 | $this->parseHaving(!empty($options['having'])?$options['having']:''), 822 | $this->parseOrder(!empty($options['order'])?$options['order']:''), 823 | $this->parseLimit(!empty($options['limit'])?$options['limit']:''), 824 | $this->parseUnion(!empty($options['union'])?$options['union']:''), 825 | $this->parseComment(!empty($options['comment'])?$options['comment']:'') 826 | ),$sql); 827 | return $sql; 828 | } 829 | 830 | /** 831 | * 获取最近一次查询的sql语句 832 | * @param string $model 模型名 833 | * @access public 834 | * @return string 835 | */ 836 | public function getLastSql($model='') { 837 | return $model?$this->modelSql[$model]:$this->queryStr; 838 | } 839 | 840 | /** 841 | * 获取最近插入的ID 842 | * @access public 843 | * @return string 844 | */ 845 | public function getLastInsID() { 846 | return $this->lastInsID; 847 | } 848 | 849 | /** 850 | * 获取最近的错误信息 851 | * @access public 852 | * @return string 853 | */ 854 | public function getError() { 855 | return $this->error; 856 | } 857 | 858 | /** 859 | * SQL指令安全过滤 860 | * @access public 861 | * @param string $str SQL字符串 862 | * @return string 863 | */ 864 | public function escapeString($str) { 865 | return addslashes($str); 866 | } 867 | 868 | /** 869 | * 设置当前操作模型 870 | * @access public 871 | * @param string $model 模型名 872 | * @return void 873 | */ 874 | public function setModel($model){ 875 | $this->model = $model; 876 | } 877 | 878 | /** 879 | * 析构方法 880 | * @access public 881 | */ 882 | public function __destruct() { 883 | // 释放查询 884 | if ($this->queryID){ 885 | $this->free(); 886 | } 887 | // 关闭连接 888 | $this->close(); 889 | } 890 | 891 | // 关闭数据库 由驱动类定义 892 | public function close(){} 893 | 894 | public function test(){ 895 | print_r('success'); 896 | } 897 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /code_test/6.php: -------------------------------------------------------------------------------- 1 | 10 | // +---------------------------------------------------------------------- 11 | namespace Think; 12 | /** 13 | * ThinkPHP Model模型类 14 | * 实现了ORM和ActiveRecords模式 15 | */ 16 | class Model { 17 | // 操作状态 18 | const MODEL_INSERT = 1; // 插入模型数据 19 | const MODEL_UPDATE = 2; // 更新模型数据 20 | const MODEL_BOTH = 3; // 包含上面两种方式 21 | const MUST_VALIDATE = 1; // 必须验证 22 | const EXISTS_VALIDATE = 0; // 表单存在字段则验证 23 | const VALUE_VALIDATE = 2; // 表单值不为空则验证 24 | 25 | // 当前数据库操作对象 26 | protected $db = null; 27 | // 主键名称 28 | protected $pk = 'id'; 29 | // 主键是否自动增长 30 | protected $autoinc = false; 31 | // 数据表前缀 32 | protected $tablePrefix = null; 33 | // 模型名称 34 | protected $name = ''; 35 | // 数据库名称 36 | protected $dbName = ''; 37 | //数据库配置 38 | protected $connection = ''; 39 | // 数据表名(不包含表前缀) 40 | protected $tableName = ''; 41 | // 实际数据表名(包含表前缀) 42 | protected $trueTableName = ''; 43 | // 最近错误信息 44 | protected $error = ''; 45 | // 字段信息 46 | protected $fields = array(); 47 | // 数据信息 48 | protected $data = array(); 49 | // 查询表达式参数 50 | protected $options = array(); 51 | protected $_validate = array(); // 自动验证定义 52 | protected $_auto = array(); // 自动完成定义 53 | protected $_map = array(); // 字段映射定义 54 | protected $_scope = array(); // 命名范围定义 55 | // 是否自动检测数据表字段信息 56 | protected $autoCheckFields = true; 57 | // 是否批处理验证 58 | protected $patchValidate = false; 59 | // 链操作方法列表 60 | protected $methods = array('order','alias','having','group','lock','distinct','auto','filter','validate','result','token','index'); 61 | 62 | /** 63 | * 架构函数 64 | * 取得DB类的实例对象 字段检查 65 | * @access public 66 | * @param string $name 模型名称 67 | * @param string $tablePrefix 表前缀 68 | * @param mixed $connection 数据库连接信息 69 | */ 70 | public function __construct($name='',$tablePrefix='',$connection='') { 71 | // 模型初始化 72 | $this->_initialize(); 73 | // 获取模型名称 74 | if(!empty($name)) { 75 | if(strpos($name,'.')) { // 支持 数据库名.模型名的 定义 76 | list($this->dbName,$this->name) = explode('.',$name); 77 | }else{ 78 | $this->name = $name; 79 | } 80 | }elseif(empty($this->name)){ 81 | $this->name = $this->getModelName(); 82 | } 83 | // 设置表前缀 84 | if(is_null($tablePrefix)) {// 前缀为Null表示没有前缀 85 | $this->tablePrefix = ''; 86 | }elseif('' != $tablePrefix) { 87 | $this->tablePrefix = $tablePrefix; 88 | }elseif(!isset($this->tablePrefix)){ 89 | $this->tablePrefix = C('DB_PREFIX'); 90 | } 91 | 92 | // 数据库初始化操作 93 | // 获取数据库操作对象 94 | // 当前模型有独立的数据库连接信息 95 | $this->db(0,empty($this->connection)?$connection:$this->connection,true); 96 | } 97 | 98 | /** 99 | * 自动检测数据表信息 100 | * @access protected 101 | * @return void 102 | */ 103 | protected function _checkTableInfo() { 104 | // 如果不是Model类 自动记录数据表信息 105 | // 只在第一次执行记录 106 | if(empty($this->fields)) { 107 | // 如果数据表字段没有定义则自动获取 108 | if(C('DB_FIELDS_CACHE')) { 109 | $db = $this->dbName?:C('DB_NAME'); 110 | $fields = F('_fields/'.strtolower($db.'.'.$this->tablePrefix.$this->name)); 111 | if($fields) { 112 | $this->fields = $fields; 113 | $this->pk = $fields['_pk']; 114 | return ; 115 | } 116 | } 117 | // 每次都会读取数据表信息 118 | $this->flush(); 119 | } 120 | } 121 | 122 | /** 123 | * 获取字段信息并缓存 124 | * @access public 125 | * @return void 126 | */ 127 | public function flush() { 128 | // 缓存不存在则查询数据表信息 129 | $this->db->setModel($this->name); 130 | $fields = $this->db->getFields($this->getTableName()); 131 | if(!$fields) { // 无法获取字段信息 132 | return false; 133 | } 134 | $this->fields = array_keys($fields); 135 | foreach ($fields as $key=>$val){ 136 | // 记录字段类型 137 | $type[$key] = $val['type']; 138 | if($val['primary']) { 139 | $this->pk = $key; 140 | $this->fields['_pk'] = $key; 141 | if($val['autoinc']) $this->autoinc = true; 142 | } 143 | } 144 | // 记录字段类型信息 145 | $this->fields['_type'] = $type; 146 | 147 | // 2008-3-7 增加缓存开关控制 148 | if(C('DB_FIELDS_CACHE')){ 149 | // 永久缓存数据表信息 150 | $db = $this->dbName?:C('DB_NAME'); 151 | F('_fields/'.strtolower($db.'.'.$this->tablePrefix.$this->name),$this->fields); 152 | } 153 | } 154 | 155 | /** 156 | * 设置数据对象的值 157 | * @access public 158 | * @param string $name 名称 159 | * @param mixed $value 值 160 | * @return void 161 | */ 162 | public function __set($name,$value) { 163 | // 设置数据对象属性 164 | $this->data[$name] = $value; 165 | } 166 | 167 | /** 168 | * 获取数据对象的值 169 | * @access public 170 | * @param string $name 名称 171 | * @return mixed 172 | */ 173 | public function __get($name) { 174 | return isset($this->data[$name])?$this->data[$name]:null; 175 | } 176 | 177 | /** 178 | * 检测数据对象的值 179 | * @access public 180 | * @param string $name 名称 181 | * @return boolean 182 | */ 183 | public function __isset($name) { 184 | return isset($this->data[$name]); 185 | } 186 | 187 | /** 188 | * 销毁数据对象的值 189 | * @access public 190 | * @param string $name 名称 191 | * @return void 192 | */ 193 | public function __unset($name) { 194 | unset($this->data[$name]); 195 | } 196 | 197 | /** 198 | * 利用__call方法实现一些特殊的Model方法 199 | * @access public 200 | * @param string $method 方法名称 201 | * @param array $args 调用参数 202 | * @return mixed 203 | */ 204 | public function __call($method,$args) { 205 | if(in_array(strtolower($method),$this->methods,true)) { 206 | // 连贯操作的实现 207 | $this->options[strtolower($method)] = $args[0]; 208 | return $this; 209 | }elseif(in_array(strtolower($method),array('count','sum','min','max','avg'),true)){ 210 | // 统计查询的实现 211 | $field = isset($args[0])?$args[0]:'*'; 212 | return $this->getField(strtoupper($method).'('.$field.') AS tp_'.$method); 213 | }elseif(strtolower(substr($method,0,5))=='getby') { 214 | // 根据某个字段获取记录 215 | $field = parse_name(substr($method,5)); 216 | $where[$field] = $args[0]; 217 | return $this->where($where)->find(); 218 | }elseif(strtolower(substr($method,0,10))=='getfieldby') { 219 | // 根据某个字段获取记录的某个值 220 | $name = parse_name(substr($method,10)); 221 | $where[$name] =$args[0]; 222 | return $this->where($where)->getField($args[1]); 223 | }elseif(isset($this->_scope[$method])){// 命名范围的单独调用支持 224 | return $this->scope($method,$args[0]); 225 | }else{ 226 | E(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_')); 227 | return; 228 | } 229 | } 230 | // 回调方法 初始化模型 231 | protected function _initialize() {} 232 | 233 | /** 234 | * 对保存到数据库的数据进行处理 235 | * @access protected 236 | * @param mixed $data 要操作的数据 237 | * @return boolean 238 | */ 239 | protected function _facade($data) { 240 | 241 | // 检查数据字段合法性 242 | if(!empty($this->fields)) { 243 | if(!empty($this->options['field'])) { 244 | $fields = $this->options['field']; 245 | unset($this->options['field']); 246 | if(is_string($fields)) { 247 | $fields = explode(',',$fields); 248 | } 249 | }else{ 250 | $fields = $this->fields; 251 | } 252 | foreach ($data as $key=>$val){ 253 | if(!in_array($key,$fields,true)){ 254 | if(APP_DEBUG){ 255 | E(L('_DATA_TYPE_INVALID_').':['.$key.'=>'.$val.']'); 256 | } 257 | unset($data[$key]); 258 | }elseif(is_scalar($val)) { 259 | // 字段类型检查 和 强制转换 260 | $this->_parseType($data,$key); 261 | } 262 | } 263 | } 264 | 265 | // 安全过滤 266 | if(!empty($this->options['filter'])) { 267 | $data = array_map($this->options['filter'],$data); 268 | unset($this->options['filter']); 269 | } 270 | $this->_before_write($data); 271 | return $data; 272 | } 273 | 274 | // 写入数据前的回调方法 包括新增和更新 275 | protected function _before_write(&$data) {} 276 | 277 | /** 278 | * 新增数据 279 | * @access public 280 | * @param mixed $data 数据 281 | * @param array $options 表达式 282 | * @param boolean $replace 是否replace 283 | * @return mixed 284 | */ 285 | public function add($data='',$options=array(),$replace=false) { 286 | if(empty($data)) { 287 | // 没有传递数据,获取当前数据对象的值 288 | if(!empty($this->data)) { 289 | $data = $this->data; 290 | // 重置数据 291 | $this->data = array(); 292 | }else{ 293 | $this->error = L('_DATA_TYPE_INVALID_'); 294 | return false; 295 | } 296 | } 297 | // 分析表达式 298 | $options = $this->_parseOptions($options); 299 | // 数据处理 300 | $data = $this->_facade($data); 301 | if(false === $this->_before_insert($data,$options)) { 302 | return false; 303 | } 304 | // 写入数据到数据库 305 | $result = $this->db->insert($data,$options,$replace); 306 | if(false !== $result ) { 307 | $insertId = $this->getLastInsID(); 308 | if($insertId) { 309 | // 自增主键返回插入ID 310 | $data[$this->getPk()] = $insertId; 311 | if(false === $this->_after_insert($data,$options)) { 312 | return false; 313 | } 314 | return $insertId; 315 | } 316 | if(false === $this->_after_insert($data,$options)) { 317 | return false; 318 | } 319 | } 320 | return $result; 321 | } 322 | // 插入数据前的回调方法 323 | protected function _before_insert(&$data,$options) {} 324 | // 插入成功后的回调方法 325 | protected function _after_insert($data,$options) {} 326 | 327 | public function addAll($dataList,$options=array(),$replace=false){ 328 | if(empty($dataList)) { 329 | $this->error = L('_DATA_TYPE_INVALID_'); 330 | return false; 331 | } 332 | // 分析表达式 333 | $options = $this->_parseOptions($options); 334 | // 数据处理 335 | foreach ($dataList as $key=>$data){ 336 | $dataList[$key] = $this->_facade($data); 337 | } 338 | // 写入数据到数据库 339 | $result = $this->db->insertAll($dataList,$options,$replace); 340 | if(false !== $result ) { 341 | $insertId = $this->getLastInsID(); 342 | if($insertId) { 343 | return $insertId; 344 | } 345 | } 346 | return $result; 347 | } 348 | 349 | /** 350 | * 通过Select方式添加记录 351 | * @access public 352 | * @param string $fields 要插入的数据表字段名 353 | * @param string $table 要插入的数据表名 354 | * @param array $options 表达式 355 | * @return boolean 356 | */ 357 | public function selectAdd($fields='',$table='',$options=array()) { 358 | // 分析表达式 359 | $options = $this->_parseOptions($options); 360 | // 写入数据到数据库 361 | if(false === $result = $this->db->selectInsert($fields?:$options['field'],$table?:$this->getTableName(),$options)){ 362 | // 数据库插入操作失败 363 | $this->error = L('_OPERATION_WRONG_'); 364 | return false; 365 | }else { 366 | // 插入成功 367 | return $result; 368 | } 369 | } 370 | 371 | /** 372 | * 保存数据 373 | * @access public 374 | * @param mixed $data 数据 375 | * @param array $options 表达式 376 | * @return boolean 377 | */ 378 | public function save($data='',$options=array()) { 379 | if(empty($data)) { 380 | // 没有传递数据,获取当前数据对象的值 381 | if(!empty($this->data)) { 382 | $data = $this->data; 383 | // 重置数据 384 | $this->data = array(); 385 | }else{ 386 | $this->error = L('_DATA_TYPE_INVALID_'); 387 | return false; 388 | } 389 | } 390 | // 数据处理 391 | $data = $this->_facade($data); 392 | if(empty($data)){ 393 | // 没有数据则不执行 394 | $this->error = L('_DATA_TYPE_INVALID_'); 395 | return false; 396 | } 397 | // 分析表达式 398 | $options = $this->_parseOptions($options); 399 | $pk = $this->getPk(); 400 | if(!isset($options['where']) ) { 401 | // 如果存在主键数据 则自动作为更新条件 402 | if(isset($data[$pk])) { 403 | $where[$pk] = $data[$pk]; 404 | $options['where'] = $where; 405 | unset($data[$pk]); 406 | }else{ 407 | // 如果没有任何更新条件则不执行 408 | $this->error = L('_OPERATION_WRONG_'); 409 | return false; 410 | } 411 | } 412 | if(is_array($options['where']) && isset($options['where'][$pk])){ 413 | $pkValue = $options['where'][$pk]; 414 | } 415 | if(false === $this->_before_update($data,$options)) { 416 | return false; 417 | } 418 | $result = $this->db->update($data,$options); 419 | if(false !== $result) { 420 | if(isset($pkValue)) $data[$pk] = $pkValue; 421 | $this->_after_update($data,$options); 422 | } 423 | return $result; 424 | } 425 | // 更新数据前的回调方法 426 | protected function _before_update(&$data,$options) {} 427 | // 更新成功后的回调方法 428 | protected function _after_update($data,$options) {} 429 | 430 | /** 431 | * 删除数据 432 | * @access public 433 | * @param mixed $options 表达式 434 | * @return mixed 435 | */ 436 | public function delete($options=array()) { 437 | if(empty($options) && empty($this->options['where'])) { 438 | // 如果删除条件为空 则删除当前数据对象所对应的记录 439 | if(!empty($this->data) && isset($this->data[$this->getPk()])) 440 | return $this->delete($this->data[$this->getPk()]); 441 | else 442 | return false; 443 | } 444 | $pk = $this->getPk(); 445 | if(is_numeric($options) || is_string($options)) { 446 | // 根据主键删除记录 447 | if(strpos($options,',')) { 448 | $where[$pk] = array('IN', $options); 449 | }else{ 450 | $where[$pk] = $options; 451 | } 452 | $options = array(); 453 | $options['where'] = $where; 454 | } 455 | // 分析表达式 456 | $options = $this->_parseOptions($options); 457 | if(empty($options['where'])){ 458 | // 如果条件为空 不进行删除操作 除非设置 1=1 459 | return false; 460 | } 461 | if(is_array($options['where']) && isset($options['where'][$pk])){ 462 | $pkValue = $options['where'][$pk]; 463 | } 464 | 465 | if(false === $this->_before_delete($options)) { 466 | return false; 467 | } 468 | $result = $this->db->delete($options); 469 | if(false !== $result) { 470 | $data = array(); 471 | if(isset($pkValue)) $data[$pk] = $pkValue; 472 | $this->_after_delete($data,$options); 473 | } 474 | // 返回删除记录个数 475 | return $result; 476 | } 477 | // 删除数据前的回调方法 478 | protected function _before_delete($options) {} 479 | // 删除成功后的回调方法 480 | protected function _after_delete($data,$options) {} 481 | 482 | /** 483 | * 查询数据集 484 | * @access public 485 | * @param array $options 表达式参数 486 | * @return mixed 487 | */ 488 | public function select($options=array()) { 489 | if(is_string($options) || is_numeric($options)) { 490 | // 根据主键查询 491 | $pk = $this->getPk(); 492 | if(strpos($options,',')) { 493 | $where[$pk] = array('IN',$options); 494 | }else{ 495 | $where[$pk] = $options; 496 | } 497 | $options = array(); 498 | $options['where'] = $where; 499 | }elseif(false === $options){ // 用于子查询 不查询只返回SQL 500 | $options = array(); 501 | // 分析表达式 502 | $options = $this->_parseOptions($options); 503 | return '( '.$this->db->buildSelectSql($options).' )'; 504 | } 505 | // 分析表达式 506 | $options = $this->_parseOptions($options); 507 | // 判断查询缓存 508 | if(isset($options['cache'])){ 509 | $cache = $options['cache']; 510 | $key = is_string($cache['key'])?$cache['key']:md5(serialize($options)); 511 | $data = S($key,'',$cache); 512 | if(false !== $data){ 513 | return $data; 514 | } 515 | } 516 | $resultSet = $this->db->select($options); 517 | if(false === $resultSet) { 518 | return false; 519 | } 520 | if(empty($resultSet)) { // 查询结果为空 521 | return null; 522 | } 523 | $resultSet = array_map(array($this,'_read_data'),$resultSet); 524 | $this->_after_select($resultSet,$options); 525 | if(isset($options['index'])){ // 对数据集进行索引 526 | $index = explode(',',$options['index']); 527 | foreach ($resultSet as $result){ 528 | $_key = $result[$index[0]]; 529 | if(isset($index[1]) && isset($result[$index[1]])){ 530 | $cols[$_key] = $result[$index[1]]; 531 | }else{ 532 | $cols[$_key] = $result; 533 | } 534 | } 535 | $resultSet = $cols; 536 | } 537 | if(isset($cache)){ 538 | S($key,$resultSet,$cache); 539 | } 540 | return $resultSet; 541 | } 542 | // 查询成功后的回调方法 543 | protected function _after_select(&$resultSet,$options) {} 544 | 545 | /** 546 | * 生成查询SQL 可用于子查询 547 | * @access public 548 | * @param array $options 表达式参数 549 | * @return string 550 | */ 551 | public function buildSql($options=array()) { 552 | // 分析表达式 553 | $options = $this->_parseOptions($options); 554 | return '( '.$this->db->buildSelectSql($options).' )'; 555 | } 556 | 557 | /** 558 | * 分析表达式 559 | * @access protected 560 | * @param array $options 表达式参数 561 | * @return array 562 | */ 563 | protected function _parseOptions($options=array()) { 564 | if(is_array($options)) 565 | $options = array_merge($this->options,$options); 566 | 567 | if(!isset($options['table'])){ 568 | // 自动获取表名 569 | $options['table'] = $this->getTableName(); 570 | $fields = $this->fields; 571 | }else{ 572 | // 指定数据表 则重新获取字段列表 但不支持类型检测 573 | $fields = $this->getDbFields(); 574 | } 575 | 576 | // 数据表别名 577 | if(!empty($options['alias'])) { 578 | $options['table'] .= ' '.$options['alias']; 579 | } 580 | // 记录操作的模型名称 581 | $options['model'] = $this->name; 582 | 583 | // 字段类型验证 584 | if(isset($options['where']) && is_array($options['where']) && !empty($fields) && !isset($options['join'])) { 585 | // 对数组查询条件进行字段类型检查 586 | foreach ($options['where'] as $key=>$val){ 587 | $key = trim($key); 588 | if(in_array($key,$fields,true)){ 589 | if(is_scalar($val)) { 590 | $this->_parseType($options['where'],$key); 591 | } 592 | }elseif(!is_numeric($key) && '_' != substr($key,0,1) && false === strpos($key,'.') && false === strpos($key,'(') && false === strpos($key,'|') && false === strpos($key,'&')){ 593 | if(APP_DEBUG){ 594 | E(L('_ERROR_QUERY_EXPRESS_').':['.$key.'=>'.$val.']'); 595 | } 596 | unset($options['where'][$key]); 597 | } 598 | } 599 | } 600 | // 查询过后清空sql表达式组装 避免影响下次查询 601 | $this->options = array(); 602 | // 表达式过滤 603 | $this->_options_filter($options); 604 | return $options; 605 | } 606 | // 表达式过滤回调方法 607 | protected function _options_filter(&$options) {} 608 | 609 | /** 610 | * 数据类型检测 611 | * @access protected 612 | * @param mixed $data 数据 613 | * @param string $key 字段名 614 | * @return void 615 | */ 616 | protected function _parseType(&$data,$key) { 617 | if(!isset($this->options['bind'][':'.$key]) && isset($this->fields['_type'][$key])){ 618 | $fieldType = strtolower($this->fields['_type'][$key]); 619 | if(false !== strpos($fieldType,'enum')){ 620 | // 支持ENUM类型优先检测 621 | }elseif(false === strpos($fieldType,'bigint') && false !== strpos($fieldType,'int')) { 622 | $data[$key] = intval($data[$key]); 623 | }elseif(false !== strpos($fieldType,'float') || false !== strpos($fieldType,'double')){ 624 | $data[$key] = floatval($data[$key]); 625 | }elseif(false !== strpos($fieldType,'bool')){ 626 | $data[$key] = (bool)$data[$key]; 627 | } 628 | } 629 | } 630 | 631 | /** 632 | * 数据读取后的处理 633 | * @access protected 634 | * @param array $data 当前数据 635 | * @return array 636 | */ 637 | protected function _read_data($data) { 638 | // 检查字段映射 639 | if(!empty($this->_map) && C('READ_DATA_MAP')) { 640 | foreach ($this->_map as $key=>$val){ 641 | if(isset($data[$val])) { 642 | $data[$key] = $data[$val]; 643 | unset($data[$val]); 644 | } 645 | } 646 | } 647 | return $data; 648 | } 649 | 650 | /** 651 | * 查询数据 652 | * @access public 653 | * @param mixed $options 表达式参数 654 | * @return mixed 655 | */ 656 | public function find($options=array()) { 657 | if(is_numeric($options) || is_string($options)) { 658 | $where[$this->getPk()] = $options; 659 | $options = array(); 660 | $options['where'] = $where; 661 | } 662 | // 总是查找一条记录 663 | $options['limit'] = 1; 664 | // 分析表达式 665 | $options = $this->_parseOptions($options); 666 | // 判断查询缓存 667 | if(isset($options['cache'])){ 668 | $cache = $options['cache']; 669 | $key = is_string($cache['key'])?$cache['key']:md5(serialize($options)); 670 | $data = S($key,'',$cache); 671 | if(false !== $data){ 672 | $this->data = $data; 673 | return $data; 674 | } 675 | } 676 | $resultSet = $this->db->select($options); 677 | if(false === $resultSet) { 678 | return false; 679 | } 680 | if(empty($resultSet)) {// 查询结果为空 681 | return null; 682 | } 683 | // 读取数据后的处理 684 | $data = $this->_read_data($resultSet[0]); 685 | $this->_after_find($data,$options); 686 | if(!empty($this->options['result'])) { 687 | return $this->returnResult($data,$this->options['result']); 688 | } 689 | $this->data = $data; 690 | if(isset($cache)){ 691 | S($key,$data,$cache); 692 | } 693 | return $this->data; 694 | } 695 | // 查询成功的回调方法 696 | protected function _after_find(&$result,$options) {} 697 | 698 | protected function returnResult($data,$type=''){ 699 | if ($type){ 700 | if(is_callable($type)){ 701 | return call_user_func($type,$data); 702 | } 703 | switch (strtolower($type)){ 704 | case 'json': 705 | return json_encode($data); 706 | case 'xml': 707 | return xml_encode($data); 708 | } 709 | } 710 | return $data; 711 | } 712 | 713 | /** 714 | * 处理字段映射 715 | * @access public 716 | * @param array $data 当前数据 717 | * @param integer $type 类型 0 写入 1 读取 718 | * @return array 719 | */ 720 | public function parseFieldsMap($data,$type=1) { 721 | // 检查字段映射 722 | if(!empty($this->_map)) { 723 | foreach ($this->_map as $key=>$val){ 724 | if($type==1) { // 读取 725 | if(isset($data[$val])) { 726 | $data[$key] = $data[$val]; 727 | unset($data[$val]); 728 | } 729 | }else{ 730 | if(isset($data[$key])) { 731 | $data[$val] = $data[$key]; 732 | unset($data[$key]); 733 | } 734 | } 735 | } 736 | } 737 | return $data; 738 | } 739 | 740 | /** 741 | * 设置记录的某个字段值 742 | * 支持使用数据库字段和方法 743 | * @access public 744 | * @param string|array $field 字段名 745 | * @param string $value 字段值 746 | * @return boolean 747 | */ 748 | public function setField($field,$value='') { 749 | if(is_array($field)) { 750 | $data = $field; 751 | }else{ 752 | $data[$field] = $value; 753 | } 754 | return $this->save($data); 755 | } 756 | 757 | /** 758 | * 字段值增长 759 | * @access public 760 | * @param string $field 字段名 761 | * @param integer $step 增长值 762 | * @return boolean 763 | */ 764 | public function setInc($field,$step=1) { 765 | return $this->setField($field,array('exp',$field.'+'.$step)); 766 | } 767 | 768 | /** 769 | * 字段值减少 770 | * @access public 771 | * @param string $field 字段名 772 | * @param integer $step 减少值 773 | * @return boolean 774 | */ 775 | public function setDec($field,$step=1) { 776 | return $this->setField($field,array('exp',$field.'-'.$step)); 777 | } 778 | 779 | /** 780 | * 获取一条记录的某个字段值 781 | * @access public 782 | * @param string $field 字段名 783 | * @param string $spea 字段数据间隔符号 NULL返回数组 784 | * @return mixed 785 | */ 786 | public function getField($field,$sepa=null) { 787 | $options['field'] = $field; 788 | $options = $this->_parseOptions($options); 789 | // 判断查询缓存 790 | if(isset($options['cache'])){ 791 | $cache = $options['cache']; 792 | $key = is_string($cache['key'])?$cache['key']:md5($sepa.serialize($options)); 793 | $data = S($key,'',$cache); 794 | if(false !== $data){ 795 | return $data; 796 | } 797 | } 798 | $field = trim($field); 799 | if(strpos($field,',') && false !== $sepa) { // 多字段 800 | if(!isset($options['limit'])){ 801 | $options['limit'] = is_numeric($sepa)?$sepa:''; 802 | } 803 | $resultSet = $this->db->select($options); 804 | if(!empty($resultSet)) { 805 | $_field = explode(',', $field); 806 | $field = array_keys($resultSet[0]); 807 | $key = array_shift($field); 808 | $key2 = array_shift($field); 809 | $cols = array(); 810 | $count = count($_field); 811 | foreach ($resultSet as $result){ 812 | $name = $result[$key]; 813 | if(2==$count) { 814 | $cols[$name] = $result[$key2]; 815 | }else{ 816 | $cols[$name] = is_string($sepa)?implode($sepa,array_slice($result,1)):$result; 817 | } 818 | } 819 | if(isset($cache)){ 820 | S($key,$cols,$cache); 821 | } 822 | return $cols; 823 | } 824 | }else{ // 查找一条记录 825 | // 返回数据个数 826 | if(true !== $sepa) {// 当sepa指定为true的时候 返回所有数据 827 | $options['limit'] = is_numeric($sepa)?$sepa:1; 828 | } 829 | $result = $this->db->select($options); 830 | if(!empty($result)) { 831 | if(true !== $sepa && 1==$options['limit']) { 832 | $data = reset($result[0]); 833 | if(isset($cache)){ 834 | S($key,$data,$cache); 835 | } 836 | return $data; 837 | } 838 | foreach ($result as $val){ 839 | $array[] = $val[$field]; 840 | } 841 | if(isset($cache)){ 842 | S($key,$array,$cache); 843 | } 844 | return $array; 845 | } 846 | } 847 | return null; 848 | } 849 | 850 | /** 851 | * 创建数据对象 但不保存到数据库 852 | * @access public 853 | * @param mixed $data 创建数据 854 | * @param string $type 状态 855 | * @return mixed 856 | */ 857 | public function create($data='',$type='') { 858 | // 如果没有传值默认取POST数据 859 | if(empty($data)) { 860 | $data = I('post.'); 861 | }elseif(is_object($data)){ 862 | $data = get_object_vars($data); 863 | } 864 | // 验证数据 865 | if(empty($data) || !is_array($data)) { 866 | $this->error = L('_DATA_TYPE_INVALID_'); 867 | return false; 868 | } 869 | 870 | // 状态 871 | $type = $type?:(!empty($data[$this->getPk()])?self::MODEL_UPDATE:self::MODEL_INSERT); 872 | 873 | // 检查字段映射 874 | if(!empty($this->_map)) { 875 | foreach ($this->_map as $key=>$val){ 876 | if(isset($data[$key])) { 877 | $data[$val] = $data[$key]; 878 | unset($data[$key]); 879 | } 880 | } 881 | } 882 | 883 | // 检测提交字段的合法性 884 | if(isset($this->options['field'])) { // $this->field('field1,field2...')->create() 885 | $fields = $this->options['field']; 886 | unset($this->options['field']); 887 | }elseif($type == self::MODEL_INSERT && isset($this->insertFields)) { 888 | $fields = $this->insertFields; 889 | }elseif($type == self::MODEL_UPDATE && isset($this->updateFields)) { 890 | $fields = $this->updateFields; 891 | } 892 | if(isset($fields)) { 893 | if(is_string($fields)) { 894 | $fields = explode(',',$fields); 895 | } 896 | // 判断令牌验证字段 897 | if(C('TOKEN_ON')) $fields[] = C('TOKEN_NAME'); 898 | foreach ($data as $key=>$val){ 899 | if(!in_array($key,$fields)) { 900 | unset($data[$key]); 901 | } 902 | } 903 | } 904 | 905 | // 数据自动验证 906 | if(!$this->autoValidation($data,$type)) return false; 907 | 908 | // 表单令牌验证 909 | if(!$this->autoCheckToken($data)) { 910 | $this->error = L('_TOKEN_ERROR_'); 911 | return false; 912 | } 913 | 914 | // 验证完成生成数据对象 915 | if($this->autoCheckFields) { // 开启字段检测 则过滤非法字段数据 916 | $fields = $this->getDbFields(); 917 | foreach ($data as $key=>$val){ 918 | if(!in_array($key,$fields)) { 919 | unset($data[$key]); 920 | }elseif(MAGIC_QUOTES_GPC && is_string($val)){ 921 | $data[$key] = stripslashes($val); 922 | } 923 | } 924 | } 925 | 926 | // 创建完成对数据进行自动处理 927 | $this->autoOperation($data,$type); 928 | // 赋值当前数据对象 929 | $this->data = $data; 930 | // 返回创建的数据以供其他调用 931 | return $data; 932 | } 933 | 934 | // 自动表单令牌验证 935 | // TODO ajax无刷新多次提交暂不能满足 936 | public function autoCheckToken($data) { 937 | // 支持使用token(false) 关闭令牌验证 938 | if(isset($this->options['token']) && !$this->options['token']) return true; 939 | if(C('TOKEN_ON')){ 940 | $name = C('TOKEN_NAME', null, '__hash__'); 941 | if(!isset($data[$name]) || !isset($_SESSION[$name])) { // 令牌数据无效 942 | return false; 943 | } 944 | 945 | // 令牌验证 946 | list($key,$value) = explode('_',$data[$name]); 947 | if($value && $_SESSION[$name][$key] === $value) { // 防止重复提交 948 | unset($_SESSION[$name][$key]); // 验证完成销毁session 949 | return true; 950 | } 951 | // 开启TOKEN重置 952 | if(C('TOKEN_RESET')) unset($_SESSION[$name][$key]); 953 | return false; 954 | } 955 | return true; 956 | } 957 | 958 | /** 959 | * 使用正则验证数据 960 | * @access public 961 | * @param string $value 要验证的数据 962 | * @param string $rule 验证规则 963 | * @return boolean 964 | */ 965 | public function regex($value,$rule) { 966 | $validate = array( 967 | 'require' => '/\S+/', 968 | 'email' => '/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/', 969 | 'url' => '/^http(s?):\/\/(?:[A-za-z0-9-]+\.)+[A-za-z]{2,4}(?:[\/\?#][\/=\?%\-&~`@[\]\':+!\.#\w]*)?$/', 970 | 'currency' => '/^\d+(\.\d+)?$/', 971 | 'number' => '/^\d+$/', 972 | 'zip' => '/^\d{6}$/', 973 | 'integer' => '/^[-\+]?\d+$/', 974 | 'double' => '/^[-\+]?\d+(\.\d+)?$/', 975 | 'english' => '/^[A-Za-z]+$/', 976 | ); 977 | // 检查是否有内置的正则表达式 978 | if(isset($validate[strtolower($rule)])) 979 | $rule = $validate[strtolower($rule)]; 980 | return preg_match($rule,$value)===1; 981 | } 982 | 983 | /** 984 | * 自动表单处理 985 | * @access public 986 | * @param array $data 创建数据 987 | * @param string $type 创建类型 988 | * @return mixed 989 | */ 990 | private function autoOperation(&$data,$type) { 991 | if(!empty($this->options['auto'])) { 992 | $_auto = $this->options['auto']; 993 | unset($this->options['auto']); 994 | }elseif(!empty($this->_auto)){ 995 | $_auto = $this->_auto; 996 | } 997 | // 自动填充 998 | if(isset($_auto)) { 999 | foreach ($_auto as $auto){ 1000 | // 填充因子定义格式 1001 | // array('field','填充内容','填充条件','附加规则',[额外参数]) 1002 | if(empty($auto[2])) $auto[2] = self::MODEL_INSERT; // 默认为新增的时候自动填充 1003 | if( $type == $auto[2] || $auto[2] == self::MODEL_BOTH) { 1004 | if(empty($auto[3])) $auto[3] = 'string'; 1005 | switch(trim($auto[3])) { 1006 | case 'function': // 使用函数进行填充 字段的值作为参数 1007 | case 'callback': // 使用回调方法 1008 | $args = isset($auto[4])?(array)$auto[4]:array(); 1009 | if(isset($data[$auto[0]])) { 1010 | array_unshift($args,$data[$auto[0]]); 1011 | } 1012 | if('function'==$auto[3]) { 1013 | $data[$auto[0]] = call_user_func_array($auto[1], $args); 1014 | }else{ 1015 | $data[$auto[0]] = call_user_func_array(array(&$this,$auto[1]), $args); 1016 | } 1017 | break; 1018 | case 'field': // 用其它字段的值进行填充 1019 | $data[$auto[0]] = $data[$auto[1]]; 1020 | break; 1021 | case 'ignore': // 为空忽略 1022 | if($auto[1]===$data[$auto[0]]) 1023 | unset($data[$auto[0]]); 1024 | break; 1025 | case 'string': 1026 | default: // 默认作为字符串填充 1027 | $data[$auto[0]] = $auto[1]; 1028 | } 1029 | if(isset($data[$auto[0]]) && false === $data[$auto[0]] ) unset($data[$auto[0]]); 1030 | } 1031 | } 1032 | } 1033 | return $data; 1034 | } 1035 | 1036 | /** 1037 | * 自动表单验证 1038 | * @access protected 1039 | * @param array $data 创建数据 1040 | * @param string $type 创建类型 1041 | * @return boolean 1042 | */ 1043 | protected function autoValidation($data,$type) { 1044 | if(!empty($this->options['validate'])) { 1045 | $_validate = $this->options['validate']; 1046 | unset($this->options['validate']); 1047 | }elseif(!empty($this->_validate)){ 1048 | $_validate = $this->_validate; 1049 | } 1050 | // 属性验证 1051 | if(isset($_validate)) { // 如果设置了数据自动验证则进行数据验证 1052 | if($this->patchValidate) { // 重置验证错误信息 1053 | $this->error = array(); 1054 | } 1055 | foreach($_validate as $key=>$val) { 1056 | // 验证因子定义格式 1057 | // array(field,rule,message,condition,type,when,params) 1058 | // 判断是否需要执行验证 1059 | if(empty($val[5]) || $val[5]== self::MODEL_BOTH || $val[5]== $type ) { 1060 | if(0==strpos($val[2],'{%') && strpos($val[2],'}')) 1061 | // 支持提示信息的多语言 使用 {%语言定义} 方式 1062 | $val[2] = L(substr($val[2],2,-1)); 1063 | $val[3] = isset($val[3])?$val[3]:self::EXISTS_VALIDATE; 1064 | $val[4] = isset($val[4])?$val[4]:'regex'; 1065 | // 判断验证条件 1066 | switch($val[3]) { 1067 | case self::MUST_VALIDATE: // 必须验证 不管表单是否有设置该字段 1068 | if(false === $this->_validationField($data,$val)) 1069 | return false; 1070 | break; 1071 | case self::VALUE_VALIDATE: // 值不为空的时候才验证 1072 | if('' != trim($data[$val[0]])) 1073 | if(false === $this->_validationField($data,$val)) 1074 | return false; 1075 | break; 1076 | default: // 默认表单存在该字段就验证 1077 | if(isset($data[$val[0]])) 1078 | if(false === $this->_validationField($data,$val)) 1079 | return false; 1080 | } 1081 | } 1082 | } 1083 | // 批量验证的时候最后返回错误 1084 | if(!empty($this->error)) return false; 1085 | } 1086 | return true; 1087 | } 1088 | 1089 | /** 1090 | * 验证表单字段 支持批量验证 1091 | * 如果批量验证返回错误的数组信息 1092 | * @access protected 1093 | * @param array $data 创建数据 1094 | * @param array $val 验证因子 1095 | * @return boolean 1096 | */ 1097 | protected function _validationField($data,$val) { 1098 | if($this->patchValidate && isset($this->error[$val[0]])) 1099 | return ; //当前字段已经有规则验证没有通过 1100 | if(false === $this->_validationFieldItem($data,$val)){ 1101 | if($this->patchValidate) { 1102 | $this->error[$val[0]] = $val[2]; 1103 | }else{ 1104 | $this->error = $val[2]; 1105 | return false; 1106 | } 1107 | } 1108 | return ; 1109 | } 1110 | 1111 | /** 1112 | * 根据验证因子验证字段 1113 | * @access protected 1114 | * @param array $data 创建数据 1115 | * @param array $val 验证因子 1116 | * @return boolean 1117 | */ 1118 | protected function _validationFieldItem($data,$val) { 1119 | switch(strtolower(trim($val[4]))) { 1120 | case 'function':// 使用函数进行验证 1121 | case 'callback':// 调用方法进行验证 1122 | $args = isset($val[6])?(array)$val[6]:array(); 1123 | if(is_string($val[0]) && strpos($val[0], ',')) 1124 | $val[0] = explode(',', $val[0]); 1125 | if(is_array($val[0])){ 1126 | // 支持多个字段验证 1127 | foreach($val[0] as $field) 1128 | $_data[$field] = $data[$field]; 1129 | array_unshift($args, $_data); 1130 | }else{ 1131 | array_unshift($args, $data[$val[0]]); 1132 | } 1133 | if('function'==$val[4]) { 1134 | return call_user_func_array($val[1], $args); 1135 | }else{ 1136 | return call_user_func_array(array(&$this, $val[1]), $args); 1137 | } 1138 | case 'confirm': // 验证两个字段是否相同 1139 | return $data[$val[0]] == $data[$val[1]]; 1140 | case 'unique': // 验证某个值是否唯一 1141 | if(is_string($val[0]) && strpos($val[0],',')) 1142 | $val[0] = explode(',',$val[0]); 1143 | $map = array(); 1144 | if(is_array($val[0])) { 1145 | // 支持多个字段验证 1146 | foreach ($val[0] as $field) 1147 | $map[$field] = $data[$field]; 1148 | }else{ 1149 | $map[$val[0]] = $data[$val[0]]; 1150 | } 1151 | if(!empty($data[$this->getPk()])) { // 完善编辑的时候验证唯一 1152 | $map[$this->getPk()] = array('neq',$data[$this->getPk()]); 1153 | } 1154 | if($this->where($map)->find()) return false; 1155 | return true; 1156 | default: // 检查附加规则 1157 | return $this->check($data[$val[0]],$val[1],$val[4]); 1158 | } 1159 | } 1160 | 1161 | /** 1162 | * 验证数据 支持 in between equal length regex expire ip_allow ip_deny 1163 | * @access public 1164 | * @param string $value 验证数据 1165 | * @param mixed $rule 验证表达式 1166 | * @param string $type 验证方式 默认为正则验证 1167 | * @return boolean 1168 | */ 1169 | public function check($value,$rule,$type='regex'){ 1170 | $type = strtolower(trim($type)); 1171 | switch($type) { 1172 | case 'in': // 验证是否在某个指定范围之内 逗号分隔字符串或者数组 1173 | case 'notin': 1174 | $range = is_array($rule)? $rule : explode(',',$rule); 1175 | return $type == 'in' ? in_array($value ,$range) : !in_array($value ,$range); 1176 | case 'between': // 验证是否在某个范围 1177 | case 'notbetween': // 验证是否不在某个范围 1178 | if (is_array($rule)){ 1179 | $min = $rule[0]; 1180 | $max = $rule[1]; 1181 | }else{ 1182 | list($min,$max) = explode(',',$rule); 1183 | } 1184 | return $type == 'between' ? $value>=$min && $value<=$max : $value<$min || $value>$max; 1185 | case 'equal': // 验证是否等于某个值 1186 | case 'notequal': // 验证是否等于某个值 1187 | return $type == 'equal' ? $value == $rule : $value != $rule; 1188 | case 'length': // 验证长度 1189 | $length = mb_strlen($value,'utf-8'); // 当前数据长度 1190 | if(strpos($rule,',')) { // 长度区间 1191 | list($min,$max) = explode(',',$rule); 1192 | return $length >= $min && $length <= $max; 1193 | }else{// 指定长度 1194 | return $length == $rule; 1195 | } 1196 | case 'expire': 1197 | list($start,$end) = explode(',',$rule); 1198 | if(!is_numeric($start)) $start = strtotime($start); 1199 | if(!is_numeric($end)) $end = strtotime($end); 1200 | return NOW_TIME >= $start && NOW_TIME <= $end; 1201 | case 'ip_allow': // IP 操作许可验证 1202 | return in_array(get_client_ip(),explode(',',$rule)); 1203 | case 'ip_deny': // IP 操作禁止验证 1204 | return !in_array(get_client_ip(),explode(',',$rule)); 1205 | case 'regex': 1206 | default: // 默认使用正则验证 可以使用验证类中定义的验证名称 1207 | // 检查附加规则 1208 | return $this->regex($value,$rule); 1209 | } 1210 | } 1211 | 1212 | /** 1213 | * SQL查询 1214 | * @access public 1215 | * @param string $sql SQL指令 1216 | * @param mixed $parse 是否需要解析SQL 1217 | * @return mixed 1218 | */ 1219 | public function query($sql,$parse=false) { 1220 | if(!is_bool($parse) && !is_array($parse)) { 1221 | $parse = func_get_args(); 1222 | array_shift($parse); 1223 | } 1224 | $sql = $this->parseSql($sql,$parse); 1225 | return $this->db->query($sql); 1226 | } 1227 | 1228 | /** 1229 | * 执行SQL语句 1230 | * @access public 1231 | * @param string $sql SQL指令 1232 | * @param mixed $parse 是否需要解析SQL 1233 | * @return false | integer 1234 | */ 1235 | public function execute($sql,$parse=false) { 1236 | if(!is_bool($parse) && !is_array($parse)) { 1237 | $parse = func_get_args(); 1238 | array_shift($parse); 1239 | } 1240 | $sql = $this->parseSql($sql,$parse); 1241 | return $this->db->execute($sql); 1242 | } 1243 | 1244 | /** 1245 | * 解析SQL语句 1246 | * @access public 1247 | * @param string $sql SQL指令 1248 | * @param boolean $parse 是否需要解析SQL 1249 | * @return string 1250 | */ 1251 | protected function parseSql($sql,$parse) { 1252 | // 分析表达式 1253 | if(true === $parse) { 1254 | $options = $this->_parseOptions(); 1255 | $sql = $this->db->parseSql($sql,$options); 1256 | }elseif(is_array($parse)){ // SQL预处理 1257 | $parse = array_map(array($this->db,'escapeString'),$parse); 1258 | $sql = vsprintf($sql,$parse); 1259 | }else{ 1260 | $sql = strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>$this->tablePrefix)); 1261 | $prefix = $this->tablePrefix; 1262 | $sql = preg_replace_callback("/__([A-Z_-]+)__/sU", function($match) use($prefix){ return $prefix.strtolower($match[1]);}, $sql); 1263 | } 1264 | $this->db->setModel($this->name); 1265 | return $sql; 1266 | } 1267 | 1268 | /** 1269 | * 切换当前的数据库连接 1270 | * @access public 1271 | * @param integer $linkNum 连接序号 1272 | * @param mixed $config 数据库连接信息 1273 | * @param boolean $force 强制重新连接 1274 | * @return Model 1275 | */ 1276 | public function db($linkNum='',$config='',$force=false) { 1277 | if('' === $linkNum && $this->db) { 1278 | return $this->db; 1279 | } 1280 | 1281 | static $_db = array(); 1282 | if(!isset($_db[$linkNum]) || $force ) { 1283 | // 创建一个新的实例 1284 | if(!empty($config) && is_string($config) && false === strpos($config,'/')) { // 支持读取配置参数 1285 | $config = C($config); 1286 | } 1287 | $_db[$linkNum] = Db::getInstance($config); 1288 | }elseif(NULL === $config){ 1289 | $_db[$linkNum]->close(); // 关闭数据库连接 1290 | unset($_db[$linkNum]); 1291 | return ; 1292 | } 1293 | 1294 | // 切换数据库连接 1295 | $this->db = $_db[$linkNum]; 1296 | $this->_after_db(); 1297 | // 字段检测 1298 | if(!empty($this->name) && $this->autoCheckFields) $this->_checkTableInfo(); 1299 | return $this; 1300 | } 1301 | // 数据库切换后回调方法 1302 | protected function _after_db() {} 1303 | 1304 | /** 1305 | * 得到当前的数据对象名称 1306 | * @access public 1307 | * @return string 1308 | */ 1309 | public function getModelName() { 1310 | if(empty($this->name)){ 1311 | $name = substr(get_class($this),0,-strlen(C('DEFAULT_M_LAYER'))); 1312 | if ( $pos = strrpos($name,'\\') ) {//有命名空间 1313 | $this->name = substr($name,$pos+1); 1314 | }else{ 1315 | $this->name = $name; 1316 | } 1317 | } 1318 | return $this->name; 1319 | } 1320 | 1321 | /** 1322 | * 得到完整的数据表名 1323 | * @access public 1324 | * @return string 1325 | */ 1326 | public function getTableName() { 1327 | if(empty($this->trueTableName)) { 1328 | $tableName = !empty($this->tablePrefix) ? $this->tablePrefix : ''; 1329 | if(!empty($this->tableName)) { 1330 | $tableName .= $this->tableName; 1331 | }else{ 1332 | $tableName .= parse_name($this->name); 1333 | } 1334 | $this->trueTableName = strtolower($tableName); 1335 | } 1336 | return (!empty($this->dbName)?$this->dbName.'.':'').$this->trueTableName; 1337 | } 1338 | 1339 | /** 1340 | * 启动事务 1341 | * @access public 1342 | * @return void 1343 | */ 1344 | public function startTrans() { 1345 | $this->commit(); 1346 | $this->db->startTrans(); 1347 | return ; 1348 | } 1349 | 1350 | /** 1351 | * 提交事务 1352 | * @access public 1353 | * @return boolean 1354 | */ 1355 | public function commit() { 1356 | return $this->db->commit(); 1357 | } 1358 | 1359 | /** 1360 | * 事务回滚 1361 | * @access public 1362 | * @return boolean 1363 | */ 1364 | public function rollback() { 1365 | return $this->db->rollback(); 1366 | } 1367 | 1368 | /** 1369 | * 返回模型的错误信息 1370 | * @access public 1371 | * @return string 1372 | */ 1373 | public function getError(){ 1374 | return $this->error; 1375 | } 1376 | 1377 | /** 1378 | * 返回数据库的错误信息 1379 | * @access public 1380 | * @return string 1381 | */ 1382 | public function getDbError() { 1383 | return $this->db->getError(); 1384 | } 1385 | 1386 | /** 1387 | * 返回最后插入的ID 1388 | * @access public 1389 | * @return string 1390 | */ 1391 | public function getLastInsID() { 1392 | return $this->db->getLastInsID(); 1393 | } 1394 | 1395 | /** 1396 | * 返回最后执行的sql语句 1397 | * @access public 1398 | * @return string 1399 | */ 1400 | public function getLastSql() { 1401 | return $this->db->getLastSql($this->name); 1402 | } 1403 | // 鉴于getLastSql比较常用 增加_sql 别名 1404 | public function _sql(){ 1405 | return $this->getLastSql(); 1406 | } 1407 | 1408 | /** 1409 | * 获取主键名称 1410 | * @access public 1411 | * @return string 1412 | */ 1413 | public function getPk() { 1414 | return $this->pk; 1415 | } 1416 | 1417 | /** 1418 | * 获取数据表字段信息 1419 | * @access public 1420 | * @return array 1421 | */ 1422 | public function getDbFields(){ 1423 | if(isset($this->options['table'])) {// 动态指定表名 1424 | $array = explode(' ',$this->options['table']); 1425 | $fields = $this->db->getFields($array[0]); 1426 | return $fields?array_keys($fields):false; 1427 | } 1428 | if($this->fields) { 1429 | $fields = $this->fields; 1430 | unset($fields['_type'],$fields['_pk']); 1431 | return $fields; 1432 | } 1433 | return false; 1434 | } 1435 | 1436 | /** 1437 | * 设置数据对象值 1438 | * @access public 1439 | * @param mixed $data 数据 1440 | * @return Model 1441 | */ 1442 | public function data($data=''){ 1443 | if('' === $data && !empty($this->data)) { 1444 | return $this->data; 1445 | } 1446 | if(is_object($data)){ 1447 | $data = get_object_vars($data); 1448 | }elseif(is_string($data)){ 1449 | parse_str($data,$data); 1450 | }elseif(!is_array($data)){ 1451 | E(L('_DATA_TYPE_INVALID_')); 1452 | } 1453 | $this->data = $data; 1454 | return $this; 1455 | } 1456 | 1457 | /** 1458 | * 指定当前的数据表 1459 | * @access public 1460 | * @param mixed $table 1461 | * @return Model 1462 | */ 1463 | public function table($table) { 1464 | $prefix = $this->tablePrefix; 1465 | if(is_array($table)) { 1466 | $this->options['table'] = $table; 1467 | }elseif(!empty($table)) { 1468 | //将__TABLE_NAME__替换成带前缀的表名 1469 | $table = preg_replace_callback("/__([A-Z_-]+)__/sU", function($match) use($prefix){ return $prefix.strtolower($match[1]);}, $table); 1470 | $this->options['table'] = $table; 1471 | } 1472 | return $this; 1473 | } 1474 | 1475 | /** 1476 | * 查询SQL组装 join 1477 | * @access public 1478 | * @param mixed $join 1479 | * @param string $type JOIN类型 1480 | * @return Model 1481 | */ 1482 | public function join($join,$type='INNER') { 1483 | $prefix = $this->tablePrefix; 1484 | if(is_array($join)) { 1485 | foreach ($join as $key=>&$_join){ 1486 | $_join = preg_replace_callback("/__([A-Z_-]+)__/sU", function($match) use($prefix){ return $prefix.strtolower($match[1]);}, $_join); 1487 | $_join = false !== stripos($_join,'JOIN')? $_join : $type.' JOIN ' .$_join; 1488 | } 1489 | $this->options['join'] = $join; 1490 | }elseif(!empty($join)) { 1491 | //将__TABLE_NAME__字符串替换成带前缀的表名 1492 | $join = preg_replace_callback("/__([A-Z_-]+)__/sU", function($match) use($prefix){ return $prefix.strtolower($match[1]);}, $join); 1493 | $this->options['join'][] = false !== stripos($join,'JOIN')? $join : $type.' JOIN '.$join; 1494 | } 1495 | return $this; 1496 | } 1497 | 1498 | /** 1499 | * 查询SQL组装 union 1500 | * @access public 1501 | * @param mixed $union 1502 | * @param boolean $all 1503 | * @return Model 1504 | */ 1505 | public function union($union,$all=false) { 1506 | if(empty($union)) return $this; 1507 | if($all) { 1508 | $this->options['union']['_all'] = true; 1509 | } 1510 | if(is_object($union)) { 1511 | $union = get_object_vars($union); 1512 | } 1513 | // 转换union表达式 1514 | if(is_string($union) ) { 1515 | $prefix = $this->tablePrefix; 1516 | //将__TABLE_NAME__字符串替换成带前缀的表名 1517 | $options = preg_replace_callback("/__([A-Z_-]+)__/sU", function($match) use($prefix){ return $prefix.strtolower($match[1]);}, $union); 1518 | }elseif(is_array($union)){ 1519 | if(isset($union[0])) { 1520 | $this->options['union'] = array_merge($this->options['union'],$union); 1521 | return $this; 1522 | }else{ 1523 | $options = $union; 1524 | } 1525 | }else{ 1526 | E(L('_DATA_TYPE_INVALID_')); 1527 | } 1528 | $this->options['union'][] = $options; 1529 | return $this; 1530 | } 1531 | 1532 | /** 1533 | * 查询缓存 1534 | * @access public 1535 | * @param mixed $key 1536 | * @param integer $expire 1537 | * @param string $type 1538 | * @return Model 1539 | */ 1540 | public function cache($key=true,$expire=null,$type=''){ 1541 | if(false !== $key) 1542 | $this->options['cache'] = array('key'=>$key,'expire'=>$expire,'type'=>$type); 1543 | return $this; 1544 | } 1545 | 1546 | /** 1547 | * 指定查询字段 支持字段排除 1548 | * @access public 1549 | * @param mixed $field 1550 | * @param boolean $except 是否排除 1551 | * @return Model 1552 | */ 1553 | public function field($field,$except=false){ 1554 | if(true === $field) {// 获取全部字段 1555 | $fields = $this->getDbFields(); 1556 | $field = $fields?:'*'; 1557 | }elseif($except) {// 字段排除 1558 | if(is_string($field)) { 1559 | $field = explode(',',$field); 1560 | } 1561 | $fields = $this->getDbFields(); 1562 | $field = $fields?array_diff($fields,$field):$field; 1563 | } 1564 | $this->options['field'] = $field; 1565 | return $this; 1566 | } 1567 | 1568 | /** 1569 | * 调用命名范围 1570 | * @access public 1571 | * @param mixed $scope 命名范围名称 支持多个 和直接定义 1572 | * @param array $args 参数 1573 | * @return Model 1574 | */ 1575 | public function scope($scope='',$args=NULL){ 1576 | if('' === $scope) { 1577 | if(isset($this->_scope['default'])) { 1578 | // 默认的命名范围 1579 | $options = $this->_scope['default']; 1580 | }else{ 1581 | return $this; 1582 | } 1583 | }elseif(is_string($scope)){ // 支持多个命名范围调用 用逗号分割 1584 | $scopes = explode(',',$scope); 1585 | $options = array(); 1586 | foreach ($scopes as $name){ 1587 | if(!isset($this->_scope[$name])) continue; 1588 | $options = array_merge($options,$this->_scope[$name]); 1589 | } 1590 | if(!empty($args) && is_array($args)) { 1591 | $options = array_merge($options,$args); 1592 | } 1593 | }elseif(is_array($scope)){ // 直接传入命名范围定义 1594 | $options = $scope; 1595 | } 1596 | 1597 | if(is_array($options) && !empty($options)){ 1598 | $this->options = array_merge($this->options,array_change_key_case($options)); 1599 | } 1600 | return $this; 1601 | } 1602 | 1603 | /** 1604 | * 指定查询条件 支持安全过滤 1605 | * @access public 1606 | * @param mixed $where 条件表达式 1607 | * @param mixed $parse 预处理参数 1608 | * @return Model 1609 | */ 1610 | public function where($where,$parse=null){ 1611 | if(!is_null($parse) && is_string($where)) { 1612 | if(!is_array($parse)) { 1613 | $parse = func_get_args(); 1614 | array_shift($parse); 1615 | } 1616 | $parse = array_map(array($this->db,'escapeString'),$parse); 1617 | $where = vsprintf($where,$parse); 1618 | }elseif(is_object($where)){ 1619 | $where = get_object_vars($where); 1620 | } 1621 | if(is_string($where) && '' != $where){ 1622 | $map = array(); 1623 | $map['_string'] = $where; 1624 | $where = $map; 1625 | } 1626 | if(isset($this->options['where'])){ 1627 | $this->options['where'] = array_merge($this->options['where'],$where); 1628 | }else{ 1629 | $this->options['where'] = $where; 1630 | } 1631 | 1632 | return $this; 1633 | } 1634 | 1635 | /** 1636 | * 指定查询数量 1637 | * @access public 1638 | * @param mixed $offset 起始位置 1639 | * @param mixed $length 查询数量 1640 | * @return Model 1641 | */ 1642 | public function limit($offset,$length=null){ 1643 | if(is_null($length) && strpos($offset,',')){ 1644 | list($offset,$length) = explode(',',$offset); 1645 | } 1646 | $this->options['limit'] = intval($offset).( $length? ','.intval($length) : '' ); 1647 | return $this; 1648 | } 1649 | 1650 | /** 1651 | * 指定分页 1652 | * @access public 1653 | * @param mixed $page 页数 1654 | * @param mixed $listRows 每页数量 1655 | * @return Model 1656 | */ 1657 | public function page($page,$listRows=null){ 1658 | if(is_null($listRows) && strpos($page,',')){ 1659 | list($page,$listRows) = explode(',',$page); 1660 | } 1661 | $this->options['page'] = array(intval($page),intval($listRows)); 1662 | return $this; 1663 | } 1664 | 1665 | /** 1666 | * 查询注释 1667 | * @access public 1668 | * @param string $comment 注释 1669 | * @return Model 1670 | */ 1671 | public function comment($comment){ 1672 | $this->options['comment'] = $comment; 1673 | return $this; 1674 | } 1675 | 1676 | /** 1677 | * 参数绑定 1678 | * @access public 1679 | * @param string $key 参数名 1680 | * @param mixed $value 绑定的变量及绑定参数 1681 | * @return Model 1682 | */ 1683 | public function bind($key,$value=false) { 1684 | if(is_array($key)){ 1685 | $this->options['bind'] = $key; 1686 | }else{ 1687 | $num = func_num_args(); 1688 | if($num>2){ 1689 | $params = func_get_args(); 1690 | array_shift($params); 1691 | $this->options['bind'][$key] = $params; 1692 | }else{ 1693 | $this->options['bind'][$key] = $value; 1694 | } 1695 | } 1696 | return $this; 1697 | } 1698 | 1699 | /** 1700 | * 设置模型的属性值 1701 | * @access public 1702 | * @param string $name 名称 1703 | * @param mixed $value 值 1704 | * @return Model 1705 | */ 1706 | public function setProperty($name,$value) { 1707 | if(property_exists($this,$name)) 1708 | $this->$name = $value; 1709 | return $this; 1710 | } 1711 | 1712 | } 1713 | 1714 | echo 'success'; --------------------------------------------------------------------------------