├── 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 |
2 | $name = 'Index';
3 | echo parse_name($name, 1), "\r\n";
4 |
5 | function get_a($a){
6 | return '1';
7 | }
8 | function parse_name($name, $type = 0) {
9 | if ($type) {
10 | return ucfirst(preg_replace_callback('/_([a-zA-Z])/', function ($match) {
11 | return strtoupper($match[1]);
12 | }, $name));
13 | } else {
14 | return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
15 | }
16 | }
17 |
18 | ?>
--------------------------------------------------------------------------------
/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 | function block_icons($list) { ?>
2 |
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 | 
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 | 
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, '$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})/', '$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';
--------------------------------------------------------------------------------