博客项目(一)
常用SQL语句封装
在一个项目中,增、删、改、查的SQL语句比较多,我们可以封装生成增、删、改、查的SQL语句的方法,就会达到一劳永逸。
封装insert语句
用到的知识点:
- array_map():数组中每个元素依次调用匿名函数
- use():将变量引入到匿名函数内部使用。
代码实现:
<?php
$table=’stu’; //表名
$data[‘id’]=1; //插入数据 键是字段名,值是插入的值
$data[‘name’]=’tom’;
$data[‘score’]=99;
/**********拼接字段字符串**************/
$keys=array_keys($data); //获取所有键
$keys=array_map(function($k){ //keys数组中元素依次调用匿名函数,每个元素添加反引号
return “`{$k}`”;
},$keys);
$keys=implode(‘,’,$keys); //数组中元素通过逗号连接起来
/**********拼接值字符串**************/
$values=array_values($data); //获取所有值
$values=array_map(function($v){ //在values数组中所有元素添加单引号
return “‘{$v}'”;
},$values);
$values=implode(‘,’,$values); //连接成字符串
//拼接SQL语句
$sql=”insert into `{$table}` ($keys) values ($values)”;
echo $sql;
运行结果
封装update方法
用到的知识点:
- 查看表的主键
代码实现:
<?php
$table=’products’; //表名
$data[‘proID’]=1;
$data[‘proname’]=’mobile’;
$data[‘proprice’]=50;
mysql_connect(‘localhost’,’root’,’root’);
mysql_select_db(‘data’);
mysql_query(‘set names utf8’);
/**************获取表中的主键************/
function getPrimaryKey($table) {
$rs=mysql_query(“desc `{$table}`”);
while($rows=mysql_fetch_assoc($rs)) {
if($rows[‘Key’]==’PRI’)
return $rows[‘Field’];
}
}
/**************拼接update语句****************/
$pk=getPrimaryKey($table); //获取主键
$keys=array_keys($data); //获取所有的键
$index=array_search($pk,$keys); //主键在数组中的索引
unset($keys[$index]); //删除键数组中的主键
$fields=array_map(function($k) use ($data){
return “`{$k}`='{$data[$k]}'”;
},$keys);
$fields=implode(‘,’,$fields);
$sql=”update `{$table}` set $fields where `{$pk}`='{$data[$pk]}'”;
echo $sql;
运行结果
获取表名
用到的知识点:
- get_class():获取对象的类名
实现代码
<?php
namespace China;
//基础模型
class Model {
private $table;
public function __construct($table=”) {
$this->table=$table;
if($table==”)
$this->table=substr(basename(get_class($this)),0,-5); //获取模型名
echo $this->table,'<br>’;
}
}
//Prodcuts表模型
class ProductsModel extends Model {}
//User表模型
class UserModel extends Model {}
//实例化模型
$model=new \China\ProductsModel();
$model=new \China\UserModel();
$model=new Model(‘user’);
运行结果
在类中封装增、删、改、查语句
因为这些方法供所有的模型使用,将这些方法封装到基础模型类中。
<?php
//基础模型类
namespace Core;
class Model {
protected $mypdo; //保存mypdo对象
private $table; //操作的表名
public function __construct($table=”) {
$this->initMyPDO();
$this->getTable($table);
}
//初始化mypdo对象
private function initMyPDO() {
$this->mypdo= MyPDO::getInstance($GLOBALS[‘config’][‘database’]);
}
//获取表名
private function getTable($table){
$this->table=$table;
if($table==”)
$this->table=substr(basename(get_class($this)),0,-5); //获取模型名
}
//获取表的主键
private function getPrimaryKey(){
$rs= $this->mypdo->fetchAll(“desc `{$this->table}`”);
foreach($rs as $rows){
if($rows[‘Key’]==’PRI’)
return $rows[‘Field’];
}
return null;
}
//封装insert方法
public function insert($data){
$keys=array_keys($data);
$keys=array_map(function($k){
return “`{$k}`”;
},$keys);
$keys=implode(‘,’,$keys);
$values=array_values($data);
$values=array_map(function($v){
return “‘{$v}'”;
},$values);
$values=implode(‘,’,$values);
//拼接SQL语句
$sql=”insert into `{$this->table}` ($keys) values ($values)”;
if($this->mypdo->exec($sql))
return $this->mypdo->lastInsertId();
return false;
}
//封装update方法
public function update($data){
$pk= $this->getPrimaryKey();
$keys=array_keys($data);
$index=array_search($pk,$keys);
unset($keys[$index]);
$fields=array_map(function($k) use ($data){
return “`{$k}`='{$data[$k]}'”;
},$keys);
$fields=implode(‘,’,$fields);
$sql=”update `{$this->table}` set $fields where `{$pk}`='{$data[$pk]}'”;
return $this->mypdo->exec($sql);
}
//封装delete方法
public function delete($id){
$pk= $this->getPrimaryKey();
$sql=”delete from `{$this->table}` where `{$pk}`=’$id'”;
return $this->mypdo->exec($sql);
}
//封装查询的方法,返回二维数组
public function select($field=”,$order=’asc’){
if($field==”)
$field= $this->getPrimaryKey ();
$sql=”select * from `{$this->table}` order by `{$field}` $order”;
return $this->mypdo->fetchAll($sql);
}
//封装查询的方法,返回一维数组
public function find($id){
$pk= $this->getPrimaryKey();
$sql=”select * from `{$this->table}` where `{$pk}`=’$id'”;
return $this->mypdo->fetchRow($sql);
}
}
web项目的开发流程
用户表
注意事项
- 密码需要md5加密:md5是一个加密的算法,提供任意一个字符串都可以加密成32位的字符。理论上所有密码都可以解密,md5目前不能反向解密。
多学一招:虽然md5不能反向解密,但是可以通过数据字典破解md5加密的字符串。
- 保存登录的IP地址用整形:如果用varchar保存需要15个字节,如果用int保存只要4个字节。
- 登录时间用int保存,如果用datetime需要占用8个字节,int只占4个字节。
drop table if exists `user`;
— 创建user表
create table `user`(
user_id int unsigned auto_increment primary key comment ‘用户id’,
user_name varchar(20) not null unique comment ‘用户名’,
user_pwd varchar(32) not null comment ‘密码’,
user_face varchar(50) not null comment ‘头像地址’,
last_login_ip int comment ‘最后登录的IP地址’,
last_login_time int comment ‘最后登录的时间’,
login_count int unsigned default 0 not null comment ‘登录次数’,
is_admin tinyint not null default 0 comment ‘是否在超级管理员’
)engine=innodb charset=utf8 comment ‘用户表’
— 插入测试数据
insert into `user` (user_id,user_name,user_pwd) values (1,’aa’,md5(‘aa’));
表中数据如下:
实现用户注册
显示后台登录页面
- 将后台的模板和后台的静态资源拷贝到View\Admin目录中
- 在Controller\Admin目录下创建LoginController.class.php页面(登录控制器)
- 访问登录页面
多学一招:引入静态资源建议使用绝对路径,从根目录开始匹配(/)。
- 引入静态资源
- 再次访问登录页面
注册用户
- 在LoginController类中添加用户注册的方法
- 对应的模板页面(register.html)
用户登录
模型:在Model文件夹下创建UserModel.class.php(User模型类)
控制器(LoginController)
视图
没有变化
显示后台管理页面
- 在Controller/Admin文件夹中创建AdminController.class.php页面(后台管理员控制器)
<?php
/*
* 管理员控制器
*/
namespace Controller\Admin;
class AdminController extends \Core\Controller{
public function adminAction(){
$this->smarty->display(‘admin.html’);
}
public function topAction(){
$this->smarty->display(‘top.html’);
}
public function menuAction(){
$this->smarty->display(‘menu.html’);
}
public function mainAction(){
$this->smarty->display(‘main.html’);
}
}
- 更新admin.html中的URL地址
- 更改top.html,menu.html,main.html中的静态资源的路径
- 登录成功后跳转到admin页面。
防止翻墙
问题:可以不用登录,直接引入后台管理页面
解决:登录成功后,给用户付一个令牌(会话),在后面的操作中,检查令牌是否存在,如果不存在跳转到登录页面。
代码实现
- 由于前后台都要使用会话,所以开启会话放在基础控制器中
- 检查是否登录只在后台中使用,检查是否登录的方法写在后台基础控制器中
- 在基础控制器中, 开启会话
- 在Controller\Admin文件夹中创建BaseController.class.php(后台基础控制器) ,在此控制器中验证是否登录
- 登录成功后,将用户信息保存到会话中
- 所有后台控制器都继承后台基础控制器
- 测试
防止注入
通过传递参数拼接SQL语句达到攻击的目的
原理
解决:
- 如果传递的是数字,必须进行整形转换
- 如果传递字符串,必须添加转义
-
方法
- mysql_escape_string — 转义一个字符串用于 mysql_query
- mysql_real_escape_string — 转义 SQL 语句中使用的字符串中的特殊字符
- addslashes()
- PDO->quote()
代码实现
方法一:
方法二:可以调用pdo的quote方法
- 在MyPDO中添加addQuote()方法,pdo->quote()用来给参数添加单引号,如果参数本身带有单引号则添加转义。
- 在UserModel类中调用addQuote()方法
防止注册成功
更新登录信息
模型(UserModel)类中
在控制器中调用updateLoginInfo()
- 能够理解一个web项目的开发流程
- 能够搭建或使用自定义的MVC框架
- 能够实现后台的登录身份验证功能
- 能够理解SQL注入行为并加以防范
- 能够使用会话技术进行后台功能的身份验证