PHP 菜鳥教程

PHP 高級教程

PHP & MySQL

PHP 參考手冊

PHP MySQL 預(yù)處理語句

在本教程中,您將學習如何使用PHP在MySQL中使用預(yù)處理語句。

預(yù)處理語句是什么

預(yù)處理語句(也稱為參數(shù)化語句)只是一個SQL查詢模板,其中包含占位符而不是實際參數(shù)值。在執(zhí)行語句時,這些占位符將被實際值替換。

MySQLi支持使用匿名位置占位符(?),如下所示:

INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?);

而PDO支持匿名位置占位符(?)和命名占位符。命名占位符以冒號(:)開頭,后跟標識符,如下所示:

INSERT INTO persons (first_name, last_name, email)
VALUES (:first_name, :last_name, :email);

預(yù)處理語句執(zhí)行包括兩個階段:準備和執(zhí)行。

  • 準備 - 在準備階段,將創(chuàng)建一個SQL語句模板并將其發(fā)送到數(shù)據(jù)庫服務(wù)器。服務(wù)器解析語句模板,執(zhí)行語法檢查和查詢優(yōu)化,并將其存儲以備后用。

  • 執(zhí)行 - 執(zhí)行期間,參數(shù)值將發(fā)送到服務(wù)器。服務(wù)器從語句模板和這些值創(chuàng)建一個語句以執(zhí)行它。

預(yù)處理語句非常有用,尤其是當您多次使用不同的值(例如一系列語句)多次執(zhí)行一條特定的INSERT語句時。以下部分描述了使用它的一些主要優(yōu)點。

使用預(yù)處理語句的優(yōu)點

一個預(yù)處理語句可以高效地重復(fù)執(zhí)行同一條語句,因為該語句僅被再次解析一次,而它可以多次執(zhí)行。由于每次執(zhí)行時僅需要將占位符值傳輸?shù)綌?shù)據(jù)庫服務(wù)器,而不是傳輸完整的SQL語句,因此它還可以最大程度地減少帶寬使用。

預(yù)處理語句還提供了強大的保護,可防止SQL注入,因為參數(shù)值未直接嵌入在SQL查詢字符串中。使用不同的協(xié)議將參數(shù)值與查詢分開發(fā)送到數(shù)據(jù)庫服務(wù)器,因此不會干擾它。在解析語句模板之后,服務(wù)器直接在執(zhí)行時使用這些值。這就是為什么預(yù)處理語句不太容易出錯的原因,因此被認為是數(shù)據(jù)庫安全性中最關(guān)鍵的元素之一。

以下示例將向您展示預(yù)準備語句的實際工作方式:

示例:面向過程方式

<?php
/* 嘗試MySQL服務(wù)器連接。 假設(shè)您正在運行MySQL。
具有默認設(shè)置的服務(wù)器(沒有密碼的用戶“root”) */
$link = mysqli_connect("localhost", "root", "", "demo");
 
//檢查連接
if($link === false){
    die("錯誤:無法連接。 " . mysqli_connect_error());
}
 
//使用預(yù)處理語句
$sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)";
 
if($stmt = mysqli_prepare($link, $sql)){
    //將變量作為參數(shù)綁定到預(yù)處理語句
    mysqli_stmt_bind_param($stmt, "sss", $first_name, $last_name, $email);
    
    /* 設(shè)置參數(shù)值并執(zhí)行,該語句再次插入另一行 */
    $first_name = "Hermione";
    $last_name = "Granger";
    $email = "hermionegranger@mail.com";
    mysqli_stmt_execute($stmt);
    
    /* 設(shè)置參數(shù)值并執(zhí)行插入行的語句 */
    $first_name = "Ron";
    $last_name = "Weasley";
    $email = "ronweasley@mail.com";
    mysqli_stmt_execute($stmt);
    
    echo "記錄插入成功。";
} else{
    echo "錯誤:無法準備查詢: $sql. " . mysqli_error($link);
}
 
//關(guān)閉語句
mysqli_stmt_close($stmt);
 
//關(guān)閉連接
mysqli_close($link);
?>

示例:面向?qū)ο蠓绞?/h3>
<?php
/* 嘗試MySQL服務(wù)器連接。 假設(shè)您正在運行MySQL。
具有默認設(shè)置的服務(wù)器(沒有密碼的用戶“root”) */
$mysqli = new mysqli("localhost", "root", "", "demo");
 
//檢查連接
if($mysqli === false){
    die("錯誤:無法連接。 " . $mysqli->connect_error);
}
 
// 使用預(yù)處理語句
$sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)";
 
