作者简介:

       姜海强:闷骚码农,互联网行业摸爬滚打数余载,先后担任中国体育直播TV主程、网信集团先锋支付架构师、奇虎360服务器端资深开发。热爱技术,喜欢分享,热衷领域:PHP/Golang语言、面向对象设计模式、Redis、Yaf、Yii2、微服务等。

视频课程

yaf+yar微服务-腾讯课堂
yaf+yar微服务-51CTO学院
CSDN学院

Github

个人主页
swoole-boot
roach
roach-orm

QQ群:

姜海强的QQ群

公众号:

360tryst公众号

pdo-mysql

PHP连接数据库推荐使用PDO,PDO扩展为PHP访问数据库定义了一个轻量级接口。我们可以通过实现PDO接口的每个数据库驱动来访问数据库服务。

访问mysql数据库服务,我们使用PDO_MYSQL驱动

1.PDO实现CRUD

192.168.1.13:3306的mysql实例上创建数据库roach,创建roach用户并授权,在roach库中创建表t_user,sql如下

  1. CREATE TABLE `t_user` (
  2. `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户id',
  3. `user_name` varchar(32) CHARACTER SET utf8 DEFAULT 'NULL' COMMENT '登录名',
  4. `true_name` varchar(32) CHARACTER SET utf8 DEFAULT '' COMMENT '真实姓名',
  5. `password` char(32) CHARACTER SET utf8 DEFAULT '' COMMENT '密码',
  6. `is_on` tinyint(3) unsigned DEFAULT '0' COMMENT '是否启用(0禁用1启用)',
  7. `last_login_ip` bigint(20) unsigned DEFAULT '0' COMMENT '上次登录ip',
  8. `add_time` int(10) unsigned DEFAULT '0' COMMENT '添加时间',
  9. `update_time` int(10) unsigned DEFAULT '0' COMMENT '修改时间',
  10. `version` int(10) unsigned DEFAULT '0' COMMENT '乐观锁版本',
  11. PRIMARY KEY (`id`),
  12. UNIQUE KEY `user_name` (`user_name`)
  13. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT COMMENT='用户表';

使用PDO操作t_user

  1. <?php
  2. //\PDO构造函数有四个参数,第一个参数是dsn,第二个参数是用户名,第三个参数是密码,第四个参数是连接选项
  3. //mysql数据库服务的dsn示例如下
  4. $pdo = new \PDO('mysql:host=192.168.1.13;port=3306;dbname=roach;charset=utf8', 'roach', 'roach', [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]);
  5. //1.insert
  6. /**
  7. * @var \PDOStatement $stmt
  8. */
  9. $stmt = $pdo->query("INSERT INTO `t_user`(`user_name`,`true_name`,`add_time`) VALUES('".uniqid('u')."','".uniqid('t')."',".time().")");
  10. $userId = $pdo->lastInsertId();
  11. //输出受影响行数和lastInsertId
  12. echo 'insert受影响行数:'.$stmt->rowCount().';插入的用户id:'.$userId.PHP_EOL;
  13. //2.update
  14. $stmt = $pdo->query('UPDATE `t_user` SET `user_name`=\'pdo-mysql\' WHERE id='.$userId);
  15. echo 'update受影响行数:'.$stmt->rowCount().PHP_EOL;
  16. //3.select
  17. $stmt = $pdo->query('SELECT * FROM `t_user` WHERE `id`='.$userId);
  18. echo 'select查询结果:'.json_encode($stmt->fetchAll(\PDO::FETCH_ASSOC), JSON_UNESCAPED_UNICODE).PHP_EOL;
  19. //4.delete
  20. $stmt = $pdo->query('DELETE FROM `t_user` WHERE id='.$userId);
  21. echo 'delete受影响行数:'.$stmt->rowCount().PHP_EOL;

以上例程输出

  1. insert受影响行数:1;插入的用户id:1
  2. update受影响行数:1
  3. select查询结果:[{"id":"1","user_name":"pdo-mysql","true_name":"t5f2d341bc4b43","password":"","is_on":"0","last_login_ip":"add_time":"0000-00-00 00:00:00","update_time":"0","version":"0"}]
  4. delete受影响行数:1

2.防sql注入

以上例程我们可以看到,并未做sql注入安全防范处理,使用PDO操作mysql数据库一般使用参数绑定来防止sql注入,参数绑定有以下两种方式

2.1 ?占位符绑定

1例程中的CRUD代码通过?参数绑定修改后的代码

  1. <?php
  2. //\PDO构造函数有四个参数,第一个参数是dsn,第二个参数是用户名,第三个参数是密码,第四个参数是连接选项
  3. //mysql数据库服务的dsn示例如下
  4. $pdo = new \PDO('mysql:host=192.168.1.13;port=3306;dbname=roach;charset=utf8', 'roach', 'roach', [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]);
  5. //1.insert
  6. /**
  7. * @var \PDOStatement $stmt
  8. */
  9. //$stmt = $pdo->query("INSERT INTO `t_user`(`user_name`,`true_name`,`add_time`) VALUES('".uniqid('u')."','".uniqid('t')."',".time().")");
  10. $stmt = $pdo->prepare("INSERT INTO `t_user`(`user_name`,`true_name`,`add_time`) VALUES(?, ?, ?)");
  11. $stmt->bindValue(1, uniqid('u'), \PDO::PARAM_STR);
  12. $stmt->bindValue(2, uniqid('t'), \PDO::PARAM_STR);
  13. $stmt->bindValue(3, time(), \PDO::PARAM_INT);
  14. $stmt->execute();
  15. //以上四行可以简写成
  16. //$stmt->execute([uniqid('u'), uniqid('t'), time()]);
  17. $userId = $pdo->lastInsertId();
  18. //输出受影响行数和lastInsertId
  19. echo 'insert受影响行数:'.$stmt->rowCount().';插入的用户id:'.$userId.PHP_EOL;
  20. //2.update
  21. //$stmt = $pdo->query('UPDATE `t_user` SET `user_name`=\'pdo-mysql\' WHERE id='.$userId);
  22. $stmt = $pdo->prepare('UPDATE `t_user` SET `user_name`=? WHERE id=?');
  23. $stmt->bindValue(1, 'pdo-msyql');
  24. $stmt->bindValue(2, $userId, \PDO::PARAM_INT);
  25. $stmt->execute();
  26. //以上三句可以简写成
  27. //$stmt->execute(['pdo-mysql', $userId]);
  28. echo 'update受影响行数:'.$stmt->rowCount().PHP_EOL;
  29. //3.select
  30. //$stmt = $pdo->query('SELECT * FROM `t_user` WHERE `id`='.$userId);
  31. $stmt = $pdo->prepare('SELECT * FROM `t_user` WHERE `id`=?');
  32. $stmt->bindValue(1, $userId, \PDO::PARAM_INT);
  33. $stmt->execute();
  34. //以上两句可以简写成
  35. //$stmt->execute([$userId]);
  36. echo 'select查询结果:'.json_encode($stmt->fetchAll(\PDO::FETCH_ASSOC), JSON_UNESCAPED_UNICODE).PHP_EOL;
  37. //4.delete
  38. //$stmt = $pdo->query('DELETE FROM `t_user` WHERE id='.$userId);
  39. $stmt = $pdo->prepare('DELETE FROM `t_user` WHERE id=?');
  40. $stmt->bindValue(1, $userId, \PDO::PARAM_INT);
  41. $stmt->execute();
  42. //以上两句可以简写成
  43. //$stmt->execute([$userId]);
  44. echo 'delete受影响行数:'.$stmt->rowCount().PHP_EOL;

例程的执行结果是一致的

2.2 自定义占位符绑定

参数绑定也可以自定义占位符,如一下例程

  1. <?php
  2. //\PDO构造函数有四个参数,第一个参数是dsn,第二个参数是用户名,第三个参数是密码,第四个参数是连接选项
  3. //mysql数据库服务的dsn示例如下
  4. $pdo = new \PDO('mysql:host=192.168.1.13;port=3306;dbname=roach;charset=utf8', 'roach', 'roach', [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]);
  5. //1.insert
  6. /**
  7. * @var \PDOStatement $stmt
  8. */
  9. //$stmt = $pdo->query("INSERT INTO `t_user`(`user_name`,`true_name`,`add_time`) VALUES('".uniqid('u')."','".uniqid('t')."',".time().")");
  10. $stmt = $pdo->prepare("INSERT INTO `t_user`(`user_name`,`true_name`,`add_time`) VALUES(:user_name, :true_name, :add_time)");
  11. $stmt->bindValue(':user_name', uniqid('u'), \PDO::PARAM_STR);
  12. $stmt->bindValue(':true_name', uniqid('t'), \PDO::PARAM_STR);
  13. $stmt->bindValue(':add_time', time(), \PDO::PARAM_INT);
  14. $stmt->execute();
  15. $userId = $pdo->lastInsertId();
  16. //输出受影响行数和lastInsertId
  17. echo 'insert受影响行数:'.$stmt->rowCount().';插入的用户id:'.$userId.PHP_EOL;
  18. //2.update
  19. //$stmt = $pdo->query('UPDATE `t_user` SET `user_name`=\'pdo-mysql\' WHERE id='.$userId);
  20. $stmt = $pdo->prepare('UPDATE `t_user` SET `user_name`=:user_name WHERE id=:id');
  21. $stmt->bindValue(':user_name', 'pdo-msyql');
  22. $stmt->bindValue(':id', $userId, \PDO::PARAM_INT);
  23. $stmt->execute();
  24. echo 'update受影响行数:'.$stmt->rowCount().PHP_EOL;
  25. //3.select
  26. //$stmt = $pdo->query('SELECT * FROM `t_user` WHERE `id`='.$userId);
  27. $stmt = $pdo->prepare('SELECT * FROM `t_user` WHERE `id`=:id');
  28. $stmt->bindValue(':id', $userId, \PDO::PARAM_INT);
  29. $stmt->execute();
  30. echo 'select查询结果:'.json_encode($stmt->fetchAll(\PDO::FETCH_ASSOC), JSON_UNESCAPED_UNICODE).PHP_EOL;
  31. //4.delete
  32. //$stmt = $pdo->query('DELETE FROM `t_user` WHERE id='.$userId);
  33. $stmt = $pdo->prepare('DELETE FROM `t_user` WHERE id=:id');
  34. $stmt->bindValue(':id', $userId, \PDO::PARAM_INT);
  35. $stmt->execute();
  36. echo 'delete受影响行数:'.$stmt->rowCount().PHP_EOL;

例程执行结果也是一致的

3.事务

以下例程演示用户id只能为奇数,否则回滚事务的示例

  1. <?php
  2. //\PDO构造函数有四个参数,第一个参数是dsn,第二个参数是用户名,第三个参数是密码,第四个参数是连接选项
  3. //mysql数据库服务的dsn示例如下
  4. $pdo = new \PDO('mysql:host=192.168.1.13;port=3306;dbname=roach;charset=utf8', 'roach', 'roach');
  5. //开启事务
  6. $pdo->beginTransaction();
  7. try {
  8. $stmt = $pdo->prepare("INSERT INTO `t_user`(`user_name`,`true_name`,`add_time`) VALUES(:user_name, :true_name, :add_time)");
  9. $stmt->bindValue(':user_name', uniqid('u'), \PDO::PARAM_STR);
  10. $stmt->bindValue(':true_name', uniqid('t'), \PDO::PARAM_STR);
  11. $stmt->bindValue(':add_time', time(), \PDO::PARAM_INT);
  12. $stmt->execute();
  13. $userId = $pdo->lastInsertId();
  14. //仅用于演示
  15. if($userId % 2 === 0) {
  16. throw new \Exception('用户id不能为偶数');
  17. }
  18. //update
  19. $stmt = $pdo->prepare('UPDATE `t_user` SET `user_name`=:user_name WHERE id=:id');
  20. $stmt->bindValue(':user_name', 'pdo-msyql'.uniqid());
  21. $stmt->bindValue(':id', $userId, \PDO::PARAM_INT);
  22. $stmt->execute();
  23. //select
  24. $stmt = $pdo->prepare('SELECT * FROM `t_user` WHERE `id`=:id');
  25. $stmt->bindValue(':id', $userId, \PDO::PARAM_INT);
  26. $stmt->execute();
  27. //提交事务
  28. $pdo->commit();
  29. echo '刚刚插入并修改的记录为:'.json_encode($stmt->fetchAll(\PDO::FETCH_ASSOC), JSON_UNESCAPED_UNICODE).PHP_EOL;
  30. }catch (\Exception $exception) {
  31. $pdo->rollBack();
  32. exit($exception->getMessage());
  33. }
  • 注意:此处的try...catch是必须的,因为\PDO::ATTR_ERRMODE设置为\PDO::ERRMODE_EXCEPTION,当出现异常时会被catch捕捉到,并回滚事务。

QQ群:

姜海强的QQ群

公众号:

360tryst公众号