เมื่อคำสั่ง mysql_xxx ถูกเลิกใช้ใน PHP 5.5

ในภาษา PHP นั้นมีวิธีเชื่อมต่อกับฐานข้อมูล MySQL อยู่หลายแบบมากครับ  เช่น mysql, mysqli, หรือ pdodb ซึ่งวิธีที่โปรแกรมเมอร์ไทยๆ นิยมและถูกสอนให้ใช้มากที่สุด  คือวิธี mysql (พวกคำสั่ง mysql_connect(), mysql_query(), หรือ mysql_fetch_array() นั่นแหละ)

ปัญหาที่เกิดขึ้นแล้วในตอนนี้คือคำสั่งตระกูล mysql ทั้งหลาย  ถูกประกาศ deprecated (เลิกใช้) ไปเรียบร้อยแล้วใน PHP5.5 แม้ว่าในตอนนี้คำสั่งตระกูล mysql จะยังใช้งานได้ตามปกติ  แต่ก็ยังใช้งานได้เพื่อให้โค๊ดเก่าๆ ทำงานได้เท่านั้น (จะขึ้น deprecated error ถ้าเราเปิดแสดง error เอาไว้) และในอนาคตฟังก์ชันกลุ่มนี้ก็จะถูกถอดออกจาก PHP แน่นอน

สาเหตุการเลิกใช้นั้นหลักๆ เนื่องมาจากคำสั่ง mysql พวกนี้เขียนอยู่บน C Wrapper รุ่นเก่าที่ไม่มีการสนับสนุนแล้ว  หรือพูดง่ายๆ คือคำสั่งกลุ่มนี้โดนลอยแพแล้วนั่นเอง  ทาง PHP ก็แนะนำว่าให้ใช้คำสั่งกลุ่ม mysqli หรือ pdodb ในการเชื่อมต่อกับ MySQL แทน  และคำสั่งชุด MySQLi ก็ทำงานร่วมกับ MySQL 4.1 ขึ้นไปได้ดีกว่าอีกด้วย

MySQLi vs PDODB

ทางออกที่ PHP เสนอนั้นมีอยู่สองทาง (จริงๆ มันก็มีทางอื่นอีก) คือ MySQLi และ PDODB ซึ่งยังคงมีการสนับสนุนอยู่ (และสองคำสั่งนี้ก็ทำงานได้เร็วกว่าคำสั่ง mysql เดิมด้วย)  แม้โดยรวมแล้วคำสั่งทั้งสองชุดจะมีการใช้งานที่คล้ายกัน (รองรับการเขียนแบบ OOP) แต่ก็ยังมีข้อแตกต่างกันอยู่ดังนี้

  1. PDODB รองรับฐานข้อมูล 12 รูปแบบ  ในขณะที่ MySQLi รองรับเพียง MySQL เท่านั้น
  2. PDODB สามารถตั้งชื่อพารามิเตอร์ในการ Prepare ได้
  3. MySQLi รองรับการเขียนแบบ Procedural (เขียนเป็นลำดับโปรแกรมแบบเดิมๆ) ในขณะที่ PDODB ต้องเขียนเป็น OOP เท่านั้น
  4. MySQLi ทำงานกับ MySQL ได้เร็วกว่า PDODB เล็กน้อย

ส่วนตัวผมจะทำงานกับ Framework หรือ CMS เป็นหลัก  มันก็จะมี wrapper ครอบคำสั่งพวกนี้อีกที (ส่วนใหญ่ที่เจอจะเป็น MySQLi) แม้ในความรู้สึกแล้วผมชอบ PDODB มากกว่าเพราะความยืดหยุ่นและฟีเจอร์บางอย่างที่เหนือกว่าของมัน  แต่สำหรับคนที่ถนัดกับการเขียนโปรแกรมแบบ procedural แบบเดิมๆ  น่าจะชอบใจกับ MySQLi มากกว่า  เพราะว่าการใช้งานนั้นเหมือนกับคำสั่ง mysql เดิมแทบทุกประการครับ

Procedural MySQLi

คนที่เรียนหรือหัดเขียน PHP มา  คงจะคุ้นเคยกับการใช้งาน MySQL แบบนี้

<?php
$connect = mysql_connect("localhost", "root", "password");
$db = mysql_select_db("mydatabase");

$sql = "SELECT * FROM `tbl_data` ORDER BY `id` ASC LIMIT 0,20";
$query = mysql_query($sql);
$num_rows = mysql_num_rows($query);

for($i=0; $i < $num_rows; $++;){
	$result = mysql_fetch_array($query);
	... do something ...
}
?>

ซึ่งการเปลี่ยนมาใช้ MySQLi สำหรับการเขียนแบบ procedural แบบนี้นั้นง่ายมากครับ  คือเปลี่ยน mysql_xxx เป็น mysqli_xxx เสียให้หมด  แบบนี้

<?php
$connect = mysqli_connect("localhost", "root", "password", "mydatabase");

$sql = "SELECT * FROM `tbl_data` ORDER BY `id` ASC LIMIT 0,20";
$query = mysqli_query($connect, $sql);
$num_rows = mysqli_num_rows($query);

for($i=0; $i < $num_rows; $++;){
	$result = mysqli_fetch_array($query);
	... do something ...
}
?>

ใน MySQLi เราสามารถเลือกฐานข้อมูลในขั้นตอนการ connect กับฐานข้อมมูลได้ในทันที  และอีกข้อหนึ่งที่ต้องระวังคือคำสั่ง mysqli_query()  จำเป็นจะต้องระบุ link identifier ด้วยทุกครั้งครับ (link identifier คือตัวแปรที่เก็บ mysqli_connect()  นั่นแหละ  ในที่นี้คือ $connect)

