Office.in.th
คู่มือระบบ เนื้อหาผ่านการตรวจสอบแล้ว

Prepared Statements เบื้องต้น: การใช้งานbindParam()

บทความนี้แนะนำ Prepared Statements ซึ่งเป็นเครื่องมือสำคัญในการป้องกัน SQL Injection และเพิ่มประสิทธิภาพการทำงานของฐานข้อมูล โดยเน้นการใช้งาน `bindParam()` เพื่...

Prepared Statements เบื้องต้น: การใช้งาน bindParam()

จากบทที่แล้ว เราได้เรียนรู้เกี่ยวกับการตั้งค่า Attribute ของ PDO เพื่อปรับแต่งการทำงานของฐานข้อมูล ในบทนี้ เราจะมาเจาะลึกเรื่อง Prepared Statements ซึ่งเป็นเครื่องมือสำคัญในการป้องกัน SQL Injection และทำให้โค้ดของเราปลอดภัยยิ่งขึ้น โดยเฉพาะการใช้งาน bindParam()

ความสำคัญของ Prepared Statements

Prepared Statements คืออะไร? ลองนึกภาพว่าเรากำลังจะทำอาหารเมนูเดิมซ้ำๆ หลายครั้ง แทนที่จะเตรียมส่วนผสมใหม่ทุกครั้ง เราสามารถเตรียมส่วนผสมทั้งหมดไว้ล่วงหน้า แล้วค่อยนำมาประกอบอาหารได้เลย

Prepared Statements ก็คล้ายกัน เราจะส่งคำสั่ง SQL ไปยังฐานข้อมูลเพียงครั้งเดียว ฐานข้อมูลจะทำการ "เตรียม" คำสั่งนั้นไว้ จากนั้นเราค่อยส่งข้อมูลที่เราต้องการใส่เข้าไปในคำสั่งที่เตรียมไว้แล้ว ซึ่งวิธีนี้มีข้อดีหลายอย่าง:

  • ความปลอดภัย: ป้องกัน SQL Injection ได้อย่างมีประสิทธิภาพ เพราะข้อมูลจะถูกแยกจากคำสั่ง SQL อย่างชัดเจน ฐานข้อมูลจะมองข้อมูลเป็นเพียงข้อมูล ไม่ใช่ส่วนหนึ่งของคำสั่ง SQL
  • ประสิทธิภาพ: ในกรณีที่ต้อง execute คำสั่ง SQL เดิมซ้ำๆ หลายครั้ง Prepared Statements จะเร็วกว่า เพราะฐานข้อมูลไม่ต้อง parse และ optimize คำสั่ง SQL ใหม่ทุกครั้ง
  • ความสะดวก: ช่วยให้โค้ดของเราอ่านง่ายขึ้น เพราะเราแยกคำสั่ง SQL และข้อมูลออกจากกัน

การใช้งาน bindParam()

bindParam() เป็นฟังก์ชันที่ใช้ผูกตัวแปร PHP กับ placeholder ใน Prepared Statement Placeholder คือเครื่องหมายที่เราใช้แทนที่ข้อมูลที่เราจะใส่เข้าไปในคำสั่ง SQL มีสองแบบหลักๆ คือ:

  • Named placeholders: ใช้เครื่องหมาย : นำหน้าชื่อ placeholder เช่น :username, :email
  • Question mark placeholders: ใช้เครื่องหมาย ? แทน placeholder แต่ละตำแหน่ง

เราจะเน้นการใช้ Named placeholders เพราะอ่านง่ายและบำรุงรักษาได้ง่ายกว่า

ตัวอย่างการใช้งาน bindParam()

สมมติว่าเรามีตาราง users ในฐานข้อมูล MySQL ที่มีฟิลด์ id, username, และ email เราต้องการเพิ่มข้อมูลผู้ใช้ใหม่เข้าไปในตาราง


<?php

// ข้อมูลผู้ใช้ที่จะเพิ่ม
$username = 'johndoe';
$email = '[email protected]';

try {
  // เชื่อมต่อฐานข้อมูล (แทนที่ด้วยข้อมูลการเชื่อมต่อของคุณ)
  $db = new PDO('mysql:host=localhost;dbname=mydatabase;charset=utf8', 'username', 'password');
  $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

  // สร้าง Prepared Statement
  $stmt = $db->prepare("INSERT INTO users (username, email) VALUES (:username, :email)");

  // ผูกตัวแปรกับ placeholder
  $stmt->bindParam(':username', $username);
  $stmt->bindParam(':email', $email);

  // Execute คำสั่ง
  $stmt->execute();

  echo "เพิ่มข้อมูลผู้ใช้สำเร็จ!";

} catch(PDOException $e) {
  echo "เกิดข้อผิดพลาด: " . $e->getMessage();
}

?>

คำอธิบายโค้ด:

  1. เราสร้างตัวแปร $username และ $email เพื่อเก็บข้อมูลผู้ใช้ที่เราต้องการเพิ่ม
  2. เราสร้าง Prepared Statement โดยใช้เมธอด prepare() ของอ็อบเจ็กต์ PDO คำสั่ง SQL ของเรามี placeholder :username และ :email
  3. เราใช้ bindParam() เพื่อผูกตัวแปร $username กับ placeholder :username และตัวแปร $email กับ placeholder :email
  4. เราใช้ execute() เพื่อ execute คำสั่ง SQL ที่เตรียมไว้

