zoukankan      html  css  js  c++  java
  • PDO数据库抽象层总结

      PDO(PHP Data Objects)是一种在PHP里连接数据库的使用接口。PDO与mysqli曾经被建议用来取代原本PHP在用的mysql相关函数,基于数据库使用的安全性,因为后者欠缺对于SQL注入的防护。PDO的出现让PHP达到了一个新的高度。PDO扩展类库为PHP访问数据库定义了一个轻量级、一致性的接口,它提供了一个数据访问抽象层,这样,无论使用什么数据库,都可以通过一致的函数执行查询和获取数据,这大大简化了数据库的操作,并能够屏蔽不同数据库之间的差异。使用 PDO 可以很方便地进行跨数据库程序的开发,以及不同数据库间的移植,是将来PHP在数据库处理方面的主要发展方向。

       一、pod安装:

       php5.1以上的源代码包环境中,向configure命令中添加如下代码:

    --with-pdo-MySQL=/url/local/MySQL    //其中“/url/local/MySQL”为MySQL安装目录

       二、php.ini 开启 PDO

    extension = php_pdo.so
    
    #下面开启其中之一
    extension = php_pdo_MySQL.so    #使用 MySQL 驱动程序
    ;extension = php_pdo_mssql.so    #使用 SQL server 驱动程序
    ;extension = php_pdo_odbc.so    #使用 ODBC 驱动程序
    ;extension = php_pdo_ocl.so    #使用 oracle 驱动程序

      三、PDO 连接数据库:

    <?php
    
    /**
      * 连接 Oracle 数据库示例 -------------------------------------------------------------
      */
    try {
    
        //第一个参数是 DSN 字符串加载 Oracle 数据库,第一参数指定数据库,第二个参数指定字符集
        $dbh = new PDO("OCI:dbname=accounts;charset=UTF-8", "userName", "password");
    } catch(PDOException $e) {
        exit('数据库连接失败' . $e->getMessage());
    }
    
    /**
      * 连接 MySQL 数据库示例 -------------------------------------------------------------
      */
    try {
    
        //第一个参数是 DSN 字符串加载 mysql 数据库,DSN 的第一参数指定数据库,第二个参数指定地址
    //"mysql:dbname=DBtest;host=127.0.0.1" 就是 DSN 字符串,“mysql”就是驱动且必须小写,“dbname”指数据库,“host”指地址,DSN字符串内不能有空格。
    $dbh = new PDO("mysql:dbname=DBtest;host=127.0.0.1", "username", "password"); } catch(PDOException $e) { exit('数据库连接失败' . $e->getMessage()); } /** * 将参数放入文件 -------------------------------------------------------------------------- */ try { //只要将文件/usr/local/dbconnect中的DSN驱动改变,就可以在多个数据库系统之间切换,但要确保该文件由负责执行PHP脚本的用户所拥有,而且此用户拥有必要的权限。 $dbh = new PDO("uri:file:///usr/local/dbconnect", "webuser", "password"); } catch(PDOException $e) { exit('数据库连接失败' . $e->getMessage()); } ?>

      

      只要在php.ini文件中把DSN信息赋给一个名为pdo.dsn.aliasname的配置参数,就可以在PHP服务器的配置文件中维护DSN信息,这里aliasname是后面将提供给构造函数的DSN别名。
      示例:

    [PDO]
    pdo.dsn.oraclepdo = "OCI:dbname=//localhost:1521/mysqldb;charset=UTF-8";

      四、PDO 操作数据库:

      1.示例数据库:

    //结构:
    CREATE table contactInfo (
        uid mediumint(8) unsigned NOT NULL AUTO_INCREMENT,  #联系人ID
        name varchar(50) NOT NULL,                          #姓名
        departmentId char(3) NOT NULL,                      #部门编号
        address varchar(80) NOT NULL,                       #联系地址
        phone varchar(20),                                  #联系电话
        email varchar(100),                                 #联系人的电子邮箱
        PRIMARY KEY(uid)                                    #设置用户ID为主键
    );
    
    //插入的示例数据:
    INSERT INTO contactInfo (name, departmentId, address, phone, email) VALUES ('孙先生', 'D01', '北京市海淀区', '1580168001', 'shunsir@188.com');
    INSERT INTO contactInfo (name, departmentId, address, phone, email) VALUES ('猪先生', 'D02', '北京市朝阳区', '1580168002', 'zhusir@188.com');
    INSERT INTO contactInfo (name, departmentId, address, phone, email) VALUES ('沙先生', 'D03', '北京市东城区', '1580168003', 'shasir@188.com');
    INSERT INTO contactInfo (name, departmentId, address, phone, email) VALUES ('唐先生', 'D01', '北京市西城区', '1580168004', 'tangsir@188.com');
    INSERT INTO contactInfo (name, departmentId, address, phone, email) VALUES ('白先生', 'D01', '北京市昌平区', '1580168005', 'baisir@188.com');

      2.当执行INSERT、UPDATE和DELETE等没有结果集的查询时,使用PDO对象中的exec()方法。该方法成功执行后,将返回受影响的行数。注意,该方法不能用于SELECT查询。

    <?php
    
    try {
        $dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
    } catch (PDOException $e) {
        exit('数据库连接失败' . $e->getMessage());
    }
    
    $query = 'UPDATE contactInfo SET phone = 13812345678 WHERE name = "孙先生"';
    
    //使用 exec() 方法可以执行INSERT、UPDATE和DELETE等操作
    $affected = $dbh->exec($query);
    
    if($affected){
        echo '数据表 contactInfo 受影响的行数为' . $affected;
    } else {
        print_r($dbh->errorInfo());
    }
    ?>

      3.当执行返回结果集的SELECT查询,或者所影响的行数无关紧要时,应当使用PDO对象中的query()方法。

    <?php
    
    $dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $query = 'SELECT name, phone, email FROM contactInfo WHERE departmentId = "D01"';
    
    try {
    
        //执行 SELECT 查询,并返回 PDOStatement 对象
        $pdostatement = $dbh->query($query);
        echo '一共从表中获取到' . $pdostatement->rowCount() . '条数据:<br>';
        foreach($pdostatement as $row){
            echo $row['name'] . '&nbsp;&nbsp;';
            echo $row['phone'] . '&nbsp;&nbsp;';
            echo $row['email'] . '<br>';
        }
    } catch (PDOException $e) {
        echo $e->getMessage();
    }
    ?>

      4.可以使用PDO过滤一些特殊字符,以防一些能引起SQL注入的代码混入。我们在PDO中使用quote()方法实现,示例如下:

    <?php
    
    $dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $query = 'SELECT * FROM users WHERE login = "' . $dbh->quote($_POST['user']) . ' AND password = ' . $dbh->quote($_POST['pass']);
    
    ?>

      五、PDO对预处理语句的支持

      SQL注入的最大原因是SQL语言的命令和数据混编,造成数据中被非法植入命令,特别是SQL语言的拼接是SQL注入的一大危害来源。PDO预处理语句使SQL语言的命令和数据分离,能有效减少SQL注入的危害,但没人打包票说是绝对有效的,因为技术每时每刻都在进步,就像发明SQL语言的人没有意识到还有SQL注入这个鬼一样。

      1.使用命名参数作为占位符的INSERT查询。

    <?php
    
    try {
        $dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
    } catch (PDOException $e) {
        exit('数据库连接失败' . $e->getMessage());
    }
    
    $query = 'INSERT INTO contactInfo (name, address, phone) VALUES ( :name, :address, :phone)';
    
    $stmt = $dbh->prerare($query);
    $stmt->bindParam(':name', $name);
    $stmt->bindParam(':address', $address);
    $stmt->bindParam(':phone', $phone);
    
    $name = '齐天大圣';
    $address = '花果山水帘洞';
    $phone = '15800000001';
    
    $stmt->execute();    //执行绑定参数后的 SQL 语句
    
    $name = '猪八戒';
    $address = '高老庄';
    $phone = '15800000003';
    
    $stmt->execute();    //再次执行绑定参数后的 SQL 语句
    ?>

      2.使用 ? 号数作为占位符的INSERT查询。

    <?php
    
    try {
        $dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
    } catch (PDOException $e) {
        exit('数据库连接失败' . $e->getMessage());
    }
    
    $query = 'INSERT INTO contactInfo (name, address, phone) VALUES ( ?, ?, ?)';
    
    $stmt = $dbh->prerare($query);
    $stmt->bindParam(1, $name);         //1是第一个?号
    $stmt->bindParam(2, $address);     //2是第二个?号
    $stmt->bindParam(3, $phone);        //3是第三个?号
    
    $name = '齐天大圣';
    $address = '花果山水帘洞';
    $phone = '15800000001';
    
    $stmt->execute();    //执行绑定参数后的 SQL 语句
    
    $name = '猪八戒';
    $address = '高老庄';
    $phone = '15800000003';
    
    $stmt->execute();    //再次执行绑定参数后的 SQL 语句
    ?>

      3.预处理查询在执行中替换输入参数的方式。此语法能够省去对$stmt->bindParam()的调用。此方法要在 execute() 中传入关联数组。

    <?php
    
    try {
        $dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
    } catch (PDOException $e) {
        exit('数据库连接失败' . $e->getMessage());
    }
    
    $query = 'INSERT INTO contactInfo (name, address, phone) VALUES ( :name, :address, :phone);
    $stmt->execute(":name"=>"齐天大圣", ":address"=>"花果山水帘洞", ":phone"=>"15800000001");
    $stmt->execute(":name"=>"猪八戒", ":address"=>"高老庄", ":phone"=>"15800000002");
    ?>

      4.如果使用的是问号(?)参数,则需要传递一个索引数组,数组中每个值的位置都要对应每个问号参数。

    <?php
    
    try {
        $dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
    } catch (PDOException $e) {
        exit('数据库连接失败' . $e->getMessage());
    }
    
    $query = 'INSERT INTO contactInfo (name, address, phone) VALUES ( ?, ?, ?)';
    $stmt->execute("齐天大圣", "花果山水帘洞", "15800000001");
    $stmt->execute("猪八戒", "高老庄", "15800000002");
    
    ?>

      5.使用 fetch() 查询数据库

    <?php
    
    try {
        $dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
    } catch (PDOException $e) {
        exit('数据库连接失败' . $e->getMessage());
    }
    
    $query = 'SELECT uid, name, address, phone, email FROM contactInfo';
    
    echo <<<Eof
        <table border="1" align="center" width="90%">
            <caption><h1>联系人信息表</h1></caption>
            <tr bgcolor="#ccc">
                <th>UID</th>
                <th>姓名</th>
                <th>地址</th>
                <th>电话</th>
                <th>电子邮箱</th>
    Eof;
    
    //使用 query() 方式查询数据库,建议用 prepare() 和 execute() 预处理语句执行查询
    $stmt = $dbh->query($query);
    
    while(list($uid, $name, $address, $phone, $email) = $stmt->fetch(PDO::FETCH_NUM)){
        echo '<tr>';
        echo '<td>' . $uid . '</td>';
        echo '<td>' . $name . '</td>';
        echo '<td>' . $address . '</td>';
        echo '<td>' . $phone . '</td>';
        echo '<td>' . $email . '</td>';
        echo '</tr>';
    }
    
    echo <<<Eof
        <table>
    Eof;
    
    ?>

      6.fetchAll()方法与fetch()方法类似,但是该方法只需要调用一次就可以获取结果集中的所有行,并赋给返回的二维数组。使用fetchAll()方法代替fetch()方法,在很大程度上是出于方便的考虑。然而,使用fetchAll()方法处理特别大的结果集时,会给数据库服务器资源和网络带宽带来很大的负担。

    <?php
    
    try {
        $dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
    } catch (PDOException $e) {
        exit('数据库连接失败' . $e->getMessage());
    }
    
    $query = 'SELECT uid, name, address, phone, email FROM contactInfo';
    
    echo <<<Eof
        <table border="1" align="center" width="90%">
            <caption><h1>联系人信息表</h1></caption>
            <tr bgcolor="#ccc">
                <th>UID</th>
                <th>姓名</th>
                <th>地址</th>
                <th>电话</th>
                <th>电子邮箱</th>
    Eof;
    
    $stmt = $dbh->prepare($query);
    $stmt->execute();
    $allRows = $stmt->fetchall(PDO::FETCH_ASSOC);
    
    while($allRows as $row){
        echo '<tr>';
        echo '<td>' . $row['uid'] . '</td>';
        echo '<td>' . $row['name'] . '</td>';
        echo '<td>' . $row['address'] . '</td>';
        echo '<td>' . $row['phone'] . '</td>';
        echo '<td>' . $row['email'] . '</td>';
        echo '</tr>';
    }
    
    echo <<<Eof
        <table>
    Eof;
    
    /* 以下是 fetchall() 方法中使用两个特别参数的示例 */
    $stmt->execute();
    $rows = $stmt->fetchall(PDO::FETCH_COLUMN, 1);
    echo '所有联系人的性名:';
    print_r($rows);
    
    ?>

      7.bindColumn() 方法,使用该方法可以将一个列和一个指定的变量名绑定,这样在每次使用fetch()方法获取各行记录时,会自动将相应的列值赋给该变量,但前提是 fetch()方法的第一个参数必须设置为 PDO::FETCH_BOTH的值。

    <?php
    
    try {
        $dbh = new PDO('mysql:dbname=testdb;host=localhost', 'dbuser', 'dbpassword');
        $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    } catch (PDOException $e) {
        exit('数据库连接失败' . $e->getMessage());
    }
    
    $query = 'SELECT uid, name, phone, email FROM contactInfo WHERE departmentId = "D01"';
    
    try {
        $stmt = $dbh->prepare($query);
        $stmt->execute();
    
        $stmt->bindColumn(1, $uid);                     //通过列位置偏移量绑定变量 $uid
        $stmt->bindColumn(2, $name);                //通过列位置偏移量绑定变量 $name
        $stmt->bindColumn('phone', $phone);     //通过列名称绑定变量 $phone
        $stmt->bindColumn('email', $email);       //通过列名称绑定变量 $email
    
        while($stmt->fetchall(PDO::FETCH_BOUND)){
            echo $uid . '&nbsp;&nbsp;' . $name . '&nbsp;&nbsp;' . $phone . '&nbsp;&nbsp;' . $email . '<br>';
        }
    } catch (PDOException $e) {
        echo $e->getMessage();
    }
    
    ?>

    8.在进行项目开发时,有时需要在数据库中存储“大型”数据。大型对象可以是文本数据,也可以是二进制数据形式的图片、视频等。PDO 允许在 bindParam()或 bindColumn()调用中通过使用PDO::PARAM_LOB类型代码来使用大型数据类型。PDO::PARAM_LOB告诉PDO将数据映射为流,所以可以使用PHP中的文件处理函数来操纵这样的数据。

      示例:

      数据写入代码:

    <?php
    /**
     * 数据库:testdb
     *
     * 数据表创建的结构
     *
     * CREATE TABLE `images`(
     *    `id` mediumint(8) unsigned NOT NULL AOTU_INCREMENT,
     *    `contenttype` varchar(50) NOT NULL,
     *    `imagedata`    blob NOT NULL,
     *    PRIMARY KEY(`id`)
     * )
     */
     
    if(filter_has_var(INPUT_POST, "isFile")){
        try {
            $dbh = new PDO('mysql:dbname=testdb;host=localhost', 'mysqlUser', 'mysqlPass');
            $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch (PDOException $e) {
            exit('数据库连接失败' . $e->getMessage());
        }
    
         $stmt = $dbh->prepare("INSERT INTO images(contenttype, imagedata) VALUE (?, ?)");
     
         $fp = fopen($_FILES['file']['tmp_name'], 'rb');
     
         $stmt->bindParam(1, $_FILES['file']['type']);
         $stmt->bindParam(2, $fp, PDO::PARAM_LOB);
         $stmt->execute();
    }
    ?>
        
    <form action="" method="post" enctype="multipart/form-data">
        <input type="file" name="file">
        <input type="hidden" name="isFile">
        <input type="submit" value="提交">
    </form>

      数据读取代码:

    <?php
    try {
        $dbh = new PDO('mysql:dbname=testdb;host=localhost', 'mysqlUser', 'mysqlPass');
        $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    } catch (PDOException $e) {
        exit('数据库连接失败' . $e->getMessage());
    }
    
    $stmt = $dbh->prepare("SELECT contenttype, imagedata FROM images WHERE id=?");
    $stmt->execute(array($_GET["id"]));
    list($type, $lob) = $stmt->fetch(PDO::FETCH_NUM);
    if(!empty($type)){
        header("Content-Type: $type");
        echo $lob;
    }else{
        echo 'No pictures';
    }
    ?>

      9.PDO 对 MySQL 数据库的事务处理:

    <?php
    /**
      * use testdb
      *
      * CREATE TABLE account(
      *    id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
      *    name VARCHAR(50) NOT NULL,
      *    cash DECIMAL(9,2) NOT NULL,
      *    PRIMARY KEY(id)
      * ) engine=InnoDB;
      *
      * INSERT INTO account (name, cash) VALUES ("userA", 1000);
      * INSERT INTO account (name, cash) VALUES ("userB", 9000);
      *
      * 以下是 userA 用户转 80 元给 userB 用户
      */
      
    try {
        $dbh = new PDO('mysql:dbname=testdb;host=localhost', 'mysqlUser', 'mysqlPassword');
        $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    } catch (PDOException $e) {
        exit('数据库连接失败' . $e->getMessage());
    }
    
    $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, 0);    //关闭数据库自动提交
    
    try{
        $price = 80;    //转账金额 userA -> userB
        $dbh->beginTransaction();    //开启数据库事务
        $affected_rows = $dbh->exec("UPDATE `account` SET `cash` = `cash` - {$price} WHERE `name` = 'userA'");    //转出
        
        if($affected_rows > 0){
            echo "userA用户成功转出{$price}元人民币<br>";
        }else{
            throw new PDOException("userA用户转出失败");
        }
        
        $affected_rows = $dbh->exec("UPDATE `account` SET `cash` = `cash` + {$price} WHERE `name` = 'userB'");    //转入
        if($affected_rows > 0){
            echo "userB用户成功转入{$price}元人民币<br>";
        }else{
            throw new PDOException("userB用户转入失败");
        }
        
        $dbh->commit();    //事务处理
        echo '交易成功!';
    } catch (PDOExcaption $e){
        echo '交易失败' . $e->getMessage();
        $dbh->roolback();    //SQL 提交失败,数据库回滚
    }
    
    $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, 1);    //重启数据库自动提交功能
    ?>
  • 相关阅读:
    腾讯为什么会出Q立方浏览器?
    String,StringBuffer与StringBuilder的区别??
    Linux Socket编程(不限Linux)
    将div显示在屏幕正中央
    计算鼠标坐标是否在指定范围内
    正则
    ajax异步通信
    CSS Float 换行
    jQuery强大的jQuery选择器
    给display字段增加筛选功能
  • 原文地址:https://www.cnblogs.com/qingsong/p/13982909.html
Copyright © 2011-2022 走看看