แค่นี้ทุกอย่างก็เรียบร้อยครับ ง่ายเนอะ  ดังนั้นถ้าทำได้ก็ให้ทำเสียตั้งแต่ตอนนี้ครับ  เพิ่ม i เข้าไปตัวเดียว  แลกกับ compatibility ที่ยาวนานขึ้น  ผมว่าคุ้มนะ

OOP MySQLi

ข้อดีของ MySQLi (รวมถึง PDODB) นั่นคือมันสามารถเขียนในรูปแบบเชิงวัตถุหรือ OOP ได้ครับ  ทำให้เราสามารถเขียนคลาสฐานข้อมูลของเรามา extend คลาส MySQLi นี้ได้ในทันที  และสามารถนำโค๊ดเก่ากลับมาใช้ใหม่ได้เรื่อยๆ ได้ง่ายและเป็นระเบียบด้วยครับ

การใช้งานคร่าวๆ นั้น คือเราจะประกาศออพเจ็กท์ mysqli และจัดการเชื่อมต่อกับเลือกฐานข้อมูลในขั้นตอน construct ได้ทันทีครับ

$db = new mysqli("localhost", "username", "password", "database");

และเมื่อเราต้องการคิวรี่คำสั่งต่างๆ  ก็สามารถสั่งคิวรี่ผ่าน mysqli::query ได้ทันที  เช่น

$query = $db->query("SELECT * FROM `mytbl` WHERE `id` = 1");

และสำหรับการใช้งานอื่นๆ  สามารถลองไปดูได้ใน PHP Manual ครับ

Prepared statement

นอกจากการคิวรี่ข้อมูลตรงๆ แล้ว  ยังมีรูปแบบการคิวรี่อีกประเภทหนึ่ง  นั่นคือ Prepared statement

รูปแบบการใช้งานของมันคือเราจะเขียน sql เปล่าๆ ทิ้งไว้อันหนึ่ง  แล้วก็ bind พารามิเตอร์เข้าไปใน sql ที่เราเขียนไว้ (พร้อมกำหนดประเภทข้อมูล) ข้อดีของมันคือการ escape ข้อมูล (เช่นถอด quote เพื่อกัน sql injection) สามารถทำได้ง่ายและสะดวกกว่าการมานั่ง mysqli_real_escape_string()  เอาเองทีละตัว (เมธอด bind_param จะจัดการ escape ข้อมูลทุกชิ้นให้ในทีเดียว) และการเขียนแบบ prepared ยังทำงานได้เร็วกว่าเดิมด้วยเช่นกัน

การคิวรี่แบบ prepared มีลักษณะแบบนี้ครับ

$id = $_GET['id'];

$db = new mysqli("localhost", "username", "password", "mydatabase");
$query = $db->prepare("
	SELECT *
	FROM `tbl_data`
	WHERE id = ?
");
$query->bind_param("i", $id);
$query->execute();

ใน $query->bind_param เราจำเป็นต้องกำหนดประเภทข้อมูลของพารามิเตอร์ต่างๆ ด้วยเช่นกัน  โดยมันจะมีพารามิเตอร์อยู่ 4 ประเภทคือ

  1. i สำหรับ interger
  2. d สำหรับ double
  3. s สำหรับ string
  4. b สำหรับ blob

โดยเราจะจำหนดเป็นสตริงยาวๆ อยู่ในพารามิเตอร์แรกของ bind_param ครับ (อย่างในตัวอย่างที่ $id เป็น interger จึงมีการกำหนดไว้เป็น i) สมมุติว่าเราจะ bind_param ทั้งหมด 3 ตัวคือ

  1. $id (int)
  2. $name (string)
  3. $email (string)

เราก็จะ bind_param แบบนี้ครับ

$query->bind_param("iss", $id, $name, $email)

และในการดึงข้อมูลออกมาแสดง  เราจะต้อง bind_result เสียก่อน  แล้วจึง fetch ข้อมูลออกมาครับ  เช่น

$query->bind_result($userid, $username, $password, $email);
while($query->fetch()){
	echo "ID: $userid , Username: $username , Email: $email \n";
}

สำหรับการเขียน Prepared statement ใน PDODB นั้นรูปแบบก็จะคล้ายๆ กันนี่แหละครับ  ผมขอไม่พูดถึงแล้วกันนะครับ

ทิ้งท้ายสำหรับคนที่ใช้เฟรมเวิร์กหรือ CMS ต่างๆ

สำหรับใครที่ใช้เฟรมเวิร์กหรือ CMS อาจจะหมดห่วงไปได้ครับ  เพราะเท่าที่ทราย  บรรดา CMS และ Framework ต่างๆ ที่ได้เตรียมฟังก์ชันหรือคลาสของฐานข้อมูลเอาให้ไว้แล้วนั้นจะหันไปใช้ MySQLi กันหมดแล้ว (และรองรับกันนานแล้วด้วย) และ CMS บางตัวก็มีตัวเลือกให้ใช้ทั้ง MySQL และ MySQLi สิ่งที่ต้องทำก็มีเพียงแค่เข้าไปแก้ไข Config ของมันก็เท่านั้นเองครับ

Posted by Jirayu

WordPress Developer ที่พอมีประสบการณ์อยู่บ้าง วันไหนไม่ทำงานอยู่บ้านว่างๆ ก็นั่งเลี้ยงแมว

Comments