ข้อสังเกตสำคัญ:

  • bindParam() ผูกตัวแปร *โดยอ้างอิง (by reference)* นั่นหมายความว่าถ้าค่าของตัวแปรเปลี่ยนแปลงหลังจากที่เราเรียก bindParam() ค่าที่ถูกส่งไปยังฐานข้อมูลก็จะเป็นค่าใหม่
  • bindParam() จะต้องถูกเรียกก่อนที่จะ execute คำสั่ง SQL

ความแตกต่างระหว่าง bindParam() และ bindValue()

bindParam() และ bindValue() เป็นฟังก์ชันที่ใช้ผูกตัวแปรกับ placeholder ใน Prepared Statement ทั้งคู่ แต่มีความแตกต่างกันที่สำคัญ:

  • bindParam(): ผูกตัวแปร *โดยอ้างอิง (by reference)* นั่นหมายความว่าถ้าค่าของตัวแปรเปลี่ยนแปลงหลังจากที่เราเรียก bindParam() ค่าที่ถูกส่งไปยังฐานข้อมูลก็จะเป็นค่าใหม่
  • bindValue(): ผูกตัวแปร *โดยค่า (by value)* นั่นหมายความว่าค่าที่ถูกส่งไปยังฐานข้อมูลจะเป็นค่าของตัวแปร ณ เวลาที่เราเรียก bindValue() แม้ว่าค่าของตัวแปรจะเปลี่ยนแปลงในภายหลังก็ตาม

ตัวอย่างเพื่อแสดงความแตกต่าง:


<?php

$value = 10;

try {
  // เชื่อมต่อฐานข้อมูล (แทนที่ด้วยข้อมูลการเชื่อมต่อของคุณ)
  $db = new PDO('mysql:host=localhost;dbname=mydatabase;charset=utf8', 'username', 'password');
  $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

  // Prepared Statement
  $stmt = $db->prepare("SELECT * FROM products WHERE price > :price");

  // ใช้ bindParam()
  $stmt->bindParam(':price', $value);
  $value = 20; // เปลี่ยนค่าของ $value

  // Execute คำสั่ง
  $stmt->execute();

  // ดึงข้อมูล
  $results = $stmt->fetchAll(PDO::FETCH_ASSOC);

  // แสดงผลลัพธ์
  echo "

ผลลัพธ์จากการใช้ bindParam():

"; echo "<pre>"; print_r($results); echo "</pre>"; // Reset statement $stmt = $db->prepare("SELECT * FROM products WHERE price > :price"); // ใช้ bindValue() $stmt->bindValue(':price', 10); // ผูกค่า 10 โดยตรง $value = 20; // เปลี่ยนค่าของ $value (ไม่มีผล) // Execute คำสั่ง $stmt->execute(); // ดึงข้อมูล $results = $stmt->fetchAll(PDO::FETCH_ASSOC); // แสดงผลลัพธ์ echo "

ผลลัพธ์จากการใช้ bindValue():

"; echo "<pre>"; print_r($results); echo "</pre>"; } catch(PDOException $e) { echo "เกิดข้อผิดพลาด: " . $e->getMessage(); } ?>

ในตัวอย่างนี้ เมื่อเราใช้ bindParam() และเปลี่ยนค่าของ $value เป็น 20 หลังจากเรียก bindParam() คำสั่ง SQL จะถูก execute โดยใช้ค่า 20 นั่นคือจะดึงข้อมูล products ที่มีราคามากกว่า 20

แต่เมื่อเราใช้ bindValue() และผูกค่า 10 โดยตรง การเปลี่ยนค่าของ $value ในภายหลังจะไม่มีผล คำสั่ง SQL จะถูก execute โดยใช้ค่า 10 นั่นคือจะดึงข้อมูล products ที่มีราคามากกว่า 10

สรุป:

  • ใช้ bindParam() เมื่อคุณต้องการให้ค่าของตัวแปรถูกส่งไปยังฐานข้อมูลโดยอ้างอิง
  • ใช้ bindValue() เมื่อคุณต้องการให้ค่าที่ถูกส่งไปยังฐานข้อมูลเป็นค่า ณ เวลาที่คุณเรียก bindValue()

ในกรณีส่วนใหญ่ bindValue() จะเป็นตัวเลือกที่ปลอดภัยกว่า เพราะช่วยป้องกันข้อผิดพลาดที่อาจเกิดขึ้นจากการเปลี่ยนแปลงค่าของตัวแปรโดยไม่ตั้งใจ

ในบทถัดไป เราจะมาดูวิธีการใช้งาน execute() ร่วมกับ Prepared Statements เพื่อ execute คำสั่ง SQL ที่ซับซ้อนยิ่งขึ้น

Office.in.th Custom Service

ต้องการพัฒนาระบบหรือปรับแต่งจากโครงสร้างนี้?

Office.in.th รับพัฒนาและออกแบบ Enterprise Web Application, API Integration และระบบบริหารงานภาครัฐ/เอกชน พร้อมการสนับสนุนระยะยาว

ดูบริการรับพัฒนาซอฟต์แวร์