在前文代理类中提到,本框架的数据库调用方法通过代理的模式实现多接口、多数据库调用,具体声明方式详见代理类说明。
扩展类
数据库类中包含两个扩展类,分别是 base_db 和 base_sql。
base_db
本扩展类中设置了通用的数据库方法,包括:
1 | safeName | 规范数据表字段名称,去除非法字符 |
2 | safeValue | 规范数据内容,去除非法字符,避免注入危险,相对于 mysql_real_escape_string 等方法,可以在未连接数据库的情况下执行 |
3 | cache | 设置外部缓存模式 0 - 关闭, 1 - 可读, 2 - 可写,3 - 可读写 |
4 | setCache | 传入缓存实例,此实例必须为框架自带的myCache类(可通过代理模式扩展) |
5 | getCache | 获取缓存内容,参考缓存模式权限 |
6 | writeCache | 写入缓存内容,参考缓存模式权限 |
base _sql
本扩展类是针对标准数据库服务的扩充,预定义了以下内容:
- 服务器配置参数,包括数据库服务器地址($host)、用户($user)、密码($pwd)、字符编码($charset)、当前查询($sql)、当前数据库($db)、定界符($delimiter )等,相关参数均为内部参数,如功能类引用了此扩展,需要遵循以上变量名称。
- SQL构建方法,基于SQLBuilder 类,并扩充了CRUD的多关系表操作,包括 select, update, delete, record(返回单一记录), records(返回全部记录), result(返回第一行第一项), count(返回某查询记录数) 等,相关方法基于mysql,如有语法不同,可编写对应转换方法,或在功能类中重写对应方法。
调用方式
只要在对应的功能类中 use 对应的扩展类即可,如下:
class someDatabase {
use base_db, base_sql;
......
}
单表查询
SQLBuilder 为查询语句生成类,可以通过简单的链式操作,生成复杂的SQL查询语句。
构造函数
本函数包含四个参数,如下:
1 | $tbl | 数据表名称 |
2 | $join | 连接关系,包含 mode(left,right 或 inner),field(关联字段名),field_join(如关联字段与主表名称不一致,此项为主表对应字段的名称) |
3 | $idx | 数据表别名(便于引用) |
4 | $delimiter | 名称定界符,用于区分关键字,如mysql中的“`”或mssql中的“[]” |
reset
将类内所有变量初始化,如参数1设定为true的话,连表名、别名和连接关系也一同复位。
field
指定当前表查询字段,可为如下几种情况:
- 针对 select 查询,参数可为以逗号间隔的字符串,或者每个字段为一个独立项的数组,也可以为多个参数,每个参数对应一个字段。字段项目可以为如下特殊格式:
- ['alias_name', 'col_name'] 或 ‘alias_name as col_name’ 或 ‘alias_name:col_name’ 或 ‘alias_name=>col_name’,表示给对应字段指派别名。
- ‘func(para1, para2)’,表示使用数据库自带函数,如 length(col_name)。
相关示例如下:$db->build('table_name') ->field('col_a, col_b, col_c') ->field([ 'col_name', 'name:name_alias', 'count(*) as cnt', 'sum(amount*price) as v1', ['length(col1)+length(col2)', 'v2'], ]);
以上代码将被转换为:select `col_a`,`col_b`,`col_c`, `col_name`, `name` as `name_alias`, count(*) as `cnt`, sum(amount*price) as `v1`, length(col1)+length(col2) as `v2` from `table_name` as t0
注意,如果是非函数模式格式,非标准名称字符([^/w]+)将被自动替换掉。 - 针对 update 更新,参数为一个键、值对应的数据,其中键为字段名,值为需要更新的数据,也可以将这个数组拆开为多个参数。数据值可以为以下格式:
- ['table_name', 'col_name'] 模式的数组,表示跨表更新
- null 或 字符串‘null’,表示空值
- ‘+n’ 或 ‘-n’ ,其中 n 为数字,表示在现有基础上加减 n
- ‘(表达式或数据库函数)’,直接执行对应的表达式或数据库函数
- 针对 insert 添加,模式与 update 或 select的普通模式 一样,如果插入数据包含数据表中的每一项,也可以不用field,直接通过values添加。
- 参数为 'distinct' 或 'distinctrow' 表示为查询添加唯一限定,需要与其他 field 方法组合使用,如 $db->field('distinct')->field('name')。
- 如果参数为 ‘[reset]’ ,则清空现有字段数据
- 如果未提供参数,则返回解析好的目前所记录的字段信息。
本方法可连续多次使用,相关设置将被累加,可通过 ‘[reset]’ 关键字或 reset 方法复位。
values
为 insert 语句添加记录行,内容可以为数组,每项对应一行所需字段,可以为多参数,每参数对应一个指定字段。所对应的指定字段由 field方法 传入,如为update格式,则会将对应的值转换为一条value数据,如:
$db->build('table_name')->field(array(
'id' => 0,
'name' => 'name',
'file' => 'file',
'path' => '/',
'comment' => 'nothing'
))
->values(0,'name','file','/','nothing')
->values(
[0,'name','file','/','nothing'],
[0,'name','file','/','nothing']
);
以上代码将被解析为:
insert LOW_PRIORITY into `table_name`
(`id`,`name`,`file`,`path`,`comment`)
values
('0','name','file','/','nothing'), #由 field 方法添加
('0','name','file','/','nothing'), #由第一种方式的 value 方法添加
('0','name','file','/','nothing'), #由第二种方式的 value 方法添加
('0','name','file','/','nothing'); #由第二种方式的 value 方法添加
本方法可连续多次使用,相关设置将被累加,可通过 ‘[reset]’ 关键字或 reset 方法复位。
where
用于生成查询条件,包含如下模式:
- $db->where($field, $condition, $value, $mode),本方法的通用模式,四个参数分别对应字段、条件、判断值和条件关系(and 或 or,默认为 and),其中条件参数后面将展开说明,如果 $field 为表达式或者算式等不需要解析的内容,则需要在内容外加上一个括号;如果 $value 不需要解析,则需要使用 'f' 系列的 $condition,如:
$db->build('table_name') ->where('idx', '>', 'a01') ->where('left(x,3)','f>','right(y,3)')
->where('(sign & 1)','n=',1);以上代码将被解析为:select * from `table_name` as t0 where `idx` > 'a01' and left(x,3) > right(y,3) and (sign & 1) = 1
- $db->where($expression),$expression可以为表达式;如果为字段名,则表示此字段不能为空;如果为函数或表达式,则需要用括号括起,如:
$db->build('table_name') ->where('image') ->where('left(x,3)>right(y,3)') ->where('(isnull(style))');
以上代码将被解析为:select * from `table_name` as t0 where `image` is not NULL and left(x,3) > right(y,3) and isnull(style)
需要注意的是,如果最后一个 where('(isnull(style))') 不带括号而改为 where('isnull(style)') ,则会被解析为 `isnull(style)` is not NULL - $db->where($condition_array),用于符合条件的生成,由多级数组构成,每一级数组为一个条件组,最低阶格式为 [$field, $condition, $value, $mode] 的模式,每组条件最后一个值表示 条件关系(and 或 or),具体模式参考如下:
其中,每个数组为一个组,每组第一项无需设置 条件关系,其他组如未设置的话,默认为 and ,可以通过多级子数组嵌套来生成复杂查询,其中里面用到了 n> , d>, nin 等条件,其意义将在下文讲解,以上条件将生成如下查询语句:$db->build('table_name')->where( array( array('no', 'n>', '1\' and no!=5 and name!=\''), array( array('tag', 'like', '1'), array('tag', 'like', '2%', 'or'), array('tag', 'like', '%3', 'or'), 'and' ), array( array('name', '<>', 'xxxx'), array('id', 'nin', '1, 2, a3, 5a, 5', 'or'), 'or' ), array('date', 'd>', array('now()', 'y-1'), 'or'), ), )
select * from `table_name` as t0 where ( `no` > 1 and ( `tag` like '%1%' or `tag` like '2%' or `tag` like '%3' ) or ( `name` <> 'xxxx' or `id` in (1,2,0,5,5) ) or `date` > DATE_SUB(now(), INTERVAL 1 YEAR) )
- $db->where('[reset]'),将清空现有条件数据。
- 如果未提供参数,则返回解析好的条件字符串。
本方法可连续多次使用,相关设置将被累加,可通过 ‘[reset]’ 关键字或 reset 方法复位。
order
用于指定排序依据,包含两个参数,参数一表示排序字段,参数二表示是否倒叙(布尔值),如:
$db->build('table_name')->order('id', true);
将被翻译为:
select * from `table_name` as t0 order by `id` desc;
如果参数为 ‘[reset]’ ,则清空现有字段数据,参数也可以为 ‘rand()’ 等数据库方法。
如果未提供参数,则返回解析好的排序字符串。
本方法可连续多次使用,相关设置将被累加,可通过 ‘[reset]’ 关键字或 reset 方法复位。
limit
用于指定结果集返回记录数量,可以有如下形式:
$db->build('table_name')->limit(5);
$db->build('table_name')->limit(5, 10);
$db->build('table_name')->limit('5, 10');
本方法保持数字安全,非数字字符将被忽略。
group
用于指分组信息及相关取值条件,可以有如下形式:
$db->build('table_name')->field(['field_name', 'count(*) as cnt'])
->group('field_name')
->group(['field_name', 'cnt>3'])
->group('field_name', ['cnt','n>',3])
->group(['field'=>'field_name', 'having'=>'cnt>3']);
每次使用group方法将会自动覆盖之前所设置的参数,以上代码将被解析为:
select `field_name`,count(*) as `cnt`
from `table_name` as t0
group by `field_name` having `cnt` > 3;
select
依照以上各查询属性的设置,返回生成select语句,需要注意的是本方法仅针对生成器类 SQLBuilder 的单数据库。
insert
依照以上各查询属性的设置,返回生成insert语句,参数一默认为 false,如果设置为 true,将生成 MySQL 的replace语句。
update
依照以上各查询属性的设置,返回生成update语句,需要在 field 方法中按照对应的格式传入参数,本方法无参数。
delete
依照以上各查询属性的设置,返回生成delete语句,需预先设置条件(where),否则将清空数据库,无参数。
查询条件
在where方法中,基本条件判断的模式是 [比较值, 检测条件, 检测值] 的模式,规则如下:
- 比较值:可为字段名、表达式或者数据库函数,如为比较复杂的非字段内容,建议在外层加上括号,以避免安全性检测将其中特殊字符去除。
- 检测条件:及其对应说明如下:
1 = , < , <= , > , >= ,
<> , !=直接将比较值与检测值进行判断,检测值只做安全性处理 2 b= 进行二进制模式的字符串比较,保证大小写一致 3 n= , n< , n<= , n> , n>= ,
n<> , n!=数字比较,检测值将被强制转换为数值 4 d= , d< , d<= , d> , d>= ,
d<> , d!=日期比较,检测值为一个两项数据,第一项为需比较的时间,第二项为时间差距格式为:时间代码+/-差距数值,其中时间代码为:y年,m月,d日,q季度,w星期,h小时,具体模式为:['now()', 'y-1'] 表示据当前时间1年以前,['date', 'd+1'] 表示据date字段的时间1天以后。
注:本方法仅针对MySQL5 f= , f< , f<= , f> , f>= ,
f<> , f!=本方法用于表达式或数据库函数比较,检测值将被视为表达式或数据库函数,不会进行字符串安全检测。 6 like, like binary, not like,
not like binary字符串模糊查询,其中binary选项对大小写敏感,检测值如果首尾不带 % 等同于首尾均带有 %,也可以根据需要在某一边加上 % 7 rlike, regexp, rlike binary,
regexp binary, not rlike,
not regexp, not rlike binary,
not regexp binary基于正则表达式的字符串判断。
注:本方法仅针对MySQL8 in, nin, not in, not nin 包含判断,也就是 field_name in ('V1', 'V2',....,'Vn') 的模式,带nin的条件表示相关取值范围均为数值。 9 is, is not 判断检测值是否为 NULL 10 ! 表示比较值本身即为符合条件判断的表达式或者数据库函数,并将忽略检测条件和检测值 11 空值NULL 如比较值外部有一对括号,则按照比较值本身即为符合条件判断的表达式或者数据库函数处理,否则将作为 is not null 的判断,本模式将忽略检测条件和检测值 - 检测值:用于和比较值通过检测条件相比较的数值、字符串、表达式或数据库函数,如为表达式或数据库函数,建议在外层加上括号,以避免安全性检测将其中特殊字符去除。
条件查询中除个别函数或表达式外,大部分值判断都会对检测值进行字符串安全性处理,去除可能造成注入的代码。
多表查询
与单表查询类似,多表查询通过数据库类实现,主要步骤如下:
数据库类添加base_sql 扩展,引入 build 方法,此方法的功能是,针对某一个表建立一个 SQLBuilder 类实例,如果已建立则直接返回已建立的实例,本方法包含两个参数,参数一为表名,参数二为关系表连接信息(参考 SQLBuilder 类构造函数相关说明)。本方法会自动为每个表设定别名,并传入本数据库类内定义的数据库关键字定界符($this->delimiter)。如果第一个参数为“[reset]”,则将清除所有已设置的表查询设置。
用户需要通过 $db->build('table_name') 所返回的查询构造类实例来设定相关查询参数,具体方法与单表查询一致。在多表关联查询时,扩展类多提供的方法可以直接生成连接查询语句,如对应数据库与MySQL语法差距较大,可依照扩展类的相关代码,重写对应方法,或者编写对应的转换方法(可参考 mssql.class.php 中的 convertSQL 和convertLimit 两个方法)。
select 连接查询生成:
$db->build('tbl1')->field('col1, col2');
$db->build('tbl1')
->where('id', 'nin', '1, 2, a3, 5a, 5')
->where('date', 'd>', array('now()', 'y-1'))
->where('col1', '<>', 'xxx')
->where('col2', 'f=', 'left(subject, 10)')
->where('col3', 'like', 'xxx');
$db->build('tbl2', array(
'mode' => 'left',
'field' => 'news_id'
))
->field(['field_name', 'count(*) as cnt'])
->group(['field_name', 'cnt>3']);
$db->build('tbl1')
->where('col4')
->where('(isnull(col5))')
->where('id', 'n<', '10')
->limit(5);
以上代码将被解析为:
select
`t0`.`col1`,`t0`.`col2`,
`t1`.`field_name`,count(*) as `cnt`
from
`tbl1` as t0 left join `tbl2` as t1 using(`news_id`)
where
`t0`.`id` in (1,2,0,5,5)
and `t0`.`date` > DATE_SUB(now(), INTERVAL 1 YEAR)
and `t0`.`col1` <> 'xxx'
and `t0`.`col2` = left(subject, 10)
and `t0`.`col3` like '%xxx%'
and `t0`.`col4` is not NULL
and isnull(t1.`col5`)
and `t1`.`id` < 10
group by
t1.`field_name`
having
t1.`cnt` > 3
limit 5;
以上代码通过 $db->select() 直接执行,需入获取解析查询,可通过$db->select(true) 方式返回。
update 连接查询生成:
$db->build('tbl1')->field(array(
'views' => 5,
'tag' => 'tag',
'date'=>'(now())'
))
->where('news_id', 'n=', '1');
$db->build('tbl2', array(
'mode' => 'left',
'field' => 'id',
'field_join' => 't0.news_id'
))
->field(array('date'=>['t0','date']))
->where('page', 'n>=', '1');
以上代码将生成:
update
`tbl1` as t0
left join
`tbl2` as t1 on t1.`id`=`t0`.`news_id`
set
`t0`.`views`='5',
`t0`.`tag`='tag',
`t0`.`date`=now(),
`t1`.`date`=`t0`.`date`
where
`t0`.`news_id` = 1
and `t1`.`page` >= 1;
以上代码通过 $db->update() 直接执行,需入获取解析查询,可通过$db->update(true) 方式返回。
insert 和 delete 的查询生成与以上示例类似,这里就不赘述了,可参考框架函数文档及示例栏目进一步了解。
由于数据库实例可能对应多个关系表,并生成组合查询语句,指定条件时必须要通过 $db->build('table_name') 的方法获取构造实例,如为第二各关联表,只需在第一次声明传入 join 相关参数即可,如无其他更改,第二次调用时只需传入表名即可。$db->build('table_name') 所返回的实例支持链式调用的模式,可以根据需要选择链式调用,或者通过 build 方法重新获取实例再传入对应参数的模式。
接口规范
为保证所有被代理数据库类都具有同样的基础结构,需通过自定义接口规范,如下:
<?PHP
interface interface_db {
public function connect(); //数据路连接
public function selectDB($the_db); //数据库选择
public function insert(); //数据插入
public function select(); //数据查询
public function update(); //数据更新
public function delete(); //数据删除
public function query($sql); //执行语句
public function record($sql); //返回单行
public function records($sql); //返回所有行
public function result($sql); //返回单值
public function getFields($the_tbl); //获取字段列表
public function close(); //关闭数据库
}
interface interface_sql {
public function getRS(); //取得下一条记录
public function getDBs(); //获取数据库列表
public function getTbls(); //获取数据表列表
public function getPri($the_tbl); //获取主键
public function getInsertId(); //获取最新插入自增ID
public function getStat(); //获取数据库基本信息
public function getCreateScript($the_tbl); //获取数据表建立语句
public function getDataScript($the_tbl); //获取数据插入语句
public function getIdxScript($the_tbl); //获取索引建立语句
public function batchExe($SQLs); //批量执行语句
public function handleSQL($strSQL); //将多语句字符串分割为单语句数组
public function file($file); //执行多语句文件
public function check($obj); //检测数据库连接、结果集等对象是否存在
}
注:扩展类中引用的方法适用于接口要求。
功能类声明方式如下:
class someDatabase extends myBase implements interface_db, interface_sql {
use base_db, base_sql;
......
}