离线下载
PDF版 ePub版

Alex Cabal · 更新于 2018-11-17 12:00:37

PHP 与 MySQL

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

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

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

示例

<?php
try{
    // 新建一个数据库连接
    // You'll probably want to replace hostname with localhost in the first parameter.
    // The PDO options we pass do the following:
    // \PDO::ATTR_ERRMODE enables exceptions for errors.  This is optional but can be handy.
    // \PDO::ATTR_PERSISTENT disables persistent connections, which can cause concurrency issues in certain cases.  See "Gotchas".
    // \PDO::MYSQL_ATTR_INIT_COMMAND alerts the connection that we'll be passing UTF-8 data.
    // This may not be required depending on your configuration, but it'll save you headaches down the road
    // if you're trying to store Unicode strings in your database.  See "Gotchas".
    $link = new \PDO(   'mysql:host=your-hostname;dbname=your-db', 
                        'your-username', 
                        'your-password', 
                        array(
                            \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, 
                            \PDO::ATTR_PERSISTENT => false, 
                            \PDO::MYSQL_ATTR_INIT_COMMAND => 'set names utf8mb4'
                        )
                    );

    $handle = $link->prepare('select Username from Users where UserId = ? or Username = ? limit ?');

    // PHP bug: if you don't specify PDO::PARAM_INT, PDO may enclose the argument in quotes.
    // This can mess up some MySQL queries that don't expect integers to be quoted.
    // See: https://bugs.php.net/bug.php?id=44639
    // If you're not sure whether the value you're passing is an integer, use the is_int() function.
    $handle->bindValue(1, 100, PDO::PARAM_INT);
    $handle->bindValue(2, 'Bilbo Baggins');
    $handle->bindValue(3, 5, PDO::PARAM_INT);

    $handle->execute();

    // Using the fetchAll() method might be too resource-heavy if you're selecting a truly massive amount of rows.
    // If that's the case, you can use the fetch() method and loop through each result row one by one.
    // You can also return arrays and other things instead of objects.  See the PDO documentation for details.
    $result = $handle->fetchAll(\PDO::FETCH_OBJ);

    foreach($result as $row){
        print($row->Username);
    }
}
catch(\PDOException $ex){
    print($ex->getMessage());
}
?>

陷阱

  • 当绑定整型变量时,如果不传递 PDO::PARAM_INT 参数有事可能会导致 PDO 对数据加引号。 这会搞坏特定的 MySQL 查询。查看该 bug 报告

  • 未使用 set names utf8mb4 作为首个查询,可能会导致 Unicode 数据错误地存储进数据库,这依赖于你的配置。 如果你绝对有把握你的 Unicode 编码数据不会出问题,那你可以不管这个。

  • 启用持久连接可能会导致怪异的并发相关的问题。 这不是一个 PHP 的问题,而是一个应用层面的问题。只要你仔细考虑了后果,持久连接一般会是安全的。 查看 Stack Overfilow 这个问题

  • 即使你使用了 set names utf8mb4,你也得确认实际的数据库表使用的是 utf8mb4 字符集!

  • 可以在单个 execute() 调用中执行多条 SQL 语句。 只需使用分号分隔语句,但注意这个 bug,在该文档所针对的 PHP 版本中还没修复。

  • Laruence:PDOStatement::bindParam 的一个陷阱

进一步阅读

上一篇: 存储密码 下一篇: PHP 标签