if($stmt = $mysqli->prepare($sql)){
    // 將變量作為參數(shù)綁定到預(yù)處理語句
    $stmt->bind_param("sss", $first_name, $last_name, $email);
    
    /* 設(shè)置參數(shù)值并執(zhí)行。
    再次執(zhí)行該語句以插入另一行 */
    $first_name = "Hermione";
    $last_name = "Granger";
    $email = "hermionegranger@mail.com";
    $stmt->execute();
    
    /* 設(shè)置參數(shù)值并執(zhí)行
        要插入行的語句 */
    $first_name = "Ron";
    $last_name = "Weasley";
    $email = "ronweasley@mail.com";
    $stmt->execute();
    
    echo "已成功插入記錄。";
} else{
    echo "錯誤:無法準備查詢:$sql. " . $mysqli->error;
}
 
//關(guān)閉語句
$stmt->close();
 
//關(guān)閉連接
$mysqli->close();
?>

示例:PDO方式

<?php
/* 嘗試MySQL服務(wù)器連接。 假設(shè)您正在運行MySQL。
具有默認設(shè)置的服務(wù)器(沒有密碼的用戶“root”) */
try{
    $pdo = new PDO("mysql:host=localhost;dbname=demo", "root", "");
    // 將PDO錯誤模式設(shè)置為異常
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e){
    die("錯誤:無法連接。 " . $e->getMessage());
}
 
//嘗試執(zhí)行插入查詢
try{
    //使用預(yù)處理語句
    $sql = "INSERT INTO persons (first_name, last_name, email) VALUES (:first_name, :last_name, :email)";
    $stmt = $pdo->prepare($sql);
    
    //將參數(shù)綁定到語句
    $stmt->bindParam(':first_name', $first_name, PDO::PARAM_STR);
    $stmt->bindParam(':last_name', $last_name, PDO::PARAM_STR);
    $stmt->bindParam(':email', $email, PDO::PARAM_STR);
    
    /* 設(shè)置參數(shù)值并執(zhí)行,
      再次執(zhí)行該語句以插入另一行 */
    $first_name = "Hermione";
    $last_name = "Granger";
    $email = "hermionegranger@mail.com";
    $stmt->execute();
    
    /* 設(shè)置參數(shù)值并執(zhí)行
        要插入行的語句 */
    $first_name = "Ron";
    $last_name = "Weasley";
    $email = "ronweasley@mail.com";
    $stmt->execute();
    
    echo "記錄插入成功。";
} catch(PDOException $e){
    die("錯誤:無法準備/執(zhí)行查詢: $sql. " . $e->getMessage());
}
 
// 關(guān)閉語句
unset($stmt);
 
//關(guān)閉連接
unset($pdo);
?>

如您在上面的示例中看到的,我們INSERT僅準備了一次語句,但是通過傳遞不同的參數(shù)集多次執(zhí)行了該語句。

代碼的用法(程序風格)

在上面示例的SQL INSERT語句中,問號用作first_name,last_nameemail字段值的占位符。

mysqli_stmt_bind_param()函數(shù)將變量綁定到占位符(?)在SQL語句模板中。占位符(?)將替換為執(zhí)行時變量中保存的實際值。作為第二個參數(shù)提供的類型定義字符串,即“sss”字符串指定每個綁定變量的數(shù)據(jù)類型為string(字符串)。

類型定義字符串指定相應(yīng)綁定變量的數(shù)據(jù)類型,參數(shù)有以下四種類型:

  • i - integer(整型)

  • d - double(雙精度浮點型)

  • s - string(字符串)

  • b - BLOB(binary large object:二進制大對象)

類型定義字符串中的綁定變量數(shù)和字符數(shù)必須與SQL語句模板中的占位符數(shù)匹配。

使用通過Web表單接收的輸入

如果您還記得上一章,我們已經(jīng)創(chuàng)建了一個HTML表單,用于將數(shù)據(jù)插入數(shù)據(jù)庫。在這里,我們將通過執(zhí)行預(yù)處理語句來擴展該示例。您可以使用相同的HTML表單來測試以下插入腳本示例,但只需確保action在表單的屬性中使用了正確的文件名即可。

這是用于插入數(shù)據(jù)的更新的PHP代碼。如果仔細查看示例,您會發(fā)現(xiàn)我們沒有mysqli_real_escape_string()像上一章中的示例那樣使用來轉(zhuǎn)義用戶輸入。由于在預(yù)處理語句中,用戶輸入永遠不會直接替換為查詢字符串,因此不需要正確地對它們進行轉(zhuǎn)義。

示例:面向過程方式

