สอนการใช้ PHP ติดต่อฐานข้อมูล MySQL ด้วยชุดคำสั่งของ mysqli ตัวอย่างจะสอนการเขียนโค้ดเพื่อติดต่อ MySQL ด้วยคำสั่ง mysqli_connect โดยติดต่อฐานข้อมูลชื่อ db_test ด้วยผู้ใช้งาน root สามารถเขียนโปรแกรมได้ดังนี้ Show ตัวอย่าง การใช้ PHP ติดต่อฐานข้อมูล MySQL ด้วย mysqli
ผลลัพธ์
คำอธิบาย การใช้ PHP ติดต่อฐานข้อมูล MySQL ตัวอย่างใช้คำสั่ง mysqli_connect สำหรับเชื่อมต่อ โดยกำหนดค่า arguments ทั้งหมด 4 ค่า คือ 1. localhost คือ กำหนดที่อยู่ หรือหมายเลข IP Address ของเครื่องผู้ให้บริการ MySQL โดย localhost หมายถึงเครื่องที่กำลังรันโค้ดนี้อยู่ 2. root คือ กำหนดชื่อผู้เข้าใช้งานฐานข้อมูล MySQL 3. ค่าว่าง คือ รหัสผ่านสำหรับเข้าใช้งานฐานข้อมูล MySQL 4. db_test คือ ชื่อฐานข้อมูล (Database) ที่ต้องการติดต่อ
ถ้าคุณเพิ่งจะหัดเขียน PHP แล้วเจอข้อผิดพลาดข้างบน นั่นเป็นเพราะว่าตั้งแต่ PHP 7.0 เป็นต้นมา (ถ้าไม่มีเหตุจำเป็นจริงๆ ไม่มีใครใช้ PHP5 กันอีกแล้วเนอะ) ส่วนขยาย mysql ได้ถูกนำออกไปจาก PHP โดยสมบูรณ์ ทำให้ฟังก์ชันกลุ่ม mysql ทั้งหมด เช่น แต่อย่าเพิ่งตกใจไป
PHP ได้เอาส่วนเสริมตัวใหม่สำหรับ MySQL เข้ามาแทน นั่นคือ mysqli หรือ MySQL Improved ที่ compatible กับโค้ดเดิมเกือบจะ 100% เพียงแค่เปลี่ยนชื่อฟังก์ชันจาก mysql_xxx เป็น mysqli_xxx แทน และต้องแนบ link identifier ตามไปด้วยทุกครั้ง (ตัวแปรที่เก็บ นอกจาก mysqli แล้วก็ยังมี PDO หรือ PHP Data Object อีกตัวที่ใช้เชื่อมต่อกับฐานข้อมูล MySQL ได้ แต่ PDO ยังสามารถเชื่อมต่อกับฐานข้อมูลประเภทอื่นๆ ได้อีกทั้งหมดเพียบ (ขึ้นอยู่กับว่ามีไดรเวอร์หรือเปล่า) ซึ่งจริงๆ PDO มันก็มีมาตั้งแต่สมัย PHP 5.1 แล้ว แต่เพิ่งมาได้เฉิดฉายกันจริงๆ ก็ตอน PHP7 ที่คนจำเป็นต้องเลิกใช้ mysql กันอย่างเลี่ยงไม่ได้ ในบทความนี้จะพาไปดูการเชื่อมต่อฐานข้อมูลทั้งสามรูปแบบ นั่นคือ Procedural mysqli, OOP mysqli, และ PDO
PDO vs mysqliเป็นคำถามคลาสสิคว่าในเมื่อมันมีทั้ง PDO และ mysqli แล้วแบบนี้จะเลือกใช้อะไรดี? ว่ากันจริงๆ แล้วทั้งสองอย่างนั้นมีข้อดีข้อเสียที่ต่างกันออกไป เช่น PDO
mysqli
การเชื่อมต่อฐานข้อมูลฝั่ง mysqli ทั้งแบบ Procedural และ OOP จะระบุแยกระหว่าง $hostname, $username, $password, และ $database ทั้งคู่ ในขณะที่ PDO จะระบุเป็น $dsn, $username, $password, และสามารถมี $options เป็นพารามิเตอร์สุดท้าย เพื่อกำหนดออปชันของ connection นั้นๆ ได้ด้วย $host = 'vvv.test'; $username = 'external'; $password = 'external'; $database = 'sampledb'; // PDO $pdo = new PDO( "mysql:host={$host}; dbname={$database}", $username, $password, [ PDO::ATTR_EMULATE_PREPARES => false ] ); // OOP mysqli $mysqli = new mysqli( $host, $username, $password, $database ); // Procedural mysqli $mysqli_p = mysqli_connect( $host, $username, $password, $database ); ในสตัวอย่างจะมีการตั้งค่าออปชัน การคิวรี่ข้อมูล
การคิวรี่ข้อมูลนั้นจะมีสองวิธีคือ
และจริงๆ ยังมีการคิวรี่แบบ transaction อีกแบบหนึ่งที่เราจะไว้พูดถึงในโพสต์อื่น และในหัวข้อนี้เราจะพูดถึงการคิวรี่ตรงๆ ผ่านฟังก์ชัน/เมท็อด query กันก่อน ดังอย่างโค้ดด้านล่างนี้ $statement = "SELECT * FROM `person`"; // PDO $pdo->query( $statement ); // OOP mysqli $mysqli->query( $statement ); // Procedural mysqli $mysqli_query = mysqli_query( $mysqli_p, $statement ); การสั่งคิวรี่ฐานข้อมูลโดยตรงนั้นมีความเสี่ยงต่อการถูกโจมตีจากข้อมูลที่ทำการ sanitize ได้ไม่ดีพอ (หรือที่เราเรียกกันว่า sql injection) หรือแม้แต่การเขียนคิวรี่ผิดเพราะสับสนการ concat ระหว่างข้อความและตัวแปร ซึ่งเราสามารถแก้ปัญหานี้ได้ด้วยการเปลี่ยนไปใช้ prepared statement แทนการคิวรี่ฐานข้อมูลโดยตรง
Prepared statementการคิวรี่ด้วย Prepared statement นั้นจะปลอดภัยกว่าการคิวรี่ตรงๆ อยู่ระดับหนึ่ง
หลักการง่ายๆ ของมันคือเราจะสร้าง query template ขึ้นมาก่อน และเว้นว่างส่วนที่จะเป็น value ที่เราต้องการคิวรี่เอาไว้ เช่นปกติเราจะคิวรี่ด้วย แบบ PDO// PDO $pdo_statement = $pdo->prepare( 'SELECT * FROM `person` WHERE `country` = :country AND `state` = :state AND `city` = :city' ); $pdo_statement->execute([ ':country' => 'United States', ':state' => 'Washington', ':city' => 'Seattle' ]); $result = $pdo_statement->fetch(); while ( $result ) { var_dump( $result ); } แบบ OOP mysqli// OOP mysqli $mysqli_statement = $mysqli->prepare( 'SELECT * FROM `person` WHERE `country` = ? AND `state` = ? AND `city` = ?' ); $mysqli_statement->bind_param( 'sss', $country, $state, $city ); $country = 'United States'; $state = 'Washington'; $city = 'Seattle'; $mysqli_statement->execute(); $result = $mysqli_statement->get_result(); while ( $row = $result->fetch_array() ) { var_dump( $row ); } แบบ Procedural mysqli// Procedural mysqli $procedural_statement = mysqli_prepare( $mysqli_p, 'SELECT * FROM `person` WHERE `country` = ? AND `state` = ? AND `city` = ?' ); mysqli_stmt_bind_param($procedural_statement, 'sss', $country, $state, $city); $country = 'United States'; $state = 'Washington'; $city = 'Seattle'; $result = mysqli_stmt_get_result( $procedural_statement ); while ( $row = mysqli_fetch_array( $result ) ) { var_dump( $row ); } นอกจากนี้ การที่ prepared statement นั้นจะทำการส่งตัว prepared statement และพารามิเตอร์ต่างๆ แยกกัน ทำให้เรายังสามารถทำการ prepare statement แค่รอบเดียว จากนั้นเปลี่ยนค่าพารามิเตอร์ต่างๆ ใหม่ แล้วสั่งคิวรี่อีกรอบได้ทันทีโดยไม่จำเป็นต้องเขียน sql statement ใหม่ทั้งหมด การ bind parametersในตัวอย่างของการใช้ prepared statement จะเห็นว่ามีการ bind parameters เข้าไปด้วย (เพื่อแทนค่า :param ใน PDO หรือ ? ใน mysqli) ซึ่งระหว่าง mysqli และ PDO นั้นจะมีวิธีการ bind ต่างกันอยู่เล็กน้อย การ bind parameters บน mysqliใน mysqli
จะมีเมท็อด
Data type ของ mysqli มีอยู่ 4 ตัวคือ
ตัวอย่างจากข้างบนคือเรามี Prepared statement ดังนี้ $mysqli_statement = $mysqli->prepare( 'SELECT * FROM `person` WHERE `country` = ? AND `state` = ? AND `city` = ?' ); เท่ากับว่าจะมีพารามิเตอร์ 3 ตัว คือ ? ของ country, state, และ city ตามลำดับ ทั้งสามค่าจะรับค่า string ธรรมดาเข้ามา ดังนั้นค่าของ data type ก็จะเป็น s สามตัวเรียงกัน คือ ตัวแปรที่จะใช้ในการ bind นั้น เราจะใช้ค่าจาก $country สำหรับ country, $state สำหรับ state, และ $city สำหรับ city ดังนั้นเราจะเขียนคำสั่ง // OOP mysqli $mysqli_statement->bind_param( 'sss', $country, $state, $city ); // Procedural mysqli mysqli_stmt_bind_param($procedural_statement, 'sss', $country, $state, $city); หลังจากเรากำหนดค่าการ bind parameter เรียบร้อยแล้ว
ก็จัดการใส่ค่าให้ตัวแปรและสั่ง // OOP mysqli $country = 'Thailand'; $state = 'Bangkok'; $city = 'Sathorn'; $mysqli_statement->execute(); $result = $mysqli_statement->get_result(); // Procedural mysqli $country = 'Thailand'; $state = 'Bangkok'; $city = 'Sathorn'; mysqli_stmt_execute( $procedural_statement ); $result = mysqli_stmt_get_result( $procedural_statement ); การ bind parameters บน PDOในเรื่องของการ bind parameter นั้น PDO จะมีวิธีที่สะดวกกว่า mysqli อยู่เล็กน้อย อย่างแรกคือเราสามารถส่งค่า bind เข้าไปยังเมท็อด
การส่งค่าเข้าไปใน // Unnamed parameters $pdo_statement = $pdo->prepare( 'SELECT * FROM `person` WHERE `country` = ? AND `state` = ? AND `city` = ?' ); $pdo_statement->execute(['Thailand', 'Bangkok', 'Sathorn']); $result = $pdo_statement->fetch(); // Named parameters $pdo_statement_named = $pdo->prepare( 'SELECT * FROM `person` WHERE `country` = :country AND `state` = :state AND `city` = :city' ); $pdo_statement_named->execute([ ':country' => 'Thailand', ':state' => 'Bangkok', ':city' => 'Sathorn' ]); $result = $pdo_statement_named->fetch(); นับ num_rowsฟีเจอร์หนึ่งที่เรามักจะใช้ร่วมกับการ SELECT นั่นคือการนับว่าคิวรี่ข้อมูลออกมาได้กี่แถว ใน mysqli และ PDO สามารถทำได้ดังนี้ // PDO $result = $pdo_statement->fetchAll(); $num_rows = count( $result ); // OOP mysqli $result = $mysqli_statement->get_result(); $num_rows = $result->num_rows; // Procedural mysqli $result = mysqli_stmt_get_result( $procedural_statement ); $num_rows = mysqli_num_rows( $result ); ใน PDO นั้นจะไม่มีพร็อพเพอร์ตี้หรือเมท็อดสำหรับเก็บจำนวนแถวให้ในตัว แต่เราสามารถใช้เมท็อด affected rows และ insert idการคิวรี่แบบ SELECT นั้นไม่ใช่เรื่องน่าปวดหัวอะไรมากนัก เพราะเราสามารถตรวจสอบผลลัพธิ์ได้ง่ายๆ ด้วยการลูปคำสั่ง แต่ในกรณีคำสั่งอื่นเช่น INSERT, UPDATE, หรือ DELETE นั้นเรามักจะมีการใช้ค่า affected rows หรือจำนวนแถวที่คิวรี่นั้นๆ มีผลด้วย และในคำสั่ง INSERT ก็จะมีการใช้ insert id เพื่อดึงค่า primary key ของข้อมูลล่าสุดที่เพิ่ง insert ลงฐานข้อมูลกลับออกมาใช้งาน ทางฝั่ง mysqli จะเตรียมพร็อพเพอร์ตี้ แบบ PDO// Last insert ID $pdo_insert = $pdo->prepare( 'INSERT INTO `person` (`firtname`, `lastname`) VALUES ( ?, ? )' ); $pdo_insert->execute(['John', 'Doe']); $insert_id = $pdo->lastInsertId(); // Affected rows $pdo_delete = $pdo->prepare( 'DELETE FROM `person` WHERE `state` = ?'); $pdo_delete->execute(['Bangkok']); $affected_rows = $pdo_delete->rowCount(); สังเกตว่าเราเรียก แบบ OOP mysqli// Last insert ID $mysqli_statement = $mysqli->prepare( 'INSERT INTO `person` (`firtname`, `lastname`) VALUES ( ?, ? )' ); $mysqli_statement->bind_param( 'ss', $firstname, $lastname ); $firstname = 'John'; $lastname = 'Doe'; $mysqli_statement->execute(); $insert_id = $mysqli_statement->insert_id; // Afftected rows $mysqli_statement = $mysqli->prepare( 'DELETE FROM `person` WHERE `state` = ?' ); $mysqli_statement->bind_param( 's', $state ); $state = 'Bangkok'; $mysqli_statement->execute(); $affected_rows = $mysqli_statement->affected_rows; สังเกตว่าใน mysqli เราจะเรียกทั้ง affected rows และ last insert id ออกมาจากตัวแปรเดียวกันคือ แบบ Procedural mysqli// Last insert ID $mysqli_statement = mysqli_prepare( $mysqli_p, 'INSERT INTO `person` (`firtname`, `lastname`) VALUES ( ?, ? )' ); mysqli_stmt_bind_param( $mysqli_statement, 'ss', $firstname, $lastname ); $firstname = 'John'; $lastname = 'Doe'; mysqli_stmt_execute( $mysqli_statement ); $insert_id = mysqli_stmt_insert_id( $mysqli_statement ); // Afftected rows $mysqli_statement = mysqli_prepare( $mysqli_p, 'DELETE FROM `person` WHERE `state` = ?' ); mysqli_stmt_bind_param( $mysqli_statement, 's', $state ); $state = 'Bangkok'; mysqli_stmt_execute( $mysqli_statement ); $affected_rows = mysqli_stmt_affected_rows( $mysqli_statement ); เช่นเดียวกับการเขียนแบบ OOP ที่เราจะใช้ ตอนไหนควรใช้ query ตอนไหนควรใช้ prepareตอบแบบง่ายๆ เลยก็คือเราจะใช้ query โดยตรงก็ต่อเมื่อเป็นคิวรี่ที่เราเขียน statement เองทั้งหมดโดยไม่มีการรับอินพุทใดๆ เข้ามาทั้งสิ้น ส่วนถ้ามีการรับอินพุทใดๆ เข้ามาด้วยนั้น แนะนำอย่างยิ่งว่าควรใช้ Prepared statement จะดีกว่าเพื่อความปลอดภัย จริงๆ แล้วในการสั่งคิวรี่ตรงๆ โดยไม่ทำการ prepare นั้น เรายังมีฟังก์ชัน |