连接数据库

PHP 操作数据库的方式有多种如 mysqlmysqliPDO ,目前主要使用的是 PDO 处理。

PDO 提供了一个数据访问抽象层,这意味着,不管使用哪种数据库,都可以用相同的函数(方法)来查询和获取数据。

下面是使用 PDO 连接数据库的操作,当连接失败时将抛出异常。

  1. <?php
  2. $config = [
  3. 'host' => 'localhost',
  4. 'user' => 'root',
  5. 'password' => '',
  6. 'db' => 'test',
  7. 'charset' => 'utf8'
  8. ];
  9. try{
  10. $dsn = sprintf('mysql:host=%s;dbname=%s;charset=%s',$config['host'], $config['db'],$config['charset']);
  11. $pdo = new PDO($dsn, $config['user'],$config['password']);
  12. }catch(PDOException $e){
  13. }

错误处理

错误类别 说明
PDO::ERRMODE_SILENT 不显示错误
PDO::ERRMODE_WARNING 显示警告错误
PDO::ERRORMODE_EXCEPTION 抛出异常

可以在连接时设置错误类型

  1. $pdo = new PDO($dsn, $config['user'],$config['password'],[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);

也可以使用 setAttribute 方法设置错误处理方式。

  1. ...
  2. $pdo = new PDO($dns, $config['user'], $config['password']);
  3. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
  4. ...

执行语句

使用 exec 方法可以发送执行语言

  1. $pdo->exec("INSERT INTO news (title) VALUES('0x208.cc')");
  2. echo "自增主键:".$pdo->lastInsertId();

执行删除操作

  1. $num = $pdo->exec("DELETE FROM news WHERE id = 1");
  2. echo "受影响的条数".$num;

发送查询

设置返回列名

属性 说明
PDO::CASE_LOWER 强制列名小写
PDO::CASE_NATURAL 保留数据库驱动返回的列名
PDO::CASE_UPPER 强制列名大写
  1. $pdo->setAttribute(PDO::ATTR_CASE,PDO::CASE_LOWER);


返回结果

返回类型 说明
$result->fetchAll(PDO::FETCH_ASSOC) 获得关联数组
$result->fetchAll(PDO::FETCH_NUM) 获得索引数组
$result->fetchAll(PDO::FETCH_BOTH) 同时获取关联于索引数组
$result->fetchAll(PDO::FETCH_OBJ) 获取对象类型数据

fetchAll

一次获取所有结果

  1. ...
  2. $query = $pdo->query("select * from news");
  3. $rows = $query->fetchAll();
  4. print_r($rows);
  5. ...

fetch

每次获取结果中的一条数据。

  1. ...
  2. $pdo = new PDO($dns, $config['user'], $config['password']);
  3. $query = $pdo->query("select * from news");
  4. while ($field = $query->fetch(PDO::FETCH_ASSOC)) {
  5. echo sprintf("编号:%s\t标题:%s<br/>", $field['id'], $field['title']);
  6. }
  7. ...

预准备(预处理)

预准备是将 SQL 的解析阶段与参数绑定分开执行,从而可以有效的防止 SQL 注入。

SQL注入

下面来看一个 SQL 注入的例子。

  1. $query = $pdo->query("SELECT * FROM news WHERE id={$_GET['id']}");

如果GET参数如下将产生 SQL 注入

  1. a.php?id=3 or id>0

使用预准备

因为预准备是将解析与参数分开处理,可以有效的防止 SQL 注入。

  1. $sql = "SELECT * FROM news WHERE id=:id";
  2. $sth = $pdo->prepare($sql);
  3. $sth->execute([':id' => $_GET['id']]);
  4. $rows = $sth->fetchAll(PDO::FETCH_ASSOC);
  5. print_r($rows);

预准备添加记录

  1. $sql = "INSERT INTO news (title) VALUES(?)";
  2. $sth = $pdo->prepare($sql);
  3. $sth->execute(['0x208.cc']);
  4. echo $pdo->lastInsertId();

使用占位符操作

  1. $sth = $pdo->prepare("SELECT * FROM news WHERE id>?");
  2. $sth->execute([3]);
  3. print_r($sth->fetchAll());

SQL生成器

处理类

下面构建一个SQL语句生成器,用于快速操作数据库。

  1. <?php
  2. namespace Database;
  3. use PDO;
  4. use Exception;
  5. class DB{
  6. protected $link = null;
  7. protected $options = ['table'=>'','field'=>'','limit'=>'','order'=>'','where'=>''];
  8. public function __construct($config){
  9. $this->connect($config);
  10. }
  11. public function connect($config){
  12. if(is_null($this->link)){
  13. $dsn = sprintf('mysql:host=%s;dbname=%s;charset=utf8', $config['host'], $config['db']);
  14. $this->link = new PDO($dsn, $config['user'], $config['password'], [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
  15. }
  16. return $this->link;
  17. }
  18. public function query($sql,array $vars=[]){
  19. $sth = $this->link->prepare($sql);
  20. $sth->execute($vars);
  21. return $sth->fetchAll();
  22. }
  23. public function execute($sql,array $vars=[]){
  24. $sth = $this->link->prepare($sql);
  25. return $sth->execute($vars);
  26. }
  27. public function field(...$fields){
  28. $this->options['field'] = '`'.implode('`,`', $fields) . '`';
  29. return $this;
  30. }
  31. public function table(string $table){
  32. $this->options['table'] = $table;
  33. return $this;
  34. }
  35. public function get(){
  36. $sql = "SELECT {$this->options['field']} FROM {$this->options['table']} {$this->options['where']} {$this->options['order']} {$this->options['limit']}";
  37. return $this->link->query($sql)->fetchAll();
  38. }
  39. public function orderBy(string $order){
  40. $this->options['order'] = " ORDER BY ".$order;
  41. return $this;
  42. }
  43. public function limit(...$limit){
  44. $this->options['limit'] = " LIMIT ".implode(',', $limit);
  45. return $this;
  46. }
  47. public function where(string $where){
  48. $this->options['where'] = " WHERE " . $where;
  49. return $this;
  50. }
  51. public function insert(array $vars){
  52. $sql = "INSERT INTO {$this->options['table']} (".implode(',', array_keys($vars)).") VALUES (".implode(',', array_fill(0,count($vars), '?')).")";
  53. return $this->link->execute($sql,array_values($vars));
  54. }
  55. public function delete(){
  56. $sql = "DELETE FROM {$this->options['table']} {$this->options['where']}";
  57. return $this->execute($sql);
  58. }
  59. public function update(array $vars){
  60. if(empty($this->options['where'])){
  61. throw new Exception("不能缺少更新条件");
  62. }
  63. $sql = "UPDATE {$this->options['table']} SET ".implode('=?', array_keys($vars))."=? {$this->options['where']}";
  64. return $this->execute($sql, array_values($vars));
  65. }
  66. }

对象实例

  1. use Database\DB;
  2. include 'DB.php';
  3. $config = [
  4. 'host' => 'localhost',
  5. 'user' => 'root',
  6. 'password' => '',
  7. 'db' => 'test'
  8. ];
  9. try {
  10. ...
  11. $db = new DB($config);
  12. ...
  13. }catch(Exception $e){
  14. die($e->getMessage());
  15. }

预准备查询

  1. ...
  2. $db->query("SELECT * FROM news WHERE id>=:id", ['id' => 1]);
  3. ...

预准备执行

  1. $db->execute("INSERT INTO news (title) VALUES(?)", ['世界和平']);

查询操作

  1. ...
  2. $rows = $db->table('news')->field('id', 'title')->limit(1, 3)->orderBy('id desc')->where("id>3")->get();
  3. ...

新增操作

  1. ...
  2. $db->table('news')->insert(['title'=>'天亮了']);
  3. ...

更新操作

  1. ...
  2. $db->table('news')->where('id=2')->update(['title'=>'更新标题']);
  3. ...

删除操作

  1. ...
  2. $db->table('news')->where('id=2')->delete();
  3. ...