PHP 与 MySQL

使用 PDO 及其预处理语句功能。

在 PHP 中,有很多方式来连接到一个 MySQL 数据库。PDO(PHP 数据对象)是其中最新且最健壮的一种。
PDO 跨多种不同类型数据库有一个一致的接口,使用面向对象的方式,支持更多的新数据库支持的特性。

你应该使用 PDO 的预处理语句函数来帮助防范 SQL 注入攻击。
使用函数 bindValue 来确保你的 SQL 免于一级 SQL 注入攻击。
(虽然并不是 100% 安全的,查看进一步阅读获取更多细节。)
在以前,这必须使用一些「魔术引号(magic quotes)」函数的组合来实现。PDO 使得那堆东西不再需要。

示例

  1. <?php
  2. try{
  3. // 新建一个数据库连接
  4. // You'll probably want to replace hostname with localhost in the first parameter.
  5. // The PDO options we pass do the following:
  6. // \PDO::ATTR_ERRMODE enables exceptions for errors. This is optional but can be handy.
  7. // \PDO::ATTR_PERSISTENT disables persistent connections, which can cause concurrency issues in certain cases. See "Gotchas".
  8. // \PDO::MYSQL_ATTR_INIT_COMMAND alerts the connection that we'll be passing UTF-8 data.
  9. // This may not be required depending on your configuration, but it'll save you headaches down the road
  10. // if you're trying to store Unicode strings in your database. See "Gotchas".
  11. $link = new \PDO( 'mysql:host=your-hostname;dbname=your-db',
  12. 'your-username',
  13. 'your-password',
  14. array(
  15. \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
  16. \PDO::ATTR_PERSISTENT => false,
  17. \PDO::MYSQL_ATTR_INIT_COMMAND => 'set names utf8mb4'
  18. )
  19. );
  20.  
  21. $handle = $link->prepare('select Username from Users where UserId = ? or Username = ? limit ?');
  22.  
  23. // PHP bug: if you don't specify PDO::PARAM_INT, PDO may enclose the argument in quotes.
  24. // This can mess up some MySQL queries that don't expect integers to be quoted.
  25. // See: https://bugs.php.net/bug.php?id=44639
  26. // If you're not sure whether the value you're passing is an integer, use the is_int() function.
  27. $handle->bindValue(1, 100, PDO::PARAM_INT);
  28. $handle->bindValue(2, 'Bilbo Baggins');
  29. $handle->bindValue(3, 5, PDO::PARAM_INT);
  30.  
  31. $handle->execute();
  32.  
  33. // Using the fetchAll() method might be too resource-heavy if you're selecting a truly massive amount of rows.
  34. // If that's the case, you can use the fetch() method and loop through each result row one by one.
  35. // You can also return arrays and other things instead of objects. See the PDO documentation for details.
  36. $result = $handle->fetchAll(\PDO::FETCH_OBJ);
  37.  
  38. foreach($result as $row){
  39. print($row->Username);
  40. }
  41. }
  42. catch(\PDOException $ex){
  43. print($ex->getMessage());
  44. }
  45. ?>

陷阱

  • 当绑定整型变量时,如果不传递PDO::PARAM_INT参数有事可能会导致PDO对数据加引号。
    这会搞坏特定的MySQL查询。查看该bug报告
  • 未使用setnamesutf8mb4作为首个查询,可能会导致Unicode数据错误地存储进数据库,这依赖于你的配置。
    如果你绝对有把握你的Unicode编码数据不会出问题,那你可以不管这个。
  • 启用持久连接可能会导致怪异的并发相关的问题。
    这不是一个PHP的问题,而是一个应用层面的问题。只要你仔细考虑了后果,持久连接一般会是安全的。
    查看Stack
    Overfilow这个问题
  • 即使你使用了setnamesutf8mb4,你也得确认实际的数据库表使用的是utf8mb4字符集!
  • 可以在单个execute()调用中执行多条SQL语句。
    只需使用分号分隔语句,但注意这个bug,在该文档所针对的PHP版本中还没修复。
  • Laruence:PDOStatement::bindParam的一个陷阱

进一步阅读