PDO预编译与MVC初探

前言

抽空了解了一下PDO预编译和MVC结构基础东西,记录一下。

PDO

简介

PDO是PHP的一个轻量级的接口,使得访问和操作数据库得以简便。它使得访问多种数据库(除了mysql)可以用相同的方法。

同时正确使用它的预编译,可以有效防止sql注入。它的基本使用语法也比较简便。

语法

在使用PDO之前,应该需要做相应的配置(这个配置方法可以自行百度),这里简单对PDO的常见语法做一个梳理。

创建PDO对象

  • 创建PDO对象:(这里是以mysql为例子)
1
2
3
4
5
6
7
8
9
10
11
<?php
$dsn='mysql:dbname=test;host=127.0.0.1;charset=utf-8';
$user='root';
$password='root';
try{
$pdo=new PDO($dsn,$user,$password);
}
catch(PDOException $e)
{
echo 'Connection failed: '.$e->getMessage();
}

常用方法

  • exec()方法 –执行sql语句,并返回被修改或者被删除的SQL语句影响的行数(int)
1
2
3
4
//接着上面的对象,不重复写
<?php
$sql="INSERT INTO USER (id,name) VALUES (1,'My0n9s')";
$rel=$pdo->exec($sql);
  • query()方法 –执行SQL语句,但是返回的是一个结果集对象(PDOstatement)
1
2
3
<?php
$sql="SELECT * FROM user ORDERY BY id DESC";
$rel=$pdo->query($sql);
  • fetch()方法 – 从结果集中获取一行,并向下移动指针
1
2
3
4
5
6
7
<?php
$sql="SELECT * FROM user ORDER BY id DESC";
$stmt=$pdo->query($sql);
while($rel=$stmt->fetch(PDO::FETCH_ASSOC))
{
var_dump($rel);
}
  • fetchALL()方法–返回一个结果集的所有数组
1
2
3
4
5
<?php
$sql="SELECT * FROM user ORDER BY id DESC";
$stmt=$pdo->query($sql);
$rel=$stmt->fetchALL(PDO::FETCH_ASSOC);
var_dump($rel); //直接全部一次性输出

预编译

概念

PDO有两种模式,一种是本地预处理,一种是模拟预处理,模拟预处理用于数据库不支持预编译的情况,其实它并没有做到真正的预编译,其本质也是转义用户输入的数据,如果编码设置不规范,就存在宽字节注入的风险,而本地预处理,只要使用规范,可以从本质上去防止SQL注入。

浅谈原理

预编译为什么能做到防注入?

从预编译的运行机制说起,通常,以Mysql为例,一条SQL语句从传入到执行经历一下几个过程:

  • 检查缓存
  • 规则验证
  • 解析器解析为语法树
  • 预处理器进一步优化语法树
  • 优化SQL
  • 生成执行计划
  • 执行

PDO完成预处里需要的步骤

  • 提取相同结构的sql部分(把数据部分,可变部分进行去除)
  • 编译相同的结构,并把编译结果保存
  • 把不同的数据部分进行替换
  • 执行sql
  • 提取相同结构的sql语句
  • 在sql语句中,使用命名参数和问号参数,来代替可变的数据
  • 使用占位符“:value”和”?”来代替可变的数据

来看两条语句:

select * from user where id=1select * from users where id=2 ,这两条语句语法结构是差不多的,区别只在于后面的id的字段值,预编译就是把相同的语法部分拿过去先编译,构建语法树,最后再去传字段值,做到数据和代码进行了一个分离。 从而有效的防止了SQL注入

语法

简单的Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
//创建一个连接mysql数据库的PDO对象
$dsn='mysql:dbname=test;host=127.0.0.1;charset=utf8';
$user='root';
$password='root';
try
{
$pdo=new PDO($dsn,$user,$password);
//使用参数名进行绑定
$sql="INSERT INTO USER(id,name)VALUES(:id,:name)";
//对内容进行预编译
$stmt=$pdo->prepare($sql);
//对占位数据进行绑定
$stmt->bindValue(':id',1)
$stmt->bindValue(':name','My0n9s');
//执行已预编译的sql
$rel=$stmt->execute();
}
catch(PDOException $e)
{
echo "Connection faile:".$e->getMessage();
echo "错误行号:".$e->getCode();
echo "错误行号:".$e->getLine();
}
//也可以使用问号来绑定参数 eg: $sql="INSERT INTO user(id,name) VALUES(?,?,?)";
//同时对应的占位数据绑定也得改 eg:$stmt->bindValue(1,2021);$stmt->bindValue(2,'My0n9s');

MVC

写在前面

很早就听说MVC,并且在AWD中也接触到了MVC结构的框架,然后一直都是一头雾水,好吧,其实包括现在对MVC的理解也没有很透彻,不过大抵是比之前要好些了。这里参考了feng师傅发给我的一篇文章。

简介

MVC是编程的一种架构,一种规范,使得业务逻辑、数据、视图三者做到了一个分离,后期也便于扩展。

M指的是Model(模型),这里主要是对数据进行处理,和数据库进行一个交互。

V指的是View (视图),最后呈现给用户的界面,一般来说,视图是依据模型数据创立的。