<?php
/* 嘗試MySQL服務(wù)器連接。 假設(shè)您正在運行MySQL。
具有默認設(shè)置的服務(wù)器(沒有密碼的用戶“root”) */
$link = mysqli_connect("localhost", "root", "", "demo");
 
//檢查連接
if($link === false){
    die("錯誤:無法連接。 " . mysqli_connect_error());
}
 
//使用預(yù)處理語句
$sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)";
 
if($stmt = mysqli_prepare($link, $sql)){
    //將變量綁定到準備好的語句作為參數(shù)
    mysqli_stmt_bind_param($stmt, "sss", $first_name, $last_name, $email);
    
    //設(shè)定參數(shù)
    $first_name = $_REQUEST['first_name'];
    $last_name = $_REQUEST['last_name'];
    $email = $_REQUEST['email'];
    
    //嘗試執(zhí)行預(yù)處理語句
    if(mysqli_stmt_execute($stmt)){
        echo "記錄插入成功。";
    } else{
        echo "錯誤:無法執(zhí)行查詢: $sql. " . mysqli_error($link);
    }
} else{
    echo "錯誤:無法執(zhí)行查詢: $sql. " . mysqli_error($link);
}
 
// 關(guān)閉語句
mysqli_stmt_close($stmt);
 
//關(guān)閉連接
mysqli_close($link);
?>

示例:面向?qū)ο蠓绞?/h3>
<?php
/* 嘗試MySQL服務(wù)器連接。 假設(shè)您正在運行MySQL。
具有默認設(shè)置的服務(wù)器(沒有密碼的用戶“root”) */
$mysqli = new mysqli("localhost", "root", "", "demo");
 
//檢查連接
if($mysqli === false){
    die("錯誤:無法連接。 " . $mysqli->connect_error);
}
 
//使用預(yù)處理語句
$sql = "INSERT INTO persons (first_name, last_name, email) VALUES (?, ?, ?)";
 
if($stmt = $mysqli->prepare($sql)){
    //將變量作為參數(shù)綁定到預(yù)處理語句
    $stmt->bind_param("sss", $first_name, $last_name, $email);
    
    //設(shè)置參數(shù)
    $first_name = $_REQUEST['first_name'];
    $last_name = $_REQUEST['last_name'];
    $email = $_REQUEST['email'];
    
    //嘗試執(zhí)行預(yù)處理語句
    if($stmt->execute()){
        echo "記錄插入成功。";
    } else{
        echo "錯誤:無法執(zhí)行查詢: $sql. " . $mysqli->error;
    }
} else{
    echo "錯誤:無法執(zhí)行查詢: $sql. " . $mysqli->error;
}
 
//關(guān)閉語句
$stmt->close();
 
//關(guān)閉連接
$mysqli->close();
?>

示例:PDO方式

<?php
/* 嘗試MySQL服務(wù)器連接。 假設(shè)您正在運行MySQL。
具有默認設(shè)置的服務(wù)器(沒有密碼的用戶“root”) */
try{
    $pdo = new PDO("mysql:host=localhost;dbname=demo", "root", "");
    //將PDO錯誤模式設(shè)置為異常
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e){
    die("錯誤:無法連接。 " . $e->getMessage());
}
 
//嘗試執(zhí)行插入查詢
try{
    //使用預(yù)處理語句
    $sql = "INSERT INTO persons (first_name, last_name, email) VALUES (:first_name, :last_name, :email)";
    $stmt = $pdo->prepare($sql);
    
    // 將參數(shù)綁定到語句
    $stmt->bindParam(':first_name', $_REQUEST['first_name'], PDO::PARAM_STR);
    $stmt->bindParam(':last_name', $_REQUEST['last_name'], PDO::PARAM_STR);
    $stmt->bindParam(':email', $_REQUEST['email'], PDO::PARAM_STR);
    
    // 執(zhí)行預(yù)處理語句
    $stmt->execute();
    echo "記錄插入成功。";
} catch(PDOException $e){
    die("錯誤:無法準備/執(zhí)行查詢 $sql. " . $e->getMessage());
}
 
//關(guān)閉語句
unset($stmt);
 
//關(guān)閉連接
unset($pdo);
?>

注意:盡管在預(yù)處理語句中不需要轉(zhuǎn)義用戶輸入,但是您應(yīng)始終驗證從外部源接收到的數(shù)據(jù)的類型和大小,并實施適當?shù)南拗埔苑乐瓜到y(tǒng)資源的利用。

丰满人妻一级特黄a大片,午夜无码免费福利一级,欧美亚洲精品在线,国产婷婷成人久久Av免费高清