C指的是Controller(控制器),处理和用户交互的部分,个人觉得像是一个指挥官的角色。

关于三者的关系,有一个图很形象的进行了呈现:

web_mvc.gif

小型的MVC框架

然后就是对一个轻便级的MVC框架进行了一个初步的了解:

框架整个的一个结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
project
--app
*controllers
**ItemController.php
*models
**ItemModel.php
*views
**item
***add.php
***delete.php
***index.php
***manage.php
***update.php
**footer.php
**head.php
--config
*config.php
--fastphp
*base
**Controller.php
**Model.php
**View.php
*db
**Db.php
**Sql.php
*Fastphp.php
--static
--.htaccess
--index.php

分析一个框架找到它的入口是很重要的,这个框架的入口是index.php:

1
2
3
4
5
6
7
8
9
10
11
<?php
//应用目录为当前目录
define('APP_PATH',__DIR__.'/');
//开启调试模式
define('APP_DEBUG',true);
//加载框架文件
require(APP_PATH.'fastphp/Fastphp.php');
//加载配置文件
$config=require(APP_PATH.'config/config.php');
//实例化框架类
(new fastphp\Fastphp($config))->run();

可以看到它主要是加载和实例化框架(这里是调用fastphp\Fastphp这个类),这是框架的一个核心类,我们来看看Fastphp的部分内容:

1
2
3
4
5
6
7
8
9
public function run()
{
spl_autoload_register(array($this,'loadClass'));
$this->setReporting();
$this->removeMagicQuotes();
$this->unregisterGlobals();
$this->setDbConfig();
$this->route();
}

这里就是调用各种函数,主要我们来看一下这个route():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public function  route()
{
$controllerName = $this->config['defaultController'];
//一开始 Controller和Action都设置为默认的,为Item和Index
$actionName = $this->config['defaultAction'];
$param = array();
$url = $_SERVER['REQUEST_URI'];
$position = strpos($url, '?');
$url = $position === false ? $url : substr($url, 0, $position);
//删除前后的"/"
$url = trim($url, '/');
if ($url) {
// 使用"/"分隔字符串,并保存在数组中
$urlArray = explode('/', $url);
//删除空的数组元素
$urlArray = array_filter($urlArray);
//获取控制器名,这里ucfirst函数将首写字母变大写了。
$controllerName = ucfirst($urlArray[0]);
//获取动作名
array_shift($urlArray); //array_shift函数直接将数组第一个元素移除
$actionName = $urlArray ? $urlArray[0] : $actionName;
//获取url参数
array_shift($urlArray);
$param = $urlArray ? $urlArray : array();
}
//判断控制器和操作是否存在
$controller = 'app\\controllers\\' . $controllerName . 'Controller';
if (!class_exists($controller)) {
exit($controller . '控制器不存在!');
}
if (!method_exists($controller, $actionName)) {
exit($actionName . '方法不存在');
}
//如果控制器和操作名都存在,则实例化控制器,因为控制器对象里面
//还会用到控制器名和操作名,所以实例化的时候把他们俩的名称也传进去,结合Controller基类一起看
$dispatch = new $controller($controllerName, $actionName);
//dispatch保存控制器实例化后的对象,我们可以调用它的方法
//也可以像方法中传入参数,以下等同于:$dispatch->$actionName($param)
call_user_func_array(array($dispatch, $actionName), $param);
}

这里主要是对URL做一个分隔,然后调用相应的Controller和Actionname,然后传入相应的参数,类似 /controllername/actionname/params 这种形式。

然后fastphp\base目录下是 控制器,视图,模型的一个基类,后面具体一个类型的控制器只需要继承这个基类就好,这样避免了代码重用,也使得层次很清晰,每一个功能对应一个Controller。比如后面的app文件夹下的ItemController直接继承了base下的Controller基类,然后单独负责一个功能板块。db文件下的Db.php和Sql.php主要是对数据库操作的一个封装。Db.php的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Db
{
private static $pdo = null;
public static function pdo()
{
if (self::$pdo !== null) {
return self::$pdo;
}
try {
$dsn = sprintf('mysql:host=%s;dbname=%s;charset=utf8', DB_HOST, DB_NAME);
$option = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC);
return self::$pdo = new PDO($dsn, DB_USER, DB_PASS, $option);
} catch (PDOException $e) {
exit($e->getMessage());
}
}
}

可以看到这里是直接返回了一个PDO对象。

然后就是Sql.php,对数据库进行抽象层的交互。增删改查等操作

然后Model这个基类继承了Sql,ItemModel则是进一步进行继承,负责搜索功能,(依据keyworld的值)

config就是一些默认的参数配置,view就主要是进行一个页面的渲染。

每一个模块各司其职,最后都把力使到app这个目录下的controller , view , model 。

小结

对PDO的基础概念和使用语法做了一个简单的了解,其具体底层机制还有待挖掘。

同时对MVC一个执行流程做了一个简单的了解,记录的文字感觉还是不太通俗,也似乎只有我自己知道自己在写些啥。。。

后面随着对MVC的加深应该会改把。。

又水完了一篇